master
Bel LaPointe 2024-05-22 14:04:38 -04:00
parent 3c132528e3
commit 039c4dad04
3 changed files with 77 additions and 34 deletions

9
pttodoer/Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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<String>,
//pub sub: Vec<Task>,
#[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<Vec<Task>, String> {
let mut result = vec![];
match serde_yaml::from_reader::<_, Vec<serde_yaml::Value>>(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<String> {
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<serde_yaml::Value> {
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))