diff --git a/cli-client/src/cli.rs b/cli-client/src/cli.rs new file mode 100644 index 0000000..74c6544 --- /dev/null +++ b/cli-client/src/cli.rs @@ -0,0 +1,30 @@ +use clap::{Parser, Subcommand}; + +#[derive(Subcommand, Clone, Debug)] +pub enum Command { + #[command(about = "Add a strike", alias = "s")] + Strike { name: String }, + #[command(about = "List all strikes")] + Ls, + #[command(about = "Clear strikes", alias = "c")] + Clear, +} + +#[derive(Clone, Debug, Parser)] +#[command( + name = "Strikes CLI", + version = "0.1.0", + about = "Track and assign strikes", + long_about = "Simple CLI tool to track and assign strikes" +)] +pub struct Cli { + #[arg( + short, + long, + help = "Specify the path to the configuration file where the strikes are stored" + )] + pub config_path: Option, + + #[command(subcommand)] + pub command: Option, +} diff --git a/cli-client/src/clients/client.rs b/cli-client/src/clients/client.rs index b28c225..28a19da 100644 --- a/cli-client/src/clients/client.rs +++ b/cli-client/src/clients/client.rs @@ -6,4 +6,5 @@ pub trait StrikeClient { fn add_strike(&self, name: &str) -> HashMap; fn get_tarnished(&self) -> Vec; fn clear_strikes(&self); + fn check_health(&self) -> Result<(), Box>; } diff --git a/cli-client/src/clients/local_client.rs b/cli-client/src/clients/local_client.rs index 1310af3..52b60f5 100644 --- a/cli-client/src/clients/local_client.rs +++ b/cli-client/src/clients/local_client.rs @@ -1,8 +1,8 @@ use serde_json::json; use std::collections::HashMap; -use crate::tarnished::Tarnished; use super::client::StrikeClient; +use crate::tarnished::Tarnished; pub struct LocalClient { pub db_path: std::path::PathBuf, @@ -41,6 +41,10 @@ impl StrikeClient for LocalClient { std::fs::write(db_path, json!({}).to_string()).unwrap(); } } + + fn check_health(&self) -> Result<(), Box> { + Ok(()) + } } #[cfg(test)] diff --git a/cli-client/src/clients/mod.rs b/cli-client/src/clients/mod.rs index 7532306..603fcbe 100644 --- a/cli-client/src/clients/mod.rs +++ b/cli-client/src/clients/mod.rs @@ -1,3 +1,3 @@ +pub mod client; pub mod local_client; pub mod remote_client; -pub mod client; diff --git a/cli-client/src/clients/remote_client.rs b/cli-client/src/clients/remote_client.rs index fb2cb88..10c1e5f 100644 --- a/cli-client/src/clients/remote_client.rs +++ b/cli-client/src/clients/remote_client.rs @@ -1,4 +1,29 @@ use reqwest; +use std::collections::HashMap; + +use super::client::StrikeClient; +use crate::tarnished::Tarnished; + +pub struct RemoteClient { + pub api_key: String, + pub base_url: String, +} + +impl StrikeClient for RemoteClient { + fn add_strike(&self, _name: &str) -> HashMap { + HashMap::new() + } + + fn get_tarnished(&self) -> Vec { + vec![] + } + + fn clear_strikes(&self) {} + + fn check_health(&self) -> Result<(), Box> { + Ok(()) + } +} pub async fn check_health(base_url: String, api_key: String) { let client = reqwest::Client::new(); diff --git a/cli-client/src/configuration.rs b/cli-client/src/configuration.rs index 9f8e744..47603dc 100644 --- a/cli-client/src/configuration.rs +++ b/cli-client/src/configuration.rs @@ -1,8 +1,9 @@ use std::path::PathBuf; +use crate::cli::Cli; + #[derive(serde::Deserialize)] pub struct Settings { - pub use_remote: bool, pub remote: Option, pub local: Option, } @@ -21,7 +22,6 @@ pub struct LocalSettings { impl Default for Settings { fn default() -> Self { Self { - use_remote: false, remote: None, local: { Some(LocalSettings { @@ -34,17 +34,23 @@ impl Default for Settings { } } -pub fn get_configuration(path: std::path::PathBuf) -> Settings { +pub fn get_configuration(args: &Cli) -> Settings { + let home = &std::env::var("HOME").unwrap(); + let config_path = args + .config_path + .clone() + .unwrap_or_else(|| PathBuf::from(home).join(".strikes/config.yaml")); + let settings = config::Config::builder() .add_source(config::File::new( - path.to_str().unwrap(), + config_path.to_str().unwrap(), config::FileFormat::Yaml, )) .build(); match settings { Ok(settings) => settings.try_deserialize::().unwrap_or_default(), - Err(_) => Settings::default() + Err(_) => Settings::default(), } } @@ -55,9 +61,11 @@ mod tests { #[test] fn parse_valid_config() { - let configuration = - get_configuration(PathBuf::from("tests/fixtures/valid_config.yaml")); - assert_eq!(configuration.use_remote, true); + let args = Cli { + config_path: Some(PathBuf::from("tests/fixtures/valid_config.yaml")), + command: None, + }; + let configuration = get_configuration(&args); assert_eq!(configuration.remote.as_ref().unwrap().api_key, "abc"); assert_eq!( configuration.remote.as_ref().unwrap().base_url, @@ -72,8 +80,12 @@ mod tests { #[test] fn parse_default_config() { std::env::set_var("HOME", "/home/user"); + let args = Cli { + config_path: Some(PathBuf::from("tests/fixtures/empty_config.yaml")), + command: None, + }; - let configuration = get_configuration(PathBuf::from("tests/fixtures/empty_config.yaml")); + let configuration = get_configuration(&args); assert_eq!( configuration.local.unwrap().db_path, @@ -83,8 +95,16 @@ mod tests { #[test] fn parse_invalid_config() { - let configuration = get_configuration(PathBuf::from("tests/fixtures/invalid_config.yaml")); + let args = Cli { + config_path: Some(PathBuf::from("tests/fixtures/invalid_config.yaml")), + command: None, + }; + + let configuration = get_configuration(&args); - assert_eq!(configuration.use_remote, false); + assert_eq!( + configuration.local.unwrap().db_path, + PathBuf::from("/home/user/.strikes/db.json") + ) } } diff --git a/cli-client/src/lib.rs b/cli-client/src/lib.rs index 81f5da5..872248f 100644 --- a/cli-client/src/lib.rs +++ b/cli-client/src/lib.rs @@ -1,4 +1,5 @@ -pub mod configuration; +pub mod cli; pub mod clients; +pub mod configuration; pub mod output; pub mod tarnished; diff --git a/cli-client/src/main.rs b/cli-client/src/main.rs index 4541cbd..7a29c53 100644 --- a/cli-client/src/main.rs +++ b/cli-client/src/main.rs @@ -1,64 +1,20 @@ -use clap::{Parser, Subcommand}; +use clap::Parser; +use strikes::cli::{Cli, Command}; use strikes::clients::client::StrikeClient; -use std::path::PathBuf; use strikes::clients::local_client::LocalClient; -use strikes::configuration::get_configuration; +use strikes::clients::remote_client::RemoteClient; +use strikes::configuration::{get_configuration, Settings}; use strikes::output::print_as_table; -#[derive(Subcommand, Clone, Debug)] -enum Command { - #[command(about = "Add a strike", alias = "s")] - Strike { name: String }, - #[command(about = "List all strikes")] - Ls, - #[command(about = "Clear strikes", alias = "c")] - Clear, -} - -#[derive(Debug, Parser)] -#[command( - name = "Strikes CLI", - version = "0.1.0", - about = "Track and assign strikes", - long_about = "Simple CLI tool to track and assign strikes" -)] -struct Cli { - #[arg( - short, - long, - help = "Specify the path to the configuration file where the strikes are stored" - )] - config_path: Option, - - #[arg( - short, - long, - help = "Specify the path to the database json file (i.e. db.json)" - )] - db_path: Option, - #[command(subcommand)] - command: Option, -} - #[tokio::main] async fn main() { - let args = Cli::parse(); + let args = &Cli::parse(); + let settings = &get_configuration(args); + let client = create_client(settings); - let home = &std::env::var("HOME").unwrap(); - let config_path = PathBuf::from(home).join(".strikes/configuration.yaml"); - let config = get_configuration(args.config_path.unwrap_or(config_path)); - - // check_health(config.base_url, config.api_key).await; - let db_path = args.db_path.unwrap_or(config.local.map_or_else( - || PathBuf::from(home).join(".strikes/db.json"), - |local| local.db_path, - )); - - let client = LocalClient { db_path }; - - match args.command.unwrap() { + match &args.clone().command.unwrap() { Command::Strike { name } => { - client.add_strike(&name); + client.add_strike(name); println!("{} has been tarnished!", name); } Command::Ls => { @@ -77,3 +33,19 @@ async fn main() { } } } + +fn create_client(settings: &Settings) -> Box { + settings.remote.as_ref().map_or_else( + || { + Box::new(LocalClient { + db_path: settings.local.as_ref().unwrap().db_path.clone(), + }) as Box + }, + |remote| { + Box::new(RemoteClient { + api_key: remote.api_key.clone(), + base_url: remote.base_url.clone(), + }) as Box + }, + ) +} diff --git a/cli-client/src/tarnished.rs b/cli-client/src/tarnished.rs index bcea193..2d6954f 100644 --- a/cli-client/src/tarnished.rs +++ b/cli-client/src/tarnished.rs @@ -22,4 +22,3 @@ impl Tarnished { .collect() } } - diff --git a/cli-client/tests/cli.rs b/cli-client/tests/cli.rs index b2eca95..8bb5eca 100644 --- a/cli-client/tests/cli.rs +++ b/cli-client/tests/cli.rs @@ -17,12 +17,12 @@ fn missing_subcommand() -> Result<(), Box> { #[test] fn it_should_add_strike() -> Result<(), Box> { - let file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; - file.write_str("{}")?; + let file = assert_fs::NamedTempFile::new("./tests/fixtures/configuration.yaml")?; + file.write_str("{\"local\": {\"db_path\": \"./tests/fixtures/db.json\"}}")?; let mut cmd = Command::cargo_bin("strikes")?; - cmd.arg("--db-path") + cmd.arg("--config-path") .arg(file.path()) .arg("strike") .arg("guenther"); @@ -33,12 +33,21 @@ fn it_should_add_strike() -> Result<(), Box> { #[test] fn it_should_list_strikes_in_descending_order() -> Result<(), Box> { - let file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; - file.write_str("{\"guenther\": 1, \"heinz\": 2}")?; + let db_file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; + let config_file = assert_fs::NamedTempFile::new("./tests/fixtures/configuration.yaml")?; + config_file.write_str( + format!( + "{{\"local\": {{\"db_path\": \"{}\"}}}}", + db_file.path().to_str().unwrap() + ) + .as_str(), + )?; + + db_file.write_str("{\"guenther\": 1, \"heinz\": 2}")?; let mut cmd = Command::cargo_bin("strikes")?; - cmd.arg("--db-path").arg(file.path()).arg("ls"); + cmd.arg("--config-path").arg(config_file.path()).arg("ls"); let expected_output = "Tarnished | Strikes |\n\ heinz | 2 |\n\ @@ -49,46 +58,41 @@ fn it_should_list_strikes_in_descending_order() -> Result<(), Box Result<(), Box> { - let file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; - - let mut cmd = Command::cargo_bin("strikes")?; - cmd.arg("--db-path") - .arg(file.path()) - .arg("strike") - .arg("guenther"); - cmd.assert().success(); - - Ok(()) -} - #[test] fn it_should_clear_all_strikes() -> Result<(), Box> { - let file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; + let db_file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; + let config_file = assert_fs::NamedTempFile::new("./tests/fixtures/configuration.yaml")?; + config_file.write_str( + format!( + "{{\"local\": {{\"db_path\": \"{}\"}}}}", + db_file.path().to_str().unwrap() + ) + .as_str(), + )?; let mut cmd = Command::cargo_bin("strikes")?; - cmd.arg("--db-path") - .arg(file.path()) + cmd.arg("--config-path") + .arg(config_file.path()) .arg("strike") .arg("guenther"); cmd.assert().success(); let mut cmd = Command::cargo_bin("strikes")?; - cmd.arg("--db-path").arg(file.path()).arg("ls"); + cmd.arg("--config-path").arg(config_file.path()).arg("ls"); cmd.assert() .success() .stdout("Tarnished | Strikes |\nguenther | 1 |\n"); let mut cmd = Command::cargo_bin("strikes")?; - cmd.arg("--db-path").arg(file.path()).arg("clear"); + cmd.arg("--config-path") + .arg(config_file.path()) + .arg("clear"); cmd.assert() .success() .stdout("All strikes have been cleared!\n"); let mut cmd = Command::cargo_bin("strikes")?; - cmd.arg("--db-path").arg(file.path()).arg("ls"); + cmd.arg("--config-path").arg(config_file.path()).arg("ls"); cmd.assert() .success() .stdout("No one has been tarnished yet!\n"); diff --git a/cli-client/tests/fixtures/db.json b/cli-client/tests/fixtures/db.json new file mode 100644 index 0000000..2347931 --- /dev/null +++ b/cli-client/tests/fixtures/db.json @@ -0,0 +1,3 @@ +{ + "guenther": 6 +} \ No newline at end of file