pub fn apply_entry_patches(
data: &[EntryOptions],
patches: &[PatchOptions],
warn: impl FnMut(&str),
) -> Vec<EntryOptions>Expand description
Apply patch rows to an entry list — THE patch semantics of this crate, shared by mounting, recomposition, and offline config tooling so a dump can never drift from what boots.
Semantics (a direct port of upstream applyEntryPatches):
- The result is fully detached from both inputs, even when
patchesis empty. Recomposition must always restart from the original patch data. - The id index is built once from
data(recursing into groups) and extended only byinsertrows as they are added, so a later patch in the same list can target a row an earlier patch inserted — and rows that appear any other way (inside a replacedconfig, say) stay invisible to it. insertwithidappends to that group’s children (the target must be a group);insertwithoutidappends to the top level.- Non-insert patches require
id.name, when present and non-empty, must equal the target’s name.config,disabled, andinjectreplace the target’s fields wholesale. - A patch that matches nothing — missing id, unknown target, name
mismatch, non-group insert target — warns through
warnand is skipped, never an error: one overlay shared across surfaces does not have to match every tree. - Override keys with no Rust slot (
group,intercept,isolate, anything unknown) warn and are skipped.
§Example
use cordis_include::{apply_entry_patches, EntryOptions, Node, PatchOptions};
let base = vec![EntryOptions::new("adapter-http").with_id("http")];
let patches = vec![PatchOptions {
id: Some("http".into()),
config: Some(Node::from_iter([(
"port".to_string(),
Node::Int(8080),
)])),
..Default::default()
}];
let composed = apply_entry_patches(&base, &patches, |_| {});
assert_eq!(composed[0].config.as_ref().unwrap()["port"], Node::Int(8080));
// The input stays untouched: recomposition restarts from the originals.
assert!(base[0].config.is_none());