1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use chrono::prelude::*;
use intervals::NextTime;
use std::fmt;
use Interval;
use RunConfig;

/// A job to run on the scheduler.
/// Create these by calling [`Scheduler::every()`](::Scheduler::every).
pub struct Job<Tz = Local> where Tz: TimeZone {
    frequency: Vec<RunConfig>,
    next_run: Option<DateTime<Tz>>,
    last_run: Option<DateTime<Tz>>,
    job: Option<Box<FnMut() + Sync + Send>>,
    tz: Tz
}

impl<Tz> fmt::Debug for Job<Tz> where Tz: TimeZone {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Job {{ frequency: {:?}, next_run: {:?}, last_run: {:?}, job: ??? }}",
            self.frequency, self.next_run, self.last_run
        )
    }
}

impl<Tz> Job<Tz> where Tz: chrono::TimeZone {
    pub(crate) fn new(ival: Interval, tz: Tz) -> Self {
        Job {
            frequency: vec![RunConfig::from_interval(ival)],
            next_run: None,
            last_run: None,
            job: None,
            tz
        }
    }

    fn last_frequency(&mut self) -> &mut RunConfig {
        let last_idx = self.frequency.len() - 1;
        &mut self.frequency[last_idx]
    }

    /// Specify the time of day when a task should run, e.g.
    /// ```rust
    /// # extern crate clokwerk;
    /// # use clokwerk::*;
    /// # use clokwerk::Interval::*;
    /// let mut scheduler = Scheduler::new();
    /// scheduler.every(1.day()).at("14:32").run(|| println!("Tea time!"));
    /// scheduler.every(Wednesday).at("6:32:21 PM").run(|| println!("Writing examples is hard"));
    /// ```
    /// Times can be specified with or without seconds, and in either 24-hour or 12-hour time.
    /// Mutually exclusive with [`Job::plus()`].
    pub fn at(&mut self, s: &str) -> &mut Self {
        {
            let frequency = self.last_frequency();
            *frequency = frequency.with_time(s);
        }
        self
    }

    /// Add additional precision time to when a task should run, e.g.
    /// ```rust
    /// # extern crate clokwerk;
    /// # use clokwerk::*;
    /// # use clokwerk::Interval::*;
    /// let mut scheduler = Scheduler::new();
    /// scheduler.every(1.day())
    ///     .plus(6.hours())
    ///     .plus(13.minutes())
    ///   .run(|| println!("Time to wake up!"));
    /// ```
    /// Mutually exclusive with [`Job::at()`].
    pub fn plus(&mut self, ival: Interval) -> &mut Self {
        {
            let frequency = self.last_frequency();
            *frequency = frequency.with_subinterval(ival);
        }
        self
    }

    /// Add an additional scheduling to the task. All schedules will be considered when determining
    /// when the task should next run.
    pub fn and_every(&mut self, ival: Interval) -> &mut Self {
        self.frequency.push(RunConfig::from_interval(ival));
        self
    }

    /// Specify a task to run, and schedule its next run
    pub fn run<F>(&mut self, f: F) -> &mut Self
    where
        F: 'static + FnMut() + Sync + Send,
    {
        self.job = Some(Box::new(f));
        match self.next_run {
            Some(_) => (),
            None => {
                let now = Local::now().with_timezone(&self.tz);
                self.next_run = self.frequency.iter().map(|freq| freq.next(&now)).min();
            }
        };
        self
    }

    /// Test whether a job is scheduled to run again. This is usually only called by
    /// [Scheduler::run_pending()](::Scheduler::run_pending).
    pub fn is_pending(&self) -> bool {
        let now = Local::now().with_timezone(&self.tz);
        match &self.next_run {
            Some(dt) => *dt <= now,
            None => false,
        }
    }

    /// Run a task and re-schedule it. This is usually only called by
    /// [Scheduler::run_pending()](::Scheduler::run_pending).
    pub fn execute(&mut self) {
        let now = Local::now().with_timezone(&self.tz);
        if let Some(ref mut f) = self.job {
            f();
        }
        self.last_run = Some(now.clone());
        self.next_run = self.frequency.iter().map(|freq| freq.next(&now)).min();
    }
}