follow xdg base dirs spec by default

This commit is contained in:
Charles Hall 2024-06-11 20:41:05 -07:00
parent c46eaed0e0
commit 85e77832e9
No known key found for this signature in database
GPG key ID: 7B8E0645816E07CF
6 changed files with 69 additions and 7 deletions

7
Cargo.lock generated
View file

@ -876,6 +876,7 @@ dependencies = [
"tracing-opentelemetry", "tracing-opentelemetry",
"tracing-subscriber", "tracing-subscriber",
"trust-dns-resolver", "trust-dns-resolver",
"xdg",
] ]
[[package]] [[package]]
@ -3660,6 +3661,12 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "xdg"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
[[package]] [[package]]
name = "yap" name = "yap"
version = "0.12.0" version = "0.12.0"

View file

@ -141,6 +141,7 @@ tracing-flame = "0.2.0"
tracing-opentelemetry = "0.24.0" tracing-opentelemetry = "0.24.0"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
trust-dns-resolver = "0.23.2" trust-dns-resolver = "0.23.2"
xdg = "2.5.2"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
nix = { version = "0.29", features = ["resource"] } nix = { version = "0.29", features = ["resource"] }

View file

@ -2,7 +2,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use clap::Parser; use clap::{CommandFactory as _, FromArgMatches as _, Parser};
/// Command line arguments /// Command line arguments
#[derive(Parser)] #[derive(Parser)]
@ -10,10 +10,26 @@ use clap::Parser;
pub(crate) struct Args { pub(crate) struct Args {
/// Path to the configuration file /// Path to the configuration file
#[clap(long, short)] #[clap(long, short)]
pub(crate) config: PathBuf, pub(crate) config: Option<PathBuf>,
} }
/// Parse command line arguments into structured data /// Parse command line arguments into structured data
pub(crate) fn parse() -> Args { pub(crate) fn parse() -> Args {
Args::parse() let mut command = Args::command().mut_arg("config", |x| {
let help = "Set the path to the configuration file";
x.help(help).long_help(format!(
"{}\n\nIf this option is specified, the provided value is used \
as-is.\n\nIf this option is not specified, then the XDG Base \
Directory Specification is followed, searching for the path `{}` \
in the configuration directories.
",
help,
crate::config::DEFAULT_PATH.display(),
))
});
match Args::from_arg_matches(&command.get_matches_mut()) {
Ok(x) => x,
Err(e) => e.format(&mut command).exit(),
}
} }

View file

@ -1,8 +1,10 @@
use std::{ use std::{
borrow::Cow,
net::{IpAddr, Ipv4Addr}, net::{IpAddr, Ipv4Addr},
path::Path, path::{Path, PathBuf},
}; };
use once_cell::sync::Lazy;
use ruma::{OwnedServerName, RoomVersionId}; use ruma::{OwnedServerName, RoomVersionId};
use serde::Deserialize; use serde::Deserialize;
@ -14,6 +16,10 @@ mod proxy;
use env_filter_clone::EnvFilterClone; use env_filter_clone::EnvFilterClone;
use proxy::ProxyConfig; use proxy::ProxyConfig;
/// The default configuration file path
pub(crate) static DEFAULT_PATH: Lazy<PathBuf> =
Lazy::new(|| [env!("CARGO_PKG_NAME"), "config.toml"].iter().collect());
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub(crate) struct Config { pub(crate) struct Config {
@ -163,13 +169,29 @@ pub(crate) fn default_default_room_version() -> RoomVersionId {
RoomVersionId::V10 RoomVersionId::V10
} }
/// Load the configuration from the given path /// Search default locations for a configuration file
pub(crate) async fn load<P>(path: P) -> Result<Config, error::Config> ///
/// If one isn't found, the list of tried paths is returned.
fn search() -> Result<PathBuf, error::ConfigSearch> {
use error::ConfigSearch as Error;
xdg::BaseDirectories::new()?
.find_config_file(&*DEFAULT_PATH)
.ok_or(Error::NotFound)
}
/// Load the configuration from the given path or XDG Base Directories
pub(crate) async fn load<P>(path: Option<P>) -> Result<Config, error::Config>
where where
P: AsRef<Path>, P: AsRef<Path>,
{ {
use error::Config as Error; use error::Config as Error;
let path = match path.as_ref().map(AsRef::as_ref) {
Some(x) => Cow::Borrowed(x),
None => Cow::Owned(search()?),
};
let path = path.as_ref(); let path = path.as_ref();
toml::from_str( toml::from_str(

View file

@ -75,9 +75,25 @@ pub(crate) enum Observability {
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub(crate) enum Config { pub(crate) enum Config {
#[error("failed to find configuration file")]
Search(#[from] ConfigSearch),
#[error("failed to read configuration file {1:?}")] #[error("failed to read configuration file {1:?}")]
Read(#[source] std::io::Error, PathBuf), Read(#[source] std::io::Error, PathBuf),
#[error("failed to parse configuration file {1:?}")] #[error("failed to parse configuration file {1:?}")]
Parse(#[source] toml::de::Error, PathBuf), Parse(#[source] toml::de::Error, PathBuf),
} }
/// Errors that can occur while searching for a config file
// Missing docs are allowed here since that kind of information should be
// encoded in the error messages themselves anyway.
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub(crate) enum ConfigSearch {
#[error("XDG Base Directory error")]
Xdg(#[from] xdg::BaseDirectoriesError),
#[error("no relevant configuration files found in XDG Base Directories")]
NotFound,
}

View file

@ -106,7 +106,7 @@ async fn try_main() -> Result<(), error::Main> {
let args = args::parse(); let args = args::parse();
let config = config::load(&args.config).await?; let config = config::load(args.config.as_ref()).await?;
let _guard = observability::init(&config); let _guard = observability::init(&config);