use std::path::PathBuf;
use clap::{Args, Subcommand};
use crate::types::ConfigAction;
#[derive(Debug, Args)]
pub struct ConfigArgs {
#[arg(long, global = true)]
pub scope: Option<String>,
#[command(subcommand)]
pub action: Option<ConfigSubcommand>,
}
#[derive(Debug, Subcommand)]
pub enum ConfigSubcommand {
List,
Gen {
#[arg(short, long)]
output: Option<PathBuf>,
},
Get {
key: String,
},
Set {
key: String,
value: String,
},
Unset {
key: String,
},
}
impl ConfigArgs {
pub fn into_action(self) -> ConfigAction {
let scope = self.scope;
match self.action {
None | Some(ConfigSubcommand::List) => ConfigAction::List { scope },
Some(ConfigSubcommand::Gen { output }) => ConfigAction::Gen { output },
Some(ConfigSubcommand::Get { key }) => ConfigAction::Get { key, scope },
Some(ConfigSubcommand::Set { key, value }) => ConfigAction::Set { key, value, scope },
Some(ConfigSubcommand::Unset { key }) => ConfigAction::Unset { key, scope },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[derive(Debug, Parser)]
struct TestCli {
#[command(flatten)]
config: ConfigArgs,
}
fn parse(args: &[&str]) -> ConfigArgs {
TestCli::try_parse_from(args).unwrap().config
}
#[test]
fn parse_gen_no_output() {
let args = parse(&["test", "gen"]);
let action = args.into_action();
assert_eq!(action, ConfigAction::Gen { output: None });
}
#[test]
fn parse_gen_with_output() {
let args = parse(&["test", "gen", "-o", "out.toml"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Gen {
output: Some(PathBuf::from("out.toml"))
}
);
}
#[test]
fn parse_gen_with_long_output() {
let args = parse(&["test", "gen", "--output", "/etc/myapp.toml"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Gen {
output: Some(PathBuf::from("/etc/myapp.toml"))
}
);
}
#[test]
fn parse_get() {
let args = parse(&["test", "get", "database.url"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Get {
key: "database.url".into(),
scope: None,
}
);
}
#[test]
fn parse_set() {
let args = parse(&["test", "set", "port", "3000"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Set {
key: "port".into(),
value: "3000".into(),
scope: None,
}
);
}
#[test]
fn parse_set_string_value() {
let args = parse(&["test", "set", "host", "0.0.0.0"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Set {
key: "host".into(),
value: "0.0.0.0".into(),
scope: None,
}
);
}
#[test]
fn invalid_subcommand_errors() {
let result = TestCli::try_parse_from(["test", "nope"]);
assert!(result.is_err());
}
#[test]
fn parse_unset() {
let args = parse(&["test", "unset", "database.url"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Unset {
key: "database.url".into(),
scope: None,
}
);
}
#[test]
fn parse_bare_config_is_list() {
let args = parse(&["test"]);
let action = args.into_action();
assert_eq!(action, ConfigAction::List { scope: None });
}
#[test]
fn parse_explicit_list() {
let args = parse(&["test", "list"]);
let action = args.into_action();
assert_eq!(action, ConfigAction::List { scope: None });
}
#[test]
fn parse_set_with_scope() {
let args = parse(&["test", "set", "port", "3000", "--scope", "global"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Set {
key: "port".into(),
value: "3000".into(),
scope: Some("global".into()),
}
);
}
#[test]
fn parse_scope_before_subcommand() {
let args = parse(&["test", "--scope", "global", "set", "port", "3000"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Set {
key: "port".into(),
value: "3000".into(),
scope: Some("global".into()),
}
);
}
#[test]
fn parse_list_with_scope() {
let args = parse(&["test", "list", "--scope", "global"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::List {
scope: Some("global".into()),
}
);
}
#[test]
fn parse_get_with_scope() {
let args = parse(&["test", "get", "port", "--scope", "local"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Get {
key: "port".into(),
scope: Some("local".into()),
}
);
}
#[test]
fn parse_unset_with_scope() {
let args = parse(&["test", "unset", "port", "--scope", "global"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::Unset {
key: "port".into(),
scope: Some("global".into()),
}
);
}
#[test]
fn parse_bare_config_with_scope() {
let args = parse(&["test", "--scope", "global"]);
let action = args.into_action();
assert_eq!(
action,
ConfigAction::List {
scope: Some("global".into()),
}
);
}
}