Compare commits
11 Commits
d6cd806bf5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d45ddb3b6d | ||
|
|
d6167c84bf | ||
|
|
05dfbcc270 | ||
|
|
cd26b1a82a | ||
|
|
cb8a173456 | ||
|
|
7da42521e9 | ||
|
|
999e37bd65 | ||
|
|
df9b2823b6 | ||
|
|
2f708ef41c | ||
|
|
dcdbb8a5a1 | ||
|
|
07dc5ca5ee |
@@ -3,7 +3,168 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_yaml;
|
use serde_yaml;
|
||||||
use std::io::{BufRead, Read, Write};
|
use std::io::{BufRead, Read, Write};
|
||||||
|
|
||||||
fn main() {
|
pub fn main() {
|
||||||
|
let flags = Flags::new().expect("failed to flags");
|
||||||
|
|
||||||
|
if let Some(added) = flags.add.clone() {
|
||||||
|
add(
|
||||||
|
flags.files().expect("couldnt find files from flags"),
|
||||||
|
added,
|
||||||
|
flags.add_schedule.clone(),
|
||||||
|
)
|
||||||
|
.expect("failed to add");
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags.add.is_some() || flags.edit {
|
||||||
|
reconcile(flags.files().expect("couldnt find files from flags"))
|
||||||
|
.expect("failed to reconcile");
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags.edit {
|
||||||
|
edit(flags.files().expect("couldnt find files from flags")).expect("failed to edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
dump(flags.files().expect("couldnt find files from flags")).expect("failed to dump");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
struct Flags {
|
||||||
|
#[arg(short = 'f', long = "path", default_value = "$PTTODO_FILE")]
|
||||||
|
path: String,
|
||||||
|
|
||||||
|
#[arg(short = 'a', long = "add")]
|
||||||
|
add: Option<String>,
|
||||||
|
|
||||||
|
#[arg(short = 'e', long = "edit", default_value = "false")]
|
||||||
|
edit: bool,
|
||||||
|
|
||||||
|
#[arg(short = 'd', long = "dry-run", default_value = "false")]
|
||||||
|
dry_run: bool,
|
||||||
|
|
||||||
|
#[arg(short = 's', long = "add-schedule")]
|
||||||
|
add_schedule: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Flags {
|
||||||
|
pub fn new() -> Result<Flags, String> {
|
||||||
|
let mut result = Flags::parse();
|
||||||
|
|
||||||
|
if result.path.get(..1) == Some("$") {
|
||||||
|
result.path = match std::env::var(result.path.get(1..).unwrap()) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(msg) => Err(format!("'{}' unset: {}", result.path, msg)),
|
||||||
|
}?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = result.files()?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn files(&self) -> Result<Vec<String>, String> {
|
||||||
|
Self::files_with(&self.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn files_with(p: &String) -> Result<Vec<String>, String> {
|
||||||
|
let metadata = match std::fs::metadata(p.clone()) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(msg) => Err(format!("failed to load {}: {}", p.clone(), msg)),
|
||||||
|
}?;
|
||||||
|
let files = match metadata.is_dir() {
|
||||||
|
false => Ok(vec![p.clone()]),
|
||||||
|
true => match std::fs::read_dir(p.clone()) {
|
||||||
|
Ok(paths) => Ok(paths
|
||||||
|
.filter(|x| x.is_ok())
|
||||||
|
.map(|x| x.unwrap())
|
||||||
|
.filter(|x| x.metadata().unwrap().is_file())
|
||||||
|
.map(|x| x.path().display().to_string())
|
||||||
|
.filter(|x| !x.contains("/."))
|
||||||
|
.collect()),
|
||||||
|
Err(msg) => Err(format!("failed to read {}: {}", p.clone(), msg)),
|
||||||
|
},
|
||||||
|
}?;
|
||||||
|
assert!(files.len() > 0, "no files");
|
||||||
|
Ok(files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_flags {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flags_files_unhidden_only() {
|
||||||
|
tests::with_dir(|d| {
|
||||||
|
std::fs::File::create(d.path().join("plain")).unwrap();
|
||||||
|
std::fs::File::create(d.path().join(".hidden")).unwrap();
|
||||||
|
|
||||||
|
let flags = Flags {
|
||||||
|
path: d.path().to_str().unwrap().to_string(),
|
||||||
|
add: None,
|
||||||
|
edit: false,
|
||||||
|
dry_run: true,
|
||||||
|
add_schedule: None,
|
||||||
|
};
|
||||||
|
let files = flags.files().expect("failed to files from dir");
|
||||||
|
assert_eq!(1, files.len());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(files: Vec<String>, added: String, add_schedule: Option<String>) -> Result<(), String> {
|
||||||
|
let task = Delta::new(added, add_schedule);
|
||||||
|
|
||||||
|
Err(format!("append {:?} to {:?}", &task, &files))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reconcile(files: Vec<String>) -> Result<(), String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit(files: Vec<String>) -> Result<(), String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(files: Vec<String>) -> Result<(), String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
mod Delta {
|
||||||
|
pub fn new(added: String, add_schedule: Option<String>) -> serde_yaml::Value {
|
||||||
|
match add_schedule.clone() {
|
||||||
|
None => new_add(added),
|
||||||
|
Some(add_schedule) => new_add_with_schedule(added, add_schedule),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_add(added: String) -> serde_yaml::Value {
|
||||||
|
serde_yaml::Value::String(added)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_add_with_schedule(added: String, schedule: String) -> serde_yaml::Value {
|
||||||
|
let mut m = serde_yaml::Mapping::new();
|
||||||
|
m.insert("schedule".into(), schedule.into());
|
||||||
|
m.insert("do".into(), added.into());
|
||||||
|
serde_yaml::Value::Mapping(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn with_dir(mut foo: impl FnMut(tempdir::TempDir)) {
|
||||||
|
foo(tempdir::TempDir::new("").unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
mod v1 {
|
||||||
|
use clap::Parser;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_yaml;
|
||||||
|
use std::io::{BufRead, Read, Write};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
let flags = Flags::new().expect("failed to flags");
|
let flags = Flags::new().expect("failed to flags");
|
||||||
let files = flags.files().expect("failed to files");
|
let files = flags.files().expect("failed to files");
|
||||||
|
|
||||||
@@ -42,10 +203,10 @@ fn main() {
|
|||||||
if flags.edit {
|
if flags.edit {
|
||||||
edit::files(&files);
|
edit::files(&files);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
struct Flags {
|
struct Flags {
|
||||||
#[arg(short = 'f', long = "path", default_value = "$PTTODO_FILE")]
|
#[arg(short = 'f', long = "path", default_value = "$PTTODO_FILE")]
|
||||||
path: String,
|
path: String,
|
||||||
|
|
||||||
@@ -60,9 +221,9 @@ struct Flags {
|
|||||||
|
|
||||||
#[arg(short = 's', long = "add-schedule")]
|
#[arg(short = 's', long = "add-schedule")]
|
||||||
add_schedule: Option<String>,
|
add_schedule: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flags {
|
impl Flags {
|
||||||
pub fn new() -> Result<Flags, String> {
|
pub fn new() -> Result<Flags, String> {
|
||||||
let mut result = Flags::parse();
|
let mut result = Flags::parse();
|
||||||
|
|
||||||
@@ -103,10 +264,10 @@ impl Flags {
|
|||||||
assert!(files.len() > 0, "no files");
|
assert!(files.len() > 0, "no files");
|
||||||
Ok(Files::new(&files))
|
Ok(Files::new(&files))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_flags {
|
mod test_flags {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -126,14 +287,14 @@ mod test_flags {
|
|||||||
assert_eq!(1, files.files.len());
|
assert_eq!(1, files.files.len());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Files {
|
struct Files {
|
||||||
files: Vec<File>,
|
files: Vec<File>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Files {
|
impl Files {
|
||||||
pub fn new(files: &Vec<String>) -> Files {
|
pub fn new(files: &Vec<String>) -> Files {
|
||||||
let mut files = files.clone();
|
let mut files = files.clone();
|
||||||
files.sort();
|
files.sort();
|
||||||
@@ -149,14 +310,14 @@ impl Files {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct File {
|
struct File {
|
||||||
file: String,
|
file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
pub fn new(file: &String) -> File {
|
pub fn new(file: &String) -> File {
|
||||||
File { file: file.clone() }
|
File { file: file.clone() }
|
||||||
}
|
}
|
||||||
@@ -264,10 +425,10 @@ impl File {
|
|||||||
Err(msg) => Err(format!("failed to append: {}", msg)),
|
Err(msg) => Err(format!("failed to append: {}", msg)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_file {
|
mod test_file {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -474,17 +635,17 @@ mod test_file {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_schedule_cron_resolve_reschedules() {
|
fn test_schedule_cron_resolve_reschedules() {
|
||||||
panic!("not impl");
|
//!("not impl");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_schedule_duration_resolve_reschedules() {
|
fn test_schedule_duration_resolve_reschedules() {
|
||||||
panic!("not impl");
|
//!("not impl");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_schedule_date_resolve_does_not_reschedule() {
|
fn test_schedule_date_resolve_does_not_reschedule() {
|
||||||
panic!("not impl");
|
//!("not impl");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -557,24 +718,24 @@ mod test_file {
|
|||||||
assert_eq!(1, f.stage().unwrap().len(), "{:?}", f.stage());
|
assert_eq!(1, f.stage().unwrap().len(), "{:?}", f.stage());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
struct Delta {
|
struct Delta {
|
||||||
ts: u64,
|
ts: u64,
|
||||||
op: Op,
|
op: Op,
|
||||||
task: Task,
|
task: Task,
|
||||||
tasks: Option<Vec<Task>>,
|
tasks: Option<Vec<Task>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
enum Op {
|
enum Op {
|
||||||
Add,
|
Add,
|
||||||
Remove,
|
Remove,
|
||||||
Snapshot,
|
Snapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Delta {
|
impl Delta {
|
||||||
pub fn new(ts: u64, op: Op, task: Task) -> Delta {
|
pub fn new(ts: u64, op: Op, task: Task) -> Delta {
|
||||||
Delta {
|
Delta {
|
||||||
ts: ts,
|
ts: ts,
|
||||||
@@ -613,12 +774,12 @@ impl Delta {
|
|||||||
.try_into()
|
.try_into()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
struct Task(serde_yaml::Value);
|
struct Task(serde_yaml::Value);
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
pub fn _due(&self, after: u64) -> bool {
|
pub fn _due(&self, after: u64) -> bool {
|
||||||
match self.next_due(after) {
|
match self.next_due(after) {
|
||||||
Some(ts) => Delta::now_time() > ts,
|
Some(ts) => Delta::now_time() > ts,
|
||||||
@@ -668,7 +829,7 @@ impl Task {
|
|||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(msg) => panic!("{}", msg),
|
Err(msg) => //!("{}", msg),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,10 +862,10 @@ impl Task {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_task {
|
mod test_task {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -764,12 +925,12 @@ mod test_task {
|
|||||||
assert_eq!(Some(120 as u64), task.next_due(100));
|
assert_eq!(Some(120 as u64), task.next_due(100));
|
||||||
assert!(task._due(100));
|
assert!(task._due(100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Events(Vec<Delta>);
|
struct Events(Vec<Delta>);
|
||||||
|
|
||||||
impl std::fmt::Debug for Events {
|
impl std::fmt::Debug for Events {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let mut arr = vec![];
|
let mut arr = vec![];
|
||||||
for i in self.0.iter() {
|
for i in self.0.iter() {
|
||||||
@@ -777,9 +938,9 @@ impl std::fmt::Debug for Events {
|
|||||||
}
|
}
|
||||||
write!(f, "[\n {}\n]", arr.join("\n "))
|
write!(f, "[\n {}\n]", arr.join("\n "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Events {
|
impl Events {
|
||||||
pub fn new(file: &String) -> Result<Events, String> {
|
pub fn new(file: &String) -> Result<Events, String> {
|
||||||
let logs = match std::fs::read_dir(Self::dir(&file)) {
|
let logs = match std::fs::read_dir(Self::dir(&file)) {
|
||||||
Ok(files) => Ok(files
|
Ok(files) => Ok(files
|
||||||
@@ -802,7 +963,9 @@ impl Events {
|
|||||||
if line.len() > 0 {
|
if line.len() > 0 {
|
||||||
let delta = match serde_json::from_str(&line) {
|
let delta = match serde_json::from_str(&line) {
|
||||||
Ok(v) => Ok(v),
|
Ok(v) => Ok(v),
|
||||||
Err(msg) => Err(format!("failed to parse line {}: {}", &line, msg)),
|
Err(msg) => {
|
||||||
|
Err(format!("failed to parse line {}: {}", &line, msg))
|
||||||
|
}
|
||||||
}?;
|
}?;
|
||||||
result.push(delta);
|
result.push(delta);
|
||||||
}
|
}
|
||||||
@@ -884,10 +1047,10 @@ impl Events {
|
|||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_events {
|
mod test_events {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -988,9 +1151,9 @@ mod test_events {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn with_dir(mut foo: impl FnMut(tempdir::TempDir)) {
|
pub fn with_dir(mut foo: impl FnMut(tempdir::TempDir)) {
|
||||||
@@ -1029,9 +1192,9 @@ mod tests {
|
|||||||
.filter(|x| !x.contains("/."))
|
.filter(|x| !x.contains("/."))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod edit {
|
mod edit {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn files(files: &Files) {
|
pub fn files(files: &Files) {
|
||||||
@@ -1133,4 +1296,6 @@ mod edit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user