mod db; mod gpo; mod serve; use std::path::PathBuf; use clap::{Args, Parser, Subcommand}; use figment::{ providers::{Env, Format, Serialized, Toml}, Figment, }; use serde::Serialize; /// Otter is a lightweight implementation of the Gpodder API, designed to be used for small /// personal deployments. #[derive(Parser)] pub struct Cli { #[command(flatten)] pub config: ClapConfig, #[command(subcommand)] pub cmd: Command, } #[derive(Serialize, Args, Clone)] pub struct ClapConfig { #[arg( short, long = "config", env = "OTTER_CONFIG_FILE", value_name = "CONFIG_FILE" )] config_file: Option, #[serde(skip_serializing_if = "Option::is_none")] #[arg(long = "data", value_name = "DATA_DIR")] data_dir: Option, #[serde(skip_serializing_if = "Option::is_none")] #[arg(short, long, value_name = "DOMAIN")] domain: Option, #[serde(skip_serializing_if = "Option::is_none")] #[arg(short, long, value_name = "PORT")] port: Option, } #[derive(Subcommand)] pub enum Command { Serve, #[command(subcommand)] Db(db::DbCommand), /// Perform operations on the database through the Gpodder abstraction, allowing operations /// identical to the ones performed by the API. #[command(subcommand)] Gpo(gpo::Command), } impl Cli { pub fn run(&self) -> u8 { let mut figment = Figment::new(); if let Some(config_path) = &self.config.config_file { figment = figment.merge(Toml::file(config_path)); } let config: crate::config::Config = match figment .merge(Env::prefixed("OTTER_")) .merge(Serialized::defaults(self.config.clone())) .extract() { Ok(config) => config, Err(err) => { eprintln!("{}", err); return 1; } }; match &self.cmd { Command::Serve => serve::serve(&config), Command::Db(cmd) => cmd.run(&config), Command::Gpo(cmd) => cmd.run(&config), } } }