Added WIP support for merging yaml files

This commit is contained in:
Warren Hood 2024-10-09 00:29:33 +02:00
parent 3e56632ef0
commit 0af0d62639
3 changed files with 150 additions and 10 deletions

20
Cargo.lock generated
View file

@ -2075,6 +2075,7 @@ dependencies = [
"semver",
"serde",
"serde_json",
"serde_yaml",
"sha1",
"sha2",
"tempfile",
@ -3237,6 +3238,19 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "sha1"
version = "0.10.6"
@ -3923,6 +3937,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "untrusted"
version = "0.9.0"

View file

@ -14,6 +14,7 @@ reqwest = { version = "0.12.5", features = ["json"] }
semver = { version = "1.0.23", features = ["serde"] }
serde = { version = "1.0.207", features = ["derive"] }
serde_json = "1.0.128"
serde_yaml = "0.9.34"
sha1 = "0.10.6"
sha2 = "0.10.8"
tempfile = "3.12.0"

View file

@ -1,4 +1,4 @@
use std::{any::Any, default, str::FromStr};
use std::str::FromStr;
#[derive(Debug, Clone, Copy)]
pub enum FileType {
@ -81,50 +81,164 @@ fn test_merge_json() {
assert!(
merged_overwrite["b"]["y"]["test"] == "thing",
"//b/y/test wasn't overwritten with \"thing\". src={}, dst={}",
"//b/y/test wasn't overwritten with \"thing\". src={:#?}, dst={:#?}",
src,
merged_overwrite
);
assert!(
merged_overwrite["a"] == 3,
"//a was not set to 3. src={}, dst={}",
"//a was not set to 3. src={:#?}, dst={:#?}",
src,
merged_overwrite
);
assert!(
merged_overwrite["b"]["x"].is_object(),
"//b/x is not an object. src={}, dst={}",
"//b/x is not an object. src={:#?}, dst={:#?}",
src,
merged_overwrite
);
assert!(
merged_overwrite["c"]["foo"] == "bar",
"//c/foo != bar. src={}, dst={}",
"//c/foo != bar. src={:#?}, dst={:#?}",
src,
merged_overwrite
);
assert!(
merged_retained["b"]["y"]["test"] == "something",
"//b/y/test was overwritten. src={}, dst={}",
"//b/y/test was overwritten. src={:#?}, dst={:#?}",
src,
merged_retained
);
assert!(
merged_retained["a"] == 3,
"//a was not set to 3. src={}, dst={}",
"//a was not set to 3. src={:#?}, dst={:#?}",
src,
merged_retained
);
assert!(
merged_retained["b"]["x"].is_object(),
"//b/x is not an object. src={}, dst={}",
"//b/x is not an object. src={:#?}, dst={:#?}",
src,
merged_retained
);
assert!(
merged_retained["c"]["foo"] == "bar",
"//c/foo != bar. src={}, dst={}",
"//c/foo != bar. src={:#?}, dst={:#?}",
src,
merged_retained
);
}
fn merge_yaml(
src: &serde_yaml::Value,
dst: &mut serde_yaml::Value,
overwrite_existing: bool,
) -> anyhow::Result<()> {
if src.is_mapping() && dst.is_mapping() {
let src = src.as_mapping().unwrap();
let dst = dst.as_mapping_mut().unwrap();
for (k, v) in src.iter() {
if v.is_mapping() {
let dst_v = dst.entry(k.clone()).or_insert(serde_yaml::from_str("{}")?);
merge_yaml(v, dst_v, overwrite_existing)?;
} else {
if overwrite_existing || !dst.contains_key(k) {
dst.insert(k.clone(), v.clone());
}
}
}
} else {
// TODO: Keep track of path for better errors
anyhow::bail!("Cannot merge non-objects: {src:#?} and {dst:#?}")
}
Ok(())
}
#[test]
fn test_merge_yaml() {
let src = serde_yaml::from_str(
r#"{
"a": 3,
"b": {
"x": {
},
"y": {
"test": "thing"
}
},
"c": {}
}"#,
)
.unwrap();
let dst: serde_yaml::Value = serde_yaml::from_str(
r#"{
"b": {
"y": {
"test": "something"
}
},
"c": {
"foo": "bar"
}
}"#,
)
.unwrap();
let mut merged_overwrite = dst.clone();
let mut merged_retained = dst.clone();
merge_yaml(&src, &mut merged_overwrite, true).unwrap();
merge_yaml(&src, &mut merged_retained, false).unwrap();
assert!(
merged_overwrite["b"]["y"]["test"] == "thing",
"//b/y/test wasn't overwritten with \"thing\". src={:#?}, dst={:#?}",
src,
merged_overwrite
);
assert!(
merged_overwrite["a"] == 3,
"//a was not set to 3. src={:#?}, dst={:#?}",
src,
merged_overwrite
);
assert!(
merged_overwrite["b"]["x"].is_mapping(),
"//b/x is not a mapping. src={:#?}, dst={:#?}",
src,
merged_overwrite
);
assert!(
merged_overwrite["c"]["foo"] == "bar",
"//c/foo != bar. src={:#?}, dst={:#?}",
src,
merged_overwrite
);
assert!(
merged_retained["b"]["y"]["test"] == "something",
"//b/y/test was overwritten. src={:#?}, dst={:#?}",
src,
merged_retained
);
assert!(
merged_retained["a"] == 3,
"//a was not set to 3. src={:#?}, dst={:#?}",
src,
merged_retained
);
assert!(
merged_retained["b"]["x"].is_mapping(),
"//b/x is not a mapping. src={:#?}, dst={:#?}",
src,
merged_retained
);
assert!(
merged_retained["c"]["foo"] == "bar",
"//c/foo != bar. src={:#?}, dst={:#?}",
src,
merged_retained
);
@ -144,7 +258,12 @@ fn merge_files(
merge_json(&src_val, &mut dst_val, overwrite_existing)?;
dst_val.to_string()
}
FileType::Yaml => todo!(),
FileType::Yaml => {
let src_val = serde_yaml::Value::from(src);
let mut dst_val = serde_yaml::Value::from(dst);
merge_yaml(&src_val, &mut dst_val, overwrite_existing)?;
serde_yaml::to_string(&dst_val)?
}
FileType::Toml => todo!(),
})
}