Skip to content

Commit

Permalink
Support multiple recipe search path arguments
Browse files Browse the repository at this point in the history
Allow multiple search-path recipe arguments if the recipe search path is
the same for all of them, and raise an error message if they have
conflicting search paths.
  • Loading branch information
neunenak committed Jan 4, 2023
1 parent a1ac579 commit 55667f3
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,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 {
overrides.insert(name.clone(), value.clone());
Expand Down
2 changes: 2 additions & 0 deletions src/config_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub(crate) enum ConfigError {
message
))]
Internal { message: String },
#[snafu(display("Conflicting path-prefixed recipes: `{}` and `{}`", seen, conflicting))]
MultipleSearchDir { seen: String, conflicting: String },
#[snafu(display(
"Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
))]
Expand Down
80 changes: 69 additions & 11 deletions src/positional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,41 +36,68 @@ pub struct Positional {
pub arguments: Vec<String>,
}

#[derive(Copy, Clone)]
enum ProcessingStep {
Overrides,
SearchDir,
Arguments,
}

impl Positional {
pub fn from_values<'values>(values: Option<impl IntoIterator<Item = &'values str>>) -> Self {
pub(crate) fn from_values<'values>(
values: Option<impl IntoIterator<Item = &'values str>>,
) -> Result<Self, ConfigError> {
let mut overrides = Vec::new();
let mut search_directory = None;
let mut arguments = Vec::new();

let mut processing_step = ProcessingStep::Overrides;

if let Some(values) = values {
for value in values {
if search_directory.is_none() && arguments.is_empty() {
if let ProcessingStep::Overrides = processing_step {
if let Some(o) = Self::override_from_value(value) {
overrides.push(o);
} else if value == "." || value == ".." {
continue;
}
processing_step = ProcessingStep::SearchDir;
}

if let ProcessingStep::SearchDir = processing_step {
if value == "." || value == ".." {
search_directory = Some(value.to_owned());
continue;
} else if let Some(i) = value.rfind('/') {
let (dir, tail) = value.split_at(i + 1);

search_directory = Some(dir.to_owned());
if let Some(ref sdir) = search_directory {
if sdir != dir {
return Err(ConfigError::MultipleSearchDir {
seen: sdir.clone(),
conflicting: dir.to_string(),
});
}
} else {
search_directory = Some(dir.to_owned());
}

if !tail.is_empty() {
arguments.push(tail.to_owned());
}
} else {
arguments.push(value.to_owned());
continue;
}
} else {
arguments.push(value.to_owned());
processing_step = ProcessingStep::Arguments;
}

arguments.push(value.to_owned());
}
}

Self {
Ok(Self {
overrides,
search_directory,
arguments,
}
})
}

/// Parse an override from a value of the form `NAME=.*`.
Expand Down Expand Up @@ -107,7 +134,7 @@ mod tests {
#[test]
fn $name() {
assert_eq! (
Positional::from_values(Some($vals.iter().cloned())),
Positional::from_values(Some($vals.iter().cloned())).unwrap(),
Positional {
overrides: $overrides
.iter()
Expand Down Expand Up @@ -225,4 +252,35 @@ mod tests {
search_directory: None,
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::MultipleSearchDir { seen, conflicting } if seen == "some/path/" && conflicting == "other/path/");
}
}

0 comments on commit 55667f3

Please sign in to comment.