From 2f2a74b8101a5433872c9c15d7d7e374a52c7a33 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Sat, 28 Feb 2026 08:45:32 +0100 Subject: [PATCH] [chore](trx-app): drop per-binary config file support Only trx-rs.toml with [trx-server] / [trx-client] section headers is now supported. Simplify ConfigFile trait to a single required method section_key(); remove config_filename(), combined_key(), and default_search_paths(). load_from_file() now errors when the expected section is absent rather than falling back to flat parsing. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Stan Grams --- src/trx-app/src/config.rs | 89 +++++++++--------------------------- src/trx-app/src/lib.rs | 2 +- src/trx-client/src/config.rs | 27 +++-------- src/trx-server/src/config.rs | 27 +++-------- 4 files changed, 37 insertions(+), 108 deletions(-) diff --git a/src/trx-app/src/config.rs b/src/trx-app/src/config.rs index 51e07ec..c5d6bb9 100644 --- a/src/trx-app/src/config.rs +++ b/src/trx-app/src/config.rs @@ -15,9 +15,9 @@ pub enum ConfigError { ParseError(PathBuf, String), } -/// Returns search paths for the combined `trx-rs.toml` config file +/// Returns the default search paths for `trx-rs.toml` /// (current directory → XDG config → /etc). -pub fn combined_config_paths() -> Vec { +fn config_search_paths() -> Vec { let mut paths = vec![PathBuf::from("trx-rs.toml")]; if let Some(config_dir) = dirs::config_dir() { paths.push(config_dir.join("trx-rs").join("trx-rs.toml")); @@ -52,82 +52,37 @@ fn load_section_from_file( Ok(Some(cfg)) } -/// Trait for loading configuration files with default paths. +/// Trait for loading configuration from a `trx-rs.toml` section. pub trait ConfigFile: Sized + Default + DeserializeOwned { - /// Config filename (e.g., "server.toml" or "client.toml") - fn config_filename() -> &'static str; + /// Section key in `trx-rs.toml` (e.g. `"trx-server"` or `"trx-client"`). + fn section_key() -> &'static str; - /// Section key inside a combined `trx-rs.toml` file, e.g. `"trx-server"`. - /// Return `None` (the default) to disable combined-file support. - fn combined_key() -> Option<&'static str> { - None - } - - /// Load config from a specific file path. + /// Load the section from a specific file path. /// - /// If `combined_key()` is set and the file contains that section header, - /// only that section is deserialized. Otherwise the whole file is used, - /// preserving full backward compatibility with per-binary config files. + /// Returns an error if the file cannot be read, is not valid TOML, or + /// does not contain the expected `[]` header. fn load_from_file(path: &Path) -> Result { - if let Some(key) = Self::combined_key() { - // Peek at the file: if it contains our section, use that section. - if let Ok(Some(cfg)) = load_section_from_file::(path, key) { - return Ok(cfg); - } - } - - let content = std::fs::read_to_string(path) - .map_err(|e| ConfigError::ReadError(path.to_path_buf(), e.to_string()))?; - toml::from_str(&content) - .map_err(|e| ConfigError::ParseError(path.to_path_buf(), e.to_string())) + load_section_from_file::(path, Self::section_key())?.ok_or_else(|| { + ConfigError::ParseError( + path.to_path_buf(), + format!("missing [{}] section", Self::section_key()), + ) + }) } - /// Search default paths and load first found config. + /// Search default paths (`trx-rs.toml` in CWD → XDG → /etc) and load + /// the first file that contains the expected section. /// - /// Search order (for each location tier — CWD, XDG, /etc): - /// 1. `trx-rs.toml` with our section header (combined file) - /// 2. per-binary flat file (e.g. `trx-server.toml`) - /// - /// Returns `(config, path_where_found)` or `(Default::default(), None)`. + /// Returns `(config, path_where_found)` or `(Default::default(), None)` + /// when no config file is found. fn load_from_default_paths() -> Result<(Self, Option), ConfigError> { - let combined = combined_config_paths(); - let flat = Self::default_search_paths(); - - // Build interleaved list: (combined_path, flat_path) per tier. - let tiers = combined.len().max(flat.len()); - for i in 0..tiers { - // Combined file at this tier - if let Some(key) = Self::combined_key() { - if let Some(path) = combined.get(i) { - if path.exists() { - if let Some(cfg) = load_section_from_file::(path, key)? { - return Ok((cfg, Some(path.clone()))); - } - // Combined file present but our section absent → skip to flat. - } - } - } - // Flat file at this tier - if let Some(path) = flat.get(i) { - if path.exists() { - let cfg = Self::load_from_file(path)?; - return Ok((cfg, Some(path.clone()))); + for path in config_search_paths() { + if path.exists() { + if let Some(cfg) = load_section_from_file::(&path, Self::section_key())? { + return Ok((cfg, Some(path))); } } } Ok((Self::default(), None)) } - - /// Default search paths for the per-binary flat config file - /// (current dir → XDG → /etc). - fn default_search_paths() -> Vec { - let mut paths = vec![PathBuf::from(Self::config_filename())]; - - if let Some(config_dir) = dirs::config_dir() { - paths.push(config_dir.join("trx-rs").join(Self::config_filename())); - } - - paths.push(PathBuf::from("/etc/trx-rs").join(Self::config_filename())); - paths - } } diff --git a/src/trx-app/src/lib.rs b/src/trx-app/src/lib.rs index 96db6d9..931e981 100644 --- a/src/trx-app/src/lib.rs +++ b/src/trx-app/src/lib.rs @@ -7,7 +7,7 @@ pub mod logging; pub mod plugins; pub mod util; -pub use config::{combined_config_paths, ConfigError, ConfigFile}; +pub use config::{ConfigError, ConfigFile}; pub use logging::init_logging; pub use plugins::{load_backend_plugins, load_frontend_plugins}; pub use util::normalize_name; diff --git a/src/trx-client/src/config.rs b/src/trx-client/src/config.rs index 086eca2..ad8f27f 100644 --- a/src/trx-client/src/config.rs +++ b/src/trx-client/src/config.rs @@ -4,11 +4,12 @@ //! Configuration file support for trx-client. //! -//! Supports loading configuration from TOML files with the following search order: +//! Config is loaded from the `[trx-client]` section of `trx-rs.toml`. +//! Default search order: //! 1. Path specified via `--config` CLI argument -//! 2. `./trx-rs.toml` `[trx-client]` section, or `./trx-client.toml` -//! 3. `~/.config/trx-rs/trx-rs.toml` `[trx-client]` section, or `~/.config/trx-rs/client.toml` -//! 4. `/etc/trx-rs/trx-rs.toml` `[trx-client]` section, or `/etc/trx-rs/client.toml` +//! 2. `./trx-rs.toml` +//! 3. `~/.config/trx-rs/trx-rs.toml` +//! 4. `/etc/trx-rs/trx-rs.toml` use std::collections::HashMap; use std::net::IpAddr; @@ -482,22 +483,8 @@ fn validate_http_auth(auth: &HttpAuthConfig) -> Result<(), String> { } impl ConfigFile for ClientConfig { - fn config_filename() -> &'static str { - "client.toml" - } - - fn combined_key() -> Option<&'static str> { - Some("trx-client") - } - - fn default_search_paths() -> Vec { - let mut paths = Vec::new(); - paths.push(PathBuf::from("trx-client.toml")); - if let Some(config_dir) = dirs::config_dir() { - paths.push(config_dir.join("trx-rs").join("client.toml")); - } - paths.push(PathBuf::from("/etc/trx-rs/client.toml")); - paths + fn section_key() -> &'static str { + "trx-client" } } diff --git a/src/trx-server/src/config.rs b/src/trx-server/src/config.rs index df35b2a..83daaf1 100644 --- a/src/trx-server/src/config.rs +++ b/src/trx-server/src/config.rs @@ -4,11 +4,12 @@ //! Configuration file support for trx-server. //! -//! Supports loading configuration from TOML files with the following search order: +//! Config is loaded from the `[trx-server]` section of `trx-rs.toml`. +//! Default search order: //! 1. Path specified via `--config` CLI argument -//! 2. `./trx-rs.toml` `[trx-server]` section, or `./trx-server.toml` -//! 3. `~/.config/trx-rs/trx-rs.toml` `[trx-server]` section, or `~/.config/trx-rs/server.toml` -//! 4. `/etc/trx-rs/trx-rs.toml` `[trx-server]` section, or `/etc/trx-rs/server.toml` +//! 2. `./trx-rs.toml` +//! 3. `~/.config/trx-rs/trx-rs.toml` +//! 4. `/etc/trx-rs/trx-rs.toml` use std::net::IpAddr; use std::path::{Path, PathBuf}; @@ -789,22 +790,8 @@ fn validate_tokens(path: &str, tokens: &[String]) -> Result<(), String> { } impl ConfigFile for ServerConfig { - fn config_filename() -> &'static str { - "server.toml" - } - - fn combined_key() -> Option<&'static str> { - Some("trx-server") - } - - fn default_search_paths() -> Vec { - let mut paths = Vec::new(); - paths.push(PathBuf::from("trx-server.toml")); - if let Some(config_dir) = dirs::config_dir() { - paths.push(config_dir.join("trx-rs").join("server.toml")); - } - paths.push(PathBuf::from("/etc/trx-rs/server.toml")); - paths + fn section_key() -> &'static str { + "trx-server" } }