diff --git a/Cargo.lock b/Cargo.lock index 29a8777..b1cdbfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,7 @@ dependencies = [ "strsim", "termcolor", "textwrap", + "unicase", ] [[package]] @@ -697,6 +698,9 @@ name = "textwrap" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +dependencies = [ + "unicode-width", +] [[package]] name = "time" @@ -753,6 +757,15 @@ dependencies = [ "serde", ] +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.7" diff --git a/Cargo.toml b/Cargo.toml index e169a8b..478cea3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ sled = { version = "0.34.7", features = ["compression"] } bincode = "1.3.3" paris = { version = "1.5", features = ["macros"] } serde = { version = "1.0", features = ["derive"] } -clap = { version = "3.0.0-rc.9", features = ["derive"] } +clap = { version = "3.0.0-rc.9", features = ["derive","color","suggestions","unicode"] } exitcode = "1.1.2" serde_json = "1.0" chrono = { version = "0.4", features = ["serde"] } diff --git a/src/commands.rs b/src/commands.rs index bf220cb..febcbe0 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,5 +1,5 @@ use crate::database; -use crate::structures::{Bookmark, Container, ContainerTypes}; +use crate::structures::{Bookmark, Container, ContainerTypes, Keyspace}; use chrono::Utc; use paris::*; use serde_json::json; @@ -13,7 +13,7 @@ use uuid_simd::UuidExt; use dialoguer::{console::Term, theme::ColorfulTheme, Select}; -pub fn edit(json: bool, url: &Option, path: Option) { +pub fn edit_bookmark(json: bool, url: &Option, path: Option) { if json { println!( "{}", @@ -28,7 +28,7 @@ pub fn edit(json: bool, url: &Option, path: Option) { Some(link) => { println!("User selected item :\n{}", link); } - None => match database::get_all(json, path) { + None => match database::get_all(json, path, Keyspace::Bookmarks) { Some(bookmarks) => { let mut items: Vec<&Url> = Vec::new(); for i in &bookmarks { @@ -41,7 +41,12 @@ pub fn edit(json: bool, url: &Option, path: Option) { .unwrap(); match selection { - Some(index) => println!("User selected item :\n{}", bookmarks[index]), + Some(index) => { + println!( + "User selected item :\n{}", + bookmarks[index] + ); + } None => println!("User did not select anything"), } } @@ -63,7 +68,7 @@ pub fn edit(json: bool, url: &Option, path: Option) { } } -pub fn add( +pub fn add_bookmark( url: &Url, name: &String, description: &Option, @@ -80,7 +85,7 @@ pub fn add( created_at: Utc::now(), }; - database::insert_entry(&bookmark, json, path); + database::insert_entry(json, path, Keyspace::Bookmarks, &bookmark); if json { println!("{}", serde_json::to_string(&bookmark).unwrap()); } else { @@ -89,14 +94,17 @@ pub fn add( }; } -pub fn list(json: bool, path: Option) { - match database::get_all(json, path) { +pub fn list_bookmarks(json: bool, path: Option) { + match database::get_all(json, path, Keyspace::Bookmarks) { Some(bookmarks) => { for i in bookmarks { if json { - println!("{}", serde_json::to_string(&i).unwrap()); + println!( + "{}", + serde_json::to_string(&i).unwrap() + ); } else { - println!("{}", i); + println!("{}", &i); } } } @@ -150,7 +158,7 @@ pub fn export(file_path: PathBuf, json: bool, path: Option) { }; let writer = BufWriter::new(file); - match database::get_all(json, path) { + match database::get_all(json, path, Keyspace::Bookmarks) { Some(bookmarks) => serde_json::to_writer(writer, &bookmarks).unwrap(), None => std::process::exit(exitcode::IOERR), } @@ -209,7 +217,7 @@ pub fn import(file_path: PathBuf, json: bool, store_path: Option) { } }; - database::insert_multiple(&bookmarks, json, store_path); + database::insert_multiple(&bookmarks, json, store_path, Keyspace::Bookmarks); if json { println!( diff --git a/src/database.rs b/src/database.rs index 9e4bf47..dab8d75 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,5 +1,5 @@ use crate::commands::env_err; -use crate::structures::Bookmark; +use crate::structures::{Bookmark, Keyspace}; use paris::*; use serde_json::json; @@ -7,7 +7,7 @@ use std::env; use std::path::PathBuf; use url::Url; -fn open_database(json: bool, path: Option) -> Option { +fn open_database(json: bool, path: Option, keyspace: Keyspace) -> Option { let db: sled::Db; let database_path = match path { Some(path) => path, @@ -18,7 +18,7 @@ fn open_database(json: bool, path: Option) -> Option { let mut tmp_path = PathBuf::new(); tmp_path.push(val); tmp_path.push(".local/share/tinymark"); - tmp_path.push("bookmarks_db"); + tmp_path.push("database"); tmp_path } Err(e) => { @@ -28,6 +28,7 @@ fn open_database(json: bool, path: Option) -> Option { } } }; + match sled::open(database_path) { Ok(database) => db = database, Err(error) => { @@ -46,11 +47,38 @@ fn open_database(json: bool, path: Option) -> Option { } }; - return Some(db); + let keyspace_str = match keyspace { + Keyspace::Bookmarks => "bookmarks", + Keyspace::Containers => "containers", + }; + + let database: Option = match db.open_tree(keyspace_str) { + Ok(nya) => Some(nya), + Err(e) => { + if json { + println!( + "{}", + json!({ + "status": "fail", + "reason": e.to_string(), + }) + ); + } else { + error!("error in opening Tree: {}", e); + } + None + } + }; + return database; } -pub fn insert_multiple(entries: &Vec, json: bool, path: Option) { - let db = match open_database(json, path) { +pub fn insert_multiple( + entries: &Vec, + json: bool, + path: Option, + keyspace: Keyspace, +) { + let db = match open_database(json, path, keyspace) { Some(database) => database, None => std::process::exit(exitcode::NOINPUT), }; @@ -102,8 +130,9 @@ pub fn insert_multiple(entries: &Vec, json: bool, path: Option) { - let db = match open_database(json, path) { +pub fn insert_entry(json: bool, path: Option, keyspace: Keyspace, _entry: &Bookmark) { + let entry = _entry; + let db = match open_database(json, path, keyspace) { Some(database) => database, None => std::process::exit(exitcode::NOINPUT), }; @@ -113,8 +142,7 @@ pub fn insert_entry(entry: &Bookmark, json: bool, path: Option) { Ok(result) => bytes = result, Err(error) => { if json { - println!( - "{}", + println!("{}", json!({ "status": "fail", "reason": error.to_string(), @@ -125,9 +153,11 @@ pub fn insert_entry(entry: &Bookmark, json: bool, path: Option) { } std::process::exit(exitcode::DATAERR); } - }; + } - match db.insert(entry.link.to_string(), bytes) { + let name = &entry.link.to_string(); + + match db.insert(&name, bytes) { Ok(_) => { if json { println!( @@ -138,7 +168,7 @@ pub fn insert_entry(entry: &Bookmark, json: bool, path: Option) { }) ); } else { - info!("succesfully inserted entry {}", entry.link); + info!("succesfully inserted entry {}", name); } } Err(error) => { @@ -151,7 +181,7 @@ pub fn insert_entry(entry: &Bookmark, json: bool, path: Option) { }) ); } else { - error!("failed to insert entry {}!\n {}", entry.link, error); + error!("failed to insert entry {}!\n {}", name, error); } std::process::exit(exitcode::IOERR); } @@ -160,8 +190,8 @@ pub fn insert_entry(entry: &Bookmark, json: bool, path: Option) { db.flush().unwrap(); } -pub fn remove_entry(link: &Url, json: bool, path: Option) { - let db = match open_database(json, path) { +pub fn remove_entry(link: &Url, json: bool, path: Option, keyspace: Keyspace) { + let db = match open_database(json, path, keyspace) { Some(database) => database, None => std::process::exit(exitcode::NOINPUT), }; @@ -187,14 +217,30 @@ pub fn remove_entry(link: &Url, json: bool, path: Option) { db.flush().unwrap(); } -pub fn get_all(json: bool, path: Option) -> Option> { - let db = match open_database(json, path) { +pub fn get_all(json: bool, path: Option, keyspace: Keyspace) -> Option> { + let db = match open_database(json, path, keyspace) { Some(database) => database, None => return None, }; let first_key = match db.first() { - Ok(pair) => pair.unwrap().0, + Ok(pair) => match pair { + Some(key) => key.0, + None => { + if json { + println!( + "{}", + json!({ + "status": "error", + "reason": "could not get first key", + }) + ); + } else { + error!("error in getting first key"); + } + std::process::exit(exitcode::IOERR); + } + }, Err(error) => { if json { println!( @@ -240,7 +286,6 @@ pub fn get_all(json: bool, path: Option) -> Option> { None => break, } } - return Some(bookmarks_vector); } diff --git a/src/main.rs b/src/main.rs index 0d71266..efbcfbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,11 @@ +mod commands; +mod database; pub mod structures; mod tests; -mod database; -mod commands; -use crate::structures::{Commands, Cli, Config}; +use crate::structures::{Cli, Commands, Config, Keyspace}; use clap::Parser; use paris::*; -use std::path::PathBuf; -use std::env; fn main() { let cfg: Config = confy::load("tinymark").unwrap(); @@ -21,7 +19,6 @@ fn main() { std::process::exit(exitcode::DATAERR); } - let args = Cli::parse(); let json: bool; @@ -33,11 +30,22 @@ fn main() { match &args.command { Commands::New_Folder { name } => commands::new_folder(name, json), - Commands::Add { url, name, description, tags } => commands::add(url, name, description, tags, json, cfg.storage_location), - Commands::Edit { url } => commands::edit(json, url, cfg.storage_location), - Commands::Delete { url } => database::remove_entry(url, json, cfg.storage_location), - Commands::List { } => commands::list(json, cfg.storage_location), - Commands::Export { file } => commands::export(file.to_path_buf(), json, cfg.storage_location), - Commands::Import { file } => commands::import(file.to_path_buf(), json, cfg.storage_location), + Commands::Add { + url, + name, + description, + tags, + } => commands::add_bookmark(url, name, description, tags, json, cfg.storage_location), + Commands::Edit { url } => commands::edit_bookmark(json, url, cfg.storage_location), + Commands::Delete { url } => { + database::remove_entry(url, json, cfg.storage_location, Keyspace::Bookmarks) + } + Commands::List {} => commands::list_bookmarks(json, cfg.storage_location), + Commands::Export { file } => { + commands::export(file.to_path_buf(), json, cfg.storage_location) + } + Commands::Import { file } => { + commands::import(file.to_path_buf(), json, cfg.storage_location) + } } } diff --git a/src/structures.rs b/src/structures.rs index c40597e..ead666e 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -24,15 +24,15 @@ impl Default for Config { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Bookmark { - //pub id: Uuid, - pub link: Url, - pub label: String, - pub description: Option, - pub tags: Vec, pub container: Option, pub created_at: DateTime, + pub description: Option, + pub label: String, + //pub id: Uuid, + pub link: Url, + pub tags: Vec, } fn do_nothing() { @@ -65,10 +65,26 @@ impl fmt::Display for Bookmark { #[derive(Serialize, Deserialize, Debug)] pub struct Container { - pub id: Uuid, - pub label: String, pub container: Option, pub container_type: ContainerTypes, + pub id: Uuid, + pub label: String, +} + +impl Container { + pub fn new( + container: Option, + container_type: ContainerTypes, + id: Uuid, + label: String, + ) -> Self { + Self { + container, + container_type, + id, + label, + } + } } #[derive(Serialize, Deserialize, Debug)] @@ -78,15 +94,28 @@ pub enum ContainerTypes { } #[derive(Serialize, Deserialize)] -pub enum Thingy { - Bookmark(Bookmark), - Container(Container), +pub enum Keyspace { + Bookmarks, + Containers, } +/* +impl Keyspace { + pub fn as_bookmarks(&self) -> Option<&Option> { + if let Self::Bookmarks(v) = self { + Some(v) + } else { + None + } + } -pub struct Heirarchy { - pub root: Container, - pub heirarchy: Vec>, -} + pub fn as_containers(&self) -> Option<&Option> { + if let Self::Containers(v) = self { + Some(v) + } else { + None + } + } +}*/ #[derive(Parser)] pub struct Cli {