Change --eval to print variable value only (#806)

This commit is contained in:
Casey Rodarmor 2021-04-25 17:02:57 -07:00 committed by GitHub
parent b8a65149be
commit 09b370e10d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 137 additions and 58 deletions

View File

@ -375,20 +375,20 @@ impl Config {
(false, false) => {},
(true, false) => {
return Err(ConfigError::SubcommandOverrides {
subcommand: format!("--{}", subcommand.to_lowercase()),
subcommand,
overrides,
});
},
(false, true) => {
return Err(ConfigError::SubcommandArguments {
subcommand: format!("--{}", subcommand.to_lowercase()),
arguments: positional.arguments,
subcommand,
});
},
(true, true) => {
return Err(ConfigError::SubcommandOverridesAndArguments {
subcommand: format!("--{}", subcommand.to_lowercase()),
arguments: positional.arguments,
subcommand,
overrides,
});
},
@ -420,8 +420,19 @@ impl Config {
name: name.to_owned(),
}
} else if matches.is_present(cmd::EVALUATE) {
if positional.arguments.len() > 1 {
return Err(ConfigError::SubcommandArguments {
subcommand: cmd::EVALUATE,
arguments: positional
.arguments
.into_iter()
.skip(1)
.collect::<Vec<String>>(),
});
}
Subcommand::Evaluate {
variables: positional.arguments,
variable: positional.arguments.into_iter().next(),
overrides,
}
} else if matches.is_present(cmd::VARIABLES) {
@ -811,7 +822,7 @@ impl Config {
} else {
if self.verbosity.loud() {
eprintln!("Justfile does not contain recipe `{}`.", name);
if let Some(suggestion) = justfile.suggest(name) {
if let Some(suggestion) = justfile.suggest_recipe(name) {
eprintln!("{}", suggestion);
}
}
@ -1330,7 +1341,7 @@ ARGS:
args: ["--evaluate"],
subcommand: Subcommand::Evaluate {
overrides: map!{},
variables: vec![],
variable: None,
},
}
@ -1339,7 +1350,7 @@ ARGS:
args: ["--evaluate", "x=y"],
subcommand: Subcommand::Evaluate {
overrides: map!{"x": "y"},
variables: vec![],
variable: None,
},
}
@ -1348,7 +1359,7 @@ ARGS:
args: ["--evaluate", "x=y", "foo"],
subcommand: Subcommand::Evaluate {
overrides: map!{"x": "y"},
variables: vec!["foo".to_owned()],
variable: Some("foo".to_owned()),
},
}
@ -1577,7 +1588,7 @@ ARGS:
args: ["--completions", "zsh", "foo"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--completions");
assert_eq!(subcommand, cmd::COMPLETIONS);
assert_eq!(arguments, &["foo"]);
},
}
@ -1587,7 +1598,7 @@ ARGS:
args: ["--list", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--list");
assert_eq!(subcommand, cmd::LIST);
assert_eq!(arguments, &["bar"]);
},
}
@ -1597,7 +1608,7 @@ ARGS:
args: ["--dump", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--dump");
assert_eq!(subcommand, cmd::DUMP);
assert_eq!(arguments, &["bar"]);
},
}
@ -1607,7 +1618,7 @@ ARGS:
args: ["--edit", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--edit");
assert_eq!(subcommand, cmd::EDIT);
assert_eq!(arguments, &["bar"]);
},
}
@ -1617,7 +1628,7 @@ ARGS:
args: ["--init", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--init");
assert_eq!(subcommand, cmd::INIT);
assert_eq!(arguments, &["bar"]);
},
}
@ -1627,7 +1638,7 @@ ARGS:
args: ["--show", "foo", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--show");
assert_eq!(subcommand, cmd::SHOW);
assert_eq!(arguments, &["bar"]);
},
}
@ -1637,7 +1648,7 @@ ARGS:
args: ["--summary", "bar"],
error: ConfigError::SubcommandArguments { subcommand, arguments },
check: {
assert_eq!(subcommand, "--summary");
assert_eq!(subcommand, cmd::SUMMARY);
assert_eq!(arguments, &["bar"]);
},
}
@ -1647,7 +1658,7 @@ ARGS:
args: ["--summary", "bar=baz", "bar"],
error: ConfigError::SubcommandOverridesAndArguments { subcommand, arguments, overrides },
check: {
assert_eq!(subcommand, "--summary");
assert_eq!(subcommand, cmd::SUMMARY);
assert_eq!(overrides, map!{"bar": "baz"});
assert_eq!(arguments, &["bar"]);
},
@ -1658,7 +1669,7 @@ ARGS:
args: ["--summary", "bar=baz"],
error: ConfigError::SubcommandOverrides { subcommand, overrides },
check: {
assert_eq!(subcommand, "--summary");
assert_eq!(subcommand, cmd::SUMMARY);
assert_eq!(overrides, map!{"bar": "baz"});
},
}

View File

