parent
8e00880863
commit
afa947f907
@ -0,0 +1,9 @@ |
||||
[package] |
||||
name = "matrix_test_assignment" |
||||
version = "0.1.0" |
||||
edition = "2021" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
test-case = "2.1.0" |
@ -0,0 +1,4 @@ |
||||
30 1 /bin/run_me_daily |
||||
45 * /bin/run_me_hourly |
||||
* * /bin/run_me_every_minute |
||||
* 19 /bin/run_me_sixty_times |
@ -0,0 +1,174 @@ |
||||
use core::fmt; |
||||
use std::{env, io::{self, BufRead}, num::ParseIntError, str, cmp::Ordering}; |
||||
use test_case::test_case; |
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
||||
struct Time { |
||||
hour: u8, |
||||
minute: u8, |
||||
} |
||||
|
||||
impl str::FromStr for Time { |
||||
type Err = ParseIntError; |
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { |
||||
let parts: Vec<_> = s.split(':').collect(); |
||||
|
||||
Ok(Self { |
||||
hour: parts[0].parse()?, |
||||
minute: parts[1].parse()?, |
||||
}) |
||||
} |
||||
} |
||||
|
||||
impl fmt::Display for Time { |
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||
write!(f, "{:02}:{:02}", self.hour, self.minute) |
||||
} |
||||
} |
||||
|
||||
enum NumberOrAsterisk { |
||||
Asterisk, |
||||
Number(u8), |
||||
} |
||||
|
||||
impl str::FromStr for NumberOrAsterisk { |
||||
type Err = ParseIntError; |
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { |
||||
if s == "*" { |
||||
return Ok(Self::Asterisk) |
||||
} |
||||
|
||||
Ok(Self::Number(s.parse()?)) |
||||
} |
||||
} |
||||
|
||||
enum Schedule { |
||||
EveryMinute, |
||||
AtMinute(u8), |
||||
AtHour(u8), |
||||
AtTime(Time), |
||||
} |
||||
|
||||
struct CronTask { |
||||
schedule: Schedule, |
||||
command: String, |
||||
} |
||||
|
||||
impl str::FromStr for CronTask { |
||||
type Err = ParseIntError; |
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { |
||||
let parts: Vec<_> = s.split(char::is_whitespace).filter(|&x| !x.is_empty()).collect(); |
||||
let maybe_minute: NumberOrAsterisk = parts[0].parse()?; |
||||
let maybe_hour: NumberOrAsterisk = parts[1].parse()?; |
||||
let command = parts[2].to_owned(); |
||||
|
||||
Ok(Self { |
||||
schedule: match (maybe_hour, maybe_minute) { |
||||
(NumberOrAsterisk::Asterisk, NumberOrAsterisk::Asterisk) => Schedule::EveryMinute, |
||||
(NumberOrAsterisk::Number(hour), NumberOrAsterisk::Asterisk) => Schedule::AtHour(hour), |
||||
(NumberOrAsterisk::Asterisk, NumberOrAsterisk::Number(minute)) => Schedule::AtMinute(minute), |
||||
(NumberOrAsterisk::Number(hour), NumberOrAsterisk::Number(minute)) => Schedule::AtTime(Time { hour, minute }) |
||||
}, |
||||
command, |
||||
}) |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, PartialEq, Eq)] |
||||
enum NextRunTime { |
||||
Today(Time), |
||||
Tomorrow(Time), |
||||
} |
||||
|
||||
impl fmt::Display for NextRunTime { |
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||
match self { |
||||
Self::Today(time) => write!(f, "{} today", time), |
||||
Self::Tomorrow(time) => write!(f, "{} tomorrow", time), |
||||
} |
||||
} |
||||
} |
||||
|
||||
struct NextRun { |
||||
time: NextRunTime, |
||||
command: String, |
||||
} |
||||
|
||||
impl fmt::Display for NextRun { |
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||
write!(f, "{} - {}", self.time, self.command) |
||||
} |
||||
} |
||||
|
||||
#[test_case(Schedule::EveryMinute, "12:34".parse().unwrap() => NextRunTime::Today( "12:34".parse().unwrap()))] |
||||
#[test_case(Schedule::AtHour(11), "12:34".parse().unwrap() => NextRunTime::Tomorrow("11:00".parse().unwrap()))] |
||||
#[test_case(Schedule::AtHour(12), "12:34".parse().unwrap() => NextRunTime::Today( "12:34".parse().unwrap()))] |
||||
#[test_case(Schedule::AtHour(13), "12:34".parse().unwrap() => NextRunTime::Today( "13:00".parse().unwrap()))] |
||||
#[test_case(Schedule::AtMinute(33), "12:34".parse().unwrap() => NextRunTime::Today( "13:33".parse().unwrap()))] |
||||
#[test_case(Schedule::AtMinute(34), "12:34".parse().unwrap() => NextRunTime::Today( "12:34".parse().unwrap()))] |
||||
#[test_case(Schedule::AtMinute(35), "12:34".parse().unwrap() => NextRunTime::Today( "12:35".parse().unwrap()))] |
||||
#[test_case(Schedule::AtMinute(33), "23:34".parse().unwrap() => NextRunTime::Tomorrow("00:33".parse().unwrap()))] |
||||
#[test_case(Schedule::AtMinute(34), "23:34".parse().unwrap() => NextRunTime::Today( "23:34".parse().unwrap()))] |
||||
#[test_case(Schedule::AtMinute(35), "23:34".parse().unwrap() => NextRunTime::Today( "23:35".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("11:33".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Tomorrow("11:33".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("11:34".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Tomorrow("11:34".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("11:35".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Tomorrow("11:35".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("12:33".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Tomorrow("12:33".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("12:34".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Today( "12:34".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("12:35".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Today( "12:35".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("13:33".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Today( "13:33".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("13:34".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Today( "13:34".parse().unwrap()))] |
||||
#[test_case(Schedule::AtTime("13:35".parse().unwrap()), "12:34".parse().unwrap() => NextRunTime::Today( "13:35".parse().unwrap()))] |
||||
fn get_next_run_time(schedule: Schedule, current_time: Time) -> NextRunTime { |
||||
match schedule { |
||||
Schedule::EveryMinute => NextRunTime::Today(current_time), |
||||
Schedule::AtHour(hour) => match hour.cmp(¤t_time.hour) { |
||||
Ordering::Less => NextRunTime::Tomorrow(Time { hour, minute: 0 }), |
||||
Ordering::Equal => NextRunTime::Today(current_time), |
||||
Ordering::Greater => NextRunTime::Today(Time { hour, minute: 0 }), |
||||
}, |
||||
Schedule::AtMinute(minute) => match minute.cmp(¤t_time.minute) { |
||||
Ordering::Less => match current_time.hour { |
||||
23 => NextRunTime::Tomorrow(Time { hour: 0, minute }), |
||||
_ => NextRunTime::Today(Time { hour: current_time.hour + 1, minute }), |
||||
}, |
||||
Ordering::Equal => NextRunTime::Today(current_time), |
||||
Ordering::Greater => NextRunTime::Today(Time { hour: current_time.hour, minute } ) |
||||
}, |
||||
Schedule::AtTime(time) => match time.cmp(¤t_time) { |
||||
Ordering::Less => NextRunTime::Tomorrow(time), |
||||
_ => NextRunTime::Today(time), |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn get_next_run(task: CronTask, current_time: Time) -> NextRun { |
||||
NextRun { |
||||
command: task.command, |
||||
time: get_next_run_time(task.schedule, current_time), |
||||
} |
||||
} |
||||
|
||||
#[test_case("30 1 /bin/run_me_daily", "16:10".parse().unwrap() => "01:30 tomorrow - /bin/run_me_daily")] |
||||
#[test_case("45 * /bin/run_me_hourly", "16:10".parse().unwrap() => "16:45 today - /bin/run_me_hourly")] |
||||
#[test_case("* * /bin/run_me_every_minute", "16:10".parse().unwrap() => "16:10 today - /bin/run_me_every_minute")] |
||||
#[test_case("* 19 /bin/run_me_sixty_times", "16:10".parse().unwrap() => "19:00 today - /bin/run_me_sixty_times")] |
||||
#[test_case("0 0 all_zeroes", "16:10".parse().unwrap() => "00:00 tomorrow - all_zeroes")] |
||||
#[test_case("1 1 leading_zeroes", "16:10".parse().unwrap() => "01:01 tomorrow - leading_zeroes")] |
||||
#[test_case("12 12 no_zeroes", "16:10".parse().unwrap() => "12:12 tomorrow - no_zeroes")] |
||||
fn handle_line(line: &str, current_time: Time) -> String { |
||||
return format!("{}", get_next_run(line.parse().unwrap(), current_time)) |
||||
} |
||||
|
||||
fn main() { |
||||
let args: Vec<_> = env::args().collect(); |
||||
let time = args[1].parse().unwrap(); |
||||
let stdin = io::stdin(); |
||||
let lines = stdin.lock().lines(); |
||||
for line in lines { |
||||
println!("{}", handle_line(&line.unwrap(), time)) |
||||
} |
||||
} |
Loading…
Reference in new issue