3 use url
::{mod, Url, UrlParser}
;
6 use util
::{CargoResult, ToUrl, human, ToSemver, ChainError}
;
8 #[deriving(Clone, PartialEq, Eq)]
9 pub struct PackageIdSpec
{
11 version
: Option
<Version
>,
16 pub fn parse(spec
: &str) -> CargoResult
<PackageIdSpec
> {
17 if spec
.contains("/") {
19 Ok(url
) => return PackageIdSpec
::from_url(url
),
22 if !spec
.contains("://") {
23 match url(format
!("cargo://{}", spec
).as_slice()) {
24 Ok(url
) => return PackageIdSpec
::from_url(url
),
29 let mut parts
= spec
.as_slice().splitn(1, '
:'
);
30 let name
= parts
.next().unwrap();
31 let version
= match parts
.next() {
32 Some(version
) => Some(try
!(Version
::parse(version
).map_err(human
))),
35 for ch
in name
.chars() {
36 if !ch
.is_alphanumeric() && ch
!= '_'
&& ch
!= '
-'
{
37 return Err(human(format
!("invalid character in pkgid `{}`: `{}`",
42 name
: name
.to_string(),
48 pub fn from_package_id(package_id
: &PackageId
) -> PackageIdSpec
{
50 name
: package_id
.get_name().to_string(),
51 version
: Some(package_id
.get_version().clone()),
52 url
: Some(package_id
.get_source_id().get_url().clone()),
56 fn from_url(mut url
: Url
) -> CargoResult
<PackageIdSpec
> {
57 if url
.query
.is_some() {
58 return Err(human(format
!("cannot have a query string in a pkgid: {}",
61 let frag
= url
.fragment
.take();
62 let (name
, version
) = {
63 let path
= try
!(url
.path().chain_error(|| {
64 human(format
!("pkgid urls must have a path: {}", url
))
66 let path_name
= try
!(path
.last().chain_error(|| {
67 human(format
!("pkgid urls must have at least one path \
72 let mut parts
= fragment
.as_slice().splitn(1, '
:'
);
73 let name_or_version
= parts
.next().unwrap();
76 let version
= try
!(part
.to_semver().map_err(human
));
77 (name_or_version
.to_string(), Some(version
))
80 if name_or_version
.char_at(0).is_alphabetic() {
81 (name_or_version
.to_string(), None
)
83 let version
= try
!(name_or_version
.to_semver()
85 (path_name
.to_string(), Some(version
))
90 None
=> (path_name
.to_string(), None
),
100 pub fn get_name(&self) -> &str { self.name.as_slice() }
101 pub fn get_version(&self) -> Option
<&Version
> { self.version.as_ref() }
102 pub fn get_url(&self) -> Option
<&Url
> { self.url.as_ref() }
104 pub fn matches(&self, package_id
: &PackageId
) -> bool
{
105 if self.get_name() != package_id
.get_name() { return false }
108 Some(ref v
) => if v
!= package_id
.get_version() { return false }
,
113 Some(ref u
) => u
== package_id
.get_source_id().get_url(),
119 fn url(s
: &str) -> url
::ParseResult
<Url
> {
120 return UrlParser
::new().scheme_type_mapper(mapper
).parse(s
);
122 fn mapper(scheme
: &str) -> url
::SchemeType
{
123 if scheme
== "cargo" {
124 url
::SchemeType
::Relative(1)
126 url
::whatwg_scheme_type_mapper(scheme
)
132 impl fmt
::Show
for PackageIdSpec
{
133 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
134 let mut printed_name
= false;
137 if url
.scheme
.as_slice() == "cargo" {
138 try
!(write
!(f
, "{}/{}", url
.host().unwrap(),
139 url
.path().unwrap().connect("/")));
141 try
!(write
!(f
, "{}", url
));
143 if url
.path().unwrap().last().unwrap() != &self.name
{
145 try
!(write
!(f
, "#{}", self.name
));
148 None
=> { printed_name = true; try!(write!(f, "{}
", self.name)) }
152 try!(write!(f, "{}{}
", if printed_name {":"} else {"#"}, v));
162 use core::{PackageId, SourceId};
163 use super::{PackageIdSpec, url};
168 fn ok(spec: &str, expected: PackageIdSpec) {
169 let parsed = PackageIdSpec::parse(spec).unwrap();
170 assert_eq!(parsed, expected);
171 assert_eq!(parsed.to_string().as_slice(), spec);
174 ok("http
://crates.io/foo#1.2.3", PackageIdSpec {
175 name
: "foo".to_string(),
176 version
: Some(Version
::parse("1.2.3").unwrap()),
177 url
: Some(url("http://crates.io/foo").unwrap()),
179 ok("http://crates.io/foo#bar:1.2.3", PackageIdSpec
{
180 name
: "bar".to_string(),
181 version
: Some(Version
::parse("1.2.3").unwrap()),
182 url
: Some(url("http://crates.io/foo").unwrap()),
184 ok("crates.io/foo", PackageIdSpec
{
185 name
: "foo".to_string(),
187 url
: Some(url("cargo://crates.io/foo").unwrap()),
189 ok("crates.io/foo#1.2.3", PackageIdSpec
{
190 name
: "foo".to_string(),
191 version
: Some(Version
::parse("1.2.3").unwrap()),
192 url
: Some(url("cargo://crates.io/foo").unwrap()),
194 ok("crates.io/foo#bar", PackageIdSpec
{
195 name
: "bar".to_string(),
197 url
: Some(url("cargo://crates.io/foo").unwrap()),
199 ok("crates.io/foo#bar:1.2.3", PackageIdSpec
{
200 name
: "bar".to_string(),
201 version
: Some(Version
::parse("1.2.3").unwrap()),
202 url
: Some(url("cargo://crates.io/foo").unwrap()),
204 ok("foo", PackageIdSpec
{
205 name
: "foo".to_string(),
209 ok("foo:1.2.3", PackageIdSpec
{
210 name
: "foo".to_string(),
211 version
: Some(Version
::parse("1.2.3").unwrap()),
218 assert
!(PackageIdSpec
::parse("baz:").is_err());
219 assert
!(PackageIdSpec
::parse("baz:1.0").is_err());
220 assert
!(PackageIdSpec
::parse("http://baz:1.0").is_err());
221 assert
!(PackageIdSpec
::parse("http://#baz:1.0").is_err());
226 let sid
= SourceId
::for_central().unwrap();
227 let foo
= PackageId
::new("foo", "1.2.3", &sid
).unwrap();
228 let bar
= PackageId
::new("bar", "1.2.3", &sid
).unwrap();
230 assert
!( PackageIdSpec
::parse("foo").unwrap().matches(&foo
));
231 assert
!(!PackageIdSpec
::parse("foo").unwrap().matches(&bar
));
232 assert
!( PackageIdSpec
::parse("foo:1.2.3").unwrap().matches(&foo
));
233 assert
!(!PackageIdSpec
::parse("foo:1.2.2").unwrap().matches(&foo
));