From 56f4d8aad5ac483902b5553660ee179752eedff2 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Fri, 15 Aug 2025 16:36:58 -0300 Subject: [PATCH] feat: implement unified settings system Co-authored-by: aider (openai/andrew/openrouter/anthropic/claude-sonnet-4) --- src/config.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 103 +++++++++++------------------------------ src/modes/common.rs | 86 +++++++++++++++++++++++++++++++--- src/modes/delete.rs | 3 +- src/modes/diff.rs | 3 +- src/modes/get.rs | 7 +-- src/modes/info.rs | 23 ++++----- src/modes/list.rs | 21 +++++---- src/modes/save.rs | 33 ++++++------- src/modes/server.rs | 16 ++++--- src/modes/status.rs | 9 ++-- src/modes/update.rs | 9 ++-- 12 files changed, 283 insertions(+), 140 deletions(-) diff --git a/src/config.rs b/src/config.rs index 51726e6..f8cab18 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,15 +3,21 @@ use std::fs; use anyhow::{Result, Context}; use serde::{Deserialize, Serialize}; use log::debug; +use crate::args::{Args, KeyValue}; #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct Config { pub dir: Option, pub list_format: Option, pub human_readable: Option, + pub output_format: Option, + pub verbose: Option, + pub quiet: Option, + pub force: Option, pub server: Option, pub compression_plugin: Option, pub meta_plugins: Option>, + pub digest: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -31,6 +37,110 @@ pub struct MetaPluginConfig { pub name: String, } +/// Unified settings that merges config file and CLI arguments +#[derive(Debug, Clone)] +pub struct Settings { + pub dir: PathBuf, + pub list_format: String, + pub human_readable: bool, + pub output_format: Option, + pub verbose: u8, + pub quiet: bool, + pub force: bool, + pub server_password: Option, + pub compression: Option, + pub digest: Option, + pub meta_plugins: Vec, + pub meta: Vec, +} + +impl Settings { + /// Create unified settings from config and args with proper priority + pub fn from_config_and_args(config: &Config, args: &Args, default_dir: PathBuf) -> Result { + // Apply priority: CLI args > env vars > config file > defaults + + let dir = args.options.dir.clone() + .or_else(|| config.dir.clone()) + .unwrap_or(default_dir); + + let list_format = if args.options.list_format != "id,time,size,tags,meta:hostname" { + args.options.list_format.clone() + } else { + config.list_format.clone() + .unwrap_or_else(|| "id,time,size,tags,meta:hostname".to_string()) + }; + + let human_readable = args.options.human_readable || config.human_readable.unwrap_or(false); + + let output_format = args.options.output_format.clone() + .or_else(|| config.output_format.clone()); + + let verbose = if args.options.verbose > 0 { + args.options.verbose + } else { + config.verbose.unwrap_or(0) + }; + + let quiet = args.options.quiet || config.quiet.unwrap_or(false); + let force = args.options.force || config.force.unwrap_or(false); + + let server_password = args.options.server_password.clone() + .or_else(|| config.get_server_password().ok().flatten()); + + let compression = args.item.compression.clone() + .or_else(|| config.compression_plugin.as_ref().map(|c| c.name.clone())); + + let digest = args.item.digest.clone() + .or_else(|| config.digest.clone()); + + let meta_plugins = if !args.item.meta_plugins.is_empty() { + args.item.meta_plugins.clone() + } else { + config.meta_plugins.as_ref() + .map(|plugins| plugins.iter().map(|p| p.name.clone()).collect()) + .unwrap_or_default() + }; + + Ok(Settings { + dir, + list_format, + human_readable, + output_format, + verbose, + quiet, + force, + server_password, + compression, + digest, + meta_plugins, + meta: args.item.meta.clone(), + }) + } + + /// Get server address from args or config + pub fn get_server_address(&self, args: &Args, config: &Config) -> Option { + // CLI args take priority + if let Some(server_addr) = &args.mode.server { + return Some(server_addr.clone()); + } + + // Then config file + if let Some(server_config) = &config.server { + let mut addr = server_config.address.clone().unwrap_or_else(|| "127.0.0.1".to_string()); + if let Some(port) = server_config.port { + if !addr.contains(':') { + addr.push_str(&format!(":{}", port)); + } + } else if !addr.contains(':') { + addr.push_str(":8080"); + } + return Some(addr); + } + + None + } +} + impl Config { /// Load configuration from a file pub fn from_file(path: &PathBuf) -> Result { diff --git a/src/main.rs b/src/main.rs index 3ca6a16..c03e7a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ extern crate serde_yaml; extern crate serde; use args::{Args, NumberOrString}; -use config::Config; +use config::{Config, Settings}; /** * Main function to handle command-line arguments and execute the appropriate mode. @@ -67,6 +67,16 @@ fn main() -> Result<(), Error> { debug!("MAIN: Loaded config: {:?}", config); + // Determine default data directory + let default_dir = match proj_dirs { + Some(ref proj_dirs) => proj_dirs.data_dir().to_path_buf(), + None => return Err(anyhow!("Unable to determine data directory")), + }; + + // Create unified settings + let settings = Settings::from_config_and_args(&config, &args, default_dir)?; + debug!("MAIN: Unified settings: {:?}", settings); + let ids = &mut Vec::new(); let tags = &mut Vec::new(); @@ -114,23 +124,9 @@ fn main() -> Result<(), Error> { mode = KeepModes::Status; } else if args.mode.server.is_some() { mode = KeepModes::Server; - } else if config.server.is_some() && args.mode.server.is_none() { + } else if settings.get_server_address(&args, &config).is_some() && args.mode.server.is_none() { // If server is configured in config file but not specified via CLI - if let Some(server_config) = &config.server { - let mut server_addr = String::new(); - if let Some(address) = &server_config.address { - server_addr.push_str(address); - } else { - server_addr.push_str("127.0.0.1"); - } - if let Some(port) = server_config.port { - server_addr.push_str(&format!(":{}", port)); - } else { - server_addr.push_str(":8080"); - } - args.mode.server = Some(server_addr); - mode = KeepModes::Server; - } + mode = KeepModes::Server; } if mode == KeepModes::Unknown { @@ -142,7 +138,7 @@ fn main() -> Result<(), Error> { } // Validate output format usage - if let Some(output_format_str) = &args.options.output_format { + if let Some(output_format_str) = &settings.output_format { if output_format_str != "table" && mode != KeepModes::Info && mode != KeepModes::Status && mode != KeepModes::List { cmd.error( ErrorKind::InvalidValue, @@ -152,7 +148,7 @@ fn main() -> Result<(), Error> { } // Validate human-readable usage - if args.options.human_readable && mode != KeepModes::List && mode != KeepModes::Info { + if settings.human_readable && mode != KeepModes::List && mode != KeepModes::Info { cmd.error( ErrorKind::InvalidValue, "--human-readable can only be used with --list and --info modes" @@ -160,7 +156,7 @@ fn main() -> Result<(), Error> { } // Validate server password usage - if args.options.server_password.is_some() && mode != KeepModes::Server { + if settings.server_password.is_some() && mode != KeepModes::Server { cmd.error( ErrorKind::InvalidValue, "--server-password can only be used with --server mode" @@ -172,58 +168,11 @@ fn main() -> Result<(), Error> { debug!("MAIN: tags: {:?}", tags); debug!("MAIN: mode: {:?}", mode); - // Apply configuration priority: CLI args > env vars > config file > defaults - if args.options.dir.is_none() { - if let Some(config_dir) = &config.dir { - args.options.dir = Some(config_dir.clone()); - } else { - match proj_dirs { - Some(proj_dirs) => args.options.dir = Some(proj_dirs.data_dir().to_path_buf()), - None => return Err(anyhow!("Unable to determine data directory")), - } - } - } - - // Apply list_format from config if not set via CLI/env - if args.options.list_format == "id,time,size,tags,meta:hostname" { - if let Some(config_list_format) = &config.list_format { - args.options.list_format = config_list_format.clone(); - } - } - - // Apply human_readable from config if not set via CLI - if !args.options.human_readable { - if let Some(config_human_readable) = config.human_readable { - args.options.human_readable = config_human_readable; - } - } - - // Apply server password from config file if not set via CLI/env - if args.options.server_password.is_none() { - if let Ok(Some(password)) = config.get_server_password() { - args.options.server_password = Some(password); - } - } - - // Apply compression from config if not set via CLI/env - if args.item.compression.is_none() { - if let Some(compression_plugin) = &config.compression_plugin { - args.item.compression = Some(compression_plugin.name.clone()); - } - } - - // Apply meta_plugins from config if not set via CLI/env - if args.item.meta_plugins.is_empty() { - if let Some(meta_plugins) = &config.meta_plugins { - args.item.meta_plugins = meta_plugins.iter().map(|p| p.name.clone()).collect(); - } - } - unsafe { libc::umask(0o077); } - let data_path = args.options.dir.clone().unwrap(); + let data_path = settings.dir.clone(); let mut db_path = data_path.clone(); db_path.push("keep-1.db"); @@ -238,31 +187,31 @@ fn main() -> Result<(), Error> { match mode { KeepModes::Save => { - crate::modes::save::mode_save(&mut cmd, &args, ids, tags, &mut conn, data_path)? + crate::modes::save::mode_save(&mut cmd, &settings, &config, ids, tags, &mut conn, data_path)? } KeepModes::Get => { - crate::modes::get::mode_get(&mut cmd, &args, ids, tags, &mut conn, data_path)? + crate::modes::get::mode_get(&mut cmd, &settings, &config, ids, tags, &mut conn, data_path)? } KeepModes::Diff => { - crate::modes::diff::mode_diff(&mut cmd, &args, ids, tags, &mut conn, data_path)? + crate::modes::diff::mode_diff(&mut cmd, &settings, &config, ids, tags, &mut conn, data_path)? } KeepModes::List => { - crate::modes::list::mode_list(&mut cmd, &args, ids, tags, &mut conn, data_path)? + crate::modes::list::mode_list(&mut cmd, &settings, &config, ids, tags, &mut conn, data_path)? } KeepModes::Update => { - crate::modes::update::mode_update(&mut cmd, &args, ids, tags, &mut conn, data_path)? + crate::modes::update::mode_update(&mut cmd, &settings, &config, ids, tags, &mut conn, data_path)? } KeepModes::Info => { - crate::modes::info::mode_info(&mut cmd, &args, ids, tags, &mut conn, data_path)? + crate::modes::info::mode_info(&mut cmd, &settings, &config, ids, tags, &mut conn, data_path)? } KeepModes::Delete => { - crate::modes::delete::mode_delete(&mut cmd, &args, ids, tags, &mut conn, data_path)? + crate::modes::delete::mode_delete(&mut cmd, &settings, &config, ids, tags, &mut conn, data_path)? } KeepModes::Status => { - crate::modes::status::mode_status(&mut cmd, &args, data_path, db_path)? + crate::modes::status::mode_status(&mut cmd, &settings, &config, data_path, db_path)? } KeepModes::Server => { - crate::modes::server::mode_server(&mut cmd, &args, &mut conn, data_path)? + crate::modes::server::mode_server(&mut cmd, &settings, &config, &mut conn, data_path)? } KeepModes::Unknown => todo!(), } diff --git a/src/modes/common.rs b/src/modes/common.rs index e8beda8..58424e3 100644 --- a/src/modes/common.rs +++ b/src/modes/common.rs @@ -157,12 +157,6 @@ impl FromStr for OutputFormat { } } -pub fn get_output_format(args: &Args) -> OutputFormat { - args.options.output_format - .as_ref() - .and_then(|s| OutputFormat::from_str(s).ok()) - .unwrap_or(OutputFormat::Table) -} pub fn cmd_args_meta_plugin_types(cmd: &mut Command, args: &Args) -> Vec { let mut meta_plugin_types = Vec::new(); @@ -200,3 +194,83 @@ pub fn cmd_args_meta_plugin_types(cmd: &mut Command, args: &Args) -> Vec Vec { + let mut meta_plugin_types = Vec::new(); + + // Handle comma-separated values in each meta_plugins argument + for meta_plugin_names_str in &settings.meta_plugins { + let meta_plugin_names: Vec<&str> = meta_plugin_names_str.split(',').collect(); + + for name in meta_plugin_names { + let trimmed_name = name.trim(); + if trimmed_name.is_empty() { + continue; + } + + // Try to find the MetaPluginType by meta name + let mut found = false; + for meta_plugin_type in MetaPluginType::iter() { + let mut meta_plugin = crate::meta_plugin::get_meta_plugin(meta_plugin_type.clone()); + if meta_plugin.meta_name() == trimmed_name { + meta_plugin_types.push(meta_plugin_type); + found = true; + break; + } + } + + if !found { + cmd.error( + ErrorKind::InvalidValue, + format!("Unknown meta plugin type: {}", trimmed_name), + ) + .exit(); + } + } + } + + meta_plugin_types +} + +pub fn settings_digest_type(cmd: &mut Command, settings: &crate::config::Settings) -> MetaPluginType { + let digest_name = settings + .digest + .clone() + .unwrap_or(MetaPluginType::DigestSha256.to_string()); + + let digest_type_opt = MetaPluginType::from_str(&digest_name); + if digest_type_opt.is_err() { + cmd.error( + ErrorKind::InvalidValue, + format!("Invalid digest algorithm '{}'. Use 'sha256' or 'md5'", digest_name), + ) + .exit(); + } + + digest_type_opt.unwrap() +} + +pub fn settings_compression_type(cmd: &mut Command, settings: &crate::config::Settings) -> CompressionType { + let compression_name = settings + .compression + .clone() + .unwrap_or(CompressionType::LZ4.to_string()); + + let compression_type_opt = CompressionType::from_str(&compression_name); + if compression_type_opt.is_err() { + cmd.error( + ErrorKind::InvalidValue, + format!("Invalid compression algorithm '{}'. Supported algorithms: lz4, gzip, xz, zstd", compression_name), + ) + .exit(); + } + + compression_type_opt.unwrap() +} + +pub fn settings_output_format(settings: &crate::config::Settings) -> OutputFormat { + settings.output_format + .as_ref() + .and_then(|s| OutputFormat::from_str(s).ok()) + .unwrap_or(OutputFormat::Table) +} diff --git a/src/modes/delete.rs b/src/modes/delete.rs index f87bf50..47b9cce 100644 --- a/src/modes/delete.rs +++ b/src/modes/delete.rs @@ -10,7 +10,8 @@ use rusqlite::Connection; pub fn mode_delete( cmd: &mut Command, - _args: &crate::Args, + _settings: &crate::config::Settings, + _config: &crate::config::Config, ids: &mut Vec, tags: &mut Vec, conn: &mut Connection, diff --git a/src/modes/diff.rs b/src/modes/diff.rs index cfb3ed7..08e6023 100644 --- a/src/modes/diff.rs +++ b/src/modes/diff.rs @@ -293,7 +293,8 @@ fn handle_diff_output( pub fn mode_diff( cmd: &mut Command, - _args: &crate::Args, + _settings: &crate::config::Settings, + _config: &crate::config::Config, ids: &mut Vec, tags: &mut Vec, conn: &mut rusqlite::Connection, diff --git a/src/modes/get.rs b/src/modes/get.rs index 8753bbc..9b70b89 100644 --- a/src/modes/get.rs +++ b/src/modes/get.rs @@ -10,7 +10,8 @@ use is_terminal::IsTerminal; pub fn mode_get( cmd: &mut Command, - args: &crate::Args, + settings: &crate::config::Settings, + _config: &crate::config::Config, ids: &mut Vec, tags: &mut Vec, conn: &mut rusqlite::Connection, @@ -23,7 +24,7 @@ pub fn mode_get( } let mut meta: std::collections::HashMap = std::collections::HashMap::new(); - for item in args.item.meta.iter() { + for item in settings.meta.iter() { let item = item.clone(); meta.insert(item.key, item.value); } @@ -47,7 +48,7 @@ pub fn mode_get( item_path.push(item_id.to_string()); // Determine if we should detect binary data - let mut detect_binary = !args.options.force && std::io::stdout().is_terminal(); + let mut detect_binary = !settings.force && std::io::stdout().is_terminal(); // If we're detecting binary and there's binary metadata, check it if detect_binary { diff --git a/src/modes/info.rs b/src/modes/info.rs index ef8bab7..81dff53 100644 --- a/src/modes/info.rs +++ b/src/modes/info.rs @@ -19,7 +19,8 @@ use prettytable::{Attr, Cell, Row, Table}; pub fn mode_info( cmd: &mut Command, - args: &crate::Args, + settings: &crate::config::Settings, + _config: &crate::config::Config, ids: &mut Vec, tags: &mut Vec, conn: &mut rusqlite::Connection, @@ -32,7 +33,7 @@ pub fn mode_info( } let mut meta: std::collections::HashMap = std::collections::HashMap::new(); - for item in args.item.meta.iter() { + for item in settings.meta.iter() { let item = item.clone(); meta.insert(item.key, item.value); } @@ -46,7 +47,7 @@ pub fn mode_info( }; match item_maybe { - Some(item) => show_item(item, args, conn, data_path), + Some(item) => show_item(item, settings, conn, data_path), None => Err(anyhow!("Unable to find matching item in database")), } } @@ -67,7 +68,7 @@ struct ItemInfo { fn show_item( item: Item, // Using the provided struct definition - args: &crate::Args, + settings: &crate::config::Settings, conn: &mut rusqlite::Connection, data_path: PathBuf, ) -> anyhow::Result<()> { @@ -78,10 +79,10 @@ fn show_item( .map(|x| x.name) .collect(); - let output_format = get_output_format(args); + let output_format = crate::modes::common::settings_output_format(settings); if output_format != OutputFormat::Table { - return show_item_structured(item, args, conn, data_path, output_format); + return show_item_structured(item, settings, conn, data_path, output_format); } let mut table = Table::new(); @@ -111,7 +112,7 @@ fn show_item( ])); let size_cell = match item.size { - Some(size) => Cell::new(format_size(size as u64, args.options.human_readable).as_str()), + Some(size) => Cell::new(format_size(size as u64, settings.human_readable).as_str()), None => Cell::new("Missing") .with_style(Attr::ForegroundColor(prettytable::color::RED)) .with_style(Attr::Bold), @@ -132,7 +133,7 @@ fn show_item( let file_size_cell = match item_path_buf.metadata() { Ok(metadata) => { - Cell::new(format_size(metadata.len(), args.options.human_readable).as_str()) + Cell::new(format_size(metadata.len(), settings.human_readable).as_str()) } Err(_) => Cell::new("Missing") .with_style(Attr::ForegroundColor(prettytable::color::RED)) @@ -162,7 +163,7 @@ fn show_item( fn show_item_structured( item: Item, - args: &crate::Args, + settings: &crate::config::Settings, conn: &mut rusqlite::Connection, data_path: PathBuf, output_format: OutputFormat, @@ -178,12 +179,12 @@ fn show_item_structured( let file_size = item_path_buf.metadata().map(|m| m.len()).ok(); let file_size_formatted = match file_size { - Some(size) => format_size(size, args.options.human_readable), + Some(size) => format_size(size, settings.human_readable), None => "Missing".to_string(), }; let stream_size_formatted = match item.size { - Some(size) => format_size(size as u64, args.options.human_readable), + Some(size) => format_size(size as u64, settings.human_readable), None => "Missing".to_string(), }; diff --git a/src/modes/list.rs b/src/modes/list.rs index 5668fe5..8bf6965 100644 --- a/src/modes/list.rs +++ b/src/modes/list.rs @@ -27,7 +27,8 @@ struct ListItem { pub fn mode_list( cmd: &mut clap::Command, - args: &crate::Args, + settings: &crate::config::Settings, + _config: &crate::config::Config, ids: &mut Vec, tags: &Vec, conn: &mut rusqlite::Connection, @@ -42,7 +43,7 @@ pub fn mode_list( } let mut meta: std::collections::HashMap = std::collections::HashMap::new(); - for item in args.item.meta.iter() { + for item in settings.meta.iter() { let item = item.clone(); meta.insert(item.key, item.value); } @@ -71,16 +72,16 @@ pub fn mode_list( // Fetch all metadata for all items in a single query let meta_by_item = crate::db::get_meta_for_items(conn, &item_ids)?; - let output_format = get_output_format(args); + let output_format = crate::modes::common::settings_output_format(settings); if output_format != OutputFormat::Table { - return show_list_structured(items, tags_by_item, meta_by_item, data_path, args, output_format); + return show_list_structured(items, tags_by_item, meta_by_item, data_path, settings, output_format); } let mut table = Table::new(); table.set_format(*prettytable::format::consts::FORMAT_CLEAN); - let list_format = args.options.list_format.split(","); + let list_format = settings.list_format.split(","); let mut title_row = row!(); @@ -141,7 +142,7 @@ pub fn mode_list( )), ColumnType::Size => match item.size { Some(size) => Cell::new_align( - &size_column(size as u64, args.options.human_readable, column_width), + &size_column(size as u64, settings.human_readable, column_width), Alignment::RIGHT, ), None => match item_path.metadata() { @@ -158,7 +159,7 @@ pub fn mode_list( }, ColumnType::FileSize => match item_path.metadata() { Ok(metadata) => Cell::new_align( - &size_column(metadata.len(), args.options.human_readable, column_width), + &size_column(metadata.len(), settings.human_readable, column_width), Alignment::RIGHT, ), Err(_) => Cell::new_align("Missing", Alignment::RIGHT) @@ -195,7 +196,7 @@ fn show_list_structured( tags_by_item: std::collections::HashMap>, meta_by_item: std::collections::HashMap>, data_path: std::path::PathBuf, - args: &crate::Args, + settings: &crate::config::Settings, output_format: OutputFormat, ) -> anyhow::Result<()> { let mut list_items = Vec::new(); @@ -210,12 +211,12 @@ fn show_list_structured( let file_size = item_path.metadata().map(|m| m.len()).ok(); let file_size_formatted = match file_size { - Some(size) => crate::modes::common::format_size(size, args.options.human_readable), + Some(size) => crate::modes::common::format_size(size, settings.human_readable), None => "Missing".to_string(), }; let size_formatted = match item.size { - Some(size) => crate::modes::common::format_size(size as u64, args.options.human_readable), + Some(size) => crate::modes::common::format_size(size as u64, settings.human_readable), None => "Unknown".to_string(), }; diff --git a/src/modes/save.rs b/src/modes/save.rs index 91746dd..2d78f7a 100644 --- a/src/modes/save.rs +++ b/src/modes/save.rs @@ -4,7 +4,7 @@ use log::debug; use std::io::{Read, Write, IsTerminal}; // Import the missing functions from common module -use crate::modes::common::{cmd_args_digest_type, cmd_args_compression_type, cmd_args_meta_plugin_types}; +use crate::modes::common::{settings_digest_type, settings_compression_type, settings_meta_plugin_types}; fn validate_save_args(cmd: &mut Command, ids: &Vec) { if !ids.is_empty() { @@ -24,18 +24,18 @@ fn initialize_tags(tags: &mut Vec) { fn setup_compression_and_plugins( cmd: &mut Command, - args: &crate::Args, + settings: &crate::config::Settings, ) -> (crate::compression_engine::CompressionType, Box, Vec>) { - let digest_type = cmd_args_digest_type(cmd, &args); + let digest_type = settings_digest_type(cmd, settings); debug!("MAIN: Digest type: {:?}", digest_type); - let compression_type = cmd_args_compression_type(cmd, &args); + let compression_type = settings_compression_type(cmd, settings); debug!("MAIN: Compression type: {:?}", compression_type); let compression_engine = crate::compression_engine::get_compression_engine(compression_type.clone()).expect("Unable to get compression engine"); - // Start with meta plugin types from command line - let mut meta_plugin_types: Vec = cmd_args_meta_plugin_types(cmd, &args); + // Start with meta plugin types from settings + let mut meta_plugin_types: Vec = settings_meta_plugin_types(cmd, settings); debug!("MAIN: Meta plugin types: {:?}", meta_plugin_types); // Convert digest type to meta plugin type and add to the list if needed @@ -78,7 +78,7 @@ fn setup_compression_and_plugins( fn create_and_log_item( conn: &mut rusqlite::Connection, - args: &crate::Args, + settings: &crate::config::Settings, tags: &Vec, compression_type: &crate::compression_engine::CompressionType, ) -> Result { @@ -93,7 +93,7 @@ fn create_and_log_item( item.id = Some(id); debug!("MAIN: Added item {:?}", item.clone()); - if !args.options.quiet { + if !settings.quiet { if std::io::stderr().is_terminal() { let mut t = term::stderr().unwrap(); t.reset().unwrap_or(()); @@ -121,7 +121,7 @@ fn create_and_log_item( fn setup_item_metadata( conn: &mut rusqlite::Connection, - _args: &crate::Args, + _settings: &crate::config::Settings, item: &crate::db::Item, tags: &Vec, ) -> Result<(), anyhow::Error> { @@ -129,7 +129,7 @@ fn setup_item_metadata( Ok(()) } -fn collect_item_meta(args: &crate::Args) -> std::collections::HashMap { +fn collect_item_meta(settings: &crate::config::Settings) -> std::collections::HashMap { let mut item_meta: std::collections::HashMap = crate::modes::common::get_meta_from_env(); if let Ok(hostname) = gethostname::gethostname().into_string() { @@ -138,7 +138,7 @@ fn collect_item_meta(args: &crate::Args) -> std::collections::HashMap, tags: &mut Vec, conn: &mut rusqlite::Connection, @@ -239,14 +240,14 @@ pub fn mode_save( validate_save_args(cmd, ids); initialize_tags(tags); - let (compression_type, compression_engine, mut meta_plugins) = setup_compression_and_plugins(cmd, args); + let (compression_type, compression_engine, mut meta_plugins) = setup_compression_and_plugins(cmd, settings); - let mut item = create_and_log_item(conn, args, tags, &compression_type)?; - setup_item_metadata(conn, args, &item, tags)?; // Pass mutable reference + let mut item = create_and_log_item(conn, settings, tags, &compression_type)?; + setup_item_metadata(conn, settings, &item, tags)?; // Pass mutable reference // Save as much as possible in case something breaks - don't use transactions // This allows partial saves to succeed even if some metadata operations fail - let item_meta = collect_item_meta(args); + let item_meta = collect_item_meta(settings); let item_id = item.id.ok_or_else(|| anyhow!("Item missing ID"))?; for kv in item_meta.iter() { diff --git a/src/modes/server.rs b/src/modes/server.rs index a6bfe3a..8c546df 100644 --- a/src/modes/server.rs +++ b/src/modes/server.rs @@ -20,26 +20,28 @@ pub use common::{ServerConfig, AppState, logging_middleware, create_auth_middlew pub fn mode_server( _cmd: &mut Command, - args: &crate::Args, + settings: &crate::config::Settings, + config: &crate::config::Config, conn: &mut rusqlite::Connection, data_path: PathBuf, ) -> Result<()> { - let server_address = args.mode.server.as_ref().unwrap(); + let server_address = settings.get_server_address(&crate::args::Args::parse(), config) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()); - let config = ServerConfig { - address: server_address.clone(), - password: args.options.server_password.clone(), + let server_config = common::ServerConfig { + address: server_address, + password: settings.server_password.clone(), }; // We need to move the connection into the async runtime let rt = tokio::runtime::Runtime::new()?; // Take ownership of the connection and move it into the async runtime let owned_conn = std::mem::replace(conn, rusqlite::Connection::open_in_memory()?); - rt.block_on(run_server(config, owned_conn, data_path)) + rt.block_on(run_server(server_config, owned_conn, data_path)) } async fn run_server( - config: ServerConfig, + config: common::ServerConfig, conn: rusqlite::Connection, data_dir: PathBuf, ) -> Result<()> { diff --git a/src/modes/status.rs b/src/modes/status.rs index 55fc335..c48bbd6 100644 --- a/src/modes/status.rs +++ b/src/modes/status.rs @@ -119,15 +119,16 @@ fn build_meta_plugin_table(meta_plugin_info: &Vec) -> Table { pub fn mode_status( _cmd: &mut Command, - args: &crate::Args, + settings: &crate::config::Settings, + _config: &crate::config::Config, data_path: PathBuf, db_path: PathBuf, ) -> Result<(), anyhow::Error> { // Determine which meta plugins would be enabled for a save operation - let mut meta_plugin_types: Vec = crate::modes::common::cmd_args_meta_plugin_types(_cmd, &args); + let mut meta_plugin_types: Vec = crate::modes::common::settings_meta_plugin_types(_cmd, settings); // Add digest type if specified - let digest_type = crate::modes::common::cmd_args_digest_type(_cmd, &args); + let digest_type = crate::modes::common::settings_digest_type(_cmd, settings); let digest_meta_plugin_type = match digest_type { crate::meta_plugin::MetaPluginType::DigestSha256 => Some(MetaPluginType::DigestSha256), crate::meta_plugin::MetaPluginType::DigestMd5 => Some(MetaPluginType::DigestMd5), @@ -140,7 +141,7 @@ pub fn mode_status( } } - let output_format = get_output_format(args); + let output_format = crate::modes::common::settings_output_format(settings); let status_info = generate_status_info(data_path, db_path, &meta_plugin_types); match output_format { diff --git a/src/modes/update.rs b/src/modes/update.rs index f61f65d..5478156 100644 --- a/src/modes/update.rs +++ b/src/modes/update.rs @@ -13,7 +13,8 @@ use rusqlite::Connection; pub fn mode_update( cmd: &mut Command, - args: &crate::Args, + settings: &crate::config::Settings, + _config: &crate::config::Config, ids: &mut Vec, tags: &mut Vec, conn: &mut Connection, @@ -71,7 +72,7 @@ pub fn mode_update( } } - let digest_type = cmd_args_digest_type(cmd, args); + let digest_type = crate::modes::common::settings_digest_type(cmd, settings); let digest_meta = get_digest_type_meta(digest_type.clone()); let digest_value = db::get_item_meta_value(&tx, &item, digest_meta)?; @@ -115,9 +116,9 @@ pub fn mode_update( } } - if !args.item.meta.is_empty() { + if !settings.meta.is_empty() { debug!("MAIN: Updating item meta"); - for kv in args.item.meta.iter() { + for kv in settings.meta.iter() { let meta = db::Meta { id: item_id, name: kv.key.to_string(),