use toml_edit::{Array, DocumentMut, Item, Table, Value};
use super::dd_manifest::{DdManifest, DdManifestError, DdRepositoryEntry};
pub fn merge_into(manifest: &DdManifest, existing: &str) -> Result<String, DdManifestError> {
let mut doc: DocumentMut = existing
.parse()
.map_err(|e: toml_edit::TomlError| DdManifestError::MergeSource(e.to_string()))?;
merge_report(manifest, &mut doc)?;
merge_repositories(manifest, &mut doc)?;
Ok(doc.to_string())
}
fn merge_report(manifest: &DdManifest, doc: &mut DocumentMut) -> Result<(), DdManifestError> {
let report = doc
.entry("report")
.or_insert_with(|| Item::Table(Table::new()));
let table = report
.as_table_like_mut()
.ok_or_else(|| DdManifestError::MergeSource("`report` is not a table".to_string()))?;
table.insert("title", string(&manifest.report.title));
set_or_keep(table, "analyst", manifest.report.analyst.as_deref());
set_or_keep(table, "client", manifest.report.client.as_deref());
set_or_keep(
table,
"ticketing",
manifest
.report
.ticketing
.as_ref()
.map(|p| p.display().to_string())
.as_deref(),
);
if manifest.report.gaps.is_empty() {
table.remove("gaps");
} else {
table.insert("gaps", array(&manifest.report.gaps));
}
Ok(())
}
fn merge_repositories(manifest: &DdManifest, doc: &mut DocumentMut) -> Result<(), DdManifestError> {
let item = doc
.entry("repositories")
.or_insert_with(|| Item::ArrayOfTables(toml_edit::ArrayOfTables::new()));
let entries = item.as_array_of_tables_mut().ok_or_else(|| {
DdManifestError::MergeSource("`repositories` is not an array of tables".to_string())
})?;
for fresh in &manifest.repositories {
let at = entries.iter().position(|e| matches_entry(e, fresh));
match at {
Some(at) => {
let existing = entries
.get_mut(at)
.expect("the index came from this array a statement ago");
write_repository(existing, fresh);
}
None => {
let mut table = Table::new();
write_repository(&mut table, fresh);
entries.push(table);
}
}
}
Ok(())
}
fn matches_entry(entry: &Table, fresh: &DdRepositoryEntry) -> bool {
let declared = entry.get("path").and_then(Item::as_str);
if let Some(declared) = declared {
return declared == fresh.path.display().to_string();
}
entry.get("name").and_then(Item::as_str) == Some(fresh.name.as_str())
}
fn write_repository(entry: &mut Table, fresh: &DdRepositoryEntry) {
entry.insert("name", string(&fresh.name));
entry.insert("path", string(&fresh.path.display().to_string()));
set_or_keep(
entry,
"authorship",
fresh
.authorship
.as_ref()
.map(|p| p.display().to_string())
.as_deref(),
);
}
fn set_or_keep(table: &mut dyn toml_edit::TableLike, key: &str, value: Option<&str>) {
if let Some(value) = value {
table.insert(key, string(value));
}
}
fn string(value: &str) -> Item {
Item::Value(Value::from(value))
}
fn array(values: &[String]) -> Item {
Item::Value(Value::Array(
values.iter().map(String::as_str).collect::<Array>(),
))
}
#[cfg(test)]
#[path = "dd_manifest_merge_tests.rs"]
mod dd_manifest_merge_tests;