use clap::Parser;
use serde::Deserialize;
use std::fs;
use toml;
use chrono::NaiveDate;
use anyhow::{Context, Result};
#[derive(Parser)]
pub struct Cli {
#[arg(short, long, default_value_t = String::from("http://localhost:4242"))]
pub url: String,
#[arg(short, long)]
pub config: Option<std::path::PathBuf>,
#[arg(short, long)]
pub units: Vec<u32>,
#[arg(short, long)]
pub start_date: NaiveDate,
#[arg(short, long)]
pub end_date: NaiveDate,
#[arg(short, long, default_value_t = String::from("./backup"))]
pub backup_dir: String,
#[arg(short, long, default_value_t = 1)]
pub concurrency: usize,
#[arg(short, long, default_value_t = 7)]
pub interval_days: u64,
#[arg(short, long)]
pub metrics: Vec<String>,
#[arg(short, long, action = clap::ArgAction::SetFalse)]
pub compress: bool,
}
#[derive(Deserialize)]
pub struct Config {
pub url: Option<String>,
pub units: Option<Vec<u32>>,
pub start_date: Option<NaiveDate>,
pub end_date: Option<NaiveDate>,
pub backup_dir: Option<String>,
pub concurrency: Option<usize>,
pub interval_days: Option<u64>,
pub metrics: Option<Vec<String>>,
pub compress: Option<bool>,
}
impl Config {
pub fn load_config(path: &Option<std::path::PathBuf>) -> Result<Self> {
match path {
Some(p) => {
let contents = fs::read_to_string(p).context("Failed to read config file")?;
toml::from_str::<Self>(&contents).context("Failed to parse config")
},
None => Ok(Config::default()),
}
}
pub fn merge_configs(cli: &Cli) -> Result<Self> {
let config = Self::load_config(&cli.config)?;
Ok(Self {
url: cli.url.clone()(|| config.url.clone()).unwrap_or_else(|| "http://localhost:4242".to_string()),
units: if !cli.units.is_empty() { Some(cli.units.clone()) } else { config.units.clone() },
start_date: cli.start_date.or(config.start_date.ok_or(anyhow::anyhow!("Start date must be provided"))?)?,
end_date: cli.end_date.or(config.end_date.ok_or(anyhow::anyhow!("End date must be provided"))?)?,
backup_dir: Some(cli.backup_dir.clone().or_else(|| config.backup_dir.clone().unwrap_or_else(|| "./backup".to_string()))),
concurrency: Some(cli.concurrency.max(config.concurrency.unwrap_or(1))),
interval_days: Some(cli.interval_days.max(config.interval_days.unwrap_or(7))),
metrics: if !cli.metrics.is_empty() { Some(cli.metrics.clone()) } else { config.metrics.clone() },
compress: Some(cli.compress && config.compress.unwrap_or(true)),
})
}
}
impl Default for Config {
fn default() -> Self {
Config {
url: None,
units: None,
start_date: None,
end_date: None,
backup_dir: None,
concurrency: None,
interval_days: None,
metrics: None,
compress: None,
}
}
}
最新发布