rust_crontab/
cronjob.rs

1use serde::{Serialize, Deserialize};
2
3#[derive(Serialize, Deserialize, Debug, Clone)]
4pub struct CronJob {
5    pub line: Option<String>,
6    pub cron: String,
7    pub command: String,
8    pub comment: Option<String>,
9    pub _disabled: bool,
10}
11impl CronJob {
12    pub fn new(line: String) -> Self {
13        let mut cron_job = CronJob {
14            line: Some(line),
15            cron: String::new(),
16            command: String::new(),
17            comment: None,
18            _disabled: false,
19        };
20
21        // Parse the cron job string
22        cron_job.parse();
23
24        cron_job
25    }
26}
27
28impl CronJob {
29    pub fn parse(&mut self) -> &Self {
30        let mut job = self.line.clone().unwrap();
31        if job.trim_start().starts_with('#') {
32            self._disabled = true;
33            job = job.trim_start_matches('#').trim().to_string();
34        }
35        // Split the job by any `#` sign for potential comments
36        let mut parts = job.splitn(2, '#');
37        let cron_and_command = parts.next().unwrap_or("").trim();
38        let comment = parts.next().unwrap_or("").trim().to_string();
39
40        // Split the cron expression from the command
41        let cron_expression_length = 5; // Expected number of fields in the cron expression
42
43        let cron_and_command_parts: Vec<&str> = cron_and_command.split_whitespace().collect();
44
45        // Ensure there are enough parts for the cron expression and command
46        if cron_and_command_parts.len() >= cron_expression_length {
47            // Separate the cron part and the command part
48            self.cron = cron_and_command_parts[..cron_expression_length].join(" ");
49            self.command = cron_and_command_parts[cron_expression_length..]
50                .join(" ")
51                .trim()
52                .to_string();
53        } else {
54            // Handle case where there are not enough parts
55            self.cron = "".to_string();
56            self.command = "".to_string();
57        }
58        self.comment = Some(comment);
59        self
60    }
61
62    fn render(&self) -> String {
63        let mut job = format!("{} {}", self.cron, self.command);
64        let comment = self.comment.clone().unwrap();
65        // If there is a comment, append it with a leading `#`
66        if !comment.is_empty() {
67            job = format!("{} #{}", job, comment);
68        }
69        if self._disabled {
70            job = format!("#{}", job);
71        }
72
73        job
74    }
75
76    pub fn to_string(&self) -> String {
77        self.render()
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_cron_job_new() {
87        // Define a test cron job string with a comment
88        let job_string = "*/20 * * * * . ~/.myenv; /usr/bin/python3 /Users/wstreet7/project/check_tasks/check_tasks.py; # This is a comment";
89
90        // Create a new CronJob instance
91        let cron_job = CronJob::new(job_string.to_string());
92
93        // Define expected values
94        let expected_cron = "*/20 * * * *".to_string();
95        let expected_command =
96            ". ~/.myenv; /usr/bin/python3 /Users/wstreet7/project/check_tasks/check_tasks.py;"
97                .to_string(); // No extra space before ;
98        let expected_comment = Some("This is a comment".to_string());
99
100        // Assert that the parsed values match the expected values
101        assert_eq!(cron_job.cron, expected_cron);
102        assert_eq!(cron_job.command, expected_command);
103        assert_eq!(cron_job.comment, expected_comment);
104        assert_eq!(cron_job._disabled, false); // Not _disabled
105    }
106
107    #[test]
108    fn test_cron_job__disabled() {
109        // Test a _disabled cron job
110        let job_string = "# */20 * * * * . ~/.myenv; /usr/bin/python3 /Users/wstreet7/project/check_tasks/check_tasks.py; # This is a comment";
111
112        let cron_job = CronJob::new(job_string.to_string());
113
114        let expected_cron = "*/20 * * * *".to_string();
115        let expected_command =
116            ". ~/.myenv; /usr/bin/python3 /Users/wstreet7/project/check_tasks/check_tasks.py;"
117                .to_string();
118        let expected_comment = Some("This is a comment".to_string());
119
120        assert_eq!(cron_job.cron, expected_cron);
121        assert_eq!(cron_job.command, expected_command);
122        assert_eq!(cron_job.comment, expected_comment);
123        assert_eq!(cron_job._disabled, true); // This job should be marked as _disabled
124    }
125
126    #[test]
127    fn test_cron_job_without_comment() {
128        // Test a cron job without a comment
129        let job_string = "*/20 * * * * . ~/.myenv; /usr/bin/python3 /Users/wstreet7/project/check_tasks/check_tasks.py;";
130
131        let cron_job = CronJob::new(job_string.to_string());
132
133        let expected_cron = "*/20 * * * *".to_string();
134        let expected_command =
135            ". ~/.myenv; /usr/bin/python3 /Users/wstreet7/project/check_tasks/check_tasks.py;"
136                .to_string();
137
138        assert_eq!(cron_job.cron, expected_cron);
139        assert_eq!(cron_job.command, expected_command);
140        assert_eq!(cron_job.comment, Some("".to_string())); // No comment should be parsed
141        assert_eq!(cron_job._disabled, false); // Not _disabled
142    }
143
144    #[test]
145    fn test_empty_cron_job() {
146        // Test an empty job string
147        let job_string = "";
148
149        let cron_job = CronJob::new(job_string.to_string());
150
151        assert_eq!(cron_job.cron, "");
152        assert_eq!(cron_job.command, "");
153        assert_eq!(cron_job.comment, Some("".to_string()));
154        assert_eq!(cron_job._disabled, false); // Empty job should not be _disabled
155    }
156
157    #[test]
158    fn test_cron_job_with_only_cron_expression() {
159        // Test a cron job with only the cron expression and no command
160        let job_string = "*/20 * * * *";
161
162        let cron_job = CronJob::new(job_string.to_string());
163
164        let expected_cron = "*/20 * * * *".to_string();
165        let expected_command = "".to_string(); // No command
166
167        assert_eq!(cron_job.cron, expected_cron);
168        assert_eq!(cron_job.command, expected_command);
169        assert_eq!(cron_job.comment, Some("".to_string())); // No comment
170        assert_eq!(cron_job._disabled, false); // Not _disabled
171    }
172}