Skip to content

Commit 7ff2109

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

File tree

1 file changed

+229
-61
lines changed

1 file changed

+229
-61
lines changed

src/crates/mod.rs

Lines changed: 229 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -74,68 +74,204 @@ 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+
230+
let kind = if let Some(kind) = kind { format!{"{kind}+"}} else {String::new()};
231+
Ok(Crate::Git(GitRepo {
232+
url: format!("{kind}{proto}://{host_path}"),
233+
sha: None,
234+
}))
235+
}
236+
(Some("git"), _, Some(query))
237+
| (None, "ssh" | "git" | "http" | "https", Some(query)) => {
238+
239+
let Some((query_kind, rev)) = query.split_once('=') else {
240+
bail!(
241+
"malformed pkgid format: {}\n maybe the representation has changed?",
242+
pkgid.repr
243+
)
244+
};
245+
246+
let kind = if let Some(kind) = kind { format!{"{kind}+"}} else {String::new()};
247+
match query_kind {
248+
"branch" | "tag" | "rev" => Ok(Crate::Git(GitRepo {
249+
url: format!("{kind}{proto}://{host_path}"),
250+
sha: Some(rev.to_string()),
251+
})),
252+
_ => {
253+
bail!(
254+
"malformed pkgid format: {}\n maybe the representation has changed?",
255+
pkgid.repr
256+
)
257+
}
258+
}
259+
}
260+
_ => bail!(
261+
"malformed pkgid format: {}\n maybe the representation has changed?",
262+
pkgid.repr
263+
),
134264
}
135-
_ => bail!(
136-
"malformed pkgid format: {}\n maybe the representation has changed?",
137-
pkgid.repr
138-
),
265+
} else if let Some((package_name, version)) = pkgid.repr.split_once(['@', ':']) {
266+
Ok(Crate::Registry(RegistryCrate {
267+
name: package_name.to_string(),
268+
version: version.to_string(),
269+
}))
270+
} else {
271+
Ok(Crate::Registry(RegistryCrate {
272+
name: pkgid.repr.clone(),
273+
version: "unknown".to_string(),
274+
}))
139275
}
140276
}
141277
}
@@ -270,16 +406,48 @@ mod tests {
270406
.to_string(),
271407
sha: None
272408
}),
409+
410+
// package id spec <https://doc.rust-lang.org/cargo/reference/pkgid-spec.html>
411+
273412
"registry+https://github.com/rust-lang/crates.io-index#cookie@0.15.0" => Crate::Registry(RegistryCrate {
274413
name: "cookie".to_string(),
275414
version: "0.15.0".to_string(),
276415
}),
277-
}
416+
"regex@1.4.3" => Crate::Registry(RegistryCrate {
417+
name: "regex".to_string(),
418+
version: "1.4.3".to_string(),
419+
}),
278420

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

285453
#[test]

0 commit comments

Comments
 (0)