Skip to content

Commit cadabce

Browse files
committed
add more tests for parting Crate from PackageId
1 parent f950b27 commit cadabce

File tree

1 file changed

+235
-61
lines changed

1 file changed

+235
-61
lines changed

src/crates/mod.rs

Lines changed: 235 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -74,68 +74,210 @@ impl TryFrom<&'_ PackageId> for Crate {
7474
type Error = failure::Error;
7575

7676
fn try_from(pkgid: &PackageId) -> Fallible<Crate> {
77-
let parts = &pkgid
78-
.repr
79-
.split_ascii_whitespace()
80-
.flat_map(|s| {
81-
// remove ()
82-
s.trim_matches(|c: char| c.is_ascii_punctuation())
83-
// split resource and protocol
84-
.split('+')
85-
})
86-
.collect::<Vec<_>>();
87-
88-
match parts[..] {
89-
[name, version, "registry", _] => Ok(Crate::Registry(RegistryCrate {
90-
name: name.to_string(),
91-
version: version.to_string(),
92-
})),
93-
[_, _, "path", path] => Ok(Crate::Path(path.to_string())),
94-
[_, _, "git", repo] => {
95-
if repo.starts_with("https://github.com") {
96-
Ok(Crate::GitHub(repo.replace('#', "/").parse()?))
97-
} else {
98-
let mut parts = repo.split('#').rev().collect::<Vec<_>>();
99-
let url = parts.pop();
100-
let sha = parts.pop();
101-
102-
match (url, sha) {
103-
(Some(url), None) => Ok(Crate::Git(GitRepo {
104-
url: url.to_string(),
105-
sha: None,
106-
})),
107-
(Some(url), Some(sha)) => Ok(Crate::Git(GitRepo {
108-
// remove additional queries if the sha is present
109-
// as the crate version is already uniquely determined
110-
url: url.split('?').next().unwrap().to_string(),
111-
sha: Some(sha.to_string()),
112-
})),
113-
_ => bail!("malformed git repo: {}", repo),
77+
if pkgid.repr.contains(|c: char| c.is_ascii_whitespace()) {
78+
let parts = &pkgid
79+
.repr
80+
.split_ascii_whitespace()
81+
.flat_map(|s| {
82+
// remove ()
83+
s.trim_matches(|c: char| c.is_ascii_punctuation())
84+
// split resource and protocol
85+
.split('+')
86+
})
87+
.collect::<Vec<_>>();
88+
89+
match parts[..] {
90+
[name, version, "registry", _] => Ok(Crate::Registry(RegistryCrate {
91+
name: name.to_string(),
92+
version: version.to_string(),
93+
})),
94+
[_, _, "path", path] => Ok(Crate::Path(path.to_string())),
95+
[_, _, "git", repo] => {
96+
if repo.starts_with("https://github.com") {
97+
Ok(Crate::GitHub(repo.replace('#', "/").parse()?))
98+
} else {
99+
let mut parts = repo.split('#').rev().collect::<Vec<_>>();
100+
let url = parts.pop();
101+
let sha = parts.pop();
102+
103+
match (url, sha) {
104+
(Some(url), None) => Ok(Crate::Git(GitRepo {
105+
url: url.to_string(),
106+
sha: None,
107+
})),
108+
(Some(url), Some(sha)) => Ok(Crate::Git(GitRepo {
109+
// remove additional queries if the sha is present
110+
// as the crate version is already uniquely determined
111+
url: url.split('?').next().unwrap().to_string(),
112+
sha: Some(sha.to_string()),
113+
})),
114+
_ => bail!("malformed git repo: {}", repo),
115+
}
114116
}
115117
}
118+
_ => bail!(
119+
"malformed pkgid format: {}\n maybe the representation has changed?",
120+
pkgid.repr
121+
),
116122
}
117-
["registry", url, ..] => {
118-
let Some((_registry, krate)) = url.split_once('#') else {
119-
bail!(
120-
"malformed pkgid format: {}\n maybe the representation has changed?",
121-
pkgid.repr
122-
);
123+
} else if let Some((kind_proto, host_path_query_anchor)) = pkgid.repr.split_once("://") {
124+
let (kind, proto) = if let Some((kind, proto)) = kind_proto.split_once('+') {
125+
(Some(kind), proto)
126+
} else {
127+
(None, kind_proto)
128+
};
129+
130+
let (host_path_query, anchor) =
131+
if let Some((host_path_query, anchor)) = host_path_query_anchor.split_once('#') {
132+
(host_path_query, Some(anchor))
133+
} else {
134+
(host_path_query_anchor, None)
123135
};
124-
let Some((crate_name, crate_version)) = krate.split_once('@') else {
125-
bail!(
126-
"malformed pkgid format: {}\n maybe the representation has changed?",
127-
pkgid.repr
128-
);
136+
137+
let (host_path, query) =
138+
if let Some((host_path, query)) = host_path_query.split_once('?') {
139+
(host_path, Some(query))
140+
} else {
141+
(host_path_query, None)
129142
};
130-
Ok(Crate::Registry(RegistryCrate {
131-
name: crate_name.to_string(),
132-
version: crate_version.to_string(),
133-
}))
143+
144+
match (kind, proto, query) {
145+
(Some("path") | None, "file", None) => Ok(Crate::Path(host_path.to_string())),
146+
(Some("registry"), _, None) => {
147+
if let Some(anchor) = anchor {
148+
if let Some((package_name, version)) = anchor.split_once(['@', ':']) {
149+
Ok(Crate::Registry(RegistryCrate {
150+
name: package_name.to_string(),
151+
version: version.to_string(),
152+
}))
153+
} else if !anchor
154+
.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_' && c != '-')
155+
{
156+
// anchor appears to be a valid package name
157+
Ok(Crate::Registry(RegistryCrate {
158+
name: anchor.to_string(),
159+
version: "unknown".to_string(),
160+
}))
161+
} else {
162+
// as the anchor is not a valid package name check whether it can be a version
163+
let Some(minor_major_path) = anchor.split(['-', '+']).next() else {
164+
bail!("split always returns at least one element")
165+
};
166+
167+
if minor_major_path.split('.').count() > 3
168+
|| minor_major_path
169+
.split('.')
170+
.any(|part| part.contains(|c: char| !c.is_ascii_digit()))
171+
{
172+
bail!(
173+
"malformed pkgid format: {}\n maybe the representation has changed?",
174+
pkgid.repr
175+
)
176+
}
177+
178+
let Some(package_name) = host_path.split('/').last() else {
179+
bail!(
180+
"malformed pkgid format: {}\n maybe the representation has changed?",
181+
pkgid.repr
182+
)
183+
};
184+
185+
Ok(Crate::Registry(RegistryCrate {
186+
name: package_name.to_string(),
187+
version: anchor.to_string(),
188+
}))
189+
}
190+
} else {
191+
bail!(
192+
"malformed pkgid format: {}\n maybe the representation has changed?",
193+
pkgid.repr
194+
)
195+
}
196+
}
197+
(None, "http" | "https", _) | (Some("git"), _, _)
198+
if host_path.starts_with("github.com/") =>
199+
{
200+
let mut parts = host_path.split('/').skip(1);
201+
let Some(org) = parts.next() else {
202+
bail!(
203+
"malformed pkgid format: {}\n maybe the representation has changed?",
204+
pkgid.repr
205+
)
206+
};
207+
208+
let name = if let Some((package_name, _version)) =
209+
anchor.and_then(|anchor| anchor.split_once(['@', ':']))
210+
{
211+
package_name
212+
} else if let Some(name) = parts.next() {
213+
name
214+
} else {
215+
bail!(
216+
"malformed pkgid format: {}\n maybe the representation has changed?",
217+
pkgid.repr
218+
)
219+
};
220+
221+
Ok(Crate::GitHub(GitHubRepo {
222+
org: org.to_string(),
223+
name: name.to_string(),
224+
sha: None,
225+
}))
226+
}
227+
228+
(Some("git"), _, None) | (None, "ssh" | "git" | "http" | "https", None) => {
229+
let kind = if let Some(kind) = kind {
230+
format! {"{kind}+"}
231+
} else {
232+
String::new()
233+
};
234+
Ok(Crate::Git(GitRepo {
235+
url: format!("{kind}{proto}://{host_path}"),
236+
sha: None,
237+
}))
238+
}
239+
(Some("git"), _, Some(query))
240+
| (None, "ssh" | "git" | "http" | "https", Some(query)) => {
241+
let Some((query_kind, rev)) = query.split_once('=') else {
242+
bail!(
243+
"malformed pkgid format: {}\n maybe the representation has changed?",
244+
pkgid.repr
245+
)
246+
};
247+
248+
let kind = if let Some(kind) = kind {
249+
format! {"{kind}+"}
250+
} else {
251+
String::new()
252+
};
253+
match query_kind {
254+
"branch" | "tag" | "rev" => Ok(Crate::Git(GitRepo {
255+
url: format!("{kind}{proto}://{host_path}"),
256+
sha: Some(rev.to_string()),
257+
})),
258+
_ => {
259+
bail!(
260+
"malformed pkgid format: {}\n maybe the representation has changed?",
261+
pkgid.repr
262+
)
263+
}
264+
}
265+
}
266+
_ => bail!(
267+
"malformed pkgid format: {}\n maybe the representation has changed?",
268+
pkgid.repr
269+
),
134270
}
135-
_ => bail!(
136-
"malformed pkgid format: {}\n maybe the representation has changed?",
137-
pkgid.repr
138-
),
271+
} else if let Some((package_name, version)) = pkgid.repr.split_once(['@', ':']) {
272+
Ok(Crate::Registry(RegistryCrate {
273+
name: package_name.to_string(),
274+
version: version.to_string(),
275+
}))
276+
} else {
277+
Ok(Crate::Registry(RegistryCrate {
278+
name: pkgid.repr.clone(),
279+
version: "unknown".to_string(),
280+
}))
139281
}
140282
}
141283
}
@@ -270,16 +412,48 @@ mod tests {
270412
.to_string(),
271413
sha: None
272414
}),
415+
416+
// package id spec <https://doc.rust-lang.org/cargo/reference/pkgid-spec.html>
417+
273418
"registry+https://github.com/rust-lang/crates.io-index#cookie@0.15.0" => Crate::Registry(RegistryCrate {
274419
name: "cookie".to_string(),
275420
version: "0.15.0".to_string(),
276421
}),
277-
}
422+
"regex@1.4.3" => Crate::Registry(RegistryCrate {
423+
name: "regex".to_string(),
424+
version: "1.4.3".to_string(),
425+
}),
278426

279-
assert!(Crate::try_from(&PackageId {
280-
repr: "invalid".to_string()
281-
})
282-
.is_err());
427+
"https://github.com/rust-lang/cargo#0.52.0" => Crate::GitHub(GitHubRepo {
428+
org: "rust-lang".to_string(),
429+
name: "cargo".to_string(),
430+
sha: None
431+
}),
432+
"https://github.com/rust-lang/cargo#cargo-platform@0.1.2" => Crate::GitHub(GitHubRepo {
433+
org: "rust-lang".to_string(),
434+
name: "cargo-platform".to_string(),
435+
sha: None
436+
}),
437+
438+
"ssh://git@github.com/rust-lang/regex.git#regex@1.4.3" => Crate::Git(GitRepo {
439+
url: "ssh://git@github.com/rust-lang/regex.git".to_string(),
440+
sha: None
441+
}),
442+
"git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3" => Crate::Git(GitRepo {
443+
url: "git+ssh://git@github.com/rust-lang/regex.git".to_string(),
444+
sha: None
445+
}),
446+
"git+ssh://git@github.com/rust-lang/regex.git?branch=dev#regex@1.4.3" => Crate::Git(GitRepo {
447+
url: "git+ssh://git@github.com/rust-lang/regex.git".to_string(),
448+
sha: Some("dev".to_string())
449+
}),
450+
451+
"file:///path/to/my/project/foo" => Crate::Path("/path/to/my/project/foo".to_string()),
452+
"file:///path/to/my/project/foo#1.1.8" => Crate::Path("/path/to/my/project/foo".to_string()),
453+
"path+file:///path/to/my/project/foo#1.1.8" => Crate::Path("/path/to/my/project/foo".to_string()),
454+
455+
"invalid" => Crate::Registry(RegistryCrate{ name: "invalid".to_string(), version: "unknown".to_string() }),
456+
}
283457
}
284458

285459
#[test]

0 commit comments

Comments
 (0)