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)
| 277 | /// (`{matcher?, hooks: [{command}]}`) and flat (`{command}`/`{bash}`) shapes. |
| 278 | /// Empty groups and empty event arrays are pruned. Returns commands removed. |
| 279 | fn remove_prefixed(hooks: &mut Map<String, Value>, prefix: &str) -> usize { |
| 280 | let mut removed = 0; |
| 281 | for value in hooks.values_mut() { |
| 282 | let Some(entries) = value.as_array_mut() else { |
| 283 | continue; |
| 284 | }; |
| 285 | entries.retain_mut(|entry| { |
| 286 | // Nested shape: a group with an inner `hooks` array. |
| 287 | if let Some(inner) = entry.get_mut("hooks").and_then(Value::as_array_mut) { |
| 288 | let before = inner.len(); |
| 289 | inner.retain(|hook| !entry_command_contains(hook, prefix)); |
| 290 | removed += before - inner.len(); |
| 291 | return !inner.is_empty(); |
| 292 | } |
| 293 | // Flat shape: the entry itself carries the command. |
| 294 | if entry_command_contains(entry, prefix) { |
| 295 | removed += 1; |
| 296 | return false; |
| 297 | } |
| 298 | true |
| 299 | }); |
| 300 | } |
| 301 | // Drop event keys whose entry array is now empty. |
| 302 | hooks.retain(|_, v| v.as_array().map(|a| !a.is_empty()).unwrap_or(true)); |
| 303 | removed |
| 304 | } |
| 305 | |
| 306 | /// Count the hook commands carried by a single entry (nested or flat). |
| 307 | fn count_commands(entry: &Value) -> usize { |
no test coverage detected