diff --git a/pttodoer/Cargo.lock b/pttodoer/Cargo.lock index a4b158a..c0d7794 100644 --- a/pttodoer/Cargo.lock +++ b/pttodoer/Cargo.lock @@ -188,6 +188,7 @@ dependencies = [ "chrono", "croner", "regex", + "serde", "serde_yaml", ] @@ -237,18 +238,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.200" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", diff --git a/pttodoer/Cargo.toml b/pttodoer/Cargo.toml index 053dd57..144d2c7 100644 --- a/pttodoer/Cargo.toml +++ b/pttodoer/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" chrono = "0.4.38" croner = "2.0.4" regex = "1.10.4" +serde = { version = "1.0.202", features = [ "serde_derive" ] } serde_yaml = "0.9.34" diff --git a/pttodoer/src/main.rs b/pttodoer/src/main.rs index 59f157a..d0a31ff 100644 --- a/pttodoer/src/main.rs +++ b/pttodoer/src/main.rs @@ -1,4 +1,5 @@ use serde_yaml; +use serde; use chrono::{DateTime, Local}; use chrono::naive::NaiveDateTime; use regex::Regex; @@ -8,22 +9,41 @@ fn main() { println!("{:?}", Task::new()) } -#[derive(Debug)] -struct Task(serde_yaml::Mapping); - //pub todo: String, - //pub detail: Option, - //pub sub: Vec, +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct Task(serde_yaml::Mapping); impl Task { - fn new() -> Task { + pub fn new() -> Task { Task(serde_yaml::Mapping::new()) } - fn is_due(&self) -> bool { - self.is_due_now(TS::now()) + pub fn from_value(v: serde_yaml::Value) -> Task { + let mut result = Task::new(); + match v.as_mapping() { + Some(m) => { result.0 = m.clone(); }, + None => { result.set_value("is".to_string(), v); }, + }; + result } - fn is_due_now(&self, now: TS) -> bool { + pub fn from_reader(r: impl std::io::Read) -> Result, String> { + let mut result = vec![]; + match serde_yaml::from_reader::<_, Vec>(r) { + Ok(v) => { + result.extend(v.iter().map(|x| { + Task::from_value(x.clone()) + })); + Ok(result) + }, + Err(msg) => Err(format!("failed to read: {}", msg)), + } + } + + pub fn is_due(&self) -> bool { + self.is_due_at(TS::now()) + } + + fn is_due_at(&self, now: TS) -> bool { match self.when() { Some(when) => { now.unix() >= when.next(self.ts()).unix() @@ -56,32 +76,53 @@ impl Task { } fn get(&self, k: String) -> Option { + match self.get_value(k) { + None => None, + Some(v) => match v.as_str() { + Some(s) => Some(s.to_string()), + None => {assert!(false, "got not a string"); None }, + }, + } + } + + fn get_value(&self, k: String) -> Option { match self.0.get(k) { - Some(v) => Some( - serde_yaml::to_string(v) - .unwrap() - .to_string() - .trim() - .trim_start_matches('\'') - .trim_end_matches( '\'') - .to_string() - ), + Some(v) => Some(v.clone()), None => None, } } fn set(&mut self, k: String, v: String) { + self.set_value(k, serde_yaml::Value::String(v)); + } + + fn set_value(&mut self, k: String, v: serde_yaml::Value) { self.0.insert( serde_yaml::Value::String(k), - serde_yaml::Value::String(v) + v ); } + } #[cfg(test)] mod test_task { use super::*; + #[test] + fn from_reader_testdata() { + let tasks = Task::from_reader( + std::fs::File::open("./src/testdata/mvp.yaml").expect("failed to open file") + ).expect("failed to read file"); + eprintln!("tasks from_reader ./src/testdata/mvp.yaml: {:?}", tasks); + assert_eq!(2, tasks.len()); + assert_eq!(1, tasks[0].0.len()); + assert!(tasks[0].get("is".to_string()).is_some()); + assert_eq!("x".to_string(), tasks[0].get("is".to_string()).unwrap()); + assert_eq!(1, tasks[1].0.len()); + assert_eq!("y and z".to_string(), tasks[1].get("is".to_string()).unwrap()); + } + #[test] fn crud() { let mut t = Task::new(); @@ -100,9 +141,9 @@ mod test_task { let now = TS::new("2000-01-02T16:00Z".to_string()).unwrap(); t.set("ts".to_string(), then.to_string()); t.set("schedule".to_string(), "1h".to_string()); - assert!(!t.is_due_now(TS::from_unix(now.unix()-1))); - assert!(t.is_due_now(TS::from_unix(now.unix()))); - assert!(t.is_due_now(TS::from_unix(now.unix()+1))); + assert!(!t.is_due_at(TS::from_unix(now.unix()-1))); + assert!(t.is_due_at(TS::from_unix(now.unix()))); + assert!(t.is_due_at(TS::from_unix(now.unix()+1))); } #[test] @@ -113,9 +154,9 @@ mod test_task { let now = TS::new("2000-01-02T16:00Z".to_string()).unwrap(); t.set("ts".to_string(), then.to_string()); t.set("schedule".to_string(), "2000-01-02T16:00Z".to_string()); - assert!(!t.is_due_now(TS::from_unix(now.unix()-1))); - assert!(t.is_due_now(TS::from_unix(now.unix()))); - assert!(t.is_due_now(TS::from_unix(now.unix()+1))); + assert!(!t.is_due_at(TS::from_unix(now.unix()-1))); + assert!(t.is_due_at(TS::from_unix(now.unix()))); + assert!(t.is_due_at(TS::from_unix(now.unix()+1))); } #[test] @@ -126,9 +167,9 @@ mod test_task { let now = TS::new("2000-01-02T16:00Z".to_string()).unwrap(); t.set("ts".to_string(), then.to_string()); t.set("schedule".to_string(), "0 16 * * *".to_string()); - assert!(!t.is_due_now(TS::from_unix(now.unix()-1))); - assert!(t.is_due_now(TS::from_unix(now.unix()))); - assert!(t.is_due_now(TS::from_unix(now.unix()+1))); + assert!(!t.is_due_at(TS::from_unix(now.unix()-1))); + assert!(t.is_due_at(TS::from_unix(now.unix()))); + assert!(t.is_due_at(TS::from_unix(now.unix()+1))); } } @@ -275,7 +316,7 @@ mod test_cron { #[test] fn parse() { match Cron::new("* * * * *".to_string()) { - Ok(c) => {} + Ok(_) => {} Err(err) => assert!(false, "failed to parse cron: {}", err), }; match Cron::new("1 * * * *".to_string()) { @@ -375,7 +416,7 @@ impl TS { &format!("{}:00", src), "%Y-%m-%dT%H:%M", ) { - Ok(v) => { return Ok(TS(v.timestamp() as u64)) }, + Ok(v) => { return Ok(TS(v.and_utc().timestamp() as u64)) }, _ => {}, }; Err(format!("cannot parse date format from {}", src))