feat: implement unified settings system
Co-authored-by: aider (openai/andrew/openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
This commit is contained in:
110
src/config.rs
110
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<PathBuf>,
|
||||
pub list_format: Option<String>,
|
||||
pub human_readable: Option<bool>,
|
||||
pub output_format: Option<String>,
|
||||
pub verbose: Option<u8>,
|
||||
pub quiet: Option<bool>,
|
||||
pub force: Option<bool>,
|
||||
pub server: Option<ServerConfig>,
|
||||
pub compression_plugin: Option<CompressionPluginConfig>,
|
||||
pub meta_plugins: Option<Vec<MetaPluginConfig>>,
|
||||
pub digest: Option<String>,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
pub verbose: u8,
|
||||
pub quiet: bool,
|
||||
pub force: bool,
|
||||
pub server_password: Option<String>,
|
||||
pub compression: Option<String>,
|
||||
pub digest: Option<String>,
|
||||
pub meta_plugins: Vec<String>,
|
||||
pub meta: Vec<KeyValue>,
|
||||
}
|
||||
|
||||
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<Self> {
|
||||
// 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<String> {
|
||||
// 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<Self> {
|
||||
|
||||
101
src/main.rs
101
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,24 +124,10 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
if mode == KeepModes::Unknown {
|
||||
if !ids.is_empty() {
|
||||
@@ -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!(),
|
||||
}
|
||||
|
||||
@@ -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<MetaPluginType> {
|
||||
let mut meta_plugin_types = Vec::new();
|
||||
@@ -200,3 +194,83 @@ pub fn cmd_args_meta_plugin_types(cmd: &mut Command, args: &Args) -> Vec<MetaPlu
|
||||
|
||||
meta_plugin_types
|
||||
}
|
||||
|
||||
pub fn settings_meta_plugin_types(cmd: &mut Command, settings: &crate::config::Settings) -> Vec<MetaPluginType> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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<i64>,
|
||||
tags: &mut Vec<String>,
|
||||
conn: &mut Connection,
|
||||
|
||||
@@ -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<i64>,
|
||||
tags: &mut Vec<String>,
|
||||
conn: &mut rusqlite::Connection,
|
||||
|
||||
@@ -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<i64>,
|
||||
tags: &mut Vec<String>,
|
||||
conn: &mut rusqlite::Connection,
|
||||
@@ -23,7 +24,7 @@ pub fn mode_get(
|
||||
}
|
||||
|
||||
let mut meta: std::collections::HashMap<String, String> = 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 {
|
||||
|
||||
@@ -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<i64>,
|
||||
tags: &mut Vec<String>,
|
||||
conn: &mut rusqlite::Connection,
|
||||
@@ -32,7 +33,7 @@ pub fn mode_info(
|
||||
}
|
||||
|
||||
let mut meta: std::collections::HashMap<String, String> = 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(),
|
||||
};
|
||||
|
||||
|
||||
@@ -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<i64>,
|
||||
tags: &Vec<String>,
|
||||
conn: &mut rusqlite::Connection,
|
||||
@@ -42,7 +43,7 @@ pub fn mode_list(
|
||||
}
|
||||
|
||||
let mut meta: std::collections::HashMap<String, String> = 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<i64, Vec<String>>,
|
||||
meta_by_item: std::collections::HashMap<i64, std::collections::HashMap<String, String>>,
|
||||
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(),
|
||||
};
|
||||
|
||||
|
||||
@@ -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<i64>) {
|
||||
if !ids.is_empty() {
|
||||
@@ -24,18 +24,18 @@ fn initialize_tags(tags: &mut Vec<String>) {
|
||||
|
||||
fn setup_compression_and_plugins(
|
||||
cmd: &mut Command,
|
||||
args: &crate::Args,
|
||||
settings: &crate::config::Settings,
|
||||
) -> (crate::compression_engine::CompressionType, Box<dyn crate::compression_engine::CompressionEngine>, Vec<Box<dyn crate::meta_plugin::MetaPlugin>>) {
|
||||
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<crate::meta_plugin::MetaPluginType> = cmd_args_meta_plugin_types(cmd, &args);
|
||||
// Start with meta plugin types from settings
|
||||
let mut meta_plugin_types: Vec<crate::meta_plugin::MetaPluginType> = 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<String>,
|
||||
compression_type: &crate::compression_engine::CompressionType,
|
||||
) -> Result<crate::db::Item, anyhow::Error> {
|
||||
@@ -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<String>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
@@ -129,7 +129,7 @@ fn setup_item_metadata(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_item_meta(args: &crate::Args) -> std::collections::HashMap<String, String> {
|
||||
fn collect_item_meta(settings: &crate::config::Settings) -> std::collections::HashMap<String, String> {
|
||||
let mut item_meta: std::collections::HashMap<String, String> = 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<String, St
|
||||
}
|
||||
}
|
||||
|
||||
for item in args.item.meta.iter() {
|
||||
for item in settings.meta.iter() {
|
||||
let item = item.clone();
|
||||
item_meta.insert(item.key, item.value);
|
||||
}
|
||||
@@ -230,7 +230,8 @@ fn finalize_meta_plugins(
|
||||
|
||||
pub fn mode_save(
|
||||
cmd: &mut Command,
|
||||
args: &crate::Args,
|
||||
settings: &crate::config::Settings,
|
||||
_config: &crate::config::Config,
|
||||
ids: &mut Vec<i64>,
|
||||
tags: &mut Vec<String>,
|
||||
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() {
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -119,15 +119,16 @@ fn build_meta_plugin_table(meta_plugin_info: &Vec<MetaPluginInfo>) -> 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<MetaPluginType> = crate::modes::common::cmd_args_meta_plugin_types(_cmd, &args);
|
||||
let mut meta_plugin_types: Vec<MetaPluginType> = 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 {
|
||||
|
||||
@@ -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<i64>,
|
||||
tags: &mut Vec<String>,
|
||||
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(),
|
||||
|
||||
Reference in New Issue
Block a user