Ignore additional search path arguments (#1528)

- Revert #1475
- Add a test that arguments with different path  prefixes are allowed
This commit is contained in:
Greg Shuflin 2023-01-24 20:25:17 -08:00 committed by Casey Rodarmor
parent 6ab6588549
commit 1a5f07c672
5 changed files with 24 additions and 171 deletions

View File

@ -2174,18 +2174,6 @@ $ (cd foo && just a b)
And will both invoke recipes `a` and `b` in `foo/justfile`. And will both invoke recipes `a` and `b` in `foo/justfile`.
For consistency, it possible to use path prefixes for all recipes:
```sh
$ just foo/a foo/b
```
But they must match:
```sh
$ just foo/a bar/b
error: Conflicting path arguments: `foo/` and `bar/`
```
### Include Directives ### Include Directives
The `!include` directive, currently unstable, can be used to include the The `!include` directive, currently unstable, can be used to include the

View File

@ -434,7 +434,7 @@ impl Config {
} }
} }
let positional = Positional::from_values(matches.values_of(arg::ARGUMENTS))?; let positional = Positional::from_values(matches.values_of(arg::ARGUMENTS));
for (name, value) in positional.overrides { for (name, value) in positional.overrides {
overrides.insert(name.clone(), value.clone()); overrides.insert(name.clone(), value.clone());

View File

@ -11,8 +11,6 @@ pub(crate) enum ConfigError {
message message
))] ))]
Internal { message: String }, Internal { message: String },
#[snafu(display("Conflicting path arguments: `{}` and `{}`", seen, conflicting))]
ConflictingSearchDirArgs { seen: String, conflicting: String },
#[snafu(display( #[snafu(display(
"Path-prefixed recipes may not be used with `--working-directory` or `--justfile`." "Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
))] ))]

View File

@ -36,75 +36,41 @@ pub struct Positional {
pub arguments: Vec<String>, pub arguments: Vec<String>,
} }
#[derive(Copy, Clone)]
enum ProcessingStep {
Overrides,
SearchDir,
Arguments,
}
impl Positional { impl Positional {
pub(crate) fn from_values<'values>( pub fn from_values<'values>(values: Option<impl IntoIterator<Item = &'values str>>) -> Self {
values: Option<impl IntoIterator<Item = &'values str>>,
) -> Result<Self, ConfigError> {
let mut overrides = Vec::new(); let mut overrides = Vec::new();
let mut search_directory = None; let mut search_directory = None;
let mut arguments = Vec::new(); let mut arguments = Vec::new();
let mut processing_step = ProcessingStep::Overrides;
if let Some(values) = values { if let Some(values) = values {
let mut values = values.into_iter().peekable(); for value in values {
while let Some(value) = values.peek() { if search_directory.is_none() && arguments.is_empty() {
let value = *value;
match processing_step {
ProcessingStep::Overrides => {
if let Some(o) = Self::override_from_value(value) { if let Some(o) = Self::override_from_value(value) {
overrides.push(o); overrides.push(o);
values.next(); } else if value == "." || value == ".." {
} else {
processing_step = ProcessingStep::SearchDir;
}
}
ProcessingStep::SearchDir => {
if value == "." || value == ".." {
search_directory = Some(value.to_owned()); search_directory = Some(value.to_owned());
values.next();
} else if let Some(i) = value.rfind('/') { } else if let Some(i) = value.rfind('/') {
let (dir, tail) = value.split_at(i + 1); let (dir, tail) = value.split_at(i + 1);
if let Some(ref seen) = search_directory {
if seen != dir {
return Err(ConfigError::ConflictingSearchDirArgs {
seen: seen.clone(),
conflicting: dir.into(),
});
}
} else {
search_directory = Some(dir.to_owned()); search_directory = Some(dir.to_owned());
}
if !tail.is_empty() { if !tail.is_empty() {
arguments.push(tail.to_owned()); arguments.push(tail.to_owned());
} }
values.next();
} else { } else {
processing_step = ProcessingStep::Arguments;
}
}
ProcessingStep::Arguments => {
arguments.push(value.to_owned()); arguments.push(value.to_owned());
values.next();
} }
} else {
arguments.push(value.to_owned());
} }
} }
} }
Ok(Self { Self {
overrides, overrides,
search_directory, search_directory,
arguments, arguments,
}) }
} }
/// Parse an override from a value of the form `NAME=.*`. /// Parse an override from a value of the form `NAME=.*`.
@ -141,7 +107,7 @@ mod tests {
#[test] #[test]
fn $name() { fn $name() {
assert_eq! ( assert_eq! (
Positional::from_values(Some($vals.iter().cloned())).unwrap(), Positional::from_values(Some($vals.iter().cloned())),
Positional { Positional {
overrides: $overrides overrides: $overrides
.iter() .iter()
@ -259,35 +225,4 @@ mod tests {
search_directory: None, search_directory: None,
arguments: ["a", "a=b"], arguments: ["a", "a=b"],
} }
test! {
name: search_dir_and_recipe_only,
values: ["some/path/recipe_a"],
overrides: [],
search_directory: Some("some/path/"),
arguments: ["recipe_a"],
}
test! {
name: multiple_same_valued_search_directories,
values: ["some/path/recipe_a", "some/path/recipe_b"],
overrides: [],
search_directory: Some("some/path/"),
arguments: ["recipe_a", "recipe_b"],
}
#[test]
fn invalid_multiple_search_paths() {
let err = Positional::from_values(Some(
[
"some/path/recipe_a",
"some/path/recipe_b",
"other/path/recipe_c",
]
.iter()
.copied(),
))
.unwrap_err();
assert_matches!(err, ConfigError::ConflictingSearchDirArgs { seen, conflicting } if seen == "some/path/" && conflicting == "other/path/");
}
} }

View File

@ -1,77 +1,9 @@
use super::*; use super::*;
#[test] #[test]
fn same_path_argument() { fn argument_with_different_path_prefix_is_allowed() {
let justfile_contents = unindent( Test::new()
r#" .justfile("foo bar:")
recipe_a: .args(["./foo", "../bar"])
echo "A" .run();
recipe_b:
echo "B"
"#,
);
let tmp = temptree! {
subdir: {
justfile: justfile_contents
}
};
for arg_list in [
["subdir/recipe_a", "recipe_b"],
["subdir/recipe_a", "subdir/recipe_b"],
] {
let mut command = Command::new(executable_path("just"));
command.current_dir(tmp.path());
for arg in arg_list {
command.arg(arg);
}
let output = command.output().unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(output.status.success());
assert_eq!(stdout, "A\nB\n");
}
}
#[test]
fn different_path_arguments() {
let justfile_contents1 = unindent(
r#"
recipe_a:
echo "A"
"#,
);
let justfile_contents2 = unindent(
r#"
recipe_b:
echo "B"
"#,
);
let tmp = temptree! {
subdir: {
justfile: justfile_contents1
},
subdir2: {
justfile: justfile_contents2
}
};
let output = Command::new(executable_path("just"))
.current_dir(tmp.path())
.arg("subdir/recipe_a")
.arg("subdir2/recipe_b")
.output()
.unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();
assert_eq!(
stderr,
"error: Conflicting path arguments: `subdir/` and `subdir2/`\n"
);
} }