Skip to content

Commit 1455ef3

Browse files
author
Leonidas Loucas
committed
feat: Handle jenkins style crons
1 parent eb586c0 commit 1455ef3

File tree

2 files changed

+117
-38
lines changed

2 files changed

+117
-38
lines changed

src/description_builder.rs

Lines changed: 96 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,42 @@ pub trait DescriptionBuilder<'a> {
2222
all_description
2323
} else if string_utils::not_contains_any(expression, &SPECIAL_CHARACTERS_MINUS_STAR) {
2424
let gdf = self.get_description_format(expression);
25-
let sid = self.get_single_item_description(expression);
25+
let sid = self.get_single_item_description(expression, None);
2626
let mut vars = HashMap::new();
2727
vars.insert("0".to_string(), sid);
2828
strfmt(&gdf, &vars).unwrap()
2929
} else if expression.contains("/") {
3030
let segments = expression.split("/").collect::<Vec<_>>();
3131
let gidf = self.get_interval_description_format(&segments[1].to_string());
32-
let gsid = self.get_single_item_description(&segments[1].to_string());
32+
let gsid = self.get_single_item_description(&segments[1].to_string(), None);
3333
let mut vars = HashMap::new();
3434
vars.insert("0".to_string(), gsid);
3535
let tmpstr = strfmt(&gidf, &vars).unwrap();
3636
if segments[0].contains("-") {
3737
let between_segments_of_interval = segments[0].to_string();
3838
let between_segments = between_segments_of_interval.split("-").collect::<Vec<_>>();
3939
let gbdf = self.get_between_description_format(false);
40-
let sid0 = self.get_single_item_description(&between_segments[0].to_string());
41-
let sid1 = self.get_single_item_description(&between_segments[1].to_string());
40+
let sid0 = self.get_single_item_description(&between_segments[0].to_string(), Some(true));
41+
let sid1 = self.get_single_item_description(&between_segments[1].to_string(), Some(false));
4242
let mut vars = HashMap::new();
4343
vars.insert("0".to_string(), sid0);
4444
vars.insert("1".to_string(), sid1);
4545
format!("{}, {}", tmpstr, strfmt(&gbdf, &vars).unwrap())
4646
} else {
47-
// println!("gidf: {}, gsid: {}", gidf, gsid2);
48-
tmpstr
47+
let between_segments_of_interval = segments[0].to_string();
48+
// Un normalize
49+
let between_segments_of_interval = if between_segments_of_interval == "*" {
50+
self.get_min_bound()
51+
} else {
52+
between_segments_of_interval
53+
};
54+
let gbdf = self.get_between_description_format(false);
55+
let sid0 = self.get_single_item_description(&between_segments_of_interval.to_string(), Some(true));
56+
let sid1 = self.get_single_item_description(&self.get_max_bound(), Some(false));
57+
let mut vars = HashMap::new();
58+
vars.insert("0".to_string(), sid0);
59+
vars.insert("1".to_string(), sid1);
60+
format!("{}, {}", tmpstr, strfmt(&gbdf, &vars).unwrap())
4961
}
5062
} else if expression.contains(",") {
5163
let segments = expression.split(",").collect::<Vec<_>>();
@@ -68,14 +80,14 @@ pub trait DescriptionBuilder<'a> {
6880
if segments[i].contains("-") {
6981
let between_segments = segments[i].split("-").collect::<Vec<_>>();
7082
let gbdf = self.get_between_description_format(true);
71-
let sid0 = self.get_single_item_description(&between_segments[0].to_string());
72-
let sid1 = self.get_single_item_description(&between_segments[1].to_string());
83+
let sid0 = self.get_single_item_description(&between_segments[0].to_string(), Some(true));
84+
let sid1 = self.get_single_item_description(&between_segments[1].to_string(), Some(false));
7385
let mut vars = HashMap::new();
7486
vars.insert("0".to_string(), sid0);
7587
vars.insert("1".to_string(), sid1);
7688
description_content.append(strfmt(&gbdf, &vars).unwrap());
7789
} else {
78-
description_content.append(self.get_single_item_description(&segments[i].to_string()));
90+
description_content.append(self.get_single_item_description(&segments[i].to_string(), None));
7991
}
8092
}
8193
let mut vars = HashMap::new();
@@ -85,8 +97,8 @@ pub trait DescriptionBuilder<'a> {
8597
// println!("in get_segment_description, expression:{}, {}:{}", expression, file!(), line!());
8698
let segments = expression.split("-").collect::<Vec<_>>();
8799
let gbdf = self.get_between_description_format(false);
88-
let sid0 = self.get_single_item_description(&segments[0].to_string());
89-
let sid1 = self.get_single_item_description(&segments[1].to_string());
100+
let sid0 = self.get_single_item_description(&segments[0].to_string(), Some(true));
101+
let sid1 = self.get_single_item_description(&segments[1].to_string(), Some(false));
90102
let mut vars = HashMap::new();
91103
vars.insert("0".to_string(), sid0);
92104
vars.insert("1".to_string(), sid1);
@@ -100,9 +112,11 @@ pub trait DescriptionBuilder<'a> {
100112

