ferro_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 = CronExpression::parse(expression).expect("Invalid cron expression");
103 self
104 }
105
106 pub fn try_cron(mut self, expression: &str) -> Result<Self, String> {
108 self.expression = CronExpression::parse(expression)?;
109 Ok(self)
110 }
111
112 pub fn every_minute(mut self) -> Self {
114 self.expression = CronExpression::every_minute();
115 self
116 }
117
118 pub fn every_two_minutes(mut self) -> Self {
120 self.expression = CronExpression::every_n_minutes(2);
121 self
122 }
123
124 pub fn every_five_minutes(mut self) -> Self {
126 self.expression = CronExpression::every_n_minutes(5);
127 self
128 }
129
130 pub fn every_ten_minutes(mut self) -> Self {
132 self.expression = CronExpression::every_n_minutes(10);
133 self
134 }
135
136 pub fn every_fifteen_minutes(mut self) -> Self {
138 self.expression = CronExpression::every_n_minutes(15);
139 self
140 }
141
142 pub fn every_thirty_minutes(mut self) -> Self {
144 self.expression = CronExpression::every_n_minutes(30);
145 self
146 }
147
148 pub fn hourly(mut self) -> Self {
150 self.expression = CronExpression::hourly();
151 self
152 }
153
154 pub fn hourly_at(mut self, minute: u32) -> Self {
161 self.expression = CronExpression::hourly_at(minute);
162 self
163 }
164
165 pub fn every_two_hours(mut self) -> Self {
167 self.expression = CronExpression::parse("0 */2 * * *").unwrap();
168 self
169 }
170
171 pub fn every_three_hours(mut self) -> Self {
173 self.expression = CronExpression::parse("0 */3 * * *").unwrap();
174 self
175 }
176
177 pub fn every_four_hours(mut self) -> Self {
179 self.expression = CronExpression::parse("0 */4 * * *").unwrap();
180 self
181 }
182
183 pub fn every_six_hours(mut self) -> Self {
185 self.expression = CronExpression::parse("0 */6 * * *").unwrap();
186 self
187 }
188
189 pub fn daily(mut self) -> Self {
191 self.expression = CronExpression::daily();
192 self
193 }
194
195 pub fn daily_at(mut self, time: &str) -> Self {
202 self.expression = CronExpression::daily_at(time);
203 self
204 }
205
206 pub fn twice_daily(mut self, first_hour: u32, second_hour: u32) -> Self {
213 self.expression =
214 CronExpression::parse(&format!("0 {first_hour},{second_hour} * * *")).unwrap();
215 self
216 }
217
218 pub fn at(mut self, time: &str) -> Self {
228 self.expression = self.expression.at(time);
229 self
230 }
231
232 pub fn weekly(mut self) -> Self {
234 self.expression = CronExpression::weekly();
235 self
236 }
237
238 pub fn weekly_on(mut self, day: DayOfWeek) -> Self {
245 self.expression = CronExpression::weekly_on(day);
246 self
247 }
248
249 pub fn days(mut self, days: &[DayOfWeek]) -> Self {
256 self.expression = CronExpression::on_days(days);
257 self
258 }
259
260 pub fn weekdays(mut self) -> Self {
262 self.expression = CronExpression::weekdays();
263 self
264 }
265
266 pub fn weekends(mut self) -> Self {
268 self.expression = CronExpression::weekends();
269 self
270 }
271
272 pub fn sundays(mut self) -> Self {
274 self.expression = CronExpression::weekly_on(DayOfWeek::Sunday);
275 self
276 }
277
278 pub fn mondays(mut self) -> Self {
280 self.expression = CronExpression::weekly_on(DayOfWeek::Monday);
281 self
282 }
283
284 pub fn tuesdays(mut self) -> Self {
286 self.expression = CronExpression::weekly_on(DayOfWeek::Tuesday);
287 self
288 }
289
290 pub fn wednesdays(mut self) -> Self {
292 self.expression = CronExpression::weekly_on(DayOfWeek::Wednesday);
293 self
294 }
295
296 pub fn thursdays(mut self) -> Self {
298 self.expression = CronExpression::weekly_on(DayOfWeek::Thursday);
299 self
300 }
301
302 pub fn fridays(mut self) -> Self {
304 self.expression = CronExpression::weekly_on(DayOfWeek::Friday);
305 self
306 }
307
308 pub fn saturdays(mut self) -> Self {
310 self.expression = CronExpression::weekly_on(DayOfWeek::Saturday);
311 self
312 }
313
314 pub fn monthly(mut self) -> Self {
316 self.expression = CronExpression::monthly();
317 self
318 }
319
320 pub fn monthly_on(mut self, day: u32) -> Self {
327 self.expression = CronExpression::monthly_on(day);
328 self
329 }
330
331 pub fn quarterly(mut self) -> Self {
333 self.expression = CronExpression::quarterly();
334 self
335 }
336
337 pub fn yearly(mut self) -> Self {
339 self.expression = CronExpression::yearly();
340 self
341 }
342
343 pub fn name(mut self, name: &str) -> Self {
351 self.name = Some(name.to_string());
352 self
353 }
354
355 pub fn description(mut self, desc: &str) -> Self {
359 self.description = Some(desc.to_string());
360 self
361 }
362
363 pub fn without_overlapping(mut self) -> Self {
368 self.without_overlapping = true;
369 self
370 }
371
372 pub fn run_in_background(mut self) -> Self {
377 self.run_in_background = true;
378 self
379 }
380
381 pub(crate) fn build(self, task_index: usize) -> TaskEntry {
385 let name = self
386 .name
387 .unwrap_or_else(|| format!("closure-task-{task_index}"));
388
389 TaskEntry {
390 name,
391 expression: self.expression,
392 task: self.task,
393 description: self.description,
394 without_overlapping: self.without_overlapping,
395 run_in_background: self.run_in_background,
396 }
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403
404 fn create_test_builder() -> TaskBuilder {
405 TaskBuilder::from_async(|| async { Ok(()) })
406 }
407
408 #[test]
409 fn test_builder_schedule_methods() {
410 let builder = create_test_builder().every_minute();
411 assert_eq!(builder.expression.expression(), "* * * * *");
412
413 let builder = create_test_builder().hourly();
414 assert_eq!(builder.expression.expression(), "0 * * * *");
415
416 let builder = create_test_builder().daily();
417 assert_eq!(builder.expression.expression(), "0 0 * * *");
418
419 let builder = create_test_builder().weekly();
420 assert_eq!(builder.expression.expression(), "0 0 * * 0");
421 }
422
423 #[test]
424 fn test_builder_daily_at() {
425 let builder = create_test_builder().daily_at("14:30");
426 assert_eq!(builder.expression.expression(), "30 14 * * *");
427 }
428
429 #[test]
430 fn test_builder_at_modifier() {
431 let builder = create_test_builder().daily().at("09:15");
432 assert_eq!(builder.expression.expression(), "15 9 * * *");
433 }
434
435 #[test]
436 fn test_builder_configuration() {
437 let builder = create_test_builder()
438 .name("test-task")
439 .description("A test task")
440 .without_overlapping()
441 .run_in_background();
442
443 assert_eq!(builder.name, Some("test-task".to_string()));
444 assert_eq!(builder.description, Some("A test task".to_string()));
445 assert!(builder.without_overlapping);
446 assert!(builder.run_in_background);
447 }
448
449 #[test]
450 fn test_builder_build() {
451 let builder = create_test_builder()
452 .daily()
453 .name("my-task")
454 .description("My task description");
455
456 let entry = builder.build(0);
457
458 assert_eq!(entry.name, "my-task");
459 assert_eq!(entry.description, Some("My task description".to_string()));
460 }
461
462 #[test]
463 fn test_builder_default_name() {
464 let builder = create_test_builder().daily();
465 let entry = builder.build(5);
466
467 assert_eq!(entry.name, "closure-task-5");
468 }
469}