Skip to content

Commit cb14741

Browse files
feat(Maintain): Add recursive JSON array deduplication in build script
The `JsonEdit` function in `Source/Build/JsonEdit.rs` previously risked appending duplicate sidecar paths to the `bundle.binaries` array during repeated execution. Enhance the logic to first check for existing sidecar entries before insertion. Implement a new `DedupeJson` helper function that recursively traverses the entire JSON document. This function removes duplicate values from all arrays (preserving the first occurrence) and descends into nested objects and arrays. This ensures idempotency for the build process, preventing duplicate entries introduced by upstream changes or prior partial runs without altering array order. The `Modified` flag is now updated if any duplicates are removed, ensuring the JSON file is only rewritten when content actually changes.
1 parent 9e4580a commit cb14741

1 file changed

Lines changed: 63 additions & 2 deletions

File tree

Source/Build/JsonEdit.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ pub fn JsonEdit(File:&Path, Product:&str, Id:&str, Version:&str, SidecarPath:Opt
194194
Modified = true;
195195
}
196196

197-
// Add sidecar path if provided
197+
// Add sidecar path if provided (dedupe: only insert if not already present)
198198
if let Some(Path) = SidecarPath {
199199
let Bundle = Root
200200
.entry("bundle")
@@ -208,8 +208,19 @@ pub fn JsonEdit(File:&Path, Product:&str, Id:&str, Version:&str, SidecarPath:Opt
208208
.as_array_mut()
209209
.unwrap();
210210

211-
Bins.push(JsonValue::String(Path.to_string()));
211+
let AlreadyPresent = Bins.iter().any(|Entry| Entry.as_str() == Some(Path));
212212

213+
if !AlreadyPresent {
214+
Bins.push(JsonValue::String(Path.to_string()));
215+
216+
Modified = true;
217+
}
218+
}
219+
220+
// Recursively dedupe every array in the document. Order is preserved
221+
// (first occurrence wins). Catches duplicates introduced upstream as
222+
// well as anything left over from prior runs.
223+
if DedupeJson(&mut Parsed) {
213224
Modified = true;
214225
}
215226

@@ -230,3 +241,53 @@ pub fn JsonEdit(File:&Path, Product:&str, Id:&str, Version:&str, SidecarPath:Opt
230241

231242
Ok(Modified)
232243
}
244+
245+
/// Recursively dedupe arrays within a JSON tree. Returns `true` if any
246+
/// duplicates were removed. Equality is structural (compares whole
247+
/// `JsonValue`s), order is preserved, first occurrence wins. Object keys are
248+
/// already unique by JSON semantics, so we only descend into them.
249+
fn DedupeJson(Value:&mut JsonValue) -> bool {
250+
match Value {
251+
JsonValue::Array(Items) => {
252+
let mut Changed = false;
253+
254+
for Item in Items.iter_mut() {
255+
if DedupeJson(Item) {
256+
Changed = true;
257+
}
258+
}
259+
260+
let mut Seen:Vec<JsonValue> = Vec::with_capacity(Items.len());
261+
262+
let mut Index = 0;
263+
264+
while Index < Items.len() {
265+
if Seen.iter().any(|Existing| Existing == &Items[Index]) {
266+
Items.remove(Index);
267+
268+
Changed = true;
269+
} else {
270+
Seen.push(Items[Index].clone());
271+
272+
Index += 1;
273+
}
274+
}
275+
276+
Changed
277+
},
278+
279+
JsonValue::Object(Map) => {
280+
let mut Changed = false;
281+
282+
for (_Key, Child) in Map.iter_mut() {
283+
if DedupeJson(Child) {
284+
Changed = true;
285+
}
286+
}
287+
288+
Changed
289+
},
290+
291+
_ => false,
292+
}
293+
}

0 commit comments

Comments
 (0)