kit_rs/schedule/
builder.rs1use super::expression::{CronExpression, DayOfWeek};
6use super::task::{BoxedFuture, BoxedTask, ClosureTask, Task, TaskEntry, TaskResult};
7use std::sync::Arc;
8
9pub struct TaskBuilder {
27 pub(crate) task: BoxedTask,
28 pub(crate) expression: CronExpression,
29 pub(crate) name: Option<String>,
30 pub(crate) description: Option<String>,
31 pub(crate) without_overlapping: bool,
32 pub(crate) run_in_background: bool,
33}
34
35impl TaskBuilder {
36 pub fn new<F>(f: F) -> Self
40 where
41 F: Fn() -> BoxedFuture<'static> + Send + Sync + 'static,
42 {
43 Self {
44 task: Arc::new(ClosureTask { handler: f }),
45 expression: CronExpression::every_minute(),
46 name: None,
47 description: None,
48 without_overlapping: false,
49 run_in_background: false,
50 }
51 }
52
53 pub fn from_async<F, Fut>(f: F) -> Self
57 where
58 F: Fn() -> Fut + Send + Sync + 'static,
59 Fut: std::future::Future<Output = TaskResult> + Send + 'static,
60 {
61 Self::new(move || Box::pin(f()))
62 }
63
64 pub fn from_task<T: Task + 'static>(task: T) -> Self {
78 Self {
79 task: Arc::new(task),
80 expression: CronExpression::every_minute(),
81 name: None,
82 description: None,
83 without_overlapping: false,
84 run_in_background: false,
85 }
86 }
87
88 pub fn cron(mut self, expression: &str) -> Self {
102 self.expression =
103 CronExpression::parse(expression).expect("Invalid cron expression");
104 self
105 }
106
107 pub fn try_cron(mut self, expression: &str) -> Result<Self, String> {
109 self.expression = CronExpression::parse(expression)?;
110 Ok(self)
111 }
112
113 pub fn every_minute(mut self) -> Self {
115 self.expression = CronExpression::every_minute();
116 self
117 }
118
119 pub fn every_two_minutes(mut self) -> Self {
121 self.expression = CronExpression::every_n_minutes(2);
122 self
123 }
124
125 pub fn every_five_minutes(mut self) -> Self {
127 self.expression = CronExpression::every_n_minutes(5);
128 self
129 }
130
131 pub fn every_ten_minutes(mut self) -> Self {
133 self.expression = CronExpression::every_n_minutes(10);
134 self
135 }
136
137 pub fn every_fifteen_minutes(mut self) -> Self {
139 self.expression = CronExpression::every_n_minutes(15);
140 self
141 }
142
143 pub fn every_thirty_minutes(mut self) -> Self {
145 self.expression = CronExpression::every_n_minutes(30);
146 self
147 }
148
149 pub fn hourly(mut self) -> Self {
151 self.expression = CronExpression::hourly();
152 self
153 }
154
155 pub fn hourly_at(mut self, minute: u32) -> Self {
162 self.expression = CronExpression::hourly_at(minute);
163 self
164 }
165
166 pub fn every_two_hours(mut self) -> Self {
168 self.expression = CronExpression::parse("0 */2 * * *").unwrap();
169 self
170 }
171
172 pub fn every_three_hours(mut self) -> Self {
174 self.expression = CronExpression::parse("0 */3 * * *").unwrap();
175 self
176 }
177
178 pub fn every_four_hours(mut self) -> Self {
180 self.expression = CronExpression::parse("0 */4 * * *").unwrap();
181 self
182 }
183
184 pub fn every_six_hours(mut self) -> Self {
186 self.expression = CronExpression::parse("0 */6 * * *").unwrap();
187 self
188 }
189
190 pub fn daily(mut self) -> Self {
192 self.expression = CronExpression::daily();
193 self
194 }
195
196 pub fn daily_at(mut self, time: &str) -> Self {
203 self.expression = CronExpression::daily_at(time);
204 self
205 }
206
207 pub fn twice_daily(mut self, first_hour: u32, second_hour: u32) -> Self {
214 self.expression =
215 CronExpression::parse(&format!("0 {},{} * * *", first_hour, second_hour)).unwrap();
216 self
217 }
218
219 pub fn at(mut self, time: &str) -> Self {
229 self.expression = self.expression.at(time);
230 self
231 }
232
233 pub fn weekly(mut self) -> Self {
235 self.expression = CronExpression::weekly();
236 self
237 }
238
239 pub fn weekly_on(mut self, day: DayOfWeek) -> Self {
246 self.expression = CronExpression::weekly_on(day);
247 self
248 }
249
250 pub fn days(mut self, days: &[DayOfWeek]) -> Self {
257 self.expression = CronExpression::on_days(days);
258 self
259 }
260
261 pub fn weekdays(mut self) -> Self {
263 self.expression = CronExpression::weekdays();
264 self
265 }
266
267 pub fn weekends(mut self) -> Self {
269 self.expression = CronExpression::weekends();
270 self
271 }
272
273 pub fn sundays(mut self) -> Self {
275 self.expression = CronExpression::weekly_on(DayOfWeek::Sunday);
276 self
277 }
278
279 pub fn mondays(mut self) -> Self {
281 self.expression = CronExpression::weekly_on(DayOfWeek::Monday);
282 self
283 }
284
285 pub fn tuesdays(mut self) -> Self {
287 self.expression = CronExpression::weekly_on(DayOfWeek::Tuesday);
288 self
289 }
290
291 pub fn wednesdays(mut self) -> Self {
293 self.expression = CronExpression::weekly_on(DayOfWeek::Wednesday);
294 self
295 }
296
297 pub fn thursdays(mut self) -> Self {
299 self.expression = CronExpression::weekly_on(DayOfWeek::Thursday);
300 self
301 }
302
303 pub fn fridays(mut self) -> Self {
305 self.expression = CronExpression::weekly_on(DayOfWeek::Friday);
306 self
307 }
308
309 pub fn saturdays(mut self) -> Self {
311 self.expression = CronExpression::weekly_on(DayOfWeek::Saturday);
312 self
313 }
314
315 pub fn monthly(mut self) -> Self {
317 self.expression = CronExpression::monthly();
318 self
319 }
320
321 pub fn monthly_on(mut self, day: u32) -> Self {
328 self.expression = CronExpression::monthly_on(day);
329 self
330 }
331
332 pub fn quarterly(mut self) -> Self {
334 self.expression = CronExpression::quarterly();
335 self
336 }
337
338 pub fn yearly(mut self) -> Self {
340 self.expression = CronExpression::yearly();
341 self
342 }
343
344 pub fn name(mut self, name: &str) -> Self {
352 self.name = Some(name.to_string());
353 self
354 }
355
356 pub fn description(mut self, desc: &str) -> Self {
360 self.description = Some(desc.to_string());
361 self
362 }
363
364 pub fn without_overlapping(mut self) -> Self {
369 self.without_overlapping = true;
370 self
371 }
372
373 pub fn run_in_background(mut self) -> Self {
378 self.run_in_background = true;
379 self
380 }
381
382 pub(crate) fn build(self, task_index: usize) -> TaskEntry {
386 let name = self
387 .name
388 .unwrap_or_else(|| format!("closure-task-{}", task_index));
389
390 TaskEntry {
391 name,
392 expression: self.expression,
393 task: self.task,
394 description: self.description,
395 without_overlapping: self.without_overlapping,
396 run_in_background: self.run_in_background,
397 }
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404
405 fn create_test_builder() -> TaskBuilder {
406 TaskBuilder::from_async(|| async { Ok(()) })
407 }
408
409 #[test]
410 fn test_builder_schedule_methods() {
411 let builder = create_test_builder().every_minute();
412 assert_eq!(builder.expression.expression(), "* * * * *");
413
414 let builder = create_test_builder().hourly();
415 assert_eq!(builder.expression.expression(), "0 * * * *");
416
417 let builder = create_test_builder().daily();
418 assert_eq!(builder.expression.expression(), "0 0 * * *");
419
420 let builder = create_test_builder().weekly();
421 assert_eq!(builder.expression.expression(), "0 0 * * 0");
422 }
423
424 #[test]
425 fn test_builder_daily_at() {
426 let builder = create_test_builder().daily_at("14:30");
427 assert_eq!(builder.expression.expression(), "30 14 * * *");
428 }
429
430 #[test]
431 fn test_builder_at_modifier() {
432 let builder = create_test_builder().daily().at("09:15");
433 assert_eq!(builder.expression.expression(), "15 9 * * *");
434 }
435
436 #[test]
437 fn test_builder_configuration() {
438 let builder = create_test_builder()
439 .name("test-task")
440 .description("A test task")
441 .without_overlapping()
442 .run_in_background();
443
444 assert_eq!(builder.name, Some("test-task".to_string()));
445 assert_eq!(builder.description, Some("A test task".to_string()));
446 assert!(builder.without_overlapping);
447 assert!(builder.run_in_background);
448 }
449
450 #[test]
451 fn test_builder_build() {
452 let builder = create_test_builder()
453 .daily()
454 .name("my-task")
455 .description("My task description");
456
457 let entry = builder.build(0);
458
459 assert_eq!(entry.name, "my-task");
460 assert_eq!(entry.description, Some("My task description".to_string()));
461 }
462
463 #[test]
464 fn test_builder_default_name() {
465 let builder = create_test_builder().daily();
466 let entry = builder.build(5);
467
468 assert_eq!(entry.name, "closure-task-5");
469 }
470}