From 7f4af7c19c8ee0e8e2b6d3db774df5fe54aab7c4 Mon Sep 17 00:00:00 2001 From: maciektr Date: Wed, 27 Sep 2023 18:43:14 +0200 Subject: [PATCH] Build packages filter commit-id:d571a145 --- utils/scarb-ui/src/args/packages_filter.rs | 108 ++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/utils/scarb-ui/src/args/packages_filter.rs b/utils/scarb-ui/src/args/packages_filter.rs index b76fcf504..a863e0362 100644 --- a/utils/scarb-ui/src/args/packages_filter.rs +++ b/utils/scarb-ui/src/args/packages_filter.rs @@ -1,4 +1,5 @@ use std::collections::HashSet; +use std::ffi::OsString; use std::fmt; use anyhow::{bail, ensure, Result}; @@ -7,6 +8,8 @@ use indoc::{formatdoc, indoc}; use scarb_metadata::{Metadata, PackageMetadata}; +const PACKAGES_FILTER_DELIMITER: char = ','; + /// [`clap`] structured arguments that provide package selection. /// /// ## Usage @@ -27,7 +30,7 @@ pub struct PackagesFilter { short, long, default_value = "*", - value_delimiter = ',', + value_delimiter = PACKAGES_FILTER_DELIMITER, value_name = "SPEC", env = "SCARB_PACKAGES_FILTER" )] @@ -67,7 +70,7 @@ impl PackagesFilter { .iter() .map(|s| s.to_string()) .collect::>() - .join(","); + .join(PACKAGES_FILTER_DELIMITER.to_string().as_str()); let found = Self::do_match_all::(specs, self.workspace, members)?; ensure!( @@ -99,6 +102,34 @@ impl PackagesFilter { Self::do_match_all::(specs, self.workspace, members) } + /// Generate a new [`PackagesFilter`] for the given slice of packages. + /// + /// This is useful when you want to build an env filter from matched packages. + /// See [`PackagesFilter::to_env`] for more details. + pub fn generate_for<'a, S: PackagesSource>( + packages: impl Iterator, + ) -> Self + where + S::Package: 'a, + { + let names: Vec = packages + .map(|p| S::package_name_of(p).to_string()) + .collect(); + Self { + package: names, + workspace: false, + } + } + + /// Get the packages filter as an [`OsString`]. + /// + /// This value can be passed as `SCARB_PACKAGES_FILTER` variable to child processes. + pub fn to_env(self) -> OsString { + self.package + .join(PACKAGES_FILTER_DELIMITER.to_string().as_str()) + .into() + } + fn package_specs(&self) -> Result> { let specs = self .package @@ -280,3 +311,76 @@ impl PackagesSource for Metadata { } } } + +#[cfg(test)] +mod tests { + use crate::args::{PackagesFilter, PackagesSource, WithManifestPath}; + use camino::{Utf8Path, Utf8PathBuf}; + + #[derive(Clone)] + struct MockPackage { + pub name: String, + pub manifest_path: Utf8PathBuf, + } + + impl WithManifestPath for MockPackage { + fn manifest_path(&self) -> &Utf8Path { + &self.manifest_path + } + } + + struct MockSource { + pub members: Vec, + } + + impl PackagesSource for MockSource { + type Package = MockPackage; + + fn package_name_of(package: &Self::Package) -> &str { + package.name.as_str() + } + + fn members(&self) -> Vec { + self.members.clone() + } + + fn runtime_manifest(&self) -> Utf8PathBuf { + Utf8PathBuf::from("runtime") + } + } + + fn mock_package(name: &str) -> MockPackage { + MockPackage { + name: name.into(), + manifest_path: Utf8PathBuf::from(format!("package/{}", name)), + } + } + + fn mock_packages(names: Vec<&str>) -> Vec { + names.into_iter().map(mock_package).collect() + } + + #[test] + fn can_build_packages_filter() { + let mock = MockSource { + members: mock_packages(vec!["first", "second"]), + }; + let filter = PackagesFilter { + package: vec!["first".into()], + workspace: false, + }; + let packages = filter.match_many(&mock).unwrap(); + let filter = PackagesFilter::generate_for::(packages.iter()); + let filter = filter.to_env(); + assert_eq!(filter, "first"); + + let filter = PackagesFilter { + package: vec!["*".into()], + workspace: false, + }; + let packages = filter.match_many(&mock).unwrap(); + let filter = PackagesFilter::generate_for::(packages.iter()); + let filter = filter.to_env(); + assert_eq!(filter, "first,second"); + } +}