use figment::{
Error,
value::{Empty, Value},
};
use kdl::{KdlEntry, KdlNode, KdlValue};
pub trait KdlEntryExt {
type Error;
fn to_figment_value(&self) -> Value;
fn as_string(&self) -> Result<String, Self::Error>;
}
impl KdlEntryExt for KdlEntry {
type Error = Error;
#[allow(clippy::cast_possible_truncation)]
fn to_figment_value(&self) -> Value {
match self.value() {
KdlValue::String(s) => Value::from(s.to_owned()),
KdlValue::Integer(i) => Value::from(*i as i64),
KdlValue::Float(f) => Value::from(*f),
KdlValue::Bool(b) => Value::from(*b),
KdlValue::Null => Empty::Unit.into(),
}
}
fn as_string(&self) -> Result<String, Self::Error> {
if let KdlValue::String(s) = self.value() {
Ok(s.to_owned())
} else {
Err(Error::from(format!(
"not a string value at {}",
self.span().offset()
)))
}
}
}
pub trait KdlNodeExt {
type Error;
fn assert_single_argument(&self) -> Result<&Self, Self::Error>;
fn assert_max_arguments(&self, n: usize) -> Result<&Self, Self::Error>;
fn assert_no_children(&self) -> Result<&Self, Self::Error>;
fn get_first_string_argument(&self) -> Result<&str, Self::Error>;
fn get_string_property(&self, key: &str) -> Result<Option<&str>, Error>;
}
impl KdlNodeExt for KdlNode {
type Error = Error;
fn get_string_property(&self, key: &str) -> Result<Option<&str>, Error> {
match self.get(key) {
Some(v) => {
let Some(v) = v.as_string() else {
return Err(Error::from(format!(
r#""{key}" at {} requires a string argument"#,
self.span().offset(),
)));
};
Ok(Some(v))
}
None => Ok(None),
}
}
fn assert_max_arguments(&self, n: usize) -> Result<&Self, Self::Error> {
if self.len() > n {
return Err(Error::from(format!(
r#""{}" at {} requires at most {n} arguments, found {}"#,
self.name().value(),
self.span().offset(),
self.len()
)));
}
Ok(self)
}
fn assert_single_argument(&self) -> Result<&Self, Self::Error> {
if self.len() != 1 {
return Err(Error::from(format!(
r#""{}" at {} requires exactly one argument, found {}"#,
self.name().value(),
self.span().offset(),
self.len()
)));
}
Ok(self)
}
fn get_first_string_argument(&self) -> Result<&str, Self::Error> {
self.entry(0)
.and_then(|e| e.value().as_string())
.ok_or_else(|| {
Error::from(format!(
r#""{}" at {} requires a string argument"#,
self.name().value(),
self.span().offset()
))
})
}
fn assert_no_children(&self) -> Result<&Self, Self::Error> {
if self.children().is_some() {
return Err(Error::from(format!(
r#""{}" at {} should have no children"#,
self.name().value(),
self.span().offset()
)));
}
Ok(self)
}
}