diff --git a/pttodoer/src/main.rs b/pttodoer/src/main.rs index 25462c2..3855a8b 100644 --- a/pttodoer/src/main.rs +++ b/pttodoer/src/main.rs @@ -10,6 +10,68 @@ fn main() { } #[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct Tasks(Vec); + +impl Tasks { + pub fn new() -> Tasks { + Tasks(vec![]) + } + + pub fn from_reader(r: impl std::io::Read) -> Result { + let mut result = Tasks::new(); + match serde_yaml::from_reader::<_, Vec>(r) { + Ok(v) => { + result.0.extend(v.iter().map(|x| { + Task::from_value(x.clone()) + })); + Ok(result) + }, + Err(msg) => Err(format!("failed to read: {}", msg)), + } + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn due(&self) -> Tasks { + Tasks(self.0.iter() + .filter(|x| x.is_due()) + .map(|x| x.clone()) + .collect() + ) + } +} + +#[cfg(test)] +mod test_tasks { + use super::*; + + #[test] + fn due() { + let tasks = Tasks::from_reader( + std::fs::File::open("./src/testdata/tasks_due_scheduled_done.yaml").expect("failed to open file") + ).expect("failed to read file"); + eprintln!("{:?}", tasks); + assert_eq!(2, tasks.due().len()); + } + + #[test] + fn from_reader() { + let tasks = Tasks::from_reader( + std::fs::File::open("./src/testdata/mvp.yaml").expect("failed to open file") + ).expect("failed to read file"); + assert_eq!(2, tasks.0.len()); + assert_eq!(1, tasks.0[0].0.len()); + assert!(tasks.0[0].get("is".to_string()).is_some()); + assert_eq!("x".to_string(), tasks.0[0].get("is".to_string()).unwrap()); + assert_eq!(1, tasks.0[1].0.len()); + assert_eq!("y and z".to_string(), tasks.0[1].get("is".to_string()).unwrap()); + } +} + + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] pub struct Task(serde_yaml::Mapping); impl Task { @@ -17,7 +79,7 @@ impl Task { Task(serde_yaml::Mapping::new()) } - pub fn from_reader_value(r: impl std::io::Read) -> Result { + pub fn from_reader(r: impl std::io::Read) -> Result { match serde_yaml::from_reader::<_, serde_yaml::Value>(r) { Ok(v) => Ok(Task::from_value(v)), Err(msg) => Err(format!("failed to read value: {}", msg)), @@ -33,21 +95,12 @@ impl Task { result } - 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_done(&self) -> bool { + self.0.len() == 1 && self.get_value("done".to_string()).is_some() } pub fn is_due(&self) -> bool { - self.is_due_at(TS::now()) + self.is_due_at(TS::now()) && !self.is_done() } fn is_due_at(&self, now: TS) -> bool { @@ -87,7 +140,7 @@ impl Task { None => None, Some(v) => match v.as_str() { Some(s) => Some(s.to_string()), - None => {assert!(false, "got not a string"); None }, + None => None, }, } } @@ -117,25 +170,15 @@ 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"); - 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()); - - let task = Task::from_reader_value( + fn from_reader() { + let task = Task::from_reader( std::fs::File::open("./src/testdata/mvp.uuid-123-456-xyz.yaml").expect("failed to open file") ).expect("failed to read 123..."); assert_eq!(1, task.0.len()); assert!(task.get("is".to_string()).is_some()); assert_eq!("plaintext".to_string(), task.get("is".to_string()).unwrap()); - let task = Task::from_reader_value( + let task = Task::from_reader( std::fs::File::open("./src/testdata/mvp.uuid-789-012-abc.yaml").expect("failed to open file") ).expect("failed to read 789..."); assert_eq!(3, task.0.len()); @@ -146,6 +189,18 @@ mod test_task { assert!(task.is_due()); } + #[test] + fn is_done() { + let mut t = Task::new(); + t.set("done".to_string(), "anything".to_string()); + eprintln!("{:?}", t.0); + assert!(t.is_done()); + + t.set_value("done".to_string(), serde_yaml::Value::Null); + eprintln!("{:?}", t.0); + assert!(t.is_done()); + } + #[test] fn crud() { let mut t = Task::new(); @@ -442,6 +497,13 @@ impl TS { Ok(v) => { return Ok(TS(v.and_utc().timestamp() as u64)) }, _ => {}, }; + match NaiveDateTime::parse_from_str( + &format!("{}T00:00", src), + "%Y-%m-%dT%H:%M", + ) { + Ok(v) => { return Ok(TS(v.and_utc().timestamp() as u64)) }, + _ => {}, + }; Err(format!("cannot parse date format from {}", src)) } @@ -477,5 +539,12 @@ mod test_ts { }, Err(err) => assert!(false, "failed to parse ts: {}", err), }; + match TS::new("2024-05-01".to_string()) { + Ok(ts) => { + assert_eq!(1714521600, ts.unix()); + assert_eq!("2024-05-01T00:00Z", ts.to_string()); + }, + Err(err) => assert!(false, "failed to parse ts: {}", err), + }; } } diff --git a/pttodoer/src/testdata/tasks_due_scheduled_done.yaml b/pttodoer/src/testdata/tasks_due_scheduled_done.yaml new file mode 100644 index 0000000..8279173 --- /dev/null +++ b/pttodoer/src/testdata/tasks_due_scheduled_done.yaml @@ -0,0 +1,10 @@ +- 1do: due + schedule: 2006-04-05 +- 2do: scheduled + schedule: 2099-04-05 +- 3do: repeating scheduled + schedule: 0 0 1 1 * + ts: 2001-02-03T04:05:06Z +- done: + do: done + schedule: 2006-04-05