schedule_rs/
lib.rs

1//! [![ci-badge][]][ci] [![docs-badge][]][docs] [![crate-version]][crate-link]
2//!
3//! # schedule-rs
4//!
5//! A simple scheduling library inspired by Python's `schedule`.
6//!
7//! ## Sample usage
8//! ```rust,no_run
9//! use schedule_rs::prelude::*;
10//!
11//! let function = || {
12//!     println!("Hello World!");
13//! };
14//! let task = Schedule::every().minute().run(function);
15//! TaskRunner::one(task).run();
16//!
17//! std::thread::park();
18//! ```
19//!
20//! [ci]: https://github.com/Elinvynia/schedule-rs/actions?query=workflow%3ARust
21//! [ci-badge]: https://img.shields.io/github/workflow/status/Elinvynia/schedule-rs/Rust/master?style=flat-square
22//! [docs]: https://docs.rs/schedule-rs
23//! [docs-badge]: https://img.shields.io/badge/docs-online-5023dd.svg?style=flat-square
24//! [crate-link]: https://crates.io/crates/schedule-rs
25//! [crate-version]: https://img.shields.io/crates/v/schedule-rs.svg?style=flat-square
26
27#![forbid(unsafe_code)]
28#![warn(missing_debug_implementations)]
29#![warn(missing_docs)]
30
31use chrono::prelude::*;
32use chrono::Duration;
33use std::thread;
34
35///! Basic re-exports.
36pub mod prelude {
37    pub use crate::Schedule;
38    pub use crate::TaskRunner;
39}
40
41/// Base type for creating tasks.
42#[derive(Debug, Copy, Clone)]
43pub struct Schedule;
44
45impl Schedule {
46    /// Run a task every single interval of time.
47    pub fn every() -> ScheduleBuilderOne {
48        ScheduleBuilderOne
49    }
50
51    /// Run a task every X intervals of time.
52    pub fn every_num(amount: u32) -> ScheduleBuilderOnes {
53        ScheduleBuilderOnes { amount }
54    }
55}
56
57/// Builder for time values of 1 (minute, hour, etc).
58#[derive(Debug)]
59pub struct ScheduleBuilderOne;
60
61impl ScheduleBuilderOne {
62    /// Run a task every minute.
63    pub fn minute(self) -> ScheduleBuilderTwo {
64        ScheduleBuilderTwo {
65            interval: Interval::Minute(1),
66            limit: None,
67        }
68    }
69
70    /// Run a task every hour.
71    pub fn hour(self) -> ScheduleBuilderTwo {
72        ScheduleBuilderTwo {
73            interval: Interval::Hour(1),
74            limit: None,
75        }
76    }
77
78    /// Run a task every day.
79    pub fn day(self) -> ScheduleBuilderTwo {
80        ScheduleBuilderTwo {
81            interval: Interval::Day(1),
82            limit: None,
83        }
84    }
85
86    /// Run a task every week.
87    pub fn week(self) -> ScheduleBuilderTwo {
88        ScheduleBuilderTwo {
89            interval: Interval::Week(1),
90            limit: None,
91        }
92    }
93
94    /// Run a task every month.
95    pub fn month(self) -> ScheduleBuilderTwo {
96        ScheduleBuilderTwo {
97            interval: Interval::Month(1),
98            limit: None,
99        }
100    }
101
102    /// Run a task every year.
103    pub fn year(self) -> ScheduleBuilderTwo {
104        ScheduleBuilderTwo {
105            interval: Interval::Year(1),
106            limit: None,
107        }
108    }
109
110    /// Run a task on every Monday.
111    pub fn monday(self) -> ScheduleBuilderTwoDates {
112        ScheduleBuilderTwoDates {
113            interval: Interval::Week(1),
114            limit: None,
115            day: Weekday::Mon,
116            time: None,
117        }
118    }
119
120    /// Run a task on every Tuesday.
121    pub fn tuesday(self) -> ScheduleBuilderTwoDates {
122        ScheduleBuilderTwoDates {
123            interval: Interval::Week(1),
124            limit: None,
125            day: Weekday::Tue,
126            time: None,
127        }
128    }
129
130    /// Run a task on every Wednesday.
131    pub fn wednesday(self) -> ScheduleBuilderTwoDates {
132        ScheduleBuilderTwoDates {
133            interval: Interval::Week(1),
134            limit: None,
135            day: Weekday::Wed,
136            time: None,
137        }
138    }
139
140    /// Run a task on every Thursday.
141    pub fn thursday(self) -> ScheduleBuilderTwoDates {
142        ScheduleBuilderTwoDates {
143            interval: Interval::Week(1),
144            limit: None,
145            day: Weekday::Thu,
146            time: None,
147        }
148    }
149
150    /// Run a task on every Friday.
151    pub fn friday(self) -> ScheduleBuilderTwoDates {
152        ScheduleBuilderTwoDates {
153            interval: Interval::Week(1),
154            limit: None,
155            day: Weekday::Fri,
156            time: None,
157        }
158    }
159
160    /// Run a task on every Saturday.
161    pub fn saturday(self) -> ScheduleBuilderTwoDates {
162        ScheduleBuilderTwoDates {
163            interval: Interval::Week(1),
164            limit: None,
165            day: Weekday::Sat,
166            time: None,
167        }
168    }
169
170    /// Run a task on every Sunday.
171    pub fn sunday(self) -> ScheduleBuilderTwoDates {
172        ScheduleBuilderTwoDates {
173            interval: Interval::Week(1),
174            limit: None,
175            day: Weekday::Sun,
176            time: None,
177        }
178    }
179}
180
181/// Builder for time values of more than one (minutes, hours, etc).
182#[derive(Debug, Copy, Clone)]
183pub struct ScheduleBuilderOnes {
184    amount: u32,
185}
186
187impl ScheduleBuilderOnes {
188    /// Run a task every X minutes.
189    pub fn minutes(self) -> ScheduleBuilderTwo {
190        ScheduleBuilderTwo {
191            interval: Interval::Minute(self.amount),
192            limit: None,
193        }
194    }
195
196    /// Run a task every X hours.
197    pub fn hours(self) -> ScheduleBuilderTwo {
198        ScheduleBuilderTwo {
199            interval: Interval::Hour(self.amount),
200            limit: None,
201        }
202    }
203
204    /// Run a task every X days.
205    pub fn days(self) -> ScheduleBuilderTwo {
206        ScheduleBuilderTwo {
207            interval: Interval::Day(self.amount),
208            limit: None,
209        }
210    }
211
212    /// Run a task every X weeks.
213    pub fn weeks(self) -> ScheduleBuilderTwo {
214        ScheduleBuilderTwo {
215            interval: Interval::Week(self.amount),
216            limit: None,
217        }
218    }
219
220    /// Run a task every X months.
221    pub fn months(self) -> ScheduleBuilderTwo {
222        ScheduleBuilderTwo {
223            interval: Interval::Month(self.amount),
224            limit: None,
225        }
226    }
227
228    /// Run a task every X years.
229    pub fn years(self) -> ScheduleBuilderTwo {
230        ScheduleBuilderTwo {
231            interval: Interval::Year(self.amount),
232            limit: None,
233        }
234    }
235
236    /// Run a task every X Mondays.
237    pub fn monday(self) -> ScheduleBuilderTwoDates {
238        ScheduleBuilderTwoDates {
239            interval: Interval::Week(self.amount),
240            limit: None,
241            day: Weekday::Mon,
242            time: None,
243        }
244    }
245
246    /// Run a task every X Tuesdays.
247    pub fn tuesday(self) -> ScheduleBuilderTwoDates {
248        ScheduleBuilderTwoDates {
249            interval: Interval::Week(self.amount),
250            limit: None,
251            day: Weekday::Tue,
252            time: None,
253        }
254    }
255
256    /// Run a task every X Wednesdays.
257    pub fn wednesday(self) -> ScheduleBuilderTwoDates {
258        ScheduleBuilderTwoDates {
259            interval: Interval::Week(self.amount),
260            limit: None,
261            day: Weekday::Wed,
262            time: None,
263        }
264    }
265
266    /// Run a task every X Thursdays.
267    pub fn thursday(self) -> ScheduleBuilderTwoDates {
268        ScheduleBuilderTwoDates {
269            interval: Interval::Week(1),
270            limit: None,
271            day: Weekday::Thu,
272            time: None,
273        }
274    }
275
276    /// Run a task every X Fridays.
277    pub fn friday(self) -> ScheduleBuilderTwoDates {
278        ScheduleBuilderTwoDates {
279            interval: Interval::Week(1),
280            limit: None,
281            day: Weekday::Fri,
282            time: None,
283        }
284    }
285
286    /// Run a task every X Saturdays.
287    pub fn saturday(self) -> ScheduleBuilderTwoDates {
288        ScheduleBuilderTwoDates {
289            interval: Interval::Week(1),
290            limit: None,
291            day: Weekday::Sat,
292            time: None,
293        }
294    }
295
296    /// Run a task every X Sundays.
297    pub fn sunday(self) -> ScheduleBuilderTwoDates {
298        ScheduleBuilderTwoDates {
299            interval: Interval::Week(1),
300            limit: None,
301            day: Weekday::Sun,
302            time: None,
303        }
304    }
305}
306
307#[derive(Debug, Copy, Clone)]
308enum Interval {
309    Minute(u32),
310    Hour(u32),
311    Day(u32),
312    Week(u32),
313    Month(u32),
314    Year(u32),
315}
316
317/// Builder step that allows you to set a limit and provide the function.
318#[derive(Debug)]
319pub struct ScheduleBuilderTwo {
320    interval: Interval,
321    limit: Option<u64>,
322}
323
324impl ScheduleBuilderTwo {
325    /// Limits the amount of times the function will run, optional.
326    pub fn limit(mut self, amount: u64) {
327        self.limit = Some(amount)
328    }
329
330    /// Finishes building the task with a provided function.
331    pub fn run<F: Fn() + Send + 'static>(self, fun: F) -> Scheduled<F> {
332        Scheduled {
333            execution_time: next_interval(self.interval),
334            execution_interval: self.interval,
335            execution_limit: self.limit,
336            function: fun,
337        }
338    }
339}
340
341/// Builder step that allows you to set a limit and provide the function.
342/// Also allows to set the time of day.
343#[derive(Debug)]
344pub struct ScheduleBuilderTwoDates {
345    interval: Interval,
346    limit: Option<u64>,
347    day: Weekday,
348    time: Option<NaiveTime>,
349}
350
351impl ScheduleBuilderTwoDates {
352    /// Limits the amount of times the function will run, optional.
353    pub fn limit(mut self, amount: u64) {
354        self.limit = Some(amount)
355    }
356
357    /// Sets the time of day when the function will run, optional.
358    pub fn at(mut self, time: &str) {
359        let naive_time = NaiveTime::parse_from_str(time, "%H:%M").unwrap();
360        self.time = Some(naive_time)
361    }
362
363    /// Finishes building the task with a provided function.
364    pub fn run<F: Fn() + Send + 'static>(self, fun: F) -> Scheduled<F> {
365        Scheduled {
366            execution_time: first_day_exec(self.day, self.time),
367            execution_interval: self.interval,
368            execution_limit: self.limit,
369            function: fun,
370        }
371    }
372}
373
374fn first_day_exec(day: Weekday, time: Option<NaiveTime>) -> DateTime<Utc> {
375    let now = Utc::now();
376    let current_day = now.day();
377    let current_weekday = now.weekday().number_from_monday();
378    let to_add = (current_weekday + day.number_from_monday()) % 7;
379    let with_day = Utc::now().with_day(current_day + to_add).unwrap();
380    match time {
381        Some(t) => with_day
382            .with_hour(t.hour())
383            .unwrap()
384            .with_minute(t.minute())
385            .unwrap()
386            .with_second(t.second())
387            .unwrap(),
388        None => with_day,
389    }
390}
391
392fn next_interval(i: Interval) -> DateTime<Utc> {
393    match i {
394        Interval::Minute(a) => Utc::now() + Duration::minutes(a as i64),
395        Interval::Hour(a) => Utc::now() + Duration::hours(a as i64),
396        Interval::Day(a) => Utc::now() + Duration::days(a as i64),
397        Interval::Week(a) => Utc::now() + Duration::weeks(a as i64),
398        Interval::Month(a) => {
399            let time = Utc::now();
400            let month = time.month();
401            time.with_month(month + a).unwrap()
402        }
403        Interval::Year(a) => {
404            let time = Utc::now();
405            let year = time.year();
406            time.with_year(year + a as i32).unwrap()
407        }
408    }
409}
410
411/// A built task ready to be run by a runner.
412#[derive(Debug)]
413pub struct Scheduled<F: Fn() + Send + 'static> {
414    execution_time: DateTime<Utc>,
415    execution_interval: Interval,
416    execution_limit: Option<u64>,
417    function: F,
418}
419
420/// Runner that runs built tasks.
421#[derive(Debug)]
422pub struct TaskRunner<F: Fn() + Send + 'static> {
423    tasks: Vec<Scheduled<F>>,
424}
425
426impl<F: Fn() + Send + 'static> TaskRunner<F> {
427    /// Create a new runner for running multiple tasks.
428    pub fn new(tasks: Vec<Scheduled<F>>) -> TaskRunner<F> {
429        TaskRunner { tasks }
430    }
431
432    /// Create a new runner for running a single task.
433    pub fn one(task: Scheduled<F>) -> TaskRunner<F> {
434        TaskRunner { tasks: vec![task] }
435    }
436
437    /// Start the runner.
438    pub fn run(mut self) {
439        thread::spawn(move || loop {
440            let current_time = Utc::now();
441            for task in self.tasks.iter_mut() {
442                if current_time < task.execution_time {
443                    continue;
444                };
445                (task.function)();
446                if let Some(x) = task.execution_limit {
447                    task.execution_limit = Some(x - 1);
448                };
449                task.execution_time = next_interval(task.execution_interval);
450            }
451            self.tasks = self
452                .tasks
453                .into_iter()
454                .filter(|x| match x.execution_limit {
455                    Some(x) => x < 1,
456                    None => false,
457                })
458                .collect();
459            thread::sleep(Duration::seconds(5).to_std().unwrap())
460        });
461    }
462}