From c86df421949be040dc824b843b14df4a9f8672e8 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Tue, 17 Dec 2024 19:39:02 +0100 Subject: [PATCH] Add method for parsing namespaced names This adds the new `Purl::builder_with_combined_name` function that allows parsing package names that include the namespace in their name. The parsing is done according to its package type, which is why this function is only implemented for `Purl` rather than `GenericPurl`. --- purl/src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/purl/src/lib.rs b/purl/src/lib.rs index 142aa3b..757dc62 100644 --- a/purl/src/lib.rs +++ b/purl/src/lib.rs @@ -321,6 +321,41 @@ impl GenericPurl { } } +impl Purl { + /// Create a new [`PurlBuilder`] with a combined name and namespace. + pub fn builder_with_combined_name( + package_type: PackageType, + namespaced_name: S, + ) -> PurlBuilder + where + S: Into, + { + // Split apart namespace and name based on ecosystem. + let namespaced_name = namespaced_name.into(); + let (namespace, name) = match package_type { + PackageType::Cargo | PackageType::Gem | PackageType::NuGet | PackageType::PyPI => { + (None, namespaced_name) + }, + PackageType::Golang | PackageType::Npm => match namespaced_name.rsplit_once('/') { + Some((namespace, name)) => (Some(namespace), name.into()), + None => (None, namespaced_name), + }, + PackageType::Maven => match namespaced_name.split_once(':') { + Some((namespace, name)) => (Some(namespace), name.into()), + None => (None, namespaced_name), + }, + }; + + // Create the PURL builder. + let mut builder = GenericPurlBuilder::new(package_type, name); + if let Some(namespace) = namespace { + builder = builder.with_namespace(namespace); + } + + builder + } +} + /// Check whether a package type string is valid according to the rules of the /// PURL spec. #[must_use] @@ -554,4 +589,18 @@ mod tests { let purl = GenericPurl::new(Cow::Borrowed("type"), "name").unwrap(); assert_eq!(None, purl.subpath()); } + + #[test] + fn namespaced_name() { + let purl = + Purl::builder_with_combined_name(PackageType::Npm, "@angular/cli").build().unwrap(); + assert_eq!(purl.namespace(), Some("@angular")); + assert_eq!(purl.name(), "cli"); + + let purl = Purl::builder_with_combined_name(PackageType::Maven, "org.maven.plugins:pom") + .build() + .unwrap(); + assert_eq!(purl.namespace(), Some("org.maven.plugins")); + assert_eq!(purl.name(), "pom"); + } }