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}"
)