diff --git a/src/evaluator.rs b/src/evaluator.rs index 09c9aaa2cb..caf7e21110 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -7,6 +7,13 @@ pub(crate) struct Evaluator<'src: 'run, 'run> { pub(crate) scope: Scope<'src, 'run>, } +fn assignment_can_override( + assignment: &Binding<'_, Expression<'_>>, + settings: &Settings<'_>, +) -> bool { + !settings.allow_private_variables || assignment.is_public() +} + impl<'src, 'run> Evaluator<'src, 'run> { pub(crate) fn evaluate_assignments( config: &'run Config, @@ -15,6 +22,7 @@ impl<'src, 'run> Evaluator<'src, 'run> { overrides: &BTreeMap, parent: &'run Scope<'src, 'run>, search: &'run Search, + settings: &'run Settings<'src>, ) -> RunResult<'src, Scope<'src, 'run>> where 'src: 'run, @@ -32,8 +40,13 @@ impl<'src, 'run> Evaluator<'src, 'run> { for (name, value) in overrides { if let Some(assignment) = module.assignments.get(name) { - if assignment.is_public() { - scope.bind(assignment.export, false, assignment.name, value.clone()); + if assignment_can_override(assignment, settings) { + scope.bind( + assignment.export, + assignment.private, + assignment.name, + value.clone(), + ); } else { unknown_overrides.push(name.clone()); } diff --git a/src/justfile.rs b/src/justfile.rs index b31084ef18..ae770ccabf 100644 --- a/src/justfile.rs +++ b/src/justfile.rs @@ -110,7 +110,15 @@ impl<'src> Justfile<'src> { let root = Scope::root(); - let scope = Evaluator::evaluate_assignments(config, &dotenv, self, overrides, &root, search)?; + let scope = Evaluator::evaluate_assignments( + config, + &dotenv, + self, + overrides, + &root, + search, + &self.settings, + )?; match &config.subcommand { Subcommand::Command { @@ -284,6 +292,7 @@ impl<'src> Justfile<'src> { &BTreeMap::new(), parent, search, + &self.settings, )?; let scope = arena.alloc(scope); scopes.insert(path, scope); diff --git a/src/keyword.rs b/src/keyword.rs index ff06c39d27..8091ee192d 100644 --- a/src/keyword.rs +++ b/src/keyword.rs @@ -6,6 +6,7 @@ pub(crate) enum Keyword { Alias, AllowDuplicateRecipes, AllowDuplicateVariables, + AllowPrivateVariables, Assert, DotenvFilename, DotenvLoad, diff --git a/src/node.rs b/src/node.rs index 3ccf862d57..ca73e48e48 100644 --- a/src/node.rs +++ b/src/node.rs @@ -288,6 +288,7 @@ impl<'src> Node<'src> for Set<'src> { match &self.value { Setting::AllowDuplicateRecipes(value) | Setting::AllowDuplicateVariables(value) + | Setting::AllowPrivateVariables(value) | Setting::DotenvLoad(value) | Setting::DotenvRequired(value) | Setting::Export(value) diff --git a/src/parser.rs b/src/parser.rs index 0e69e70b01..e0e8d577ce 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -948,6 +948,9 @@ impl<'run, 'src> Parser<'run, 'src> { Keyword::AllowDuplicateVariables => { Some(Setting::AllowDuplicateVariables(self.parse_set_bool()?)) } + Keyword::AllowPrivateVariables => { + Some(Setting::AllowPrivateVariables(self.parse_set_bool()?)) + } Keyword::DotenvLoad => Some(Setting::DotenvLoad(self.parse_set_bool()?)), Keyword::DotenvRequired => Some(Setting::DotenvRequired(self.parse_set_bool()?)), Keyword::Export => Some(Setting::Export(self.parse_set_bool()?)), diff --git a/src/setting.rs b/src/setting.rs index 2b68713580..ecc9fceefe 100644 --- a/src/setting.rs +++ b/src/setting.rs @@ -4,6 +4,7 @@ use super::*; pub(crate) enum Setting<'src> { AllowDuplicateRecipes(bool), AllowDuplicateVariables(bool), + AllowPrivateVariables(bool), DotenvFilename(StringLiteral<'src>), DotenvLoad(bool), DotenvPath(StringLiteral<'src>), @@ -27,6 +28,7 @@ impl<'src> Display for Setting<'src> { match self { Self::AllowDuplicateRecipes(value) | Self::AllowDuplicateVariables(value) + | Self::AllowPrivateVariables(value) | Self::DotenvLoad(value) | Self::DotenvRequired(value) | Self::Export(value) diff --git a/src/settings.rs b/src/settings.rs index 795ddebd1f..443c6e7bc6 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -9,6 +9,7 @@ pub(crate) const WINDOWS_POWERSHELL_ARGS: &[&str] = &["-NoLogo", "-Command"]; pub(crate) struct Settings<'src> { pub(crate) allow_duplicate_recipes: bool, pub(crate) allow_duplicate_variables: bool, + pub(crate) allow_private_variables: bool, pub(crate) dotenv_filename: Option, pub(crate) dotenv_load: bool, pub(crate) dotenv_path: Option, @@ -40,6 +41,9 @@ impl<'src> Settings<'src> { Setting::AllowDuplicateVariables(allow_duplicate_variables) => { settings.allow_duplicate_variables = allow_duplicate_variables; } + Setting::AllowPrivateVariables(allow_private_variables) => { + settings.allow_private_variables = allow_private_variables; + } Setting::DotenvFilename(filename) => { settings.dotenv_filename = Some(filename.cooked); } diff --git a/src/subcommand.rs b/src/subcommand.rs index cf1eaf1dc2..5756c8faf6 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -714,10 +714,11 @@ impl Subcommand { } fn public_variables(justfile: &Justfile) { + let filter_private = justfile.settings.allow_private_variables; for (i, (_, assignment)) in justfile .assignments .iter() - .filter(|(_, binding)| binding.is_public()) + .filter(|(_, binding)| !filter_private || binding.is_public()) .enumerate() { if i > 0 { diff --git a/tests/json.rs b/tests/json.rs index aa09dd87ad..ab3f187537 100644 --- a/tests/json.rs +++ b/tests/json.rs @@ -47,6 +47,7 @@ fn alias() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -93,6 +94,7 @@ fn assignment() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -149,6 +151,7 @@ fn private_assignment() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -207,6 +210,7 @@ fn body() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -277,6 +281,7 @@ fn dependencies() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -386,6 +391,7 @@ fn dependency_argument() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -456,6 +462,7 @@ fn duplicate_recipes() { "settings": { "allow_duplicate_recipes": true, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -505,6 +512,7 @@ fn duplicate_variables() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": true, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -556,6 +564,7 @@ fn doc_comment() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -593,6 +602,7 @@ fn empty_justfile() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -751,6 +761,7 @@ fn parameters() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -842,6 +853,7 @@ fn priors() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -893,6 +905,7 @@ fn private() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -944,6 +957,7 @@ fn quiet() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -1007,6 +1021,7 @@ fn settings() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": "filename", "dotenv_load": true, "dotenv_path": "path", @@ -1064,6 +1079,7 @@ fn shebang() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -1115,6 +1131,7 @@ fn simple() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -1169,6 +1186,7 @@ fn attribute() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -1238,6 +1256,7 @@ fn module() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -1262,6 +1281,7 @@ fn module() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -1333,6 +1353,7 @@ fn module_group() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, @@ -1357,6 +1378,7 @@ fn module_group() { "settings": { "allow_duplicate_recipes": false, "allow_duplicate_variables": false, + "allow_private_variables": false, "dotenv_filename": null, "dotenv_load": false, "dotenv_path": null, diff --git a/tests/private.rs b/tests/private.rs index cdad65a8a6..00fd6d8f7d 100644 --- a/tests/private.rs +++ b/tests/private.rs @@ -39,25 +39,36 @@ fn private_attribute_for_alias() { .run(); } -#[test] -fn private_attribute_for_assignment() { - Test::new() - .justfile( - " +test! { + name: dont_list_private_variables, + justfile: " + set allow-private-variables [private] foo := 'one' bar := 'two' _baz := 'three' ", - ) - .args(["--variables"]) - .stdout("bar\n") - .run(); + args: ("--variables"), + stdout: "bar\n", +} + +test! { + name: list_private_variables_if_not_opted_in, + justfile: " + [private] + foo := 'one' + bar := 'two' + _baz := 'three' + ", + args: ("--variables"), + stdout: "_baz bar foo\n", } test! { name: no_private_overrides, justfile: " + set allow-private-variables + [private] foo := 'one' bar := 'two' @@ -75,6 +86,8 @@ test! { test! { name: no_private_implicit_overrides, justfile: " + set allow-private-variables + [private] foo := 'one' bar := 'two' @@ -92,6 +105,8 @@ test! { test! { name: allowed_public_overrides, justfile: " + set allow-private-variables + [private] foo := 'one' bar := 'two' @@ -105,3 +120,20 @@ test! { stderr: "", status: EXIT_SUCCESS, } + +test! { + name: ignore_private_without_setting, + justfile: " + [private] + foo := 'one' + bar := 'two' + _baz := 'three' + + default: + @echo hello + ", + args: ("foo=two"), + stdout: "hello\n", + stderr: "", + status: EXIT_SUCCESS, +}