use serde::Serialize;
use crate::config::{ConfigError, Schema};
use super::schema::{tidy, toml_key};
#[doc(hidden)]
pub fn __check_field<T: Serialize>(
schema: &Schema,
key: &str,
value: &T,
) -> Result<(), ConfigError> {
let Some(field) = schema.field(key) else {
return Ok(());
};
let mut one = std::collections::BTreeMap::new();
one.insert(key, value);
match toml::Value::try_from(one) {
Ok(toml::Value::Table(table)) => table.get(key).map_or(Ok(()), |v| field.check(v)),
_ if field.min.is_none() && field.max.is_none() && field.choices.is_empty() => Ok(()),
_ => Err(ConfigError::Value {
key: key.to_owned(),
need: "cannot be checked: TOML cannot hold its value".to_owned(),
}),
}
}
#[doc(hidden)]
#[derive(Debug, Default)]
pub struct __Values(toml::Table);
impl __Values {
pub fn put<T: Serialize>(&mut self, key: &str, value: &T) {
if let Ok(value) = toml::Value::try_from(value) {
self.0.insert(key.to_owned(), value);
}
}
pub fn put_group(&mut self, key: &str, text: &str) -> Result<(), ConfigError> {
let group = Self::parse(text)?;
self.0.insert(key.to_owned(), toml::Value::Table(group.0));
Ok(())
}
#[must_use]
pub fn group(&self, key: &str) -> Option<String> {
self.0
.get(key)
.and_then(toml::Value::as_table)
.map(|table| toml::Value::Table(table.clone()).to_string())
}
#[must_use]
pub fn finish(self) -> String {
toml::Value::Table(self.0).to_string()
}
pub fn parse(text: &str) -> Result<Self, ConfigError> {
let document = format!("synced = {text}");
let mut document: toml::Table = toml::from_str(&document).map_err(|e| {
ConfigError::Parse(format!("values: {}", super::reason(Some(&document), &e)))
})?;
match document.remove("synced") {
Some(toml::Value::Table(table)) if document.is_empty() => Ok(Self(table)),
_ => Err(ConfigError::Parse("values: not an inline table".to_owned())),
}
}
pub fn take<T: serde::de::DeserializeOwned>(
&self,
key: &str,
) -> Result<Option<T>, ConfigError> {
self.0
.get(key)
.map(|value| {
value.clone().try_into().map_err(|e: toml::de::Error| {
ConfigError::Parse(format!("value {key}: {}", super::reason(None, &e)))
})
})
.transpose()
}
}
pub(crate) fn changed_keys(before: &str, after: &str) -> String {
let table = |text| {
__Values::parse(text)
.map(|values| values.0)
.unwrap_or_default()
};
let (before, after) = (table(before), table(after));
let mut keys: Vec<&str> = before
.keys()
.chain(after.keys())
.filter(|key| before.get(*key) != after.get(*key))
.map(String::as_str)
.collect();
keys.sort_unstable();
keys.dedup();
keys.join(", ")
}
#[doc(hidden)]
#[must_use]
pub fn __entry<T: Serialize>(key: &str, value: &T) -> String {
match toml::Value::try_from(value) {
Ok(value) => format!("{} = {}\n\n", toml_key(key), tidy(value)),
Err(_) => format!("# {key}: no default could be written\n\n"),
}
}