From 09e81bd9260b79f1e7a1d94aef4766d29f0f5582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Sun, 2 Apr 2023 19:32:31 -0700 Subject: [PATCH] feat(config): config files are now kdl-based Fixes: https://github.com/orogene/orogene/issues/153 --- Cargo.lock | 1 + crates/oro-config/Cargo.toml | 3 +- crates/oro-config/src/error.rs | 13 ++++++ crates/oro-config/src/kdl_source.rs | 63 +++++++++++++++++++++++++++++ crates/oro-config/src/lib.rs | 44 +++++++------------- src/lib.rs | 2 +- 6 files changed, 94 insertions(+), 32 deletions(-) create mode 100644 crates/oro-config/src/error.rs create mode 100644 crates/oro-config/src/kdl_source.rs diff --git a/Cargo.lock b/Cargo.lock index a193e01d..4cbd82d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2147,6 +2147,7 @@ version = "0.3.16" dependencies = [ "clap", "config", + "kdl", "miette", "pretty_assertions", "serde", diff --git a/crates/oro-config/Cargo.toml b/crates/oro-config/Cargo.toml index 3ac0fcc5..c22a3373 100644 --- a/crates/oro-config/Cargo.toml +++ b/crates/oro-config/Cargo.toml @@ -12,7 +12,8 @@ rust-version = "1.67.1" [dependencies] clap = { workspace = true, features = ["string"] } -config = { workspace = true, features = ["toml"] } +config = { workspace = true, default-features = false } +kdl = { workspace = true } miette = { workspace = true } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } diff --git a/crates/oro-config/src/error.rs b/crates/oro-config/src/error.rs new file mode 100644 index 00000000..ec6dc569 --- /dev/null +++ b/crates/oro-config/src/error.rs @@ -0,0 +1,13 @@ +use miette::Diagnostic; +use thiserror::Error; + +#[derive(Debug, Error, Diagnostic)] +pub enum OroConfigError { + #[error(transparent)] + #[diagnostic(code(config::error))] + ConfigError(#[from] config::ConfigError), + + #[error(transparent)] + #[diagnostic(code(config::error))] + ConfigParseError(#[from] Box), +} diff --git a/crates/oro-config/src/kdl_source.rs b/crates/oro-config/src/kdl_source.rs new file mode 100644 index 00000000..5891586c --- /dev/null +++ b/crates/oro-config/src/kdl_source.rs @@ -0,0 +1,63 @@ +use config::{ConfigError, FileStoredFormat, Format, Map, Source, Value, ValueKind}; +use kdl::{KdlDocument, KdlValue}; + +#[derive(Clone, Debug)] +pub(crate) struct KdlSource(KdlDocument); + +impl Source for KdlSource { + fn clone_into_box(&self) -> Box { + Box::new(self.clone()) + } + + fn collect(&self) -> Result, ConfigError> { + let mut map = Map::new(); + for node in self.0.nodes() { + let key = node.name().to_string(); + if let Some(value) = node.get(0) { + let value = Value::new( + Some(&if let Some(str) = value.as_string() { + str.to_owned() + } else { + value.to_string() + }), + value_kind(value), + ); + map.insert(key, value); + } + } + Ok(map) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct KdlFormat; + +impl Format for KdlFormat { + fn parse( + &self, + _uri: Option<&String>, + text: &str, + ) -> Result, Box> { + Ok(KdlSource(text.parse()?).collect()?) + } +} + +impl FileStoredFormat for KdlFormat { + fn file_extensions(&self) -> &'static [&'static str] { + &["kdl"] + } +} + +fn value_kind(value: &KdlValue) -> ValueKind { + if let Some(str) = value.as_string() { + ValueKind::String(str.into()) + } else if let Some(num) = value.as_i64() { + ValueKind::I64(num) + } else if let Some(float) = value.as_f64() { + ValueKind::Float(float) + } else if let Some(boolean) = value.as_bool() { + ValueKind::Boolean(boolean) + } else { + ValueKind::Nil + } +} diff --git a/crates/oro-config/src/lib.rs b/crates/oro-config/src/lib.rs index 35b1bae7..d8fd698b 100644 --- a/crates/oro-config/src/lib.rs +++ b/crates/oro-config/src/lib.rs @@ -2,14 +2,20 @@ use std::{ collections::{HashMap, HashSet}, - path::PathBuf, ffi::OsString, + ffi::OsString, + path::PathBuf, }; pub use clap::{ArgMatches, Command}; pub use config::Config as OroConfig; -use config::{builder::DefaultState, ConfigBuilder, ConfigError, Environment, File}; -use miette::{Diagnostic, Result}; -use thiserror::Error; +use config::{builder::DefaultState, ConfigBuilder, Environment, File}; +use kdl_source::KdlFormat; +use miette::Result; + +use error::OroConfigError; + +mod error; +mod kdl_source; pub trait OroConfigLayerExt { fn with_negations(self) -> Self; @@ -70,17 +76,6 @@ impl OroConfigLayerExt for Command { } } -#[derive(Debug, Error, Diagnostic)] -pub enum OroConfigError { - #[error(transparent)] - #[diagnostic(code(config::error))] - ConfigError(#[from] ConfigError), - - #[error(transparent)] - #[diagnostic(code(config::error))] - ConfigParseError(#[from] Box), -} - #[derive(Debug, Clone)] pub struct OroConfigOptions { builder: ConfigBuilder, @@ -137,27 +132,16 @@ impl OroConfigOptions { if self.global { if let Some(config_file) = self.global_config_file { let path = config_file.display().to_string(); - builder = builder.add_source(File::with_name(&path[..]).required(false)); + builder = builder.add_source(File::new(&path, KdlFormat).required(false)); } } if self.env { builder = builder.add_source(Environment::with_prefix("oro_config")); } if let Some(root) = self.pkg_root { - builder = builder - .add_source( - File::with_name(&root.join("ororc").display().to_string()).required(false), - ) - .add_source( - File::with_name(&root.join(".ororc").display().to_string()).required(false), - ) - .add_source( - File::with_name(&root.join("ororc.toml").display().to_string()).required(false), - ) - .add_source( - File::with_name(&root.join(".ororc.toml").display().to_string()) - .required(false), - ); + builder = builder.add_source( + File::new(&root.join("oro.kdl").display().to_string(), KdlFormat).required(false), + ); } Ok(builder.build().map_err(OroConfigError::ConfigError)?) } diff --git a/src/lib.rs b/src/lib.rs index f59abf69..0fbcb7ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -269,7 +269,7 @@ impl Orogene { cfg_builder.global_config_file(Some(file.clone())).load()? } else { cfg_builder - .global_config_file(dirs.map(|d| d.config_dir().to_owned().join("ororc.toml"))) + .global_config_file(dirs.map(|d| d.config_dir().to_owned().join("oro.kdl"))) .pkg_root(Some(self.root.clone())) .load()? };