Skip to content

Commit 8821a07

Browse files
JonoYanghelio-frotamiluxjcrossley3ctron
authored
chore: update purl-spec submodule to latest commit on main branch and modify code accordingly (#2)
* chore: fix cargo metadata link * fix!: Enforce namespace restrictions and error handling for specific package types * refactor!: Use `Result` for all `with_` methods, bump version * chore: update deps * chore: fix formatting * fix: introduce 2nd encode set to handle encoded slashes in names Fixes: scm-rs#28 If upstream decides that slashes in qualifiers should be encoded to %2F, then we can remove the name-specific encode set and `.add(b'/')` to the ENCODE_SET constant. * fix: clippy * chore: release 0.6.0-rc.2 * chore: prepare for 0.6.0 Also switch to edition 2024 for the release, this should make it easier working with dependencies. * build: uptick MSRV because of edition 2024 * style: apply cargo fmt 2024 * test: run tests using reference data from purl-spec Signed-off-by: Keshav Priyadarshi <git@keshav.space> * chore: update purl-spec submodule and address issues from that Signed-off-by: Jono Yang <jyang@nexb.com> * chore: enable certain disabled tests and make changes to pass Signed-off-by: Jono Yang <jyang@nexb.com> --------- Signed-off-by: Keshav Priyadarshi <git@keshav.space> Signed-off-by: Jono Yang <jyang@nexb.com> Co-authored-by: Helio Frota <00hf11@gmail.com> Co-authored-by: Michael Lux <michael.lux@aisec.fraunhofer.de> Co-authored-by: Jim Crossley <jim@crossleys.org> Co-authored-by: Jens Reimann <ctron@dentrassi.de> Co-authored-by: Keshav Priyadarshi <git@keshav.space>
1 parent 9e18074 commit 8821a07

File tree

9 files changed

+69
-41
lines changed

9 files changed

+69
-41
lines changed

Cargo.toml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,34 @@
11
[package]
22
name = "packageurl"
3-
version = "0.5.0"
4-
edition = "2021"
3+
version = "0.6.0"
4+
edition = "2024"
55
authors = [
66
"Martin Larralde <martin.larralde@embl.de>",
77
"Jens Reimann <ctron@dentrassi.de>",
88
]
99
license = "MIT"
1010
description = "Rust implementation of the package url specification"
1111
documentation = "https://docs.rs/packageurl"
12-
repository = "https://github.com/scm-rs/packageurl-rs"
12+
repository = "https://github.com/scm-rs/packageurl.rs"
1313
readme = "README.md"
1414
keywords = ["purl", "package-url"]
1515
categories = ["parser-implementations", "encoding", "development-tools"]
16-
rust-version = "1.82.0"
16+
rust-version = "1.85.0"
1717

1818
[dependencies]
19-
percent-encoding = "2.1.0"
20-
thiserror = "2.0.12"
19+
percent-encoding = "2"
20+
thiserror = "2"
2121

22-
memchr = { version = "2.4.0", optional = true }
23-
serde = { version = "1.0.0", optional = true, features = ["derive"] }
22+
memchr = { version = "2", optional = true }
23+
serde = { version = "1", optional = true, features = ["derive"] }
2424

2525
[features]
2626
default = []
2727

2828
[dev-dependencies]
29-
criterion = "0.5.1"
30-
rstest = "0.25.0"
31-
serde = { version = "1.0.0", features = ["derive"] }
32-
serde_json = "1.0.13"
29+
criterion = "0.7"
30+
serde = { version = "1", features = ["derive"] }
31+
serde_json = "1"
3332
url = "2"
3433

3534
[[bench]]

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#
22

3-
`packageurl-rs` [![Star me](https://img.shields.io/github/stars/scm-rs/packageurl.rs.svg?style=social&label=Star)](https://github.com/scm-rs/packageurl.rs/stargazers)
3+
`packageurl.rs` [![Star me](https://img.shields.io/github/stars/scm-rs/packageurl.rs.svg?style=social&label=Star)](https://github.com/scm-rs/packageurl.rs/stargazers)
44

55
*Read and generate Package URLs in Rust.*
66

@@ -74,7 +74,7 @@ a changelog as part of the [GitHub releases](https://github.com/scm-rs/packageur
7474
## 💭 Feedback
7575

7676
Found a bug? Have an enhancement request? Head over to the
77-
[GitHub issue tracker](https://github.com/scm-rs/packageurl-rs/issues) of the project if
77+
[GitHub issue tracker](https://github.com/scm-rs/packageurl.rs/issues) of the project if
7878
you need to report or ask something. If you are filling in on a bug, please include as much
7979
information as you can about the issue, and try to recreate the same bug in a simple, easily
8080
reproducible situation.

benches/bench.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use criterion::{criterion_group, criterion_main, Criterion};
1+
use criterion::{Criterion, criterion_group, criterion_main};
22
use packageurl::PackageUrl;
33
use std::str::FromStr;
44

src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ pub enum Error {
1212
InvalidKey(String),
1313
#[error("missing name")]
1414
MissingName,
15+
#[error("no namespace allowed for type {0:?}")]
16+
TypeProhibitsNamespace(String),
1517
#[error("invalid namespace component: {0:?}")]
1618
InvalidNamespaceComponent(String),
1719
#[error("missing scheme")]

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
//! [`FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html
3434
//! [`PackageUrl`]: example_generated/struct.PackageUrl.html
3535
//! [`'static`]: https://doc.rust-lang.org/reference/items/static-items.html#static-lifetime-elision
36-
#![doc(issue_tracker_base_url = "https://github.com/althonos/packageurl-rs/issues/")]
36+
#![doc(issue_tracker_base_url = "https://github.com/althonos/packageurl.rs/issues/")]
3737

3838
mod errors;
3939
mod parser;

src/purl.rs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
1212
use super::errors::Error;
1313
use super::errors::Result;
1414
use super::parser;
15-
use super::utils::{to_lowercase, PercentCodec};
15+
use super::utils::{PercentCodec, to_lowercase};
1616
use super::validation;
1717

1818
const ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
@@ -26,8 +26,6 @@ const ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
2626
.add(b'?')
2727
.add(b'{')
2828
.add(b'}')
29-
// .add(b'/')
30-
// .add(b':')
3129
.add(b';')
3230
.add(b'=')
3331
.add(b'+')
@@ -36,7 +34,8 @@ const ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
3634
.add(b'[')
3735
.add(b']')
3836
.add(b'^')
39-
.add(b'|');
37+
.add(b'|')
38+
.add(b'/');
4039

4140
/// A Package URL.
4241
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -66,7 +65,7 @@ impl<'a> PackageUrl<'a> {
6665
/// cannot contain spaces.
6766
///
6867
/// # Name
69-
/// The package name will be canonicalize depending on the type: for instance,
68+
/// The package name will be canonicalized depending on the type: for instance,
7069
/// 'bitbucket' packages have a case-insensitive name, so the name will be
7170
/// lowercased if needed.
7271
///
@@ -87,7 +86,7 @@ impl<'a> PackageUrl<'a> {
8786
t = to_lowercase(t);
8887
// lowercase name if required by type and needed
8988
match t.as_ref() {
90-
"bitbucket" | "deb" | "github" | "hex" | "npm" => {
89+
"bitbucket" | "deb" | "github" | "hex" | "npm" | "composer" | "mlflow" => {
9190
n = to_lowercase(n);
9291
}
9392
"pypi" => {
@@ -152,20 +151,31 @@ impl<'a> PackageUrl<'a> {
152151
}
153152

154153
/// Assign a namespace to the package.
155-
pub fn with_namespace<N>(&mut self, namespace: N) -> &mut Self
154+
pub fn with_namespace<N>(&mut self, namespace: N) -> Result<&mut Self>
156155
where
157156
N: Into<Cow<'a, str>>,
158157
{
158+
// Fail if namespace is prohibited for this type
159+
match self.ty.as_ref() {
160+
"bitnami" | "cargo" | "cocoapods" | "conda" | "cran" | "gem" | "hackage" | "mlflow"
161+
| "nuget" | "oci" | "pub" | "pypi" => {
162+
return Err(Error::TypeProhibitsNamespace(self.ty.to_string()));
163+
}
164+
_ => {}
165+
}
166+
167+
// Lowercase namespace if needed for this type
159168
let mut n = namespace.into();
160169
match self.ty.as_ref() {
161-
"bitbucket" | "deb" | "github" | "golang" | "hex" | "rpm" => {
170+
"apk" | "bitbucket" | "composer" | "deb" | "github" | "golang" | "hex" | "qpkg"
171+
| "rpm" => {
162172
n = to_lowercase(n);
163173
}
164174
_ => {}
165175
}
166176

167177
self.namespace = Some(n);
168-
self
178+
Ok(self)
169179
}
170180

171181
/// Clear the namespace
@@ -175,12 +185,17 @@ impl<'a> PackageUrl<'a> {
175185
}
176186

177187
/// Assign a version to the package.
178-
pub fn with_version<V>(&mut self, version: V) -> &mut Self
188+
pub fn with_version<V>(&mut self, version: V) -> Result<&mut Self>
179189
where
180190
V: Into<Cow<'a, str>>,
181191
{
182-
self.version = Some(version.into());
183-
self
192+
let mut v = version.into();
193+
if self.ty.as_ref() == "huggingface" {
194+
v = to_lowercase(v);
195+
}
196+
197+
self.version = Some(v);
198+
Ok(self)
184199
}
185200

186201
/// Clear the version
@@ -263,10 +278,10 @@ impl FromStr for PackageUrl<'static> {
263278

264279
let mut purl = Self::new(ty, name)?;
265280
if let Some(ns) = namespace {
266-
purl.with_namespace(ns);
281+
purl.with_namespace(ns)?;
267282
}
268283
if let Some(v) = version {
269-
purl.with_version(v);
284+
purl.with_version(v)?;
270285
}
271286
if let Some(sp) = subpath {
272287
purl.with_subpath(sp)?;
@@ -367,7 +382,9 @@ mod tests {
367382
let purl_string = PackageUrl::new("type", "name")
368383
.unwrap()
369384
.with_namespace("name/space")
385+
.unwrap()
370386
.with_version("version")
387+
.unwrap()
371388
.with_subpath("sub/path")
372389
.unwrap()
373390
.add_qualifier("k1", "v1")
@@ -380,11 +397,20 @@ mod tests {
380397

381398
#[test]
382399
fn test_percent_encoding_idempotent() {
383-
let orig = "pkg:brew/openssl%25401.1@1.1.1w";
400+
let orig = "pkg:brew/open%2Fssl%25401.1@1.1.1w";
384401
let round_trip = orig.parse::<PackageUrl>().unwrap().to_string();
385402
assert_eq!(orig, round_trip);
386403
}
387404

405+
#[test]
406+
fn test_percent_encoded_name() {
407+
let raw_purl = "pkg:type/name/space/first%2Fname";
408+
let purl = PackageUrl::from_str(raw_purl).unwrap();
409+
assert_eq!(purl.ty(), "type");
410+
assert_eq!(purl.namespace(), Some("name/space"));
411+
assert_eq!(purl.name(), "first/name");
412+
}
413+
388414
#[test]
389415
fn test_percent_encoding_qualifier() {
390416
let mut purl = "pkg:deb/ubuntu/gnome-calculator@1:41.1-2ubuntu2"
@@ -396,7 +422,10 @@ mod tests {
396422
)
397423
.unwrap();
398424
let encoded = purl.to_string();
399-
assert_eq!(encoded, "pkg:deb/ubuntu/gnome-calculator@1:41.1-2ubuntu2?vcs_url=git%2Bhttps://salsa.debian.org/gnome-team/gnome-calculator.git%40debian/1%2541.1-2");
425+
assert_eq!(
426+
encoded,
427+
"pkg:deb/ubuntu/gnome-calculator@1:41.1-2ubuntu2?vcs_url=git%2Bhttps:%2F%2Fsalsa.debian.org%2Fgnome-team%2Fgnome-calculator.git%40debian%2F1%2541.1-2"
428+
);
400429
}
401430

402431
#[cfg(feature = "serde")]

tests/spec/macros.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ pub fn run_build_test(case: &SpecTestCase) {
5757

5858
let mut purl = purl_result.unwrap();
5959
if let Some(ref ns) = input.namespace {
60-
purl.with_namespace(ns.as_ref());
60+
let _ = purl.with_namespace(ns.as_ref());
6161
}
6262

6363
if let Some(ref v) = input.version {
64-
purl.with_version(v.as_ref());
64+
let _ = purl.with_version(v.as_ref());
6565
}
6666

6767
if let Some(ref sp) = input.subpath {
@@ -125,7 +125,6 @@ pub fn run_tests_from_spec(path: &Path) {
125125
}
126126
}
127127

128-
129128
macro_rules! generate_json_tests {
130129
($($test_name:ident => $file_path:expr),* $(,)?) => {
131130
$(

tests/spec/mod.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@ mod testcase;
88
generate_json_tests! {
99
alpm_test => "tests/spec/purl-spec/tests/types/alpm-test.json",
1010
apk_test => "tests/spec/purl-spec/tests/types/apk-test.json",
11-
bintray_test => "tests/spec/purl-spec/tests/types/bintray-test.json",
1211
bitbucket_test => "tests/spec/purl-spec/tests/types/bitbucket-test.json",
1312
bitnami_test => "tests/spec/purl-spec/tests/types/bitnami-test.json",
1413
cargo_test => "tests/spec/purl-spec/tests/types/cargo-test.json",
1514
cocoapods_test => "tests/spec/purl-spec/tests/types/cocoapods-test.json",
16-
// composer_test => "tests/spec/purl-spec/tests/types/composer-test.json",
15+
composer_test => "tests/spec/purl-spec/tests/types/composer-test.json",
1716
// conan_test => "tests/spec/purl-spec/tests/types/conan-test.json",
1817
conda_test => "tests/spec/purl-spec/tests/types/conda-test.json",
1918
// cpan_test => "tests/spec/purl-spec/tests/types/cpan-test.json",
20-
// cran_test => "tests/spec/purl-spec/tests/types/cran-test.json",
19+
cran_test => "tests/spec/purl-spec/tests/types/cran-test.json",
2120
deb_test => "tests/spec/purl-spec/tests/types/deb-test.json",
2221
docker_test => "tests/spec/purl-spec/tests/types/docker-test.json",
2322
gem_test => "tests/spec/purl-spec/tests/types/gem-test.json",
@@ -26,9 +25,9 @@ generate_json_tests! {
2625
golang_test => "tests/spec/purl-spec/tests/types/golang-test.json",
2726
hackage_test => "tests/spec/purl-spec/tests/types/hackage-test.json",
2827
hex_test => "tests/spec/purl-spec/tests/types/hex-test.json",
29-
// huggingface_test => "tests/spec/purl-spec/tests/types/huggingface-test.json",
28+
huggingface_test => "tests/spec/purl-spec/tests/types/huggingface-test.json",
3029
luarocks_test => "tests/spec/purl-spec/tests/types/luarocks-test.json",
31-
// maven_test => "tests/spec/purl-spec/tests/types/maven-test.json",
30+
maven_test => "tests/spec/purl-spec/tests/types/maven-test.json",
3231
// mlflow_test => "tests/spec/purl-spec/tests/types/mlflow-test.json",
3332
// npm_test => "tests/spec/purl-spec/tests/types/npm-test.json",
3433
nuget_test => "tests/spec/purl-spec/tests/types/nuget-test.json",

tests/spec/purl-spec

Submodule purl-spec updated 138 files

0 commit comments

Comments
 (0)