tests + input split
This commit is contained in:
parent
81be618072
commit
045e65c170
30
src/main.rs
30
src/main.rs
@ -1,21 +1,20 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut app = app::make_app();
|
||||
@ -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 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,22 +308,7 @@ 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 mut files: Vec<String> = get_notes_in_category(category)?;
|
||||
let selection = Select::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Optionally choose a note")
|
||||
.default(0)
|
||||
@ -303,7 +320,6 @@ pub fn list_category(category: &str) -> Result<()> {
|
||||
.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…
Reference in New Issue
Block a user