diff --git a/src/utils/glob.rs b/src/utils/glob.rs index 0185f8b..0e42d73 100644 --- a/src/utils/glob.rs +++ b/src/utils/glob.rs @@ -1,10 +1,9 @@ use glob::glob; use itertools::Itertools; -use std::env::set_current_dir; use std::path::PathBuf; use crate::errors::{Error, Paths}; -use crate::utils::path::to_absolute_path; +use crate::utils::path::{run_in_base_dir, to_absolute_path}; const NEGATE: char = '!'; @@ -13,45 +12,39 @@ pub fn collect( patterns: Option>, enable_negate: bool, ) -> Vec { - let mut entries = Vec::::new(); - for pattern in patterns.unwrap_or_default() { - if let (Some(matched), negate) = resolve_glob(&base_dir, pattern, enable_negate) { - if negate { - entries = entries - .iter() - .filter_map(|entry| { - if matched.iter().any(|p| entry.starts_with(p)) { - None - } else { - Some(entry) - } - }) - .cloned() - .collect::>(); - } else { - entries.extend(matched); + let collect = || { + let mut entries = Vec::::new(); + for pattern in patterns.unwrap_or_default() { + if let (Some(matched), negate) = resolve_glob(pattern, enable_negate) { + if negate { + entries = entries + .iter() + .filter_map(|entry| { + if matched.iter().any(|p| entry.starts_with(p)) { + None + } else { + Some(entry) + } + }) + .cloned() + .collect::>(); + } else { + entries.extend(matched); + } } } - } - let result = entries - .iter() - .unique() - .filter_map(|entry| to_absolute_path(entry).ok()) - .collect::>(); - result + let result = entries + .iter() + .unique() + .filter_map(|entry| to_absolute_path(entry).ok()) + .collect::>(); + result + }; + + run_in_base_dir(&base_dir, collect, None) } -fn resolve_glob( - base_dir: &PathBuf, - pattern: String, - enable_negate: bool, -) -> (Option>, bool) { - if let Err(error) = set_current_dir(&base_dir) { - Error::NotAccessibleError(Paths::One(base_dir.clone())) - .log_debug(error) - .log_warn(None); - return (None, false); - } +fn resolve_glob(pattern: String, enable_negate: bool) -> (Option>, bool) { let (pattern, negate) = parse_negate(pattern, enable_negate); match glob(&pattern) { Ok(entries) => { diff --git a/src/utils/path.rs b/src/utils/path.rs index 0d7b425..b1165a5 100644 --- a/src/utils/path.rs +++ b/src/utils/path.rs @@ -1,6 +1,7 @@ use path_clean::PathClean; use std::{ - env::current_dir, + env::{current_dir, set_current_dir}, + io, path::{Path, PathBuf}, }; @@ -22,3 +23,37 @@ pub fn to_absolute_path>(path: T) -> Result { }; Ok(PathClean::clean(&absolute_path)) } + +/// Run a function `f` in the base directory `base_dir`, and go back to the original cwd. +pub fn run_in_base_dir(base_dir: T, f: F, fallback: Option) -> F::Output +where + T: AsRef, + F: FnOnce() -> U, + U: Default, +{ + let on_io_error = |error: io::Error| { + Error::NotAccessibleError(Paths::One(base_dir.as_ref().to_path_buf())) + .log_debug(&error) + .log_error(None); + fallback.unwrap_or_default() + }; + + let cwd = match current_dir() { + Ok(cwd) => cwd, + Err(error) => { + return on_io_error(error); + } + }; + + if let Err(error) = set_current_dir(&base_dir) { + return on_io_error(error); + }; + + let result = f(); + + if let Err(error) = set_current_dir(&cwd) { + on_io_error(error); + } + + result +} diff --git a/src/workspaces.rs b/src/workspaces.rs index 5441f35..5b51e93 100644 --- a/src/workspaces.rs +++ b/src/workspaces.rs @@ -88,3 +88,52 @@ impl PnpmWorkspace { Err(Error::NoEntryError(Paths::Multiple(file_paths.to_vec()))) } } + +#[cfg(test)] +mod tests { + use crate::test_each_serial; + + use super::*; + + struct NewTestCase { + input: (PathBuf, PackageManagerKind, Option>), + expected: Workspaces, + } + + fn test_new_each(case: NewTestCase) { + let base_dir = case.input.0; + let workspaces = Workspaces::new(base_dir.clone(), case.input.1, case.input.2); + assert_eq!(workspaces.kind, case.expected.kind); + assert_eq!( + workspaces.packages, + case + .expected + .packages + .iter() + .map(|path| base_dir.join(path).canonicalize().unwrap()) + .collect::>() + ); + } + + test_each_serial!( + test_new, + "bun" => NewTestCase { + input: ( + PathBuf::from("tests/fixtures/workspaces/bun"), + PackageManagerKind::Bun, + Some(vec![ + String::from("packages/*"), + String::from("!packages/c"), + ]), + ), + expected: Workspaces { + kind: PackageManagerKind::Bun, + packages: vec![ + PathBuf::from("./packages/a"), + PathBuf::from("./packages/b"), + PathBuf::from("./packages/c"), + ], + }, + }, + ); +} diff --git a/tests/fixtures/workspaces/bun/bun.lockb b/tests/fixtures/workspaces/bun/bun.lockb new file mode 100755 index 0000000..da2a398 Binary files /dev/null and b/tests/fixtures/workspaces/bun/bun.lockb differ diff --git a/tests/fixtures/workspaces/bun/package.json b/tests/fixtures/workspaces/bun/package.json new file mode 100644 index 0000000..ce1e347 --- /dev/null +++ b/tests/fixtures/workspaces/bun/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "typescript": "^5.3.3" + }, + "workspaces": [ + "packages/*", + "packages/c" + ] +} diff --git a/tests/fixtures/workspaces/bun/packages/a/package.json b/tests/fixtures/workspaces/bun/packages/a/package.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/fixtures/workspaces/bun/packages/a/package.json @@ -0,0 +1 @@ +{} diff --git a/tests/fixtures/workspaces/bun/packages/b/package.json b/tests/fixtures/workspaces/bun/packages/b/package.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/fixtures/workspaces/bun/packages/b/package.json @@ -0,0 +1 @@ +{} diff --git a/tests/fixtures/workspaces/bun/packages/c/package.json b/tests/fixtures/workspaces/bun/packages/c/package.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/fixtures/workspaces/bun/packages/c/package.json @@ -0,0 +1 @@ +{}