use crate::error::{Error, Result};
pub(super) trait Fields {
fn field(&self, key: &str) -> Option<&toml::Value>;
}
impl Fields for toml::Value {
fn field(&self, key: &str) -> Option<&toml::Value> {
self.get(key)
}
}
impl Fields for toml::Table {
fn field(&self, key: &str) -> Option<&toml::Value> {
self.get(key)
}
}
pub(super) fn str_of<'a>(v: &'a impl Fields, key: &str) -> Option<&'a str> {
v.field(key).and_then(|x| x.as_str())
}
pub(super) fn str_field(v: &impl Fields, key: &str) -> String {
str_of(v, key).unwrap_or_default().to_string()
}
pub(super) fn bool_of(v: &impl Fields, key: &str) -> Option<bool> {
v.field(key).and_then(|x| x.as_bool())
}
pub(super) fn int_of(v: &impl Fields, key: &str) -> Option<i64> {
v.field(key).and_then(|x| x.as_integer())
}
pub(super) fn count_of(v: &impl Fields, where_: &str, key: &str) -> Result<Option<usize>> {
match int_of(v, key) {
None => Ok(None),
Some(n) => usize::try_from(n)
.map(Some)
.map_err(|_| Error::Other(format!("{where_}: {key} must not be negative (got {n})"))),
}
}
pub(super) fn array_of<'a>(v: &'a impl Fields, key: &str) -> Option<&'a Vec<toml::Value>> {
v.field(key).and_then(|x| x.as_array())
}
pub(super) fn table_of<'a>(v: &'a impl Fields, key: &str) -> Option<&'a toml::Table> {
v.field(key).and_then(|x| x.as_table())
}
#[cfg(test)]
mod tests {
use super::*;
fn value() -> toml::Value {
toml::from_str(
r#"
s = "text"
b = true
i = -3
a = [1, 2]
[t]
inner = 1
"#,
)
.unwrap()
}
fn check(v: &impl Fields) {
assert_eq!(str_of(v, "s"), Some("text"));
assert_eq!(bool_of(v, "b"), Some(true));
assert_eq!(int_of(v, "i"), Some(-3));
assert_eq!(array_of(v, "a").map(Vec::len), Some(2));
assert!(table_of(v, "t").is_some_and(|t| t.contains_key("inner")));
assert_eq!(str_of(v, "b"), None);
assert_eq!(bool_of(v, "s"), None);
assert_eq!(int_of(v, "s"), None);
assert!(array_of(v, "t").is_none());
assert!(table_of(v, "a").is_none());
assert_eq!(str_field(v, "s"), "text");
assert_eq!(str_field(v, "missing"), "");
assert_eq!(str_field(v, "i"), "");
assert_eq!(count_of(v, "[t]", "missing").unwrap(), None);
assert_eq!(count_of(v, "[t]", "s").unwrap(), None);
assert_eq!(
count_of(v, "[t]", "i").unwrap_err().to_string(),
"[t]: i must not be negative (got -3)"
);
}
#[test]
fn each_helper_reads_its_own_type_off_a_value() {
check(&value());
}
#[test]
fn each_helper_reads_its_own_type_off_a_table() {
let table = value().as_table().cloned().expect("a document is a table");
check(&table);
}
}