Skip to content
Snippets Groups Projects
cli.rs 2 KiB
Newer Older
Eduardo Trujillo's avatar
Eduardo Trujillo committed
use std::path::PathBuf;

use clap::{IntoApp, Parser};
use serde::{de::DeserializeOwned, Serialize};
Eduardo Trujillo's avatar
Eduardo Trujillo committed
use thiserror::Error;

use crate::config;

#[derive(Error, Debug)]
pub enum CliError {
    #[error(transparent)]
    ArgParse(#[from] clap::Error),
    #[error(transparent)]
    Config(#[from] config::ConfigError),
}

pub trait AppOpts: Parser {
Eduardo Trujillo's avatar
Eduardo Trujillo committed
    fn try_init() -> Result<Self, CliError> {
        let result = Self::try_parse().map_err(CliError::ArgParse);
Eduardo Trujillo's avatar
Eduardo Trujillo committed

        match &result {
            Ok(result) => {
                try_init_pretty_logger(
                    result.get_log_environment_variable_name(),
                    result.get_log_level_filter(),
                )
                .unwrap();
            }
            Err(_) => {
                // Initialize the logger with defaults.
                pretty_env_logger::init();
            }
        }

        result
    }

    fn get_log_level_filter(&self) -> Option<LevelFilter> {
        None
    }

    fn get_log_environment_variable_name(&self) -> Option<&str> {
        None
pub trait ConfigurableAppOpts<C: DeserializeOwned + Default + Serialize>: AppOpts {
Eduardo Trujillo's avatar
Eduardo Trujillo committed
    fn try_init_with_config() -> Result<(Self, C), CliError> {
        let opts = Self::try_init()?;

        let app = <Self as IntoApp>::into_app();

        let conf = config::from_defaults(app.get_name(), &opts.get_additional_config_paths())
Eduardo Trujillo's avatar
Eduardo Trujillo committed
            .map_err(CliError::Config)?;

        Ok((opts, conf))
    }

    fn get_additional_config_paths(&self) -> Vec<PathBuf>;
}

fn try_init_pretty_logger(
    environment_variable_name: Option<&str>,
    level_filter_override: Option<LevelFilter>,
) -> Result<(), log::SetLoggerError> {
    let mut builder = pretty_env_logger::formatted_builder();

    if let Some(level_filter_override) = level_filter_override {
        builder.filter(None, level_filter_override);
    }

    if let Ok(s) = ::std::env::var(environment_variable_name.unwrap_or("RUST_LOG")) {
        builder.parse_filters(&s);
    }

    builder.try_init()
}