Skip to content

Commit

Permalink
Refactor configuration; Remove db-path from command line arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
tbsklg committed Aug 23, 2024
1 parent d694bc7 commit 0acaa4b
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 95 deletions.
30 changes: 30 additions & 0 deletions cli-client/src/cli.rs
Original file line number Diff line number Diff line change
@@ -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<std::path::PathBuf>,

#[command(subcommand)]
pub command: Option<Command>,
}
1 change: 1 addition & 0 deletions cli-client/src/clients/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ pub trait StrikeClient {
fn add_strike(&self, name: &str) -> HashMap<String, i8>;
fn get_tarnished(&self) -> Vec<Tarnished>;
fn clear_strikes(&self);
fn check_health(&self) -> Result<(), Box<dyn std::error::Error>>;
}
6 changes: 5 additions & 1 deletion cli-client/src/clients/local_client.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -41,6 +41,10 @@ impl StrikeClient for LocalClient {
std::fs::write(db_path, json!({}).to_string()).unwrap();
}
}

fn check_health(&self) -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion cli-client/src/clients/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pub mod client;
pub mod local_client;
pub mod remote_client;
pub mod client;
25 changes: 25 additions & 0 deletions cli-client/src/clients/remote_client.rs
Original file line number Diff line number Diff line change
@@ -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<String, i8> {
HashMap::new()
}

fn get_tarnished(&self) -> Vec<Tarnished> {
vec![]
}

fn clear_strikes(&self) {}

fn check_health(&self) -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
}

pub async fn check_health(base_url: String, api_key: String) {
let client = reqwest::Client::new();
Expand Down
42 changes: 31 additions & 11 deletions cli-client/src/configuration.rs
Original file line number Diff line number Diff line change
@@ -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<RemoteSettings>,
pub local: Option<LocalSettings>,
}
Expand All @@ -21,7 +22,6 @@ pub struct LocalSettings {
impl Default for Settings {
fn default() -> Self {
Self {
use_remote: false,
remote: None,
local: {
Some(LocalSettings {
Expand All @@ -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::<Settings>().unwrap_or_default(),
Err(_) => Settings::default()
Err(_) => Settings::default(),
}
}

Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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")
)
}
}
3 changes: 2 additions & 1 deletion cli-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod configuration;
pub mod cli;
pub mod clients;
pub mod configuration;
pub mod output;
pub mod tarnished;
78 changes: 25 additions & 53 deletions cli-client/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<std::path::PathBuf>,

#[arg(
short,
long,
help = "Specify the path to the database json file (i.e. db.json)"
)]
db_path: Option<std::path::PathBuf>,
#[command(subcommand)]
command: Option<Command>,
}

#[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 => {
Expand All @@ -77,3 +33,19 @@ async fn main() {
}
}
}

fn create_client(settings: &Settings) -> Box<dyn StrikeClient> {
settings.remote.as_ref().map_or_else(
|| {
Box::new(LocalClient {
db_path: settings.local.as_ref().unwrap().db_path.clone(),
}) as Box<dyn StrikeClient>
},
|remote| {
Box::new(RemoteClient {
api_key: remote.api_key.clone(),
base_url: remote.base_url.clone(),
}) as Box<dyn StrikeClient>
},
)
}
1 change: 0 additions & 1 deletion cli-client/src/tarnished.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,3 @@ impl Tarnished {
.collect()
}
}

58 changes: 31 additions & 27 deletions cli-client/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ fn missing_subcommand() -> Result<(), Box<dyn std::error::Error>> {

#[test]
fn it_should_add_strike() -> Result<(), Box<dyn std::error::Error>> {
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");
Expand All @@ -33,12 +33,21 @@ fn it_should_add_strike() -> Result<(), Box<dyn std::error::Error>> {

#[test]
fn it_should_list_strikes_in_descending_order() -> Result<(), Box<dyn std::error::Error>> {
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\
Expand All @@ -49,46 +58,41 @@ fn it_should_list_strikes_in_descending_order() -> Result<(), Box<dyn std::error
Ok(())
}

#[test]
fn it_should_use_default_directory_if_no_configuration_directory_exists(
) -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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");
Expand Down
Loading

0 comments on commit 0acaa4b

Please sign in to comment.