diff --git a/README.md b/README.md index b4bf8d5..fc1bef7 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ SHELL_COMMAND=SHELL echo 1234 ``` #### Feature Flags -Any line in the rules configuration file that does not include a `=` character and is not a comment will enable the feature name that matches the line. Dotfiles can designate togglable features with three octothorpes followed by the feature name. +Any line in the rules configuration file that does not include a `=` character and is not a comment will enable the feature name that matches the line. Dotfiles can designate togglable features with three octothorpes followed by the feature name. Features can be nested. ``` FEATURE1 ``` diff --git a/src/lib.rs b/src/lib.rs index c835d3c..6be53b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,23 +83,21 @@ impl Config { fn template_file(&self, source: &Path, dest: &Path) -> Result<(), Box> { let source = BufReader::new(File::open(source)?); let mut dest = File::create(dest)?; - let mut in_disabled_feature = false; + let mut feature_stack = Vec::new(); for line in source.lines() { - let line = line?; - let feature = self.is_feature_enable_or_disable(&line); - - match feature { - Some(enabled) => { - if in_disabled_feature { - in_disabled_feature = false; - } else if !enabled { - in_disabled_feature = true; + let mut line = line?; + match Config::get_feature(&line) { + Some(feature) => match feature_stack.last() { + Some((last, _)) if *last == feature => { + feature_stack.pop(); + }, + _ => { + feature_stack.push((feature.to_owned(), self.is_enabled(&feature))); } } None => { - if !in_disabled_feature { - let mut line = line; + if feature_stack.iter().all(|(_, enabled)| *enabled) { for (key, value) in &self.substitutions { line = line.replace(key, value); } @@ -113,21 +111,19 @@ impl Config { Ok(()) } - fn is_feature_enable_or_disable(&self, line: &str) -> Option { + fn get_feature(line: &str) -> Option<&str> { let re = Regex::new("^\\s*### .*$").unwrap(); if re.is_match(line) { - let found_feature = &line.trim()[3..].trim(); - for feature in &self.features { - if found_feature == feature { - return Some(true); - } - } - Some(false) + Some(line.trim()[3..].trim()) } else { None } } + fn is_enabled(&self, feature: &str) -> bool { + self.features.iter().any(|f| f == feature) + } + pub fn template(&self, source_dir: &str, dest_dir: &str) -> Result<(), Box> { for entry in WalkDir::new(source_dir) { let source_file = entry?; diff --git a/test/dotfiles/template b/test/dotfiles/template index 1312c0d..55b6be6 100644 --- a/test/dotfiles/template +++ b/test/dotfiles/template @@ -18,3 +18,13 @@ this should *NOT* appear ### OTHER FEATURE this should *NOT* appear (test indentation) ### OTHER FEATURE +### FEATURE1 +### FEATURE2 +this should appear (test nested features) +### FEATURE2 +### FEATURE1 +### OTHER FEATURE +### FEATURE2 +this should *NOT* appear (test nested features) +### FEATURE2 +### OTHER FEATURE diff --git a/test/expected/template b/test/expected/template index f2bdc05..1e2d820 100644 --- a/test/expected/template +++ b/test/expected/template @@ -8,3 +8,4 @@ Test a shell command: 1234 this should appear this should appear (test indentation) +this should appear (test nested features) diff --git a/test/rules b/test/rules index f87949d..59ad991 100644 --- a/test/rules +++ b/test/rules @@ -10,6 +10,7 @@ VAR WITH SPACES=variable EMPTY VARIABLE= FEATURE1 +FEATURE2 # More comments... SHELL_COMMAND=SHELL echo 1234