148 lines
4.2 KiB
Rust
148 lines
4.2 KiB
Rust
use anyhow::{anyhow, Result};
|
|
use chrono::Utc;
|
|
use dialoguer::{theme::ColorfulTheme, Confirm, Select};
|
|
use std::{env, fs, process::Command};
|
|
use walkdir::WalkDir;
|
|
|
|
/// Get the path to the root directory of all notes.
|
|
fn get_base_path() -> Result<String> {
|
|
let home = env::var("HOME")?;
|
|
Ok(format!("{}/.rnote/", home))
|
|
}
|
|
|
|
/// Get path to a category/date directory.
|
|
fn get_path(category: &str) -> Result<String> {
|
|
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")?;
|
|
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.
|
|
pub fn find_path(header: &str) -> Result<String> {
|
|
let mut paths: Vec<String> = Vec::new();
|
|
let base = get_base_path()?;
|
|
let header = format!("{}.md", header);
|
|
for entry in WalkDir::new(base) {
|
|
let entry = entry?;
|
|
let p: String = match entry.path().to_str() {
|
|
Some(s) => s.to_owned(),
|
|
None => "".to_owned(),
|
|
};
|
|
let name: &str = match entry.file_name().to_str() {
|
|
Some(s) => s,
|
|
None => "",
|
|
};
|
|
if name == header {
|
|
paths.push(p);
|
|
}
|
|
}
|
|
if paths.is_empty() {
|
|
Err(anyhow!("Note not found."))
|
|
} else {
|
|
if paths.len() == 1 {
|
|
Ok(paths.remove(0))
|
|
} else {
|
|
let selection = Select::with_theme(&ColorfulTheme::default())
|
|
.with_prompt("Choose a note")
|
|
.default(0)
|
|
.items(&paths)
|
|
.interact()?;
|
|
Ok(paths.remove(selection))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Deletes 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()?
|
|
{
|
|
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")?;
|
|
let file = find_path(header)?;
|
|
Command::new(editor).arg(&file).status()?;
|
|
println!("Edited successfully!");
|
|
Ok(())
|
|
}
|
|
|
|
pub fn search_by_word(word: &str) -> Result<()> {
|
|
extern crate fstream;
|
|
let path = get_base_path()?;
|
|
for (_, file) in WalkDir::new(path)
|
|
.into_iter()
|
|
.filter_map(|file| file.ok())
|
|
.enumerate()
|
|
{
|
|
if file.metadata()?.is_file() {
|
|
match fstream::contains(file.path(), word) {
|
|
Some(b) => {
|
|
if b {
|
|
let path = file.path().to_str().unwrap_or("");
|
|
if !path.is_empty() {
|
|
println!("{}", path);
|
|
}
|
|
}
|
|
}
|
|
None => continue,
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|