radicle_ci_broker/
config.rsuse std::{
collections::HashMap,
fmt,
path::{Path, PathBuf},
time::Duration,
};
use duration_str::deserialize_duration;
use serde::{Deserialize, Serialize};
use crate::{filter::EventFilter, sensitive::Sensitive};
const DEFAULT_MAX_RUN_TIME: Duration = Duration::from_secs(3600);
const DEFAULT_STATUS_PAGE_UPDATE_INTERVAL: u64 = 10;
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub default_adapter: String,
pub adapters: HashMap<String, Adapter>,
#[serde(deserialize_with = "deserialize_duration")]
#[serde(default = "default_max_run_time")]
pub max_run_time: Duration,
pub filters: Vec<EventFilter>,
pub report_dir: Option<PathBuf>,
pub status_update_interval_seconds: Option<u64>,
pub db: PathBuf,
}
fn default_max_run_time() -> Duration {
DEFAULT_MAX_RUN_TIME
}
impl Config {
pub fn load(filename: &Path) -> Result<Self, ConfigError> {
let config =
std::fs::read(filename).map_err(|e| ConfigError::ReadConfig(filename.into(), e))?;
serde_yml::from_slice(&config).map_err(|e| ConfigError::ParseConfig(filename.into(), e))
}
pub fn adapter(&self, name: &str) -> Option<&Adapter> {
self.adapters.get(name)
}
pub fn status_page_update_interval(&self) -> u64 {
self.status_update_interval_seconds
.unwrap_or(DEFAULT_STATUS_PAGE_UPDATE_INTERVAL)
}
pub fn max_run_time(&self) -> Duration {
self.max_run_time
}
pub fn db(&self) -> &Path {
&self.db
}
pub fn to_json(&self) -> Result<String, ConfigError> {
serde_json::to_string_pretty(self).map_err(ConfigError::ToJson)
}
}
impl fmt::Debug for Adapter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Adapter {{ \n command: {:#?}, \n env: {:#?}, \n sensitive_env: {:#?} }}",
self.command,
self.env,
self.sensitive_env
.keys()
.map(|k| (k.to_string(), "***".to_string()))
.collect::<HashMap<String, String>>()
)
}
}
#[derive(Serialize, Deserialize)]
pub struct Adapter {
pub command: PathBuf,
pub env: HashMap<String, String>,
#[serde(default)]
pub sensitive_env: HashMap<String, Sensitive>,
}
impl Adapter {
pub fn envs(&self) -> &HashMap<String, String> {
&self.env
}
pub fn sensitive_envs(&self) -> &HashMap<String, Sensitive> {
&self.sensitive_env
}
}
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("could not read config file {0}")]
ReadConfig(PathBuf, #[source] std::io::Error),
#[error("failed to parse configuration file as YAML: {0}")]
ParseConfig(PathBuf, #[source] serde_yml::Error),
#[error("failed to convert configuration into JSON")]
ToJson(#[source] serde_json::Error),
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[allow(clippy::unwrap_used)]
fn parse_config_yaml() {
const YAML: &str = r#"---
default_adapter: foo
adapters: {}
filters: []
db: "foo.db"
max_run_time: 1min
...
"#;
let cfg: Config = serde_yml::from_str(YAML).unwrap();
assert_eq!(cfg.max_run_time(), Duration::from_secs(60));
}
#[test]
#[allow(clippy::unwrap_used)]
fn parse_config_yaml_without_max_run_time() {
const YAML: &str = r#"---
default_adapter: foo
adapters: {}
filters: []
db: "foo.db"
...
"#;
let cfg: Config = serde_yml::from_str(YAML).unwrap();
assert_eq!(cfg.max_run_time(), DEFAULT_MAX_RUN_TIME);
}
}