[feat](trx-app): support combined trx-rs.toml config file

Both trx-server and trx-client now look for a combined trx-rs.toml
with [trx-server] and [trx-client] section headers respectively,
falling back to per-binary config files as before.

Search order per tier: combined trx-rs.toml → flat per-binary file,
checked in CWD, ~/.config/trx-rs/, and /etc/trx-rs/.

--print-config now outputs the config under the appropriate section
header so the combined file can be generated directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-02-28 08:08:45 +01:00
parent 6a47fb00ad
commit 06312abe42
6 changed files with 185 additions and 11 deletions
+52
View File
@@ -411,6 +411,54 @@ impl ClientConfig {
toml::to_string_pretty(&example).unwrap_or_default()
}
/// Generate an example configuration wrapped under the `[trx-client]`
/// section header, suitable for use in a combined `trx-rs.toml` file.
pub fn example_combined_toml() -> String {
#[derive(serde::Serialize)]
struct Wrapper {
#[serde(rename = "trx-client")]
inner: ClientConfig,
}
let example = ClientConfig {
general: GeneralConfig {
callsign: Some("N0CALL".to_string()),
log_level: Some("info".to_string()),
},
remote: RemoteConfig {
url: Some("192.168.1.100:9000".to_string()),
rig_id: Some("hf".to_string()),
auth: RemoteAuthConfig {
token: Some("my-token".to_string()),
},
poll_interval_ms: 750,
},
frontends: FrontendsConfig {
http: HttpFrontendConfig {
enabled: true,
listen: IpAddr::from([127, 0, 0, 1]),
port: 8080,
auth: HttpAuthConfig {
enabled: false,
rx_passphrase: Some("rx-passphrase-example".to_string()),
control_passphrase: Some("control-passphrase-example".to_string()),
tx_access_control_enabled: true,
session_ttl_min: 480,
cookie_secure: false,
cookie_same_site: CookieSameSite::Lax,
},
},
rigctl: RigctlFrontendConfig {
enabled: false,
listen: IpAddr::from([127, 0, 0, 1]),
port: 4532,
},
http_json: HttpJsonFrontendConfig::default(),
audio: AudioClientConfig::default(),
},
};
toml::to_string_pretty(&Wrapper { inner: example }).unwrap_or_default()
}
}
fn validate_log_level(level: Option<&str>) -> Result<(), String> {
@@ -476,6 +524,10 @@ impl ConfigFile for ClientConfig {
"client.toml"
}
fn combined_key() -> Option<&'static str> {
Some("trx-client")
}
fn default_search_paths() -> Vec<PathBuf> {
let mut paths = Vec::new();
paths.push(PathBuf::from("trx-client.toml"));
+1 -1
View File
@@ -131,7 +131,7 @@ async fn async_init() -> DynResult<AppState> {
let cli = Cli::parse();
if cli.print_config {
println!("{}", ClientConfig::example_toml());
println!("{}", ClientConfig::example_combined_toml());
std::process::exit(0);
}