diff --git a/rust/src/args.rs b/rust/src/args.rs index 040fb14..8b00be2 100644 --- a/rust/src/args.rs +++ b/rust/src/args.rs @@ -1,10 +1,11 @@ use clap::{Args, Parser, Subcommand}; +use thiserror::Error; use tracing::Level; #[derive(Debug, Parser)] #[clap(version, about)] pub struct CommandArgs { - /// The path to the TOML configuration. This will override all other configuration sources + /// The path to a TOML or JSON configuration to use. This will me merged with all other configuration sources, taking priority over other files but not environment variables #[arg(short, long)] pub config: Option, @@ -24,3 +25,9 @@ pub struct ServeAction { #[arg(short, long, default_value_t = Level::WARN)] pub log_level: Level, } + +#[derive(Error, Debug)] +pub enum ArgsError { + #[error("the file extension of the provided config file is not one of .json or .toml")] + InvalidConfigExtension(Option), +} diff --git a/rust/src/error.rs b/rust/src/error.rs index 611ce1d..c622051 100644 --- a/rust/src/error.rs +++ b/rust/src/error.rs @@ -1,10 +1,15 @@ use thiserror::Error; +use crate::args; + #[derive(Error, Debug)] pub enum CommandError { #[error("the configuration is invalid")] InvalidConfig(#[from] Box), + #[error("a commandline argument is invalid")] + InvalidArgument(#[from] args::ArgsError), + #[error(transparent)] Unknown(#[from] anyhow::Error), // examples diff --git a/rust/src/main.rs b/rust/src/main.rs index 1f7e9ec..b5cc898 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use clap::Parser; use figment::{ Figment, @@ -6,22 +8,37 @@ use figment::{ use tracing::{Level, event}; mod args; -use args::{ActionType, CommandArgs}; +use crate::args::{ActionType, ArgsError, CommandArgs}; mod config; mod error; -use error::CommandError; +use crate::error::CommandError; #[tokio::main] async fn main() -> Result<(), CommandError> { let args = CommandArgs::parse(); - let config: config::CommandConfig = Figment::new() + let figment = Figment::new() .admerge(Json::file("config.json")) - .admerge(Toml::file("config.toml")) - .admerge(Env::prefixed("APP_")) - .extract()?; + .admerge(Toml::file("config.toml")); + + let config: config::CommandConfig = match &args.config { + None => figment, + Some(f) => { + let file = Path::new(&f); + + match file.extension().and_then(|e| e.to_str()) { + Some("toml") => Ok(figment.admerge(Toml::file(file))), + Some("json") => Ok(figment.admerge(Json::file(file))), + _ => Err(ArgsError::InvalidConfigExtension( + file.extension().and_then(|e| e.to_str().map(String::from)), + )), + } + }?, + } + .admerge(Env::prefixed("APP_")) + .extract()?; match &args.action { ActionType::Serve(serve_args) => { @@ -41,7 +58,9 @@ async fn main() -> Result<(), CommandError> { println!("{:#?}", config.port.ports); } - } + }; + + println!("{}", CommandError::InvalidArgument(ArgsError::InvalidConfigExtension(Some(String::from("txt"))))); Ok(()) }