diff --git a/src/recipe/parser/glob_vec.rs b/src/recipe/parser/glob_vec.rs index 3bf11c2a..fc9d209c 100644 --- a/src/recipe/parser/glob_vec.rs +++ b/src/recipe/parser/glob_vec.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter}; use std::ops::Deref; use std::path::Path; -use globset::{Glob, GlobSet}; +use globset::{Glob, GlobBuilder, GlobSet}; use serde::ser::{SerializeMap, SerializeSeq}; use serde::{Deserialize, Serialize}; @@ -14,12 +14,24 @@ use crate::recipe::custom_yaml::{ }; use crate::recipe::error::{ErrorKind, PartialParsingError}; +#[derive(Debug, Clone, PartialEq, Eq)] +struct GlobWithSource { + glob: Glob, + source: String, +} + +impl GlobWithSource { + pub fn glob(&self) -> &str { + &self.glob.glob() + } +} + /// Wrapper type to simplify serialization of Vec #[derive(Debug, Clone, PartialEq, Eq, Default)] -struct InnerGlobVec(Vec); +struct InnerGlobVec(Vec); impl Deref for InnerGlobVec { - type Target = Vec; + type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 @@ -30,7 +42,7 @@ impl InnerGlobVec { fn globset(&self) -> Result { let mut globset_builder = globset::GlobSetBuilder::new(); for glob in self.iter() { - globset_builder.add(glob.clone()); + globset_builder.add(glob.glob.clone()); } globset_builder.build() } @@ -46,18 +58,29 @@ impl From> for InnerGlobVec { } } -impl From> for InnerGlobVec { - fn from(vec: Vec) -> Self { +impl From> for InnerGlobVec { + fn from(vec: Vec) -> Self { Self(vec) } } -fn to_glob(glob: &str) -> Result { - if glob.ends_with('/') && !glob.contains('*') { +fn to_glob(glob: &str) -> Result { + // first, try to parse as a normal glob so that we get a descriptive error + let _ = Glob::new(glob)?; + if glob.ends_with('/') { // we treat folders as globs that match everything in the folder - Glob::new(&format!("{}**", glob)) + Ok(GlobWithSource { + glob: Glob::new(&format!("{glob}**"))?, + source: glob.to_string(), + }) } else { - Glob::new(glob) + // Match either file, or folder + Ok(GlobWithSource { + glob: GlobBuilder::new(&format!("{glob}{{,/**}}")) + .empty_alternates(true) + .build()?, + source: glob.to_string(), + }) } } @@ -83,7 +106,7 @@ impl Serialize for InnerGlobVec { fn serialize(&self, serializer: S) -> Result { let mut seq = serializer.serialize_seq(Some(self.len()))?; for glob in self.iter() { - seq.serialize_element(glob.glob())?; + seq.serialize_element(&glob.source)?; } seq.end() } @@ -105,7 +128,7 @@ impl Serialize for GlobVec { impl Debug for GlobVec { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_list() - .entries(self.include.iter().map(|glob| glob.glob())) + .entries(self.include.iter().map(|glob| glob.glob.glob())) .finish() } } @@ -155,12 +178,12 @@ impl GlobVec { } /// Returns an iterator over the globs - pub fn include_globs(&self) -> &Vec { + pub fn include_globs(&self) -> &Vec { &self.include } /// Returns an iterator over the globs - pub fn exclude_globs(&self) -> &Vec { + pub fn exclude_globs(&self) -> &Vec { &self.exclude } @@ -181,11 +204,12 @@ impl GlobVec { /// Only used for testing #[cfg(test)] pub fn from_vec(include: Vec<&str>, exclude: Option>) -> Self { - let include_vec: Vec = include + let include_vec: Vec = include .into_iter() .map(|glob| to_glob(glob).unwrap()) .collect(); - let exclude_vec: Vec = exclude + + let exclude_vec: Vec = exclude .unwrap_or_default() .into_iter() .map(|glob| to_glob(glob).unwrap()) @@ -423,6 +447,13 @@ mod tests { assert!(globvec.is_match(Path::new("foo/bla/bar"))); assert!(!globvec.is_match(Path::new("bar"))); assert!(!globvec.is_match(Path::new("bla"))); + + let globvec = GlobVec::from_vec(vec!["foo"], None); + assert!(globvec.is_match(Path::new("foo/bar"))); + assert!(globvec.is_match(Path::new("foo/bla"))); + assert!(globvec.is_match(Path::new("foo/bla/bar"))); + assert!(!globvec.is_match(Path::new("bar"))); + assert!(!globvec.is_match(Path::new("bla"))); } #[test] diff --git a/src/recipe/parser/snapshots/rattler_build__recipe__parser__glob_vec__tests__parsing_all_or_globvec.snap b/src/recipe/parser/snapshots/rattler_build__recipe__parser__glob_vec__tests__parsing_all_or_globvec.snap index 9cb2174a..14687916 100644 --- a/src/recipe/parser/snapshots/rattler_build__recipe__parser__glob_vec__tests__parsing_all_or_globvec.snap +++ b/src/recipe/parser/snapshots/rattler_build__recipe__parser__glob_vec__tests__parsing_all_or_globvec.snap @@ -3,7 +3,6 @@ source: src/recipe/parser/glob_vec.rs expression: "&as_yaml" --- globs: -- foo -- bar -- baz/**/qux - +- foo{,/**} +- bar{,/**} +- baz/**/qux{,/**} diff --git a/src/recipe/parser/snapshots/rattler_build__recipe__parser__glob_vec__tests__parsing_globvec.snap b/src/recipe/parser/snapshots/rattler_build__recipe__parser__glob_vec__tests__parsing_globvec.snap index 04a52162..e7557f1a 100644 --- a/src/recipe/parser/snapshots/rattler_build__recipe__parser__glob_vec__tests__parsing_globvec.snap +++ b/src/recipe/parser/snapshots/rattler_build__recipe__parser__glob_vec__tests__parsing_globvec.snap @@ -2,7 +2,6 @@ source: src/recipe/parser/glob_vec.rs expression: "&as_yaml" --- -- foo -- bar -- baz/**/qux - +- foo{,/**} +- bar{,/**} +- baz/**/qux{,/**}