feat: add config system with --config argument and priority-based configuration

Co-authored-by: aider (openai/andrew/openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-08-15 16:31:57 -03:00
parent 5689c3e5ef
commit 067cba703b
4 changed files with 180 additions and 3 deletions

View File

@@ -1,6 +1,8 @@
mod args;
mod config;
mod modes;
use std::path::PathBuf;
use anyhow::{Context, Error, Result, anyhow};
use clap::*;
use clap::error::ErrorKind;
@@ -27,6 +29,7 @@ extern crate serde_yaml;
extern crate serde;
use args::{Args, NumberOrString};
use config::Config;
/**
* Main function to handle command-line arguments and execute the appropriate mode.
@@ -48,6 +51,22 @@ fn main() -> Result<(), Error> {
debug!("MAIN: Start");
// Load configuration with priority: CLI args > env vars > config file > defaults
let config_path = if let Some(config_path) = &args.options.config {
config_path.clone()
} else if let Ok(env_config) = std::env::var("KEEP_CONFIG") {
PathBuf::from(env_config)
} else {
Config::default_config_path().unwrap_or_else(|_| PathBuf::from("~/.config/keep/config.yml"))
};
let mut config = Config::from_file(&config_path).unwrap_or_else(|e| {
debug!("CONFIG: Failed to load config: {}, using defaults", e);
Config::default()
});
debug!("MAIN: Loaded config: {:?}", config);
let ids = &mut Vec::new();
let tags = &mut Vec::new();
@@ -95,6 +114,23 @@ 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() {
// 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 {
@@ -136,10 +172,50 @@ 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() {
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")),
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();
}
}