From 1828ba42a9082c8ca889847c355d7f0edafd163d Mon Sep 17 00:00:00 2001 From: Spencer Ferris <3319370+spencewenski@users.noreply.github.com> Date: Sat, 6 Apr 2024 22:05:28 -0700 Subject: [PATCH] Add CLI command to print the app config --- src/app.rs | 8 ++--- src/cli/mod.rs | 6 ++++ src/cli/print_config.rs | 62 +++++++++++++++++++++++++++++++++++++++ src/config/environment.rs | 5 +++- 4 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 src/cli/print_config.rs diff --git a/src/app.rs b/src/app.rs index f584568d..255703ff 100644 --- a/src/app.rs +++ b/src/app.rs @@ -23,6 +23,8 @@ use sea_orm_migration::MigratorTrait; use sidekiq::{periodic, Processor}; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; +// `debug` isn't used with some feature configurations +#[allow(unused_imports)] use tracing::{debug, error, info, instrument}; use crate::app_context::AppContext; @@ -61,12 +63,12 @@ where // combine them with the roadster CLI attributes. let cli = A::Cli::augment_args(cli); let cli = if let Some((a, b)) = about.zip(cli.get_about().cloned()) { - cli.about(format!("roadster: {a}, app: {b}")) + cli.about(format!("{a}\n\n{b}")) } else { cli }; let cli = if let Some((a, b)) = long_about.zip(cli.get_long_about().cloned()) { - cli.long_about(format!("roadster: {a}, app: {b}")) + cli.long_about(format!("{a}\n\n{b}")) } else { cli }; @@ -97,8 +99,6 @@ where A::init_tracing(&config)?; - debug!("{config:?}"); - #[cfg(feature = "db-sql")] let db = Database::connect(A::db_connection_options(&config)?).await?; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index c23261fd..007600c9 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -9,6 +9,7 @@ use crate::cli::list_routes::ListRoutesArgs; use crate::cli::migrate::MigrateArgs; #[cfg(feature = "open-api")] use crate::cli::open_api_schema::OpenApiArgs; +use crate::cli::print_config::PrintConfigArgs; use crate::config::environment::Environment; #[cfg(feature = "open-api")] @@ -17,6 +18,7 @@ pub mod list_routes; pub mod migrate; #[cfg(feature = "open-api")] pub mod open_api_schema; +pub mod print_config; /// Implement to enable Roadster to run your custom CLI commands. #[async_trait] @@ -139,6 +141,9 @@ pub enum RoadsterSubCommand { #[cfg(feature = "db-sql")] #[clap(visible_aliases = ["m", "migration"])] Migrate(MigrateArgs), + + /// Print the AppConfig + PrintConfig(PrintConfigArgs), } #[async_trait] @@ -154,6 +159,7 @@ where RoadsterSubCommand::OpenApi(args) => args.run(app, cli, context).await, #[cfg(feature = "db-sql")] RoadsterSubCommand::Migrate(args) => args.run(app, cli, context).await, + RoadsterSubCommand::PrintConfig(args) => args.run(app, cli, context).await, } } } diff --git a/src/cli/print_config.rs b/src/cli/print_config.rs new file mode 100644 index 00000000..dd3b7b9f --- /dev/null +++ b/src/cli/print_config.rs @@ -0,0 +1,62 @@ +use async_trait::async_trait; +use clap::Parser; +use serde_derive::{Deserialize, Serialize}; +use strum_macros::{EnumString, IntoStaticStr}; +use tracing::info; + +use crate::app::App; +use crate::app_context::AppContext; +use crate::cli::{RoadsterCli, RunRoadsterCommand}; + +#[derive(Debug, Parser)] +pub struct PrintConfigArgs { + /// Print the config with the specified format. + #[clap(short, long, default_value = "debug")] + pub format: Format, +} + +#[derive( + Debug, Clone, Eq, PartialEq, Serialize, Deserialize, EnumString, IntoStaticStr, clap::ValueEnum, +)] +#[serde(rename_all = "kebab-case")] +#[strum(serialize_all = "kebab-case")] +pub enum Format { + Debug, + Json, + JsonPretty, + Toml, + TomlPretty, +} + +#[async_trait] +impl RunRoadsterCommand for PrintConfigArgs +where + A: App, +{ + async fn run( + &self, + _app: &A, + _cli: &RoadsterCli, + context: &AppContext, + ) -> anyhow::Result { + match self.format { + Format::Debug => { + info!("{:?}", context.config) + } + Format::Json => { + info!("{}", serde_json::to_string(&context.config)?) + } + Format::JsonPretty => { + info!("{}", serde_json::to_string_pretty(&context.config)?) + } + Format::Toml => { + info!("{}", toml::to_string(&context.config)?) + } + Format::TomlPretty => { + info!("{}", toml::to_string_pretty(&context.config)?) + } + } + + Ok(true) + } +} diff --git a/src/config/environment.rs b/src/config/environment.rs index 99acc9fc..73633d4d 100644 --- a/src/config/environment.rs +++ b/src/config/environment.rs @@ -2,6 +2,8 @@ use std::env; use std::str::FromStr; use anyhow::anyhow; +#[cfg(feature = "cli")] +use clap::ValueEnum; use const_format::concatcp; use serde_derive::{Deserialize, Serialize}; use strum_macros::{EnumString, IntoStaticStr}; @@ -9,6 +11,7 @@ use strum_macros::{EnumString, IntoStaticStr}; use crate::config::app_config::{ENV_VAR_PREFIX, ENV_VAR_SEPARATOR}; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, EnumString, IntoStaticStr)] +#[cfg_attr(feature = "cli", derive(ValueEnum))] #[serde(rename_all = "kebab-case")] #[strum(serialize_all = "kebab-case")] pub enum Environment { @@ -30,7 +33,7 @@ impl Environment { // Get the stage, and validate it by parsing to the Environment enum let environment = env::var(ENV_VAR_WITH_PREFIX) .map_err(|_| anyhow!("Env var `{ENV_VAR_WITH_PREFIX}` not defined."))?; - let environment = Environment::from_str(&environment).map_err(|err| { + let environment = ::from_str(&environment).map_err(|err| { anyhow!( "Unable to parse `{ENV_VAR_WITH_PREFIX}` env var with value `{environment}`: {err}" )