From 0b02cbd797ab13ff0877031d83e2ecc85a7916b5 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sun, 9 Aug 2026 19:23:49 +0800 Subject: feat(proj_mgr): add support for conditional directory removal Add `[[hide-dir]]` rule to remove entire directories when their condition evaluates to true, extending the existing file-level hide functionality to directory trees. --- mingling_cli/src/proj_mgr/cmd_proj_init.rs | 12 +++++++ mingling_cli/src/proj_mgr/rule_solver.rs | 52 +++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) (limited to 'mingling_cli') diff --git a/mingling_cli/src/proj_mgr/cmd_proj_init.rs b/mingling_cli/src/proj_mgr/cmd_proj_init.rs index 6e3908b..1ef8d1e 100644 --- a/mingling_cli/src/proj_mgr/cmd_proj_init.rs +++ b/mingling_cli/src/proj_mgr/cmd_proj_init.rs @@ -187,6 +187,18 @@ pub fn handle_state_project_generate(_: StateProjectGenerate, cwd: &ResCurrentDi hidden.push(target); } + // hide-dir: delete the generated directory tree when the rule is true + for hide in &rules.hide_dirs { + if !eval_rule(&hide.rule, &answers) { + continue; + } + let target = cwd.join(hide.dir.trim_start_matches("./")); + remove_path(&target).map_err(|e| { + ErrorTemplateExpandFailed::new(format!("failed to hide {}: {e}", target.display())) + })?; + hidden.push(target); + } + // Clean up the cache fs::remove_dir_all(&tmpl_cache).map_err(|e| { ErrorTemplateExpandFailed::new(format!("failed to remove {}: {e}", tmpl_cache.display())) diff --git a/mingling_cli/src/proj_mgr/rule_solver.rs b/mingling_cli/src/proj_mgr/rule_solver.rs index c33cc1e..c749937 100644 --- a/mingling_cli/src/proj_mgr/rule_solver.rs +++ b/mingling_cli/src/proj_mgr/rule_solver.rs @@ -31,6 +31,15 @@ pub struct HideFileRule { pub rule: String, } +/// A `[[hide-dir]]` rule: hide the whole directory `dir` when `rule` is true. +#[derive(Debug, Clone)] +pub struct HideDirRule { + /// Directory path relative to the project root, e.g. `./src/completion/`. + pub dir: String, + /// Boolean expression, e.g. `!completion`. + pub rule: String, +} + /// A `[[user.*]]` entry describing a checklist answer and its default. /// /// Checklist keys are declared under `[[user.input]]`, `[[user.toggle]]` or @@ -60,6 +69,7 @@ pub struct TemplateRules { pub mutexes: Vec, pub display: Vec, pub hide_files: Vec, + pub hide_dirs: Vec, } /// Parse `checklist.toml` into a flat map of answers. @@ -184,6 +194,25 @@ pub fn parse_rules(content: &str) -> Result { } } + if let Some(tables) = doc + .get("hide-dir") + .and_then(|item| item.as_array_of_tables()) + { + for table in tables { + let dir = table + .get("dir") + .and_then(|v| v.as_str()) + .ok_or("[[hide-dir]] entry is missing `dir`")? + .to_string(); + let rule = table + .get("rule") + .and_then(|v| v.as_str()) + .ok_or("[[hide-dir]] entry is missing `rule`")? + .to_string(); + rules.hide_dirs.push(HideDirRule { dir, rule }); + } + } + Ok(rules) } @@ -465,6 +494,24 @@ name = "completion" assert_eq!(merged.get("pathf").unwrap(), "false"); } + #[test] + fn rules_parse_hide_dir() { + let content = r#" +[[hide-dir]] +dir = "./src/completion/" +rule = "!completion" + +[[hide-dir]] +dir = "./src/dispatch/" +rule = "!dispatch_tree" +"#; + let rules = parse_rules(content).unwrap(); + assert_eq!(rules.hide_dirs.len(), 2); + assert_eq!(rules.hide_dirs[0].dir, "./src/completion/"); + assert_eq!(rules.hide_dirs[0].rule, "!completion"); + assert_eq!(rules.hide_dirs[1].dir, "./src/dispatch/"); + } + #[test] fn rules_parse_toggle_mutex() { let content = r#" @@ -475,7 +522,10 @@ reason = "You can only select one async runtime" let rules = parse_rules(content).unwrap(); assert_eq!(rules.mutexes.len(), 1); assert_eq!(rules.mutexes[0].mutex, vec!["tokio", "async_std", "smol"]); - assert_eq!(rules.mutexes[0].reason, "You can only select one async runtime"); + assert_eq!( + rules.mutexes[0].reason, + "You can only select one async runtime" + ); } #[test] -- cgit