use crate::cmd::style;
use crate::cmd::style::CURRENT;
use crate::settings::Settings;
use crate::{
Asset,
cmd::run::{RunArgs, process_command},
};
use actix_web::web::ServiceConfig;
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use clap::Parser;
use clap::Subcommand;
use colored::Colorize;
use config::{Case, Config, ConfigError, Environment, File, FileFormat};
use std::path::PathBuf;
#[derive(Parser, Debug, Clone)]
#[command(author, version, about)]
#[command(propagate_version = true)]
#[command(styles=style::CURRENT)]
#[command(help_template = Cli::help_template())]
pub(crate) struct Cli {
#[clap(short, long, env)]
pub config_file: Option<PathBuf>,
#[clap(short, long, env)]
pub b64_config_file: Option<String>,
#[command(subcommand)]
commands: Commands,
}
#[derive(Subcommand, Debug, Clone)]
enum Commands {
Run(RunArgs),
}
impl Cli {
fn help_template() -> String {
format!(
"\
{header}SERVER version {{version}}\n\
{header}Author:{context} {{author}}\n\n\
{{about}}\n\n\
{header}Usage: {{usage}}\n\n\
{header}Options:\n{{options}}\n\n\
{header}Commands:\n{{subcommands}}
",
header = CURRENT.get_header(),
context = CURRENT.get_context_value()
)
}
pub(crate) async fn init(
args: &Cli,
settings: &Settings,
fnconfig: Option<fn(&mut ServiceConfig)>,
) {
match &args.commands {
Commands::Run(_) => process_command(settings, fnconfig).await,
}
}
pub(crate) fn load_config(args: &Cli) -> std::result::Result<Settings, ConfigError> {
let mut builder = Config::builder();
if let Some(file) = Asset::get("config.yaml") {
let contents =
std::str::from_utf8(&file.data).map_err(|e| ConfigError::Message(e.to_string()))?;
builder = builder.add_source(File::from_str(contents, FileFormat::Yaml));
}
if let Some(path) = &args.config_file
&& path.exists()
{
builder = builder.add_source(File::from(path.as_ref()));
}
if let Some(data) = &args.b64_config_file {
let decoded = BASE64_STANDARD
.decode(data)
.map_err(|e| ConfigError::Message(e.to_string()))?;
let contents =
std::str::from_utf8(&decoded).map_err(|e| ConfigError::Message(e.to_string()))?;
builder = builder.add_source(File::from_str(contents, FileFormat::Yaml));
}
builder =
builder.add_source(Environment::with_convert_case(Case::UpperSnake).separator("_"));
let config = builder.build().map_err(build_error)?;
config.try_deserialize::<Settings>().map_err(build_error)
}
}
fn build_error<E: std::fmt::Display>(error: E) -> ConfigError {
ConfigError::Message(format!(
"{} {}",
"Failed to build configuration. Root Cause:".red(),
error.to_string().red()
))
}