diff --git a/src/daemon/config.rs b/src/daemon/config.rs index 7262282..6b70e60 100644 --- a/src/daemon/config.rs +++ b/src/daemon/config.rs @@ -6,52 +6,14 @@ */ use anyhow::{anyhow, Result}; -use async_trait::async_trait; use config::builder::AsyncState; -use config::{AsyncSource, ConfigBuilder, ConfigError, FileFormat, Format, Map, Value}; -use std::ffi::OsStr; -use std::fmt::Debug; +use config::{ConfigBuilder, FileFormat, FileStoredFormat}; use std::io::ErrorKind; -use std::path::Path; -use tokio::fs::{create_dir_all, read_dir, read_to_string, write}; -use tracing::{debug, error, info}; +use tokio::fs::{create_dir_all, read_to_string, write}; +use tracing::{error, info}; use crate::daemon::DaemonContext; - -#[derive(Debug)] -struct AsyncFileSource + Sized + Send + Sync> { - path: P, - format: F, -} - -impl + Sized + Send + Sync + Debug> AsyncFileSource { - fn from(path: P, format: F) -> AsyncFileSource { - AsyncFileSource { path, format } - } -} - -#[async_trait] -impl + Sized + Send + Sync + Debug> AsyncSource - for AsyncFileSource -{ - async fn collect(&self) -> Result, ConfigError> { - let path = self.path.as_ref(); - let text = match read_to_string(&path).await { - Ok(text) => text, - Err(e) => { - if e.kind() == ErrorKind::NotFound { - info!("No config file {} found", path.to_string_lossy()); - return Ok(Map::new()); - } - return Err(ConfigError::Foreign(Box::new(e))); - } - }; - let path = path.to_string_lossy().to_string(); - self.format - .parse(Some(&path), &text) - .map_err(ConfigError::Foreign) - } -} +use crate::{read_config_directory, AsyncFileSource}; pub(in crate::daemon) async fn read_state(context: &C) -> Result { let path = context.state_path()?; @@ -89,52 +51,29 @@ pub(in crate::daemon) async fn read_config(context: &C) -> Res system_config_path.join("config.toml"), FileFormat::Toml, )); - let builder = read_config_directory(builder, system_config_path.join("config.toml.d")).await?; + let builder = read_config_directory( + builder, + system_config_path.join("config.toml.d"), + FileFormat::Toml.file_extensions(), + FileFormat::Toml, + ) + .await?; let builder = builder.add_async_source(AsyncFileSource::from( user_config_path.join("config.toml"), FileFormat::Toml, )); - let builder = read_config_directory(builder, user_config_path.join("config.toml.d")).await?; + let builder = read_config_directory( + builder, + user_config_path.join("config.toml.d"), + FileFormat::Toml.file_extensions(), + FileFormat::Toml, + ) + .await?; let config = builder.build().await?; Ok(config.try_deserialize()?) } -async fn read_config_directory + Sync + Send>( - builder: ConfigBuilder, - path: P, -) -> Result> { - let mut dir = match read_dir(&path).await { - Ok(dir) => dir, - Err(e) => { - if e.kind() == ErrorKind::NotFound { - debug!( - "No config fragment directory {} found", - path.as_ref().to_string_lossy() - ); - return Ok(builder); - } - error!( - "Error reading config fragment directory {}: {e}", - path.as_ref().to_string_lossy() - ); - return Err(e.into()); - } - }; - let mut entries = Vec::new(); - while let Some(entry) = dir.next_entry().await? { - let path = entry.path(); - match path.extension() { - Some(ext) if ext == OsStr::new("toml") => entries.push(path), - _ => continue, - } - } - entries.sort(); - Ok(entries.into_iter().fold(builder, |builder, path| { - builder.add_async_source(AsyncFileSource::from(path, FileFormat::Toml)) - })) -} - #[cfg(test)] mod test { use super::*; diff --git a/src/lib.rs b/src/lib.rs index f55abad..dfa3013 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,12 +6,17 @@ */ use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use config::builder::AsyncState; +use config::{AsyncSource, ConfigBuilder, ConfigError, FileFormat, Format, Map, Value}; +use std::fmt::Debug; use std::future::Future; +use std::io::ErrorKind; use std::path::{Path, PathBuf}; -use tokio::fs::File; +use tokio::fs::{read_dir, read_to_string, File}; use tokio::io::AsyncWriteExt; use tokio_util::sync::CancellationToken; -use tracing::{info, warn}; +use tracing::{debug, error, info, warn}; mod ds_inhibit; mod error; @@ -69,6 +74,41 @@ where } } +#[derive(Debug)] +struct AsyncFileSource + Sized + Send + Sync> { + path: P, + format: F, +} + +impl + Sized + Send + Sync + Debug> AsyncFileSource { + fn from(path: P, format: F) -> AsyncFileSource { + AsyncFileSource { path, format } + } +} + +#[async_trait] +impl + Sized + Send + Sync + Debug> AsyncSource + for AsyncFileSource +{ + async fn collect(&self) -> Result, ConfigError> { + let path = self.path.as_ref(); + let text = match read_to_string(&path).await { + Ok(text) => text, + Err(e) => { + if e.kind() == ErrorKind::NotFound { + info!("No config file {} found", path.to_string_lossy()); + return Ok(Map::new()); + } + return Err(ConfigError::Foreign(Box::new(e))); + } + }; + let path = path.to_string_lossy().to_string(); + self.format + .parse(Some(&path), &text) + .map_err(ConfigError::Foreign) + } +} + #[cfg(not(test))] pub fn path>(path: S) -> PathBuf { PathBuf::from(path.as_ref()) @@ -126,6 +166,44 @@ pub(crate) fn get_appid(pid: u32) -> Result> { } } +pub(crate) async fn read_config_directory + Sync + Send>( + builder: ConfigBuilder, + path: P, + extensions: &[&str], + format: FileFormat, +) -> Result> { + let mut dir = match read_dir(&path).await { + Ok(dir) => dir, + Err(e) => { + if e.kind() == ErrorKind::NotFound { + debug!( + "No config fragment directory {} found", + path.as_ref().to_string_lossy() + ); + return Ok(builder); + } + error!( + "Error reading config fragment directory {}: {e}", + path.as_ref().to_string_lossy() + ); + return Err(e.into()); + } + }; + let mut entries = Vec::new(); + while let Some(entry) = dir.next_entry().await? { + let path = entry.path(); + if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { + if extensions.contains(&ext) { + entries.push(path); + } + } + } + entries.sort(); + Ok(entries.into_iter().fold(builder, |builder, path| { + builder.add_async_source(AsyncFileSource::from(path, format)) + })) +} + #[cfg(test)] mod test { use crate::testing;