tests + input split
This commit is contained in:
		
							parent
							
								
									81be618072
								
							
						
					
					
						commit
						045e65c170
					
				
							
								
								
									
										30
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,20 +1,19 @@ | ||||
| use anyhow::Result; | ||||
| use dialoguer::{theme::ColorfulTheme, Input}; | ||||
| use anyhow::{anyhow, Result}; | ||||
| use rnote::{app, process}; | ||||
| 
 | ||||
| mod rnote; | ||||
| 
 | ||||
| /// Check if variable `EDITOR` is set.
 | ||||
| /// Check if variable `EDITOR` and `XDG_DATA_HOME` are set.
 | ||||
| fn check() -> Result<()> { | ||||
|     let editor = std::env::var("EDITOR").unwrap_or("".to_owned()); | ||||
|     if editor.is_empty() { | ||||
|         let editor: String = Input::with_theme(&ColorfulTheme::default()) | ||||
|             .with_prompt("Your text editor") | ||||
|             .interact_text()?; | ||||
|         std::env::set_var("EDITOR", editor); | ||||
|     let data_home = std::env::var("XDG_DATA_HOME").unwrap_or("".to_owned()); | ||||
|     if editor.is_empty() || data_home.is_empty() { | ||||
|         Err(anyhow!( | ||||
|             "Please make sure variables EDITOR and XDG_DATA_HOME are set." | ||||
|         )) | ||||
|     } else { | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn main() -> Result<()> { | ||||
| @ -34,3 +33,14 @@ fn main() -> Result<()> { | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     #[ignore] | ||||
|     fn check_test() { | ||||
|         assert!(check().is_ok()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -7,12 +7,12 @@ use walkdir::WalkDir; | ||||
| 
 | ||||
| /// Get the path to the root directory of all notes.
 | ||||
| pub fn get_base_path() -> Result<String> { | ||||
|     let home = env::var("HOME")?; | ||||
|     let home = env::var("XDG_DATA_HOME")?; | ||||
|     Ok(format!("{}/.rnote/", home)) | ||||
| } | ||||
| 
 | ||||
| /// Get path to a category/date directory.
 | ||||
| fn get_path(category: &str) -> Result<String> { | ||||
| fn get_category_path(category: &str) -> Result<String> { | ||||
|     let base = get_base_path()?; | ||||
|     let date = Utc::now().format("%Y-%m-%d"); | ||||
|     match category.is_empty() { | ||||
| @ -21,6 +21,57 @@ fn get_path(category: &str) -> Result<String> { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Get all note paths.
 | ||||
| pub fn get_all_notes() -> Result<Vec<String>> { | ||||
|     let path = get_base_path()?; | ||||
|     let mut files: Vec<String> = Vec::new(); | ||||
|     for (_, file) in WalkDir::new(path) | ||||
|         .into_iter() | ||||
|         .filter_map(|file| file.ok()) | ||||
|         .enumerate() | ||||
|     { | ||||
|         if file.metadata()?.is_file() { | ||||
|             let p = file.path().to_str().unwrap_or(""); | ||||
|             if !p.is_empty() { | ||||
|                 files.push(p.to_owned()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if files.is_empty() { | ||||
|         Err(anyhow!("No notes found.")) | ||||
|     } else { | ||||
|         Ok(files) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Get all notes in category.
 | ||||
| pub fn get_notes_in_category(category: &str) -> Result<Vec<String>> { | ||||
|     let base = get_base_path()?; | ||||
|     let path = format!("{}{}", base, category); | ||||
|     let mut files: Vec<String> = Vec::new(); | ||||
|     if std::path::Path::new(&path).exists() { | ||||
|         for (_, file) in WalkDir::new(path) | ||||
|             .into_iter() | ||||
|             .filter_map(|file| file.ok()) | ||||
|             .enumerate() | ||||
|         { | ||||
|             if file.metadata()?.is_file() { | ||||
|                 let p = file.path().to_str().unwrap_or(""); | ||||
|                 if !p.is_empty() { | ||||
|                     files.push(p.to_owned()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if files.is_empty() { | ||||
|             Err(anyhow!("Category is empty.")) | ||||
|         } else { | ||||
|             Ok(files) | ||||
|         } | ||||
|     } else { | ||||
|         Err(anyhow!("Category no found.")) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Create directory for a note.
 | ||||
| pub fn create_dir(category: &str) -> Result<()> { | ||||
|     let base = get_base_path()?; | ||||
| @ -45,40 +96,8 @@ pub fn create_dir(category: &str) -> Result<()> { | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// Create a new note.
 | ||||
| pub fn create(header: &str, category: &str) -> Result<()> { | ||||
|     let editor = env::var("EDITOR")?; | ||||
|     let file = format!("{}{}.md", get_path(category)?, header); | ||||
|     create_dir(category)?; | ||||
|     is_duplicate(header, category)?; | ||||
|     let mut f = fs::File::create(&file)?; | ||||
|     f.set_permissions(fs::Permissions::from_mode(0o600))?; | ||||
|     f.write(format!("# {}\n", header).as_bytes())?; | ||||
|     Command::new(editor).arg(&file).status()?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// Check 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(()) | ||||
| } | ||||
| 
 | ||||
| /// Find a path to desired note.
 | ||||
| pub fn find_path(header: &str) -> Result<Option<String>> { | ||||
| pub fn get_note_path(header: &str) -> Result<Vec<String>> { | ||||
|     let mut paths: Vec<String> = Vec::new(); | ||||
|     let base = get_base_path()?; | ||||
|     let header = format!("{}.md", header); | ||||
| @ -99,60 +118,12 @@ pub fn find_path(header: &str) -> Result<Option<String>> { | ||||
|     if paths.is_empty() { | ||||
|         Err(anyhow!("Note not found.")) | ||||
|     } else { | ||||
|         if paths.len() == 1 { | ||||
|             Ok(Some(paths.remove(0))) | ||||
|         } else { | ||||
|             let selection = Select::with_theme(&ColorfulTheme::default()) | ||||
|                 .with_prompt("Optionally choose a note") | ||||
|                 .default(0) | ||||
|                 .items(&paths) | ||||
|                 .interact_opt()?; | ||||
|             match selection { | ||||
|                 Some(s) => Ok(Some(paths.remove(s))), | ||||
|                 None => Ok(None), | ||||
|             } | ||||
|         } | ||||
|         Ok(paths) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Delete a note.
 | ||||
| pub fn remove(header: &str) -> Result<()> { | ||||
|     let path = find_path(header)?; | ||||
|     if Confirm::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt(format!("Do you want to delete {}?", header)) | ||||
|         .interact()? | ||||
|         && path.is_some() | ||||
|     { | ||||
|         println!("Deleting..."); | ||||
|         fs::remove_file(path.unwrap())?; | ||||
|         remove_empty_dirs()?; | ||||
|         println!("Successfully deleted."); | ||||
|         Ok(()) | ||||
|     } else { | ||||
|         Err(anyhow!("Abort.")) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Modify a note.
 | ||||
| pub fn modify(header: &str) -> Result<()> { | ||||
|     let editor = env::var("EDITOR")?; | ||||
|     let file = find_path(header)?; | ||||
|     match file { | ||||
|         Some(f) => { | ||||
|             Command::new(editor).arg(f).status()?; | ||||
|             println!("Edited successfully!"); | ||||
|             Ok(()) | ||||
|         } | ||||
|         None => { | ||||
|             println!("Abort."); | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Search all notes that contain a given string.
 | ||||
| pub fn search_by_word(word: &str) -> Result<()> { | ||||
|     extern crate fstream; | ||||
| /// Find all notes that contain a given string.
 | ||||
| pub fn get_files_by_word(word: &str) -> Result<Vec<String>> { | ||||
|     let path = get_base_path()?; | ||||
|     let mut paths: Vec<String> = Vec::new(); | ||||
|     for (_, file) in WalkDir::new(path) | ||||
| @ -174,6 +145,103 @@ pub fn search_by_word(word: &str) -> Result<()> { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     Ok(paths) | ||||
| } | ||||
| 
 | ||||
| /// Create a new note.
 | ||||
| pub fn create(header: &str, category: &str) -> Result<()> { | ||||
|     let editor = env::var("EDITOR")?; | ||||
|     let file = format!("{}{}.md", get_category_path(category)?, header); | ||||
|     create_dir(category)?; | ||||
|     is_duplicate(header, category)?; | ||||
|     let mut f = fs::File::create(&file)?; | ||||
|     f.set_permissions(fs::Permissions::from_mode(0o600))?; | ||||
|     f.write(format!("# {}\n", header).as_bytes())?; | ||||
|     Command::new(editor).arg(&file).status()?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// Check if potentially new note name already exists.
 | ||||
| fn is_duplicate(header: &str, category: &str) -> Result<()> { | ||||
|     let file = format!("{}{}.md", get_category_path(category)?, header); | ||||
|     let path = format!("{}", get_category_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(()) | ||||
| } | ||||
| 
 | ||||
| /// Find a path to desired note and prompt to choose one to open.
 | ||||
| pub fn get_note_path_interractive(header: &str) -> Result<Option<String>> { | ||||
|     let mut paths: Vec<String> = get_note_path(header)?; | ||||
|     if paths.len() == 1 { | ||||
|         Ok(Some(paths.remove(0))) | ||||
|     } else { | ||||
|         let selection = Select::with_theme(&ColorfulTheme::default()) | ||||
|             .with_prompt("Optionally choose a note") | ||||
|             .default(0) | ||||
|             .items(&paths) | ||||
|             .interact_opt()?; | ||||
|         match selection { | ||||
|             Some(s) => Ok(Some(paths.remove(s))), | ||||
|             None => Ok(None), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Delete a note.
 | ||||
| pub fn remove_note(path: &str) -> Result<()> { | ||||
|     println!("Deleting..."); | ||||
|     fs::remove_file(path)?; | ||||
|     remove_empty_dirs()?; | ||||
|     println!("Successfully deleted."); | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// Prompt user to delete a note.
 | ||||
| pub fn remove_interractive(header: &str) -> Result<()> { | ||||
|     let path = get_note_path_interractive(header)?; | ||||
|     if Confirm::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt(format!("Do you want to delete {}?", header)) | ||||
|         .interact()? | ||||
|         && path.is_some() | ||||
|     { | ||||
|         remove_note(&path.unwrap())?; | ||||
|         Ok(()) | ||||
|     } else { | ||||
|         Err(anyhow!("Abort.")) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Modify a note.
 | ||||
| pub fn modify(header: &str) -> Result<()> { | ||||
|     let editor = env::var("EDITOR")?; | ||||
|     let file = get_note_path_interractive(header)?; | ||||
|     match file { | ||||
|         Some(f) => { | ||||
|             Command::new(editor).arg(f).status()?; | ||||
|             println!("Edited successfully!"); | ||||
|             Ok(()) | ||||
|         } | ||||
|         None => { | ||||
|             println!("Abort."); | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Prompt user to open one of found notes by word.
 | ||||
| pub fn search_by_word(word: &str) -> Result<()> { | ||||
|     let mut paths: Vec<String> = get_files_by_word(word)?; | ||||
|     let selection = Select::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("Optionally choose a note") | ||||
|         .default(0) | ||||
| @ -191,17 +259,7 @@ pub fn search_by_word(word: &str) -> Result<()> { | ||||
| 
 | ||||
| /// Show all notes.
 | ||||
| pub fn show_all() -> Result<()> { | ||||
|     let path = get_base_path()?; | ||||
|     let mut files: Vec<String> = Vec::new(); | ||||
|     for (_, file) in WalkDir::new(path) | ||||
|         .into_iter() | ||||
|         .filter_map(|file| file.ok()) | ||||
|         .enumerate() | ||||
|     { | ||||
|         if file.metadata()?.is_file() { | ||||
|             files.push(fs::read_to_string(file.path())?); | ||||
|         } | ||||
|     } | ||||
|     let mut files: Vec<String> = get_all_notes()?; | ||||
|     let skin = show::make_skin(); | ||||
|     let md = &files.join("---\n"); | ||||
|     show::run_app(skin, md)?; | ||||
| @ -210,7 +268,7 @@ pub fn show_all() -> Result<()> { | ||||
| 
 | ||||
| /// Show one note.
 | ||||
| pub fn show(header: &str) -> Result<()> { | ||||
|     let path = find_path(header)?; | ||||
|     let path = get_note_path_interractive(header)?; | ||||
|     match path { | ||||
|         Some(s) => { | ||||
|             let skin = show::make_skin(); | ||||
| @ -224,42 +282,16 @@ pub fn show(header: &str) -> Result<()> { | ||||
| 
 | ||||
| /// Show all notes in the given category.
 | ||||
| pub fn show_category(category: &str) -> Result<()> { | ||||
|     let base = get_base_path()?; | ||||
|     let path = format!("{}{}", base, category); | ||||
|     let mut files: Vec<String> = Vec::new(); | ||||
|     if std::path::Path::new(&path).exists() { | ||||
|         for (_, file) in WalkDir::new(path) | ||||
|             .into_iter() | ||||
|             .filter_map(|file| file.ok()) | ||||
|             .enumerate() | ||||
|         { | ||||
|             if file.metadata()?.is_file() { | ||||
|                 files.push(fs::read_to_string(file.path())?); | ||||
|             } | ||||
|         } | ||||
|         let skin = show::make_skin(); | ||||
|         let md = &files.join("---\n"); | ||||
|         show::run_app(skin, md)?; | ||||
|     } | ||||
|     let mut files: Vec<String> = get_notes_in_category(category)?; | ||||
|     let skin = show::make_skin(); | ||||
|     let md = &files.join("---\n"); | ||||
|     show::run_app(skin, md)?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// List all notes and optionally open one.
 | ||||
| pub fn list_all() -> Result<()> { | ||||
|     let path = get_base_path()?; | ||||
|     let mut files: Vec<String> = Vec::new(); | ||||
|     for (_, file) in WalkDir::new(path) | ||||
|         .into_iter() | ||||
|         .filter_map(|file| file.ok()) | ||||
|         .enumerate() | ||||
|     { | ||||
|         if file.metadata()?.is_file() { | ||||
|             let p = file.path().to_str().unwrap_or(""); | ||||
|             if !p.is_empty() { | ||||
|                 files.push(p.to_owned()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| /// List all notes and prompt to open one.
 | ||||
| pub fn list_all_notes() -> Result<()> { | ||||
|     let mut files: Vec<String> = get_all_notes()?; | ||||
|     let selection = Select::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("Optionally choose a note") | ||||
|         .default(0) | ||||
| @ -276,33 +308,17 @@ pub fn list_all() -> Result<()> { | ||||
| 
 | ||||
| /// List all notes in the given category and optionally open one.
 | ||||
| pub fn list_category(category: &str) -> Result<()> { | ||||
|     let base = get_base_path()?; | ||||
|     let path = format!("{}{}", base, category); | ||||
|     let mut files: Vec<String> = Vec::new(); | ||||
|     if std::path::Path::new(&path).exists() { | ||||
|         for (_, file) in WalkDir::new(path) | ||||
|             .into_iter() | ||||
|             .filter_map(|file| file.ok()) | ||||
|             .enumerate() | ||||
|         { | ||||
|             if file.metadata()?.is_file() { | ||||
|                 let p = file.path().to_str().unwrap_or(""); | ||||
|                 if !p.is_empty() { | ||||
|                     files.push(p.to_owned()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         let selection = Select::with_theme(&ColorfulTheme::default()) | ||||
|             .with_prompt("Optionally choose a note") | ||||
|             .default(0) | ||||
|             .items(&files) | ||||
|             .interact_opt()?; | ||||
|         if let Some(selection) = selection { | ||||
|             let editor = std::env::var("EDITOR")?; | ||||
|             std::process::Command::new(editor) | ||||
|                 .arg(files.remove(selection)) | ||||
|                 .status()?; | ||||
|         } | ||||
|     let mut files: Vec<String> = get_notes_in_category(category)?; | ||||
|     let selection = Select::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("Optionally choose a note") | ||||
|         .default(0) | ||||
|         .items(&files) | ||||
|         .interact_opt()?; | ||||
|     if let Some(selection) = selection { | ||||
|         let editor = std::env::var("EDITOR")?; | ||||
|         std::process::Command::new(editor) | ||||
|             .arg(files.remove(selection)) | ||||
|             .status()?; | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
| @ -346,3 +362,50 @@ fn remove_empty_dirs() -> Result<()> { | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn get_base_path_test() { | ||||
|         assert!(get_base_path().is_ok()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn find_by_word_test() { | ||||
|         assert!(get_files_by_word("test").is_ok()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn get_note_path_test() { | ||||
|         assert!(get_note_path("test").is_ok()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn get_category_path_create_dir_test() { | ||||
|         assert!(create_dir("test").is_ok()); | ||||
|         assert!(get_category_path("test").is_ok()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn create_remove_test() { | ||||
|         assert!(create("test", "test").is_ok()); | ||||
|         assert!(remove_note("test").is_ok()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn remove_empty_dirs_test() { | ||||
|         assert!(remove_empty_dirs().is_ok()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn wipe_date_test() { | ||||
|         assert!(wipe_date("1999-10-10").is_ok()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn get_notes_in_category_test() { | ||||
|         assert!(get_notes_in_category("test").is_ok()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -20,7 +20,7 @@ pub fn new(matches: &ArgMatches) -> Result<()> { | ||||
| /// Process argument `remove`.
 | ||||
| pub fn remove(matches: &ArgMatches) -> Result<()> { | ||||
|     match matches.value_of("header") { | ||||
|         Some(s) => notes::remove(s)?, | ||||
|         Some(s) => notes::remove_interractive(s)?, | ||||
|         None => match matches.is_present("date") { | ||||
|             true => { | ||||
|                 let date: String = Input::with_theme(&ColorfulTheme::default()) | ||||
| @ -33,7 +33,7 @@ pub fn remove(matches: &ArgMatches) -> Result<()> { | ||||
|                 let header: String = Input::with_theme(&ColorfulTheme::default()) | ||||
|                     .with_prompt("Name of your note") | ||||
|                     .interact_text()?; | ||||
|                 notes::remove(&header)?; | ||||
|                 notes::remove_interractive(&header)?; | ||||
|             } | ||||
|         }, | ||||
|     } | ||||
| @ -62,7 +62,7 @@ pub fn list(matches: &ArgMatches) -> Result<()> { | ||||
|                 .interact_text()?; | ||||
|             notes::list_category(&s)?; | ||||
|         } | ||||
|         false => notes::list_all()?, | ||||
|         false => notes::list_all_notes()?, | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
| @ -71,7 +71,7 @@ pub fn list(matches: &ArgMatches) -> Result<()> { | ||||
| pub fn search(matches: &ArgMatches) -> Result<()> { | ||||
|     match matches.value_of("header") { | ||||
|         Some(s) => { | ||||
|             let p = notes::find_path(s)?; | ||||
|             let p = notes::get_note_path_interractive(s)?; | ||||
|             match p { | ||||
|                 Some(s) => { | ||||
|                     let editor = std::env::var("EDITOR")?; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Volodymyr Patuta
						Volodymyr Patuta