@ -16,33 +16,33 @@ pub(crate) enum ConfigError {
))]
SearchDirConflict,
#[snafu(display(
"`{}` used with unexpected {}: {}",
subcommand,
"`--{}` used with unexpected {}: {}",
subcommand.to_lowercase(),
Count("argument", arguments.len()),
List::and_ticked(arguments)
))]
SubcommandArguments {
subcommand: String,
subcommand: &'static str,
arguments: Vec<String>,
},
#[snafu(display(
"`{}` used with unexpected overrides: {}; and arguments: {}",
subcommand,
"`--{}` used with unexpected overrides: {}; and arguments: {}",
subcommand.to_lowercase(),
List::and_ticked(overrides.iter().map(|(key, value)| format!("{}={}", key, value))),
List::and_ticked(arguments)))
]
SubcommandOverridesAndArguments {
subcommand: String,
subcommand: &'static str,
overrides: BTreeMap<String, String>,
arguments: Vec<String>,
},
#[snafu(display(
"`{}` used with unexpected overrides: {}",
subcommand,
"`--{}` used with unexpected overrides: {}",
subcommand.to_lowercase(),
List::and_ticked(overrides.iter().map(|(key, value)| format!("{}={}", key, value))),
))]
SubcommandOverrides {
subcommand: String,
subcommand: &'static str,
overrides: BTreeMap<String, String>,
},
}

View File

@ -28,7 +28,7 @@ impl<'src> Justfile<'src> {
self.recipes.len()
}
pub(crate) fn suggest(&self, input: &str) -> Option<Suggestion> {
pub(crate) fn suggest_recipe(&self, input: &str) -> Option<Suggestion> {
let mut suggestions = self
.recipes
.keys()
@ -54,6 +54,26 @@ impl<'src> Justfile<'src> {
.next()
}
pub(crate) fn suggest_variable(&self, input: &str) -> Option<Suggestion> {
let mut suggestions = self
.assignments
.keys()
.map(|name| {
(edit_distance(name, input), Suggestion {
name,
target: None,
})
})
.filter(|(distance, _suggestion)| distance < &3)
.collect::<Vec<(usize, Suggestion)>>();
suggestions.sort_by_key(|(distance, _suggestion)| *distance);
suggestions
.into_iter()
.map(|(_distance, suggestion)| suggestion)
.next()
}
pub(crate) fn run<'run>(
&'run self,
config: &'run Config,
@ -107,26 +127,24 @@ impl<'src> Justfile<'src> {
)?
};
if let Subcommand::Evaluate { variables, .. } = &config.subcommand {
if let Subcommand::Evaluate { variable, .. } = &config.subcommand {
if let Some(variable) = variable {
if let Some(value) = scope.value(variable) {
print!("{}", value);
} else {
return Err(RuntimeError::EvalUnknownVariable {
suggestion: self.suggest_variable(&variable),
variable: variable.to_owned(),
});
}
} else {
let mut width = 0;
for name in scope.names() {
if !variables.is_empty() && !variables.iter().any(|variable| variable == name) {
continue;
}
width = cmp::max(name.len(), width);
}
for binding in scope.bindings() {
if !variables.is_empty()
&& !variables
.iter()
.any(|variable| variable == binding.name.lexeme())
{
continue;
}
println!(
"{0:1$} := \"{2}\"",
binding.name.lexeme(),
@ -134,6 +152,7 @@ impl<'src> Justfile<'src> {
binding.value
);
}
}
return Ok(());
}
@ -186,7 +205,7 @@ impl<'src> Justfile<'src> {
if !missing.is_empty() {
let suggestion = if missing.len() == 1 {
self.suggest(missing.first().unwrap())
self.suggest_recipe(missing.first().unwrap())
} else {
None
};

View File

@ -25,6 +25,10 @@ pub(crate) enum RuntimeError<'src> {
Dotenv {
dotenv_error: dotenv::Error,
},
EvalUnknownVariable {
variable: String,
suggestion: Option<Suggestion<'src>>,
},
FunctionCall {
function: Name<'src>,
message: String,
@ -106,6 +110,15 @@ impl<'src> Display for RuntimeError<'src> {
write!(f, "{}", message.prefix())?;
match self {
EvalUnknownVariable {
variable,
suggestion,
} => {
write!(f, "Justfile does not contain variable `{}`.", variable,)?;
if let Some(suggestion) = *suggestion {
write!(f, "\n{}", suggestion)?;
}
},
UnknownRecipes {
recipes,
suggestion,

View File

@ -13,7 +13,7 @@ pub(crate) enum Subcommand {
Edit,
Evaluate {
overrides: BTreeMap<String, String>,
variables: Vec<String>,
variable: Option<String>,
},
Init,
List,

View File

@ -1,3 +1,5 @@
use crate::common::*;
test! {
name: evaluate,
justfile: r#"
@ -29,15 +31,49 @@ test! {
}
test! {
name: evaluate_arguments,
name: evaluate_multiple,
justfile: "
a := 'x'
b := 'y'
c := 'z'
",
args: ("--evaluate", "a", "c"),
stdout: r#"
a := "x"
c := "z"
"#,
stderr: "error: `--evaluate` used with unexpected argument: `c`\n",
status: EXIT_FAILURE,
}
test! {
name: evaluate_single,
justfile: "
a := 'x'
b := 'y'
c := 'z'
",
args: ("--evaluate", "b"),
stdout: "y",
}
test! {
name: evaluate_no_suggestion,
justfile: "
abc := 'x'
",
args: ("--evaluate", "aby"),
stderr: "
error: Justfile does not contain variable `aby`.
Did you mean `abc`?
",
status: EXIT_FAILURE,
}
test! {
name: evaluate_suggestion,
justfile: "
hello := 'x'
",
args: ("--evaluate", "goodbye"),
stderr: "
error: Justfile does not contain variable `goodbye`.
",
status: EXIT_FAILURE,
}