From 90948a1c83c23f72c2baf8e9bd1b8757f40d1cc2 Mon Sep 17 00:00:00 2001 From: Volodymyr Patuta <6977238-fiplox@users.noreply.gitlab.com> Date: Fri, 27 Nov 2020 20:59:34 +0100 Subject: [PATCH] structure of project, arguments, patches --- src/rnote/app.rs | 59 ++++++++++++++++++++ src/rnote/mod.rs | 3 + src/rnote/notes.rs | 127 +++++++++++++++++++++++++++++++++++++++++++ src/rnote/process.rs | 50 +++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 src/rnote/app.rs create mode 100644 src/rnote/mod.rs create mode 100644 src/rnote/notes.rs create mode 100644 src/rnote/process.rs diff --git a/src/rnote/app.rs b/src/rnote/app.rs new file mode 100644 index 0000000..ac1169a --- /dev/null +++ b/src/rnote/app.rs @@ -0,0 +1,59 @@ +pub use clap::{App, AppSettings, Arg, SubCommand}; + +pub fn make_app() -> App<'static, 'static> { + App::new("rnote") + .version("0.0.0") + .author("Volodymyr Patuta ") + .about("Minimal note talking cli tool.") + .setting(AppSettings::ArgRequiredElseHelp) + .subcommand( + SubCommand::with_name("new") + .alias("n") + .about("Create new note") + .arg( + Arg::with_name("category") + .help("Create note in category.") + .short("c") + .long("category"), + ) + .arg(Arg::with_name("header").help("Give name to the file.")), + ) + .subcommand( + SubCommand::with_name("remove") + .alias("r") + .about("Remove a note.") + .arg(Arg::with_name("header").help("Name of the note.")), + ) + .subcommand( + SubCommand::with_name("edit") + .alias("e") + .about("Edit a note.") + .arg(Arg::with_name("header").help("Name of the note.")), + ) + .subcommand( + SubCommand::with_name("list") + .alias("l") + .alias("ls") + .alias("show") + .about("Show all notes or one note") + .arg(Arg::with_name("header").help("Name of the note.")) + .arg( + Arg::with_name("category") + .help("List all notes from a category.") + .short("c") + .long("category"), + ), + ) + .subcommand( + SubCommand::with_name("search") + .alias("s") + .about("Search a note.") + .arg(Arg::with_name("header").help("Name of the note.")) + .arg( + Arg::with_name("word") + .help("Search by word.") + .short("w") + .long("word"), + ), + ) +} diff --git a/src/rnote/mod.rs b/src/rnote/mod.rs new file mode 100644 index 0000000..6f082da --- /dev/null +++ b/src/rnote/mod.rs @@ -0,0 +1,3 @@ +pub mod app; +mod notes; +pub mod process; diff --git a/src/rnote/notes.rs b/src/rnote/notes.rs new file mode 100644 index 0000000..87130ea --- /dev/null +++ b/src/rnote/notes.rs @@ -0,0 +1,127 @@ +use anyhow::{anyhow, Result}; +use chrono::Utc; +use std::{env, fs, process::Command}; +use text_io::read; +use walkdir::WalkDir; + +/// Get the path to the root directory of all notes. +fn get_base_path() -> Result { + let home = env::var("HOME")?; + Ok(format!("{}/.rnote/", home)) +} + +/// Get path to a category/date directory. +fn get_path(category: &str) -> Result { + let base = get_base_path()?; + let date = Utc::now().format("%Y-%m-%d"); + match category.is_empty() { + true => Ok(format!("{}{}/", base, date)), + false => Ok(format!("{}{}/", base, category)), + } +} + +/// Create directory for a note. +pub fn create_dir(category: &str) -> Result<()> { + let path = get_base_path()?; + let date = Utc::now().format("%Y-%m-%d"); + match category.is_empty() { + true => fs::create_dir_all(format!("{}{}", path, date))?, + false => fs::create_dir_all(format!("{}{}", path, category))?, + } + Ok(()) +} + +/// Create a new note. +pub fn create(header: &str, category: &str) -> Result<()> { + let editor = env::var("EDITOR").unwrap_or("/bin/vi".to_owned()); + let file = format!("{}{}.md", get_path(category)?, header); + create_dir(category)?; + is_duplicate(header, category)?; + Command::new(editor).arg(&file).status()?; + Ok(()) +} + +/// Checks if potentially new note name already exists. +fn is_duplicate(header: &str, category: &str) -> Result<()> { + let file = format!("{}{}.md", get_path(category)?, header); + let path = format!("{}", get_path(category)?); + for entry in WalkDir::new(path) { + let entry = entry?; + let p: &str = match entry.path().to_str() { + Some(s) => s, + None => "", + }; + if p == file { + return Err(anyhow!( + "Duplicate in the same category/date. Choose another name." + )); + } + } + Ok(()) +} + +/// Finds a path to desired note. +fn find_path(header: &str) -> Result { + let mut paths: Vec = Vec::new(); + let base = get_base_path()?; + let header = format!("{}.md", header); + for entry in WalkDir::new(base) { + let entry = entry?; + let p: &str = match entry.path().to_str() { + Some(s) => s, + None => "", + }; + let name: &str = match entry.file_name().to_str() { + Some(s) => s, + None => "", + }; + if name == header { + paths.push(String::from(p)); + } + } + if paths.is_empty() { + Err(anyhow!("Note not found.")) + } else { + if paths.len() == 1 { + Ok(format!("{}.md", paths[0])) + } else { + let mut n: usize; + loop { + let mut i = 1; + println!("Choose one: \n"); + for path in &paths { + println!("{}\t {}", i, path); + i += 1; + } + n = read!(); + if n >= 1 && n <= paths.len() { + break; + } + } + Ok(format!("{}.md", paths[n])) + } + } +} + +/// Deletes a note. +pub fn remove(header: &str) -> Result<()> { + let path = find_path(header)?; + println!("Are you sure you want to delete {} [Y/n]", header); + let response: String = read!(); + if response == "y" || response == "Y" || response == "yes" || response == "Yes" { + println!("Deleting..."); + fs::remove_file(path)?; + println!("Successfully deleted."); + Ok(()) + } else { + Err(anyhow!("Abort.")) + } +} + +/// Modify a note. +pub fn modify(header: &str) -> Result<()> { + let editor = env::var("EDITOR").unwrap_or("/bin/vi".to_owned()); + let file = find_path(header)?; + Command::new(editor).arg(&file).status()?; + Ok(()) +} diff --git a/src/rnote/process.rs b/src/rnote/process.rs new file mode 100644 index 0000000..1a410b5 --- /dev/null +++ b/src/rnote/process.rs @@ -0,0 +1,50 @@ +use crate::rnote::notes; +use anyhow::Result; +use clap::ArgMatches; +use notes::modify; +use text_io::read; + +pub fn new(matches: &ArgMatches) -> Result<()> { + let header = match matches.value_of("header") { + Some(s) => s.to_owned(), + None => { + print!("Enter the name of your note: "); + read!() + } + }; + let category = matches.value_of("category").unwrap_or(""); + + notes::create(&header, category)?; + Ok(()) +} +pub fn remove(matches: &ArgMatches) -> Result<()> { + let header = match matches.value_of("header") { + Some(s) => s.to_owned(), + None => { + print!("Enter the name of your note: "); + read!() + } + }; + notes::remove(&header)?; + Ok(()) +} +pub fn edit(matches: &ArgMatches) -> Result<()> { + let header = match matches.value_of("header") { + Some(s) => s.to_owned(), + None => { + print!("Enter the name of your note: "); + read!() + } + }; + + notes::modify(&header)?; + Ok(()) +} +pub fn list(matches: &ArgMatches) -> Result<()> { + unimplemented!("list all notes, one note or category"); + Ok(()) +} +pub fn search(matches: &ArgMatches) -> Result<()> { + unimplemented!("Search a note by header or by word."); + Ok(()) +}