From 687a9350643dff719ea8fdcba7977931f8e9890c Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Tue, 17 Mar 2026 16:26:28 +0300 Subject: [PATCH] konservejo: provide better feedback for config validation Signed-off-by: NotAShelf Change-Id: I1c4e94780890b1ea440ee5b72959d0d96a6a6964 --- src/main.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 099d512..ee46f54 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,11 +64,101 @@ async fn main() -> Result<()> { } }, + #[allow(clippy::print_stdout)] Commands::ValidateConfig => { - info!("Validating configuration from {}", cli.config); - config::Config::from_file(&cli.config) + use std::io::Write; + + println!("Validating configuration from: {}", cli.config); + let config = config::Config::from_file(&cli.config) .map_err(|e| eyre!("Configuration is invalid: {}", e))?; - info!("Configuration is valid"); + + println!("\n[OK] Configuration structure is valid\n"); + + println!("Service: {}", config.service.name); + println!(" Concurrency limit: {}", config.service.concurrency_limit); + println!(" State DB: {}", config.service.state_db_path); + println!(" Temp dir: {}", config.service.temp_dir); + println!( + " Retry: {} attempts, {}ms initial backoff", + config.service.retry.max_retries, + config.service.retry.initial_backoff_ms + ); + + println!("\nSources ({}):", config.sources.len()); + for (i, source) in config.sources.iter().enumerate() { + match source { + config::SourceConfig::Forgejo(cfg) => { + println!(" [{}] {} -> {}", i + 1, cfg.id, cfg.api_url); + if !cfg.scope.organizations.is_empty() { + println!( + " Organizations: {}", + cfg.scope.organizations.join(", ") + ); + } + if !cfg.scope.exclude_repos.is_empty() { + println!( + " Excluded: {}", + cfg.scope.exclude_repos.join(", ") + ); + } + }, + } + } + + println!("\nSinks ({}):", config.sinks.len()); + for (i, sink) in config.sinks.iter().enumerate() { + match sink { + config::SinkConfig::Filesystem(cfg) => { + println!(" [{}] {} -> {}", i + 1, cfg.id, cfg.path); + println!(" Verify on write: {}", cfg.verify_on_write); + if let Some(days) = cfg.retention_days { + println!(" Retention: {days} days"); + } + }, + config::SinkConfig::S3(_) => { + println!(" [{}] S3 (NOT IMPLEMENTED - will error)", i + 1); + }, + } + } + + print!("\nChecking paths... "); + std::io::stdout() + .flush() + .map_err(|e| eyre!("Failed to flush stdout: {e}"))?; + + let state_dir = std::path::Path::new(&config.service.state_db_path) + .parent() + .ok_or_else(|| eyre!("Invalid state_db_path (no parent directory)"))?; + + if !state_dir.exists() { + bail!( + "State directory does not exist: {}. Create it first.", + state_dir.display() + ); + } + + let temp_dir = std::path::Path::new(&config.service.temp_dir); + if !temp_dir.exists() { + bail!( + "Temp directory does not exist: {}. Create it first.", + temp_dir.display() + ); + } + + for sink in &config.sinks { + if let config::SinkConfig::Filesystem(cfg) = sink { + let path = std::path::Path::new(&cfg.path); + if !path.exists() { + bail!( + "Sink directory does not exist: {}. Create it first.", + path.display() + ); + } + } + } + + println!("[OK]"); + println!("\n[OK] Configuration is valid and paths are accessible"); Ok(()) },