From 8206cc61055b4d8cc36586cfedf3026a84e8cdd2 Mon Sep 17 00:00:00 2001 From: Fries Date: Sat, 1 Jul 2023 18:27:03 -0700 Subject: [PATCH 1/7] make the cli more portable, and add logging the cli now uses a new module in the shared crate, called directories which is a module that has functions for directory stuff, mainly, it uses the directories crate to have a portable way to have directories for the application like data directories and config directories. i also added logging to the shared and cli crates, so you can see debug stuff with the RUST_LOG environment variable. --- .vscode/launch.json | 24 +++--- Cargo.lock | 174 ++++++++++++++++++++++++++++++++++---- cli/Cargo.toml | 5 ++ cli/src/arguments.rs | 4 +- cli/src/commands.rs | 32 ++++--- cli/src/main.rs | 31 +++++-- shared/Cargo.toml | 2 + shared/src/directories.rs | 35 ++++++++ shared/src/errors.rs | 18 ++-- shared/src/lib.rs | 1 + shared/src/names.rs | 46 +++++++++- 11 files changed, 319 insertions(+), 53 deletions(-) create mode 100644 shared/src/directories.rs diff --git a/.vscode/launch.json b/.vscode/launch.json index 10efcb2..abb9e2b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,13 +4,17 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug", - "program": "${workspaceFolder}/", - "args": [], - "cwd": "${workspaceFolder}" - } - ] -} \ No newline at end of file + { + "type": "lldb", + "request": "launch", + "name": "run meowy-cli", + "program": "${workspaceFolder}/target/debug/meowy-cli", + "args": ["print"], + "cwd": "${workspaceFolder}", + "sourceMap": {}, + "sourceLanguages": [ + "rust" + ], + } + ] +} diff --git a/Cargo.lock b/Cargo.lock index d7d739a..2361b82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,7 +53,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -63,7 +63,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -253,9 +253,11 @@ name = "cli" version = "0.1.0" dependencies = [ "clap", + "log", "serde", "serde_json", "shared", + "simple_logger", ] [[package]] @@ -337,6 +339,27 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "either" version = "1.8.1" @@ -366,7 +389,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -653,7 +676,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -665,7 +688,7 @@ dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -790,7 +813,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -858,6 +881,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -882,7 +911,7 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", "windows-targets", ] @@ -995,6 +1024,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1004,6 +1042,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "ref-cast" version = "1.0.16" @@ -1187,7 +1236,7 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1287,6 +1336,8 @@ dependencies = [ name = "shared" version = "0.1.0" dependencies = [ + "directories", + "log", "serde", "serde_json", ] @@ -1300,6 +1351,16 @@ dependencies = [ "libc", ] +[[package]] +name = "simple_logger" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2230cd5c29b815c9b699fb610b49a5ed65588f3509d9f0108be3a885da629333" +dependencies = [ + "log", + "windows-sys 0.42.0", +] + [[package]] name = "slab" version = "0.4.8" @@ -1375,9 +1436,29 @@ dependencies = [ "autocfg", "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1432,7 +1513,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1707,6 +1788,21 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1722,51 +1818,93 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a79600c..58e580d 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] serde = "1.0" serde_json = "1.0" +log = "0.4" [dependencies.clap] version = "4" @@ -14,6 +15,10 @@ features = ["derive"] [dependencies.shared] path = "../shared" +[dependencies.simple_logger] +version = "4" +default-features = false + [[bin]] name = "meowy-cli" path = "src/main.rs" diff --git a/cli/src/arguments.rs b/cli/src/arguments.rs index 952da36..713c70b 100644 --- a/cli/src/arguments.rs +++ b/cli/src/arguments.rs @@ -3,8 +3,8 @@ use clap::{arg, command, Args, Parser, Subcommand}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] pub(crate) struct Arguments { - #[arg(help = "the path to the names.json file")] - pub(crate) path: String, + #[arg(help = "the path to the names.json file", long, short)] + pub(crate) path: Option, #[command(subcommand)] pub(crate) command: Commands, } diff --git a/cli/src/commands.rs b/cli/src/commands.rs index e2cdebb..4668d71 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use shared::{ errors::{Error, ErrorStatus}, names::Site, @@ -5,50 +7,58 @@ use shared::{ use crate::arguments::PrintGroup; -fn read_names(path: &String) -> Result, Error> { - match std::fs::read_to_string(path) { +fn read_names(path: &Path) -> Result, Error> { + match shared::names::read_names_file(path) { Ok(names) => { return shared::names::load_names(names); } Err(err) => Err(Error { status: ErrorStatus::IOError, - data: err.to_string(), + data: err.data, }), } } -pub(crate) fn print(path: &String, group: &PrintGroup) -> Result<(), Error> { +pub(crate) fn print(path: &Path, group: &PrintGroup) -> Result<(), Error> { let names = read_names(path)?; Ok(for site in names { if group.name { - println!("{}", site.name.unwrap_or_default()); + log::info!("{}", site.name.unwrap_or_default()); continue; } if group.url { - println!("{}", site.url); + log::info!("{}", site.url); continue; } - println!("{:?}", site); + log::info!("{:?}", site); }) } -pub(crate) fn add(path: &String, url: &String, name: &Option) -> Result<(), Error> { +pub(crate) fn add(path: &Path, url: &String, name: &Option) -> Result<(), Error> { let mut names = read_names(path)?; let site = Site { url: url.to_string(), name: name.to_owned(), }; - names.push(site); + log::debug!("adding {:?} to {}", site, path.display()); + names.push(site.clone()); let json = serde_json::to_string(&names).unwrap(); std::fs::write(path, json).unwrap(); + log::info!("added {:?} to names.json", site); Ok(()) } -pub(crate) fn remove(path: &String, url: &String) -> Result<(), Error> { +pub(crate) fn remove(path: &Path, url: &String) -> Result<(), Error> { let mut names = read_names(path)?; - names.retain(|site| &site.url != url); + names.retain(|site| { + if &site.url == url { + log::info!("removing {:?} from names.json", site); + } + &site.url != url + }); let json = serde_json::to_string(&names).unwrap(); std::fs::write(path, json).unwrap(); + Ok(()) } diff --git a/cli/src/main.rs b/cli/src/main.rs index 621e6b3..98f76fd 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,18 +1,39 @@ +use std::path::Path; + use arguments::{Arguments, Commands}; use clap::Parser; use commands::{add, print, remove}; -use shared::errors::Error; +use log::LevelFilter; +use shared::{ + directories, + errors::{Error, ErrorStatus}, +}; +use simple_logger::SimpleLogger; mod arguments; mod commands; -fn main() -> Result<(), Error> { +fn main() -> Result<(), Error> { + if let Err(err) = SimpleLogger::new().with_level(LevelFilter::Info).env().init() { + return Err(Error { + status: ErrorStatus::LoggerInitializationError, + data: err.to_string(), + }); + } + + let directory = directories::get_project_dir()?; let args = Arguments::parse(); + let default_path = directories::get_file_from_directory(directory.data_dir(), "names.json")?; + + let path = match &args.path { + Some(path) => Path::new(path), + None => &default_path, + }; match &args.command { - Commands::Print { group } => print(&args.path, &group)?, - Commands::Add { url, name } => add(&args.path, url, name)?, - Commands::Remove { url } => remove(&args.path, url)?, + Commands::Print { group } => print(path, &group)?, + Commands::Add { url, name } => add(path, url, name)?, + Commands::Remove { url } => remove(path, url)?, }; Ok(()) diff --git a/shared/Cargo.toml b/shared/Cargo.toml index aa08613..aba26a9 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" [dependencies] serde_json = "1.0" +directories = "5.0" +log = "0.4" [dependencies.serde] version = "1.0" diff --git a/shared/src/directories.rs b/shared/src/directories.rs new file mode 100644 index 0000000..195cf46 --- /dev/null +++ b/shared/src/directories.rs @@ -0,0 +1,35 @@ +use std::path::{Path, PathBuf}; + +use directories::ProjectDirs; + +use crate::errors::{Error, ErrorStatus, DIRECTORIES_ERROR_MESSAGE}; + +pub fn get_project_dir() -> Result { + match ProjectDirs::from("moe", "solarpunk", "meowy-webring") { + Some(project) => Ok(project), + None => Err(Error { + status: ErrorStatus::DirectoriesError, + data: DIRECTORIES_ERROR_MESSAGE.into(), + }), + } +} + +pub fn get_file_from_directory(path: &Path, filename: &str) -> Result { + if !path.exists() { + create_directory(path)?; + } + Ok(path.join(filename)) +} + +fn create_directory(path: &Path) -> Result<(), Error> { + match std::fs::create_dir_all(path) { + Ok(_) => { + log::debug!("created the directory {}", path.display()); + Ok(()) + } + Err(err) => Err(Error { + status: ErrorStatus::IOError, + data: err.to_string(), + }), + } +} diff --git a/shared/src/errors.rs b/shared/src/errors.rs index 8a033d5..22d62ef 100644 --- a/shared/src/errors.rs +++ b/shared/src/errors.rs @@ -1,16 +1,24 @@ #[derive(Debug)] pub enum ErrorStatus { IOError, - ParsingError + ParsingError, + DirectoriesError, + LoggerInitializationError } pub struct Error { pub status: ErrorStatus, - pub data: String + pub data: String, } +pub(crate) static DIRECTORIES_ERROR_MESSAGE: &str = "could not retreive a valid home path from the operating system. maybe try to define the HOME enviroment variable if you\'re on a unix or unix like operating system."; + impl core::fmt::Debug for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "A {:?} error has occured.\nDetails: {}", self.status, self.data) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "A {:?} error has occured.\nDetails: {}", + self.status, self.data + ) + } } diff --git a/shared/src/lib.rs b/shared/src/lib.rs index c986a43..cc9af96 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -1,2 +1,3 @@ pub mod names; pub mod errors; +pub mod directories; diff --git a/shared/src/names.rs b/shared/src/names.rs index 6408d6c..3683914 100644 --- a/shared/src/names.rs +++ b/shared/src/names.rs @@ -1,8 +1,10 @@ +use std::path::Path; + use serde::{Deserialize, Serialize}; use crate::errors::{Error, ErrorStatus}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Site { pub url: String, pub name: Option, @@ -10,10 +12,50 @@ pub struct Site { pub fn load_names(names: String) -> Result, Error> { match serde_json::from_str::>(&names) { - Ok(content) => Ok(content), + Ok(content) => { + log::debug!("successfully parsed names.json."); + Ok(content) + } Err(err) => Err(Error { status: ErrorStatus::ParsingError, data: err.to_string(), }), } } + +pub fn read_names_file(path: &Path) -> Result { + if !path.exists() { + log::debug!( + "the names.json file does not exist at {}. creating names.json", + path.display() + ); + create_names_file(path)? + } + + match std::fs::read_to_string(path) { + Ok(data) => { + log::debug!( + "successfully read the names.json file at {}", + path.display() + ); + Ok(data) + } + Err(err) => Err(Error { + status: ErrorStatus::IOError, + data: err.to_string(), + }), + } +} + +fn create_names_file(path: &Path) -> Result<(), Error> { + match std::fs::write(path, "[]") { + Ok(_) => { + log::debug!("created a names.json file at {}", path.display()); + Ok(()) + } + Err(err) => Err(Error { + status: ErrorStatus::IOError, + data: err.to_string(), + }), + } +} From b58b0cd78f8806c7917d068e6033d0c26b9bbd51 Mon Sep 17 00:00:00 2001 From: Fries Date: Sat, 1 Jul 2023 19:49:48 -0700 Subject: [PATCH 2/7] meowy_webring::main now uses the shared crate this has abstractions for getting the names.json file and reading them and parsing them. i also moved the cli logger initialization code into its own module. --- Cargo.lock | 2 ++ Cargo.toml | 7 +++++++ cli/src/logging.rs | 18 ++++++++++++++++++ cli/src/main.rs | 21 +++++---------------- shared/src/directories.rs | 5 +++++ shared/src/names.rs | 6 ++---- src/main.rs | 9 +++++++-- 7 files changed, 46 insertions(+), 22 deletions(-) create mode 100644 cli/src/logging.rs diff --git a/Cargo.lock b/Cargo.lock index 2361b82..16629f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -767,11 +767,13 @@ version = "0.1.0" dependencies = [ "askama", "askama_rocket", + "log", "rocket", "rust-embed", "serde", "serde_json", "shared", + "simple_logger", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index aa5abe7..40bebfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +log = "0.4" + [dependencies.rocket] version = "=0.5.0-rc.3" default_features = false @@ -38,3 +41,7 @@ default-features = false [dependencies.shared] path = "./shared" + +[dependencies.simple_logger] +version = "4" +default-features = false diff --git a/cli/src/logging.rs b/cli/src/logging.rs new file mode 100644 index 0000000..112ccf6 --- /dev/null +++ b/cli/src/logging.rs @@ -0,0 +1,18 @@ +use log::LevelFilter; +use shared::errors::{Error, ErrorStatus}; +use simple_logger::SimpleLogger; + +pub fn initialize_logger() -> Result<(), Error> { + if let Err(err) = SimpleLogger::new() + .with_level(LevelFilter::Info) + .env() + .init() + { + return Err(Error { + status: ErrorStatus::LoggerInitializationError, + data: err.to_string(), + }); + } + + Ok(()) +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 98f76fd..508a927 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,29 +1,18 @@ use std::path::Path; - use arguments::{Arguments, Commands}; use clap::Parser; use commands::{add, print, remove}; -use log::LevelFilter; -use shared::{ - directories, - errors::{Error, ErrorStatus}, -}; -use simple_logger::SimpleLogger; +use shared::{directories, errors::Error}; mod arguments; mod commands; +mod logging; fn main() -> Result<(), Error> { - if let Err(err) = SimpleLogger::new().with_level(LevelFilter::Info).env().init() { - return Err(Error { - status: ErrorStatus::LoggerInitializationError, - data: err.to_string(), - }); - } - - let directory = directories::get_project_dir()?; + logging::initialize_logger()?; + + let default_path = directories::get_names_path()?; let args = Arguments::parse(); - let default_path = directories::get_file_from_directory(directory.data_dir(), "names.json")?; let path = match &args.path { Some(path) => Path::new(path), diff --git a/shared/src/directories.rs b/shared/src/directories.rs index 195cf46..8f56795 100644 --- a/shared/src/directories.rs +++ b/shared/src/directories.rs @@ -21,6 +21,11 @@ pub fn get_file_from_directory(path: &Path, filename: &str) -> Result Result { + let directory = get_project_dir()?; + return get_file_from_directory(directory.data_dir(), "names.json"); +} + fn create_directory(path: &Path) -> Result<(), Error> { match std::fs::create_dir_all(path) { Ok(_) => { diff --git a/shared/src/names.rs b/shared/src/names.rs index 3683914..734587e 100644 --- a/shared/src/names.rs +++ b/shared/src/names.rs @@ -1,8 +1,6 @@ -use std::path::Path; - -use serde::{Deserialize, Serialize}; - use crate::errors::{Error, ErrorStatus}; +use serde::{Deserialize, Serialize}; +use std::path::Path; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Site { diff --git a/src/main.rs b/src/main.rs index ae43a88..505df51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use shared::{directories, names}; + #[macro_use] extern crate rocket; @@ -7,8 +9,11 @@ mod routes; #[launch] fn rocket() -> _ { - let names_file = std::fs::read_to_string("names.json").unwrap(); - let names = shared::names::load_names(names_file).unwrap(); + let names_path = directories::get_names_path().unwrap(); + println!("names.json path: {}", names_path.display()); + let names_file = names::read_names_file(&names_path).unwrap(); + let names = names::load_names(names_file).unwrap(); + rocket::build() .manage(names) .mount( From 7f36560e25915268404c666aed451a3b20b9188f Mon Sep 17 00:00:00 2001 From: Fries Date: Sat, 1 Jul 2023 20:34:28 -0700 Subject: [PATCH 3/7] add a seperator character this means you can print both the url and the name at the same time with a seperator character for easy parsing. --- cli/src/arguments.rs | 12 ++++++++-- cli/src/commands.rs | 53 +++++++++++++++++++++++--------------------- cli/src/main.rs | 2 +- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/cli/src/arguments.rs b/cli/src/arguments.rs index 713c70b..9dd4064 100644 --- a/cli/src/arguments.rs +++ b/cli/src/arguments.rs @@ -15,6 +15,14 @@ pub(crate) enum Commands { Print { #[command(flatten)] group: PrintGroup, + #[arg( + long, + short, + help = "a seperator character to seperate the url from the name. defaults to ,", + requires = "url", + requires = "name" + )] + seperator: Option, }, #[command(about = "add a site to the webring")] Add { @@ -48,8 +56,8 @@ pub(crate) enum Commands { #[derive(Args, Debug)] #[group(required = false)] pub struct PrintGroup { - #[arg(long, short, action = clap::ArgAction::SetTrue, conflicts_with = "name", help = "print the url only")] + #[arg(long, short, action = clap::ArgAction::SetTrue, help = "print the url only")] pub(crate) url: bool, - #[arg(long, short, action = clap::ArgAction::SetTrue, conflicts_with = "url", help = "print the name only")] + #[arg(long, short, action = clap::ArgAction::SetTrue, help = "print the name only")] pub(crate) name: bool, } diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 4668d71..52d16b4 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -1,42 +1,43 @@ use std::path::Path; -use shared::{ - errors::{Error, ErrorStatus}, - names::Site, -}; +use shared::names; +use shared::{errors::Error, names::Site}; use crate::arguments::PrintGroup; -fn read_names(path: &Path) -> Result, Error> { - match shared::names::read_names_file(path) { - Ok(names) => { - return shared::names::load_names(names); - } - Err(err) => Err(Error { - status: ErrorStatus::IOError, - data: err.data, - }), - } -} - -pub(crate) fn print(path: &Path, group: &PrintGroup) -> Result<(), Error> { - let names = read_names(path)?; +pub(crate) fn print(path: &Path, group: &PrintGroup, seperator: &Option) -> Result<(), Error> { + let names_file = names::read_names_file(path)?; + let names = names::load_names(names_file)?; Ok(for site in names { - if group.name { - log::info!("{}", site.name.unwrap_or_default()); + if !group.url && !group.name { + log::info!("{:?}", site); continue; } + + let mut string = String::new(); + let delimiter = seperator.unwrap_or(','); + if group.url { - log::info!("{}", site.url); - continue; + string += &site.url; } - log::info!("{:?}", site); + + if group.name { + if !string.is_empty() { + string += &format!("{}{}", delimiter, site.name.unwrap_or("None".to_string())) + } else { + string += &site.name.unwrap_or("None".to_string()); + } + } + + log::info!("{}", string); }) } pub(crate) fn add(path: &Path, url: &String, name: &Option) -> Result<(), Error> { - let mut names = read_names(path)?; + let names_file = names::read_names_file(path)?; + let mut names = names::load_names(names_file)?; + let site = Site { url: url.to_string(), name: name.to_owned(), @@ -50,7 +51,9 @@ pub(crate) fn add(path: &Path, url: &String, name: &Option) -> Result<() } pub(crate) fn remove(path: &Path, url: &String) -> Result<(), Error> { - let mut names = read_names(path)?; + let names_file = names::read_names_file(path)?; + let mut names = names::load_names(names_file)?; + names.retain(|site| { if &site.url == url { log::info!("removing {:?} from names.json", site); diff --git a/cli/src/main.rs b/cli/src/main.rs index 508a927..454ed42 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -20,7 +20,7 @@ fn main() -> Result<(), Error> { }; match &args.command { - Commands::Print { group } => print(path, &group)?, + Commands::Print { group, seperator } => print(path, group, seperator)?, Commands::Add { url, name } => add(path, url, name)?, Commands::Remove { url } => remove(path, url)?, }; From e93b201d7735f2025c540bb403a29279ef3b3f8d Mon Sep 17 00:00:00 2001 From: Fries Date: Sat, 1 Jul 2023 20:39:56 -0700 Subject: [PATCH 4/7] move command group printing logic into function this makes the print function a bit cleaner. --- cli/src/commands.rs | 46 +++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 52d16b4..0e9ba32 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -5,33 +5,43 @@ use shared::{errors::Error, names::Site}; use crate::arguments::PrintGroup; -pub(crate) fn print(path: &Path, group: &PrintGroup, seperator: &Option) -> Result<(), Error> { +fn group_printing(seperator: &Option, site: Site, group: &PrintGroup) { + let mut string = String::new(); + let delimiter = seperator.unwrap_or(','); + + if group.url { + string += &site.url; + } + + if group.name { + if !string.is_empty() { + string += &format!("{}{}", delimiter, site.name.unwrap_or("None".to_string())) + } else { + string += &site.name.unwrap_or("None".to_string()); + } + } + + log::info!("{}", string); +} + +pub(crate) fn print( + path: &Path, + group: &PrintGroup, + seperator: &Option, +) -> Result<(), Error> { let names_file = names::read_names_file(path)?; let names = names::load_names(names_file)?; - Ok(for site in names { + for site in names { if !group.url && !group.name { log::info!("{:?}", site); continue; } - let mut string = String::new(); - let delimiter = seperator.unwrap_or(','); + group_printing(seperator, site, group); + } - if group.url { - string += &site.url; - } - - if group.name { - if !string.is_empty() { - string += &format!("{}{}", delimiter, site.name.unwrap_or("None".to_string())) - } else { - string += &site.name.unwrap_or("None".to_string()); - } - } - - log::info!("{}", string); - }) + Ok(()) } pub(crate) fn add(path: &Path, url: &String, name: &Option) -> Result<(), Error> { From f37034b8d86823c33915d11bb6daf963479ebb49 Mon Sep 17 00:00:00 2001 From: Fries Date: Sat, 1 Jul 2023 20:49:59 -0700 Subject: [PATCH 5/7] add json string support to the cli's print command this let's you print a json string. --- cli/src/arguments.rs | 9 +++++++++ cli/src/commands.rs | 17 +++++++++++++++++ cli/src/main.rs | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/cli/src/arguments.rs b/cli/src/arguments.rs index 9dd4064..48eb76b 100644 --- a/cli/src/arguments.rs +++ b/cli/src/arguments.rs @@ -23,6 +23,15 @@ pub(crate) enum Commands { requires = "name" )] seperator: Option, + #[arg( + long, + short, + conflicts_with = "url", + conflicts_with = "name", + conflicts_with = "seperator", + help = "print the data out as a json string" + )] + json: bool, }, #[command(about = "add a site to the webring")] Add { diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 0e9ba32..d2f7177 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -1,5 +1,6 @@ use std::path::Path; +use shared::errors::ErrorStatus; use shared::names; use shared::{errors::Error, names::Site}; @@ -24,15 +25,31 @@ fn group_printing(seperator: &Option, site: Site, group: &PrintGroup) { log::info!("{}", string); } +fn json_printing(site: Site) -> Result<(), Error> { + match serde_json::to_string(&site) { + Ok(json) => { + log::info!("{}", json); + Ok(()) + } + Err(err) => Err(Error { status: ErrorStatus::ParsingError, data: err.to_string() }), + } +} + pub(crate) fn print( path: &Path, group: &PrintGroup, seperator: &Option, + json: bool, ) -> Result<(), Error> { let names_file = names::read_names_file(path)?; let names = names::load_names(names_file)?; for site in names { + if json { + json_printing(site)?; + continue; + } + if !group.url && !group.name { log::info!("{:?}", site); continue; diff --git a/cli/src/main.rs b/cli/src/main.rs index 454ed42..3cdc11f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -20,7 +20,7 @@ fn main() -> Result<(), Error> { }; match &args.command { - Commands::Print { group, seperator } => print(path, group, seperator)?, + Commands::Print { group, seperator, json } => print(path, group, seperator, *json)?, Commands::Add { url, name } => add(path, url, name)?, Commands::Remove { url } => remove(path, url)?, }; From a08502cc73617884bdab02387af96286d6b80cbf Mon Sep 17 00:00:00 2001 From: Fries Date: Sat, 1 Jul 2023 22:10:58 -0700 Subject: [PATCH 6/7] add a filter argument to the print command this lets you put a url in and the print command will filter it to that. --- cli/src/arguments.rs | 2 ++ cli/src/commands.rs | 46 ++++++++++++++++++++++++++++++++++++-------- cli/src/main.rs | 2 +- shared/src/errors.rs | 3 ++- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/cli/src/arguments.rs b/cli/src/arguments.rs index 48eb76b..5175d19 100644 --- a/cli/src/arguments.rs +++ b/cli/src/arguments.rs @@ -13,6 +13,8 @@ pub(crate) struct Arguments { pub(crate) enum Commands { #[command(about = "print the current webring sites and their names")] Print { + #[arg(help = "url you want to filter to")] + filter: Option, #[command(flatten)] group: PrintGroup, #[arg( diff --git a/cli/src/commands.rs b/cli/src/commands.rs index d2f7177..ecfac66 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -6,7 +6,7 @@ use shared::{errors::Error, names::Site}; use crate::arguments::PrintGroup; -fn group_printing(seperator: &Option, site: Site, group: &PrintGroup) { +fn group_printing(seperator: &Option, site: &Site, group: &PrintGroup) { let mut string = String::new(); let delimiter = seperator.unwrap_or(','); @@ -16,37 +16,67 @@ fn group_printing(seperator: &Option, site: Site, group: &PrintGroup) { if group.name { if !string.is_empty() { - string += &format!("{}{}", delimiter, site.name.unwrap_or("None".to_string())) + string += &format!( + "{}{}", + delimiter, + site.name.as_ref().unwrap_or(&"None".into()) + ) } else { - string += &site.name.unwrap_or("None".to_string()); + string += &site.name.as_ref().unwrap_or(&"None".into()); } } log::info!("{}", string); } -fn json_printing(site: Site) -> Result<(), Error> { +fn json_printing(site: &Site) -> Result<(), Error> { match serde_json::to_string(&site) { Ok(json) => { log::info!("{}", json); Ok(()) } - Err(err) => Err(Error { status: ErrorStatus::ParsingError, data: err.to_string() }), + Err(err) => Err(Error { + status: ErrorStatus::ParsingError, + data: err.to_string(), + }), } } +fn filter_site(site: &Site, json: bool, seperator: &Option, group: &PrintGroup) -> Result<(), Error> { + if json { + json_printing(site)?; + return Ok(()); + } + + if !group.url && !group.name { + log::info!("{:?}", site); + return Ok(()); + } + + return Ok(group_printing(seperator, site, group)); +} + pub(crate) fn print( path: &Path, + filter: &Option, group: &PrintGroup, seperator: &Option, json: bool, ) -> Result<(), Error> { let names_file = names::read_names_file(path)?; - let names = names::load_names(names_file)?; + let mut names = names::load_names(names_file)?; + + if let Some(filter) = filter { + names.retain(|f| &f.url == filter); + if names.len() == 0 { + return Err(Error { status: ErrorStatus::NotFoundError, data: "this url was not found in names.json".into() }) + } + return filter_site(&names[0], json, seperator, group); + } for site in names { if json { - json_printing(site)?; + json_printing(&site)?; continue; } @@ -55,7 +85,7 @@ pub(crate) fn print( continue; } - group_printing(seperator, site, group); + group_printing(seperator, &site, group); } Ok(()) diff --git a/cli/src/main.rs b/cli/src/main.rs index 3cdc11f..84d4e48 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -20,7 +20,7 @@ fn main() -> Result<(), Error> { }; match &args.command { - Commands::Print { group, seperator, json } => print(path, group, seperator, *json)?, + Commands::Print { filter, group, seperator, json, } => print(path, filter, group, seperator, *json)?, Commands::Add { url, name } => add(path, url, name)?, Commands::Remove { url } => remove(path, url)?, }; diff --git a/shared/src/errors.rs b/shared/src/errors.rs index 22d62ef..dcb9eae 100644 --- a/shared/src/errors.rs +++ b/shared/src/errors.rs @@ -3,7 +3,8 @@ pub enum ErrorStatus { IOError, ParsingError, DirectoriesError, - LoggerInitializationError + LoggerInitializationError, + NotFoundError } pub struct Error { From 0c5fce59cadfa58fb40b2123da2e5868150cf2af Mon Sep 17 00:00:00 2001 From: Fries Date: Sat, 1 Jul 2023 22:14:33 -0700 Subject: [PATCH 7/7] add an error if you try to add a url that exists. --- cli/src/commands.rs | 21 +++++++++++++++++++-- shared/src/errors.rs | 3 ++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/cli/src/commands.rs b/cli/src/commands.rs index ecfac66..24f91c6 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -42,7 +42,12 @@ fn json_printing(site: &Site) -> Result<(), Error> { } } -fn filter_site(site: &Site, json: bool, seperator: &Option, group: &PrintGroup) -> Result<(), Error> { +fn filter_site( + site: &Site, + json: bool, + seperator: &Option, + group: &PrintGroup, +) -> Result<(), Error> { if json { json_printing(site)?; return Ok(()); @@ -69,7 +74,10 @@ pub(crate) fn print( if let Some(filter) = filter { names.retain(|f| &f.url == filter); if names.len() == 0 { - return Err(Error { status: ErrorStatus::NotFoundError, data: "this url was not found in names.json".into() }) + return Err(Error { + status: ErrorStatus::NotFoundError, + data: "this url was not found in names.json".into(), + }); } return filter_site(&names[0], json, seperator, group); } @@ -95,6 +103,15 @@ pub(crate) fn add(path: &Path, url: &String, name: &Option) -> Result<() let names_file = names::read_names_file(path)?; let mut names = names::load_names(names_file)?; + if names.iter().any(|site| site.url.contains(url)) { + return Err(Error { + status: ErrorStatus::AlreadyExistsError, + data: + "this url already exists in names.json. you can't have more then 1 of the same url." + .into(), + }); + } + let site = Site { url: url.to_string(), name: name.to_owned(), diff --git a/shared/src/errors.rs b/shared/src/errors.rs index dcb9eae..aa744fe 100644 --- a/shared/src/errors.rs +++ b/shared/src/errors.rs @@ -4,7 +4,8 @@ pub enum ErrorStatus { ParsingError, DirectoriesError, LoggerInitializationError, - NotFoundError + NotFoundError, + AlreadyExistsError } pub struct Error {