Remove hook entries whose command contains `prefix`. Handles both nested (`{matcher?, hooks: [{command}]}`) and flat (`{command}`/`{bash}`) shapes. Empty groups and empty event arrays are pruned. Returns commands removed.
(hooks: &mut Map<String, Value>, prefix: &str)
| 315 | /// (`{matcher?, hooks: [{command}]}`) and flat (`{command}`/`{bash}`) shapes. |
| 316 | /// Empty groups and empty event arrays are pruned. Returns commands removed. |
| 317 | fn remove_prefixed(hooks: &mut Map<String, Value>, prefix: &str) -> usize { |
| 318 | let mut removed = 0; |
| 319 | for value in hooks.values_mut() { |
| 320 | let Some(entries) = value.as_array_mut() else { |
| 321 | continue; |
| 322 | }; |
| 323 | entries.retain_mut(|entry| { |
| 324 | // Nested shape: a group with an inner `hooks` array. |
| 325 | if let Some(inner) = entry.get_mut("hooks").and_then(Value::as_array_mut) { |
| 326 | let before = inner.len(); |
| 327 | inner.retain(|hook| !entry_command_contains(hook, prefix)); |
| 328 | removed += before - inner.len(); |
| 329 | return !inner.is_empty(); |
| 330 | } |
| 331 | // Flat shape: the entry itself carries the command. |
| 332 | if entry_command_contains(entry, prefix) { |
| 333 | removed += 1; |
| 334 | return false; |
| 335 | } |
| 336 | true |
| 337 | }); |
| 338 | } |
| 339 | // Drop event keys whose entry array is now empty. |
| 340 | hooks.retain(|_, v| v.as_array().map(|a| !a.is_empty()).unwrap_or(true)); |
| 341 | removed |
| 342 | } |
| 343 | |
| 344 | /// Count the hook commands carried by a single entry (nested or flat). |
| 345 | fn count_commands(entry: &Value) -> usize { |
no test coverage detected