101113
fn get_between_description_format(&self, omit_separator: bool) -> String;
102114
fn get_interval_description_format(&self, expression: &String) -> String;
103-
fn get_single_item_description(&self, expression: &String) -> String;
115+
fn get_single_item_description(&self, expression: &String, range_start: Option<bool>) -> String;
104116
fn get_description_format(&self, expression: &String) -> String;
105117
fn need_space_between_words(&self) -> bool;
118+
fn get_min_bound(&self) -> String;
119+
fn get_max_bound(&self) -> String;
106120

107121
fn get_space_opt(options: &Options) -> String {
108122
if options.need_space_between_words {
@@ -178,10 +192,10 @@ impl DescriptionBuilder<'_> for DayOfMonthDescriptionBuilder<'_> {
178192
}
179193

180194
fn get_interval_description_format(self: &Self, expression: &String) -> String {
181-
", ".to_string() + &t!("every_x") + &self.get_space() + &Self::plural(expression, &t!("day"), &t!("days"))
195+
", ".to_string() + &t!("messages.every_x") + &self.get_space() + &Self::plural(expression, &t!("day"), &t!("days"))
182196
}
183197

184-
fn get_single_item_description(&self, expression: &String) -> String { expression.to_string() }
198+
fn get_single_item_description(&self, expression: &String, range_start: Option<bool>) -> String { expression.to_string() }
185199

186200
fn get_description_format(&self, _: &String) -> String {
187201
", ".to_string() + &t!("messages.on_day_of_month")
@@ -194,6 +208,14 @@ impl DescriptionBuilder<'_> for DayOfMonthDescriptionBuilder<'_> {
194208
fn get_space(self: &Self) -> String {
195209
Self::get_space_opt(&self.options)
196210
}
211+
212+
fn get_min_bound(&self) -> String {
213+
"1".to_string()
214+
}
215+
216+
fn get_max_bound(&self) -> String {
217+
"31".to_string()
218+
}
197219
}
198220

199221
impl DescriptionBuilder<'_> for DayOfWeekDescriptionBuilder<'_> {
@@ -211,7 +233,7 @@ impl DescriptionBuilder<'_> for DayOfWeekDescriptionBuilder<'_> {
211233
String::from(", ") + &t!("messages.interval_description_format", 0 = expression)
212234
}
213235

214-
fn get_single_item_description(&self, expression: &String) -> String {
236+
fn get_single_item_description(&self, expression: &String, range_start: Option<bool>) -> String {
215237
let exp = match expression.find("#") {
216238
Some(ind) =>
217239
expression.substring(0, ind).to_string(),
@@ -273,6 +295,14 @@ impl DescriptionBuilder<'_> for DayOfWeekDescriptionBuilder<'_> {
273295
fn get_space(self: &Self) -> String {
274296
Self::get_space_opt(&self.options)
275297
}
298+
299+
fn get_min_bound(&self) -> String {
300+
"0".to_string()
301+
}
302+
303+
fn get_max_bound(&self) -> String {
304+
"7".to_string()
305+
}
276306
}
277307

278308
impl DescriptionBuilder<'_> for HoursDescriptionBuilder<'_> {
@@ -290,8 +320,12 @@ impl DescriptionBuilder<'_> for HoursDescriptionBuilder<'_> {
290320
strfmt(&gdf, &vars).unwrap()
291321
}
292322

293-
fn get_single_item_description(&self, expression: &String) -> String {
294-
format_time(expression, &String::from("0"), &self.options)
323+
fn get_single_item_description(&self, expression: &String, range_start: Option<bool>) -> String {
324+
let minutes_expr = match range_start {
325+
Some(false) => "59",
326+
_ => "0"
327+
};
328+
format_time(expression, &String::from(minutes_expr), &self.options)
295329
}
296330

297331
fn get_description_format(&self, _: &String) -> String {
@@ -305,6 +339,14 @@ impl DescriptionBuilder<'_> for HoursDescriptionBuilder<'_> {
305339
fn get_space(&self) -> String {
306340
Self::get_space_opt(&self.options)
307341
}
342+
343+
fn get_min_bound(&self) -> String {
344+
"0".to_string()
345+
}
346+
347+
fn get_max_bound(&self) -> String {
348+
"23".to_string()
349+
}
308350
}
309351

310352
impl DescriptionBuilder<'_> for MinutesDescriptionBuilder<'_> {
@@ -320,7 +362,7 @@ impl DescriptionBuilder<'_> for MinutesDescriptionBuilder<'_> {
320362
strfmt(&gdf, &vars).unwrap()
321363
}
322364

323-
fn get_single_item_description(&self, expression: &String) -> String {
365+
fn get_single_item_description(&self, expression: &String, range_start: Option<bool>) -> String {
324366
format_minutes(expression)
325367
}
326368

@@ -340,6 +382,14 @@ impl DescriptionBuilder<'_> for MinutesDescriptionBuilder<'_> {
340382
fn get_space(&self) -> String {
341383
Self::get_space_opt(&self.options)
342384
}
385+
386+
fn get_min_bound(&self) -> String {
387+
"0".to_string()
388+
}
389+
390+
fn get_max_bound(&self) -> String {
391+
"59".to_string()
392+
}
343393
}
344394

345395
impl DescriptionBuilder<'_> for MonthDescriptionBuilder<'_> {
@@ -363,7 +413,7 @@ impl DescriptionBuilder<'_> for MonthDescriptionBuilder<'_> {
363413
strfmt(&gdf, &vars).unwrap()
364414
}
365415

366-
fn get_single_item_description(&self, expression: &String) -> String {
416+
fn get_single_item_description(&self, expression: &String, range_start: Option<bool>) -> String {
367417
let month_num = expression.parse::<usize>().unwrap();
368418
let month_key = MONTHS_ARR[month_num - 1];
369419
t!(month_key)
@@ -380,6 +430,14 @@ impl DescriptionBuilder<'_> for MonthDescriptionBuilder<'_> {
380430
fn get_space(&self) -> String {
381431
Self::get_space_opt(&self.options)
382432
}
433+
434+
fn get_min_bound(&self) -> String {
435+
"1".to_string()
436+
}
437+
438+
fn get_max_bound(&self) -> String {
439+
"12".to_string()
440+
}
383441
}
384442

385443
impl DescriptionBuilder<'_> for SecondsDescriptionBuilder<'_> {
@@ -391,7 +449,8 @@ impl DescriptionBuilder<'_> for SecondsDescriptionBuilder<'_> {
391449
t!("messages.every_x_seconds")
392450
}
393451

394-
fn get_single_item_description(&self, expression: &String) -> String {
452+
// TODO : add option here to set hours minute expression to 59
453+
fn get_single_item_description(&self, expression: &String, range_start: Option<bool>) -> String {
395454
expression.to_string()
396455
}
397456

@@ -406,6 +465,14 @@ impl DescriptionBuilder<'_> for SecondsDescriptionBuilder<'_> {
406465
fn get_space(&self) -> String {
407466
Self::get_space_opt(&self.options)
408467
}
468+
469+
fn get_min_bound(&self) -> String {
470+
"0".to_string()
471+
}
472+
473+
fn get_max_bound(&self) -> String {
474+
"59".to_string()
475+
}
409476
}
410477

411478
impl DescriptionBuilder<'_> for YearDescriptionBuilder<'_> {
@@ -428,7 +495,7 @@ impl DescriptionBuilder<'_> for YearDescriptionBuilder<'_> {
428495
strfmt(&gdf, &vars).unwrap()
429496
}
430497

431-
fn get_single_item_description(&self, expression: &String) -> String {
498+
fn get_single_item_description(&self, expression: &String, range_start: Option<bool>) -> String {
432499
// return new DateTime().withYear(Integer.parseInt(expression)).toString("yyyy", I18nMessages.getCurrentLocale());
433500
expression.parse::<u16>().unwrap().to_string()
434501
}
@@ -444,4 +511,12 @@ impl DescriptionBuilder<'_> for YearDescriptionBuilder<'_> {
444511
fn get_space(&self) -> String {
445512
Self::get_space_opt(&self.options)
446513
}
514+
515+
fn get_min_bound(&self) -> String {
516+
"1970".to_string()
517+
}
518+
519+
fn get_max_bound(&self) -> String {
520+
"2099".to_string()
521+
}
447522
}

src/lib.rs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub mod cronparser {
148148
pub zero_based_day_of_week: bool,
149149
pub twenty_four_hour_time: bool,
150150
pub need_space_between_words: bool,
151+
pub normalize_cron_intervals: bool,
151152
}
152153

153154
impl Options {
@@ -159,6 +160,7 @@ pub mod cronparser {
159160
zero_based_day_of_week: true,
160161
twenty_four_hour_time: false,
161162
need_space_between_words: true,
163+
normalize_cron_intervals: true,
162164
};
163165
}
164166

@@ -280,25 +282,27 @@ pub mod cronparser {
280282
normalised[3] = normalised[3].replace("?", "*");
281283
normalised[5] = normalised[5].replace("?", "*");
282284

283-
(0..=2).for_each(|i| {
284-
normalised[i] = if normalised[i].starts_with("0/") {
285-
normalised[i].replace("0/", "*/")
286-
} else {
287-
normalised[i].to_string()
288-
}
289-
});
285+
if options.normalize_cron_intervals {
286+
(0..=2).for_each(|i| {
287+
normalised[i] = if normalised[i].starts_with("0/") {
288+
normalised[i].replace("0/", "*/")
289+
} else {
290+
normalised[i].to_string()
291+
}
292+
});
290293

291-
(3..=5).for_each(|i| {
292-
normalised[i] = if normalised[i].starts_with("1/") {
293-
normalised[i].replace("1/", "*/")
294-
} else {
295-
normalised[i].to_string()
296-
}
297-
});
294+
(3..=5).for_each(|i| {
295+
normalised[i] = if normalised[i].starts_with("1/") {
296+
normalised[i].replace("1/", "*/")
297+
} else {
298+
normalised[i].to_string()
299+
}
300+
});
298301

299-
for i in 0..normalised.len() {
300-
if normalised[i] == "*/1" {
301-
normalised[i] = "*".to_string();
302+
for i in 0..normalised.len() {
303+
if normalised[i] == "*/1" {
304+
normalised[i] = "*".to_string();
305+
}
302306
}
303307
}
304308
// println!("normalised after replacing */1: {:?}", normalised);

0 commit comments

Comments
 (0)