use std::sync::Arc; use clap::{Parser, command}; use super::{AppContext, core::App, env::Environment}; use crate::{app::config::AppConfig, errors::RecorderResult}; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] pub struct MainCliArgs { /// Explicit config file path #[arg(short, long)] config_file: Option, /// Explicit dotenv file path #[arg(short, long)] dotenv_file: Option, /// Explicit working dir #[arg(short, long)] working_dir: Option, /// Explicit environment #[arg(short, long)] environment: Option, } pub struct AppBuilder { dotenv_file: Option, config_file: Option, working_dir: String, environment: Environment, } impl AppBuilder { pub async fn from_main_cli(environment: Option) -> RecorderResult { let args = MainCliArgs::parse(); let environment = environment.unwrap_or_else(|| { args.environment.unwrap_or({ if cfg!(test) { Environment::Testing } else if cfg!(debug_assertions) { Environment::Development } else { Environment::Production } }) }); let mut builder = Self::default(); if let Some(working_dir) = args.working_dir { builder = builder.working_dir(working_dir); } if matches!( &environment, Environment::Testing | Environment::Development ) { builder = builder.working_dir_from_manifest_dir(); } builder = builder .config_file(args.config_file) .dotenv_file(args.dotenv_file) .environment(environment); Ok(builder) } pub async fn build(self) -> RecorderResult { AppConfig::load_dotenv( &self.environment, &self.working_dir, self.dotenv_file.as_deref(), ) .await?; let config = AppConfig::load_config( &self.environment, &self.working_dir, self.config_file.as_deref(), ) .await?; let app_context = Arc::new( AppContext::new(self.environment.clone(), config, self.working_dir.clone()).await?, ); Ok(App { context: app_context, builder: self, }) } pub fn working_dir(self, working_dir: String) -> Self { let mut ret = self; ret.working_dir = working_dir; ret } pub fn environment(self, environment: Environment) -> Self { let mut ret = self; ret.environment = environment; ret } pub fn config_file(self, config_file: Option) -> Self { let mut ret = self; ret.config_file = config_file; ret } pub fn dotenv_file(self, dotenv_file: Option) -> Self { let mut ret = self; ret.dotenv_file = dotenv_file; ret } pub fn working_dir_from_manifest_dir(self) -> Self { let manifest_dir = if cfg!(debug_assertions) || cfg!(test) { env!("CARGO_MANIFEST_DIR") } else { "./apps/recorder" }; self.working_dir(manifest_dir.to_string()) } } impl Default for AppBuilder { fn default() -> Self { Self { environment: Environment::Production, dotenv_file: None, config_file: None, working_dir: String::from("."), } } }