crab-vault 0.2.6

An object storage service written in rust.
Documentation
use std::{collections::HashMap, sync::LazyLock};

use clap::{CommandFactory, Parser, error::ErrorKind};
use serde::{Deserialize, Serialize};

use crate::{
    app_config::{data::DataConfig, logger::LoggerConfig, meta::MetaConfig, server::ServerConfig},
    cli::{Cli, CliCommand, run::RunArgs},
};

pub mod data;
pub mod logger;
pub mod meta;
pub mod server;

static CONFIG: LazyLock<AppConfig> =
    LazyLock::new(|| AppConfig::build_from_config_file().override_by_cli(Cli::parse()));

pub fn server() -> &'static ServerConfig {
    &CONFIG.server
}

pub fn data() -> &'static DataConfig {
    &CONFIG.data
}

pub fn meta() -> &'static MetaConfig {
    &CONFIG.meta
}

pub fn logger() -> &'static LoggerConfig {
    &CONFIG.logger
}

#[derive(Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
#[derive(Default)]
pub struct AppConfig {
    pub(super) server: ServerConfig,
    pub(super) data: DataConfig,
    pub(super) meta: MetaConfig,
    pub(super) logger: LoggerConfig,
}

impl AppConfig {
    pub fn get_field_value_map() -> HashMap<&'static str, toml_edit::Item> {
        use toml_edit::{Item, Value};
        HashMap::from([
            ("server.port", Item::Value(Value::from(0))),
            ("data.source", Item::Value(Value::from(""))),
            ("meta.source", Item::Value(Value::from(""))),
            ("logger.level", Item::Value(Value::from(""))),
            ("logger.dump_path", Item::Value(Value::from(""))),
            ("logger.with_ansi", Item::Value(Value::from(true))),
            ("logger.with_file", Item::Value(Value::from(true))),
            ("logger.with_target", Item::Value(Value::from(true))),
            ("logger.with_thread", Item::Value(Value::from(true))),
        ])
    }

    pub fn get_valid_paths() -> HashMap<&'static str, toml_edit::Item> {
        use toml_edit::{Item, Table, Value};
        HashMap::from([
            ("server", Item::Table(Table::new())),
            ("data", Item::Table(Table::new())),
            ("meta", Item::Table(Table::new())),
            ("logger", Item::Table(Table::new())),
            ("server.port", Item::Value(Value::from(0))),
            ("data.source", Item::Value(Value::from(""))),
            ("meta.source", Item::Value(Value::from(""))),
            ("logger.level", Item::Value(Value::from(""))),
            ("logger.dump_path", Item::Value(Value::from(""))),
            ("logger.with_ansi", Item::Value(Value::from(true))),
            ("logger.with_file", Item::Value(Value::from(true))),
            ("logger.with_target", Item::Value(Value::from(true))),
            ("logger.with_thread", Item::Value(Value::from(true))),
        ])
    }

    pub fn build_from_config_file() -> Self {
        let Cli {
            subcommand: _,
            config_path,
        } = Cli::parse();

        config::Config::builder()
            .add_source(
                config::File::with_name(&config_path)
                    .required(false)
                    .format(config::FileFormat::Toml),
            )
            .build()
            .unwrap_or_else(|e| {
                Cli::command()
                    .error(
                        ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
                        format!("Cannot deserialize the configuration file, details:\n\n    {e}"),
                    )
                    .exit();
            })
            .try_deserialize()
            .unwrap_or_else(|e| {
                Cli::command()
                    .error(
                        ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
                        format!("Cannot understand the configuration file, details:\n\n    {e}"),
                    )
                    .exit();
            })
    }

    pub fn override_by_cli(mut self, cli: Cli) -> Self {
        let Cli {
            subcommand: cli,
            config_path: _,
        } = cli;
        match cli {
            CliCommand::Run(run_args) => {
                let RunArgs {
                    port,
                    data_source,
                    meta_source,
                    log_level,
                    dump_path,
                    dump_level,
                } = run_args;

                if let Some(port) = port {
                    self.server.port = port
                }

                if let Some(data_source) = data_source {
                    self.data.source = data_source
                }

                if let Some(meta_source) = meta_source {
                    self.meta.source = meta_source
                }

                if let Some(log_level) = log_level {
                    self.logger.level = log_level
                }

                if let Some(dump_path) = dump_path {
                    self.logger.dump_path = Some(dump_path)
                }

                if let Some(dump_level) = dump_level {
                    self.logger.dump_level = Some(dump_level)
                }
            }
            CliCommand::Jwt(_) => {}
            CliCommand::Config(_) => {}
        };
        self
    }
}