229 lines
6.0 KiB
Rust
229 lines
6.0 KiB
Rust
use super::*;
|
|
|
|
/// A struct containing the parsed representation of positional command-line
|
|
/// arguments, i.e. arguments that are not flags, options, or the subcommand.
|
|
///
|
|
/// The DSL of positional arguments is fairly complex and mostly accidental.
|
|
/// There are three possible components: overrides, a search directory, and the
|
|
/// rest:
|
|
///
|
|
/// - Overrides are of the form `NAME=.*`
|
|
///
|
|
/// - After overrides comes a single optional search directory argument. This is
|
|
/// either '.', '..', or an argument that contains a `/`.
|
|
///
|
|
/// If the argument contains a `/`, everything before and including the slash
|
|
/// is the search directory, and everything after is added to the rest.
|
|
///
|
|
/// - Everything else is an argument.
|
|
///
|
|
/// Overrides set the values of top-level variables in the justfile being
|
|
/// invoked and are a convenient way to override settings.
|
|
///
|
|
/// For modes that do not take other arguments, the search directory argument
|
|
/// determines where to begin searching for the justfile. This allows command
|
|
/// lines like `just -l ..` and `just ../build` to find the same justfile.
|
|
///
|
|
/// For modes that do take other arguments, the search argument is simply
|
|
/// prepended to rest.
|
|
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
|
pub struct Positional {
|
|
/// Overrides from values of the form `[a-zA-Z_][a-zA-Z0-9_-]*=.*`
|
|
pub overrides: Vec<(String, String)>,
|
|
/// An argument equal to '.', '..', or ending with `/`
|
|
pub search_directory: Option<String>,
|
|
/// Everything else
|
|
pub arguments: Vec<String>,
|
|
}
|
|
|
|
impl Positional {
|
|
pub fn from_values<'values>(values: Option<impl IntoIterator<Item = &'values str>>) -> Self {
|
|
let mut overrides = Vec::new();
|
|
let mut search_directory = None;
|
|
let mut arguments = Vec::new();
|
|
|
|
if let Some(values) = values {
|
|
for value in values {
|
|
if search_directory.is_none() && arguments.is_empty() {
|
|
if let Some(o) = Self::override_from_value(value) {
|
|
overrides.push(o);
|
|
} else if value == "." || value == ".." {
|
|
search_directory = Some(value.to_owned());
|
|
} else if let Some(i) = value.rfind('/') {
|
|
let (dir, tail) = value.split_at(i + 1);
|
|
|
|
search_directory = Some(dir.to_owned());
|
|
|
|
if !tail.is_empty() {
|
|
arguments.push(tail.to_owned());
|
|
}
|
|
} else {
|
|
arguments.push(value.to_owned());
|
|
}
|
|
} else {
|
|
arguments.push(value.to_owned());
|
|
}
|
|
}
|
|
}
|
|
|
|
Self {
|
|
overrides,
|
|
search_directory,
|
|
arguments,
|
|
}
|
|
}
|
|
|
|
/// Parse an override from a value of the form `NAME=.*`.
|
|
fn override_from_value(value: &str) -> Option<(String, String)> {
|
|
let equals = value.find('=')?;
|
|
|
|
let (identifier, equals_value) = value.split_at(equals);
|
|
|
|
// exclude `=` from value
|
|
let value = &equals_value[1..];
|
|
|
|
if Lexer::is_identifier(identifier) {
|
|
Some((identifier.to_owned(), value.to_owned()))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
macro_rules! test {
|
|
{
|
|
name: $name:ident,
|
|
values: $vals:expr,
|
|
overrides: $overrides:expr,
|
|
search_directory: $search_directory:expr,
|
|
arguments: $arguments:expr,
|
|
} => {
|
|
#[test]
|
|
fn $name() {
|
|
assert_eq! (
|
|
Positional::from_values(Some($vals.iter().cloned())),
|
|
Positional {
|
|
overrides: $overrides
|
|
.iter()
|
|
.cloned()
|
|
.map(|(key, value): (&str, &str)| (key.to_owned(), value.to_owned()))
|
|
.collect(),
|
|
search_directory: $search_directory.map(str::to_owned),
|
|
arguments: $arguments.iter().cloned().map(str::to_owned).collect(),
|
|
},
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
test! {
|
|
name: no_values,
|
|
values: [],
|
|
overrides: [],
|
|
search_directory: None,
|
|
arguments: [],
|
|
}
|
|
|
|
test! {
|
|
name: arguments_only,
|
|
values: ["foo", "bar"],
|
|
overrides: [],
|
|
search_directory: None,
|
|
arguments: ["foo", "bar"],
|
|
}
|
|
|
|
test! {
|
|
name: all_overrides,
|
|
values: ["foo=bar", "bar=foo"],
|
|
overrides: [("foo", "bar"), ("bar", "foo")],
|
|
search_directory: None,
|
|
arguments: [],
|
|
}
|
|
|
|
test! {
|
|
name: override_not_name,
|
|
values: ["foo=bar", "bar.=foo"],
|
|
overrides: [("foo", "bar")],
|
|
search_directory: None,
|
|
arguments: ["bar.=foo"],
|
|
}
|
|
|
|
test! {
|
|
name: no_overrides,
|
|
values: ["the-dir/", "baz", "bzzd"],
|
|
overrides: [],
|
|
search_directory: Some("the-dir/"),
|
|
arguments: ["baz", "bzzd"],
|
|
}
|
|
|
|
test! {
|
|
name: no_search_directory,
|
|
values: ["foo=bar", "bar=foo", "baz", "bzzd"],
|
|
overrides: [("foo", "bar"), ("bar", "foo")],
|
|
search_directory: None,
|
|
arguments: ["baz", "bzzd"],
|
|
}
|
|
|
|
test! {
|
|
name: no_arguments,
|
|
values: ["foo=bar", "bar=foo", "the-dir/"],
|
|
overrides: [("foo", "bar"), ("bar", "foo")],
|
|
search_directory: Some("the-dir/"),
|
|
arguments: [],
|
|
}
|
|
|
|
test! {
|
|
name: all_dot,
|
|
values: ["foo=bar", "bar=foo", ".", "garnor"],
|
|
overrides: [("foo", "bar"), ("bar", "foo")],
|
|
search_directory: Some("."),
|
|
arguments: ["garnor"],
|
|
}
|
|
|
|
test! {
|
|
name: all_dot_dot,
|
|
values: ["foo=bar", "bar=foo", "..", "garnor"],
|
|
overrides: [("foo", "bar"), ("bar", "foo")],
|
|
search_directory: Some(".."),
|
|
arguments: ["garnor"],
|
|
}
|
|
|
|
test! {
|
|
name: all_slash,
|
|
values: ["foo=bar", "bar=foo", "/", "garnor"],
|
|
overrides: [("foo", "bar"), ("bar", "foo")],
|
|
search_directory: Some("/"),
|
|
arguments: ["garnor"],
|
|
}
|
|
|
|
test! {
|
|
name: search_directory_after_argument,
|
|
values: ["foo=bar", "bar=foo", "baz", "bzzd", "bar/"],
|
|
overrides: [("foo", "bar"), ("bar", "foo")],
|
|
search_directory: None,
|
|
arguments: ["baz", "bzzd", "bar/"],
|
|
}
|
|
|
|
test! {
|
|
name: override_after_search_directory,
|
|
values: ["..", "a=b"],
|
|
overrides: [],
|
|
search_directory: Some(".."),
|
|
arguments: ["a=b"],
|
|
}
|
|
|
|
test! {
|
|
name: override_after_argument,
|
|
values: ["a", "a=b"],
|
|
overrides: [],
|
|
search_directory: None,
|
|
arguments: ["a", "a=b"],
|
|
}
|
|
}
|