mirror of
https://github.com/WarrenHood/MCModpackManager.git
synced 2025-06-15 19:04:57 +01:00
WIP json merging
This commit is contained in:
parent
2189ce9bce
commit
3e56632ef0
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -2074,6 +2074,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"tempfile",
|
||||
|
@ -3194,9 +3195,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.125"
|
||||
version = "1.0.128"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
|
||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
|
|
@ -13,6 +13,7 @@ pathdiff = "0.2.1"
|
|||
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"
|
||||
sha1 = "0.10.6"
|
||||
sha2 = "0.10.8"
|
||||
tempfile = "3.12.0"
|
||||
|
|
150
mcmpmgr/src/file_merge.rs
Normal file
150
mcmpmgr/src/file_merge.rs
Normal file
|
@ -0,0 +1,150 @@
|
|||
use std::{any::Any, default, str::FromStr};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum FileType {
|
||||
Json,
|
||||
Yaml,
|
||||
Toml,
|
||||
}
|
||||
|
||||
impl FromStr for FileType {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> anyhow::Result<Self> {
|
||||
Ok(if s.contains("json") {
|
||||
FileType::Json
|
||||
} else if s.contains("toml") {
|
||||
FileType::Toml
|
||||
} else if s.contains("yaml") || s.contains("yml") {
|
||||
FileType::Yaml
|
||||
} else {
|
||||
anyhow::bail!("Unmergable file type: {s}")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_json(
|
||||
src: &serde_json::Value,
|
||||
dst: &mut serde_json::Value,
|
||||
overwrite_existing: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
if src.is_object() && dst.is_object() {
|
||||
let src = src.as_object().unwrap();
|
||||
let dst = dst.as_object_mut().unwrap();
|
||||
|
||||
for (k, v) in src.iter() {
|
||||
if v.is_object() {
|
||||
let dst_v = dst.entry(k).or_insert(serde_json::json!({}));
|
||||
merge_json(v, dst_v, overwrite_existing)?;
|
||||
} else {
|
||||
if overwrite_existing || !dst.contains_key(k) {
|
||||
dst.insert(k.to_string(), 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_json() {
|
||||
let src = serde_json::json!({
|
||||
"a": 3,
|
||||
"b": {
|
||||
"x": {
|
||||
|
||||
},
|
||||
"y": {
|
||||
"test": "thing"
|
||||
}
|
||||
},
|
||||
"c": {}
|
||||
});
|
||||
let dst = serde_json::json!({
|
||||
"b": {
|
||||
"y": {
|
||||
"test": "something"
|
||||
}
|
||||
},
|
||||
"c": {
|
||||
"foo": "bar"
|
||||
}
|
||||
});
|
||||
|
||||
let mut merged_overwrite = dst.clone();
|
||||
let mut merged_retained = dst.clone();
|
||||
merge_json(&src, &mut merged_overwrite, true).unwrap();
|
||||
merge_json(&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_object(),
|
||||
"//b/x is not an object. 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_object(),
|
||||
"//b/x is not an object. src={}, dst={}",
|
||||
src,
|
||||
merged_retained
|
||||
);
|
||||
assert!(
|
||||
merged_retained["c"]["foo"] == "bar",
|
||||
"//c/foo != bar. src={}, dst={}",
|
||||
src,
|
||||
merged_retained
|
||||
);
|
||||
}
|
||||
|
||||
/// Merge `src` into `dst` if it is a supported file type
|
||||
fn merge_files(
|
||||
src: &str,
|
||||
dst: &str,
|
||||
overwrite_existing: bool,
|
||||
file_type: FileType,
|
||||
) -> anyhow::Result<String> {
|
||||
Ok(match file_type {
|
||||
FileType::Json => {
|
||||
let src_val = serde_json::from_str(src)?;
|
||||
let mut dst_val = serde_json::from_str(dst)?;
|
||||
merge_json(&src_val, &mut dst_val, overwrite_existing)?;
|
||||
dst_val.to_string()
|
||||
}
|
||||
FileType::Yaml => todo!(),
|
||||
FileType::Toml => todo!(),
|
||||
})
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use crate::providers::DownloadSide;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt::Display, path::{Path, PathBuf}, str::FromStr};
|
||||
use std::{fmt::Display, path::Path, str::FromStr};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
|
||||
pub struct FileMeta {
|
||||
|
@ -9,7 +9,7 @@ pub struct FileMeta {
|
|||
/// Which side the files should be applied on
|
||||
pub side: DownloadSide,
|
||||
/// When to apply the files to the instance
|
||||
pub apply_policy: FileApplyPolicy
|
||||
pub apply_policy: FileApplyPolicy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
|
||||
|
@ -17,7 +17,11 @@ pub enum FileApplyPolicy {
|
|||
/// Always ensure the file or folder exactly matches that defined in the pack
|
||||
Always,
|
||||
/// Only apply the file or folder if it doesn't already exist in the pack
|
||||
Once
|
||||
Once,
|
||||
/// Merge into folders and files, retaining existing values in files when a file already exists
|
||||
MergeRetain,
|
||||
/// Merge into folders and files, overwriting existing values in files when a file already exists
|
||||
MergeOverwrite,
|
||||
}
|
||||
|
||||
impl FromStr for FileApplyPolicy {
|
||||
|
@ -27,6 +31,8 @@ impl FromStr for FileApplyPolicy {
|
|||
match s.to_ascii_lowercase().as_str() {
|
||||
"always" => Ok(Self::Always),
|
||||
"once" => Ok(Self::Once),
|
||||
"mergeretain" => Ok(Self::MergeRetain),
|
||||
"mergeoverwrite" => Ok(Self::MergeOverwrite),
|
||||
_ => anyhow::bail!("Invalid apply policy {}. Expected one of: always, once", s),
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +43,8 @@ impl Display for FileApplyPolicy {
|
|||
match self {
|
||||
Self::Always => write!(f, "Always"),
|
||||
Self::Once => write!(f, "Once"),
|
||||
Self::MergeRetain => write!(f, "MergeRetain"),
|
||||
Self::MergeOverwrite => write!(f, "MergeOverwrite"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
pub mod mod_meta;
|
||||
pub mod file_merge;
|
||||
pub mod file_meta;
|
||||
pub mod mod_meta;
|
||||
pub mod modpack;
|
||||
pub mod profiles;
|
||||
pub mod providers;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
mod file_merge;
|
||||
mod file_meta;
|
||||
mod mod_meta;
|
||||
mod modpack;
|
||||
|
|
Loading…
Reference in a new issue