use toml::Value;
use toml_edit::{DocumentMut, Item, TableLike};
use whycodes_core::Result;
use crate::load::toml_err;
pub(crate) fn patched_text(text: &str, before: &Value, after: &Value) -> Result<String> {
let mut doc = parse_doc(text)?;
if let (Value::Table(before), Value::Table(after)) = (before, after) {
patch_table(doc.as_table_mut(), before, after)?;
}
Ok(doc.to_string())
}
fn patch_table(doc: &mut dyn TableLike, before: &toml::Table, after: &toml::Table) -> Result<()> {
for (key, new) in after {
let old = before.get(key);
if old == Some(new) {
continue;
}
let nested = match (old, new, doc.get_mut(key)) {
(Some(Value::Table(old)), Value::Table(new), Some(item)) => {
item.as_table_like_mut().map(|table| (table, old, new))
}
_ => None,
};
match nested {
Some((table, old, new)) => patch_table(table, old, new)?,
None => {
doc.insert(key, to_item(new)?);
}
}
}
for key in before.keys().filter(|key| !after.contains_key(*key)) {
doc.remove(key);
}
Ok(())
}
fn to_item(value: &Value) -> Result<Item> {
let mut wrap = toml::Table::new();
wrap.insert("v".into(), value.clone());
let mut doc = parse_doc(&wrap.to_string())?;
Ok(doc.remove("v").unwrap_or_default())
}
fn parse_doc(text: &str) -> Result<DocumentMut> {
text.parse().map_err(|e| toml_err(format!("{e}")))
}
#[cfg(test)]
#[path = "patch_tests.rs"]
mod tests;