diff --git a/Cargo.lock b/Cargo.lock index d380d09..d3f741d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aes" @@ -1086,7 +1086,7 @@ dependencies = [ [[package]] name = "todor" -version = "1.10.1" +version = "1.10.2" dependencies = [ "anyhow", "chrono", diff --git a/src/cli.rs b/src/cli.rs index ec3a40a..c8e4fc6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -153,6 +153,9 @@ pub enum Commands { Import{ #[arg(value_name = "markdown-file")] file: Option, + + #[arg(short, long)] + from_logseq: bool, }, /// -> launch file manager on basedir diff --git a/src/main.rs b/src/main.rs index 3650eb8..39e1523 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,12 +19,13 @@ fn main() { if let Some(boxname) = args.inbox { &boxname.clone() } else { - let cmdname = arg0.split(path::MAIN_SEPARATOR).last().unwrap(); + let cmdname = arg0.split(path::MAIN_SEPARATOR).next_back().unwrap(); + if cmdname == "todor" { "inbox" } else { // e.g. "today", "tomorrow", "yesterday", "t.read", "todo.working" - cmdname.split('.').last().unwrap() + cmdname.split('.').next_back().unwrap() } }; @@ -56,7 +57,10 @@ fn main() { if cc > 0 { println!("{}", cc) } } - Some(Commands::Import{ file }) => TaskBox::new(inbox_path).import(file), + Some(Commands::Import{ file, from_logseq }) => { + TaskBox::new(inbox_path).import(file, from_logseq); + } + Some(Commands::Purge { sort }) => { if i_confirm("are you sure?") { if sort && ! i_confirm("sort cannot handle subtasks well, continue?") { return } diff --git a/src/taskbox.rs b/src/taskbox.rs index 40a9d36..f584034 100644 --- a/src/taskbox.rs +++ b/src/taskbox.rs @@ -29,6 +29,10 @@ const PREFIX_OPEN :&str = "- [ ] "; const PREFIX_DONE :&str = "- [x] "; const PREFIX_SUBT :&str = " 󱞩 "; +const PREFIX_OPEN_LOGSEQ :&str = "- TODO "; +const PREFIX_OPEN2_LOGSEQ :&str = "- LATER "; +// const PREFIX_DONE_LOGSEQ :&str = "- DONE "; + #[derive(Debug)] pub struct TaskBox { pub fpath: PathBuf, @@ -205,8 +209,8 @@ impl TaskBox { return false } else if *done { // found done sub-task for this major task - if task_status.is_some() { - *task_status.unwrap() = true; + if let Some(task_status) = task_status { + *task_status = true; } return true } @@ -519,40 +523,86 @@ impl TaskBox { } // specified markdown file -> cur - pub fn import(&mut self, file: Option) { + pub fn import(&mut self, file: Option, from_logseq: bool) { #[allow(clippy::redundant_closure)] - let mdfile= file.unwrap_or_else(|| super::util::pick_file()); - let fpath = Path::new(&mdfile); - if ! fpath.is_file() { - eprintln!("not a file or not exists: {}", S_fpath!(mdfile)); - std::process::exit(1) - } - println!("importing {} {}", S_fpath!(mdfile), PROGRESS); - - let mut newt = Vec::new(); - let mut newr = Vec::new(); - for rline in fs::read_to_string(fpath).expect("cannot read file").lines() { - let line = rline.trim(); - if line.is_empty() { continue } - - if let Some(stripped) = line.strip_prefix(PREFIX_OPEN) { - if RE_ROUTINES.is_match(stripped) { - println!(" {} : {}", S_checkbox!(ROUTINES), stripped); - newr.push(stripped.to_string()) - } else { - println!(" {} : {}", S_checkbox!(CHECKBOX), stripped); - newt.push(stripped.to_string()) + let mut newt = Vec::new(); // new tasks + let mut newr = Vec::new(); // new routines + let mut newl = Vec::new(); // new later tasks from logseq to Inbox + + let mdfiles :Vec = if from_logseq { + // get icloud path of "Logseq" + let icloud = dirs::home_dir().unwrap().join("Library/Mobile Documents/iCloud~com~logseq~logseq/Documents/journals"); + + // when logseq is true, the input "mdfile" is the date string of the journal file + // and can accept today, yesterday, week, etc. + // if date is not provided, use today + let mdfile_names = match file { + Some(d) if d == "today" => get_today(), + Some(d) if d == "yesterday" => get_yesterday(), + Some(d) if d.ends_with(".md") => d, + Some(d) => format!("{}.md", d), + + None => super::util::pick_file(icloud.to_str().unwrap()), + }; + + mdfile_names.split("\n").map(|s| icloud.join(s).to_str().unwrap().to_string()).collect() + } else { + file.unwrap_or_else(|| super::util::pick_file(".")).split("\n").map(|s| s.to_string()).collect() + }; + + for mdfile in mdfiles { + let fpath = Path::new(&mdfile); + if ! fpath.is_file() { + eprintln!("not a file or not exists: {}", S_fpath!(mdfile)); + std::process::exit(1) + } + println!("importing {} {}", S_fpath!(mdfile), PROGRESS); + + for rline in fs::read_to_string(fpath).expect("cannot read file").lines() { + let line = rline.trim(); + if line.is_empty() { continue } + + if let Some(stripped) = line.strip_prefix(PREFIX_OPEN) { + if RE_ROUTINES.is_match(stripped) { + newr.push(stripped.to_string()) + } else { + newt.push(stripped.to_string()) + } + } + + if from_logseq { + if let Some(stripped) = line.strip_prefix(PREFIX_OPEN_LOGSEQ) { + newt.push(stripped.to_string()) + } else if let Some(stripped) = line.strip_prefix(PREFIX_OPEN2_LOGSEQ) { + newl.push(stripped.to_string()) + } } } } - if newt.is_empty() && newr.is_empty() { + if newt.is_empty() && newr.is_empty() && newl.is_empty() { println!("{} found!", S_empty!("nothing")); return + } else { + println!("New tasks found in input file:"); + for task in &newt { + println!(" {} : {}", S_checkbox!(CHECKBOX), task); + } + for task in &newl { + println!(" {} : {} [{}]", S_checkbox!(CHECKBOX), task, S_checkbox!("LATER")); + } + for task in &newr { + println!(" {} : {}", S_checkbox!(ROUTINES), task); + } } - self.add_tasks(newt); + self.load(); self.add_tasks(newt); + if self.tbname == INBOX_BOXNAME { + self.add_tasks(newl); + } else { + self.sibling(INBOX_BOXNAME).add_tasks(newl); + } self.sibling(ROUTINE_BOXNAME).add_tasks(newr); } diff --git a/src/util.rs b/src/util.rs index ec0ddc6..c9d3985 100644 --- a/src/util.rs +++ b/src/util.rs @@ -40,9 +40,9 @@ pub fn path_normalize(path_str: &str) -> String { Path::new(path_str).normalize().unwrap() } -pub fn pick_file() -> String { +pub fn pick_file(dir: &str) -> String { run_fun!( - ls | fzf; + ls -r $dir | fzf -m --preview "cat '$dir/{}'"; ).unwrap_or_else(|_| std::process::exit(1) ) diff --git a/tests/taskbox.rs b/tests/taskbox.rs index 26b5e25..badb845 100644 --- a/tests/taskbox.rs +++ b/tests/taskbox.rs @@ -376,6 +376,11 @@ fn test_import_somefile_to_inbox() { - [ ] Task2 to import - [ ] {󰃯:d 2024-10-01Tue 󰳟} one daily to import - [ ] {󰃯:m 2024-10-31Mon 󰳟} one montly to import + +## belows are logseq style +- TODO Task to import to "Today" +- LATER Task to import also to "Inbox" +- DONE Task Done not to import "#; let (mut inbox, dir) = setup_test_taskbox(INBOX_BOXNAME); @@ -390,9 +395,13 @@ fn test_import_somefile_to_inbox() { assert_eq!(inbox.tasks.len(), 1); assert_eq!(routine.tasks.len(), 1); - inbox.import(Some(fpath.to_str().unwrap().to_string())); + inbox.import(Some(fpath.to_str().unwrap().to_string()), false); routine = inbox.sibling("routine"); //reset for force reload routine.load(); assert_eq!(inbox.tasks.len(), 4); assert_eq!(routine.tasks.len(), 3); + + // import again with "from_logseq" true + inbox.import(Some(fpath.to_str().unwrap().to_string()), true); + assert_eq!(inbox.tasks.len(), 6); }