2013-12-09 15:56:53 -06:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2013-12-28 11:16:48 -06:00
|
|
|
/// CrateIds identify crates and include the crate name and optionally a path
|
|
|
|
/// and version. In the full form, they look like relative URLs. Example:
|
2013-12-14 20:58:07 -06:00
|
|
|
/// `github.com/mozilla/rust#std:1.0` would be a package ID with a path of
|
|
|
|
/// `gitub.com/mozilla/rust` and a crate name of `std` with a version of
|
|
|
|
/// `1.0`. If no crate name is given after the hash, the name is inferred to
|
|
|
|
/// be the last component of the path. If no version is given, it is inferred
|
|
|
|
/// to be `0.0`.
|
2013-12-09 15:56:53 -06:00
|
|
|
#[deriving(Clone, Eq)]
|
2013-12-28 11:16:48 -06:00
|
|
|
pub struct CrateId {
|
2013-12-14 20:58:07 -06:00
|
|
|
/// A path which represents the codes origin. By convention this is the
|
|
|
|
/// URL, without `http://` or `https://` prefix, to the crate's repository
|
2013-12-09 15:56:53 -06:00
|
|
|
path: ~str,
|
2013-12-14 20:58:07 -06:00
|
|
|
/// The name of the crate.
|
2013-12-09 15:56:53 -06:00
|
|
|
name: ~str,
|
2013-12-14 20:58:07 -06:00
|
|
|
/// The version of the crate.
|
2013-12-09 15:56:53 -06:00
|
|
|
version: Option<~str>,
|
|
|
|
}
|
|
|
|
|
2013-12-28 11:16:48 -06:00
|
|
|
impl ToStr for CrateId {
|
2013-12-09 15:56:53 -06:00
|
|
|
fn to_str(&self) -> ~str {
|
|
|
|
let version = match self.version {
|
|
|
|
None => "0.0",
|
|
|
|
Some(ref version) => version.as_slice(),
|
|
|
|
};
|
2013-12-14 20:58:07 -06:00
|
|
|
if self.path == self.name || self.path.ends_with(format!("/{}", self.name)) {
|
|
|
|
format!("{}\\#{}", self.path, version)
|
2013-12-09 15:56:53 -06:00
|
|
|
} else {
|
2013-12-14 20:58:07 -06:00
|
|
|
format!("{}\\#{}:{}", self.path, self.name, version)
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-28 11:16:48 -06:00
|
|
|
impl FromStr for CrateId {
|
|
|
|
fn from_str(s: &str) -> Option<CrateId> {
|
2013-12-14 20:58:07 -06:00
|
|
|
let pieces: ~[&str] = s.splitn('#', 1).collect();
|
|
|
|
let path = pieces[0].to_owned();
|
2013-12-09 15:56:53 -06:00
|
|
|
|
2013-12-14 20:58:07 -06:00
|
|
|
if path.starts_with("/") || path.ends_with("/") ||
|
|
|
|
path.starts_with(".") || path.is_empty() {
|
2013-12-09 15:56:53 -06:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2013-12-14 20:58:07 -06:00
|
|
|
let path_pieces: ~[&str] = path.rsplitn('/', 1).collect();
|
|
|
|
let inferred_name = path_pieces[0];
|
|
|
|
|
|
|
|
let (name, version) = if pieces.len() == 1 {
|
|
|
|
(inferred_name.to_owned(), None)
|
|
|
|
} else {
|
|
|
|
let hash_pieces: ~[&str] = pieces[1].splitn(':', 1).collect();
|
|
|
|
let (hash_name, hash_version) = if hash_pieces.len() == 1 {
|
|
|
|
("", hash_pieces[0])
|
|
|
|
} else {
|
|
|
|
(hash_pieces[0], hash_pieces[1])
|
|
|
|
};
|
|
|
|
|
|
|
|
let name = if !hash_name.is_empty() {
|
|
|
|
hash_name.to_owned()
|
|
|
|
} else {
|
|
|
|
inferred_name.to_owned()
|
|
|
|
};
|
|
|
|
|
|
|
|
let version = if !hash_version.is_empty() {
|
2014-01-10 09:22:06 -06:00
|
|
|
if hash_version == "0.0" {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(hash_version.to_owned())
|
|
|
|
}
|
2013-12-14 20:58:07 -06:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
(name, version)
|
2013-12-09 15:56:53 -06:00
|
|
|
};
|
|
|
|
|
2013-12-28 11:16:48 -06:00
|
|
|
Some(CrateId {
|
2013-12-14 20:58:07 -06:00
|
|
|
path: path,
|
|
|
|
name: name,
|
2013-12-09 15:56:53 -06:00
|
|
|
version: version,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-28 11:16:48 -06:00
|
|
|
impl CrateId {
|
2013-12-09 15:56:53 -06:00
|
|
|
pub fn version_or_default<'a>(&'a self) -> &'a str {
|
|
|
|
match self.version {
|
|
|
|
None => "0.0",
|
|
|
|
Some(ref version) => version.as_slice(),
|
|
|
|
}
|
|
|
|
}
|
2014-01-10 09:22:06 -06:00
|
|
|
|
|
|
|
pub fn short_name_with_version(&self) -> ~str {
|
|
|
|
format!("{}-{}", self.name, self.version_or_default())
|
|
|
|
}
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn bare_name() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("foo").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"foo");
|
|
|
|
assert_eq!(crateid.version, None);
|
|
|
|
assert_eq!(crateid.path, ~"foo");
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn bare_name_single_char() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("f").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"f");
|
|
|
|
assert_eq!(crateid.version, None);
|
|
|
|
assert_eq!(crateid.path, ~"f");
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-12-28 11:16:48 -06:00
|
|
|
fn empty_crateid() {
|
|
|
|
let crateid: Option<CrateId> = from_str("");
|
|
|
|
assert!(crateid.is_none());
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_path() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("example.com/foo/bar").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"bar");
|
|
|
|
assert_eq!(crateid.version, None);
|
|
|
|
assert_eq!(crateid.path, ~"example.com/foo/bar");
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_version() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("foo#1.0").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"foo");
|
|
|
|
assert_eq!(crateid.version, Some(~"1.0"));
|
|
|
|
assert_eq!(crateid.path, ~"foo");
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn absolute_path() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: Option<CrateId> = from_str("/foo/bar");
|
|
|
|
assert!(crateid.is_none());
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
2013-12-14 20:58:07 -06:00
|
|
|
#[test]
|
|
|
|
fn path_ends_with_slash() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: Option<CrateId> = from_str("foo/bar/");
|
|
|
|
assert!(crateid.is_none());
|
2013-12-14 20:58:07 -06:00
|
|
|
}
|
|
|
|
|
2013-12-09 15:56:53 -06:00
|
|
|
#[test]
|
|
|
|
fn path_and_version() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("example.com/foo/bar#1.0").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"bar");
|
|
|
|
assert_eq!(crateid.version, Some(~"1.0"));
|
|
|
|
assert_eq!(crateid.path, ~"example.com/foo/bar");
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn single_chars() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("a/b#1").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"b");
|
|
|
|
assert_eq!(crateid.version, Some(~"1"));
|
|
|
|
assert_eq!(crateid.path, ~"a/b");
|
2013-12-09 15:56:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_version() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("foo#").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"foo");
|
|
|
|
assert_eq!(crateid.version, None);
|
|
|
|
assert_eq!(crateid.path, ~"foo");
|
2013-12-14 20:58:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn path_and_name() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("foo/rust-bar#bar:1.0").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"bar");
|
|
|
|
assert_eq!(crateid.version, Some(~"1.0"));
|
|
|
|
assert_eq!(crateid.path, ~"foo/rust-bar");
|
2013-12-14 20:58:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn empty_name() {
|
2013-12-28 11:16:48 -06:00
|
|
|
let crateid: CrateId = from_str("foo/bar#:1.0").expect("valid crateid");
|
|
|
|
assert_eq!(crateid.name, ~"bar");
|
|
|
|
assert_eq!(crateid.version, Some(~"1.0"));
|
|
|
|
assert_eq!(crateid.path, ~"foo/bar");
|
2013-12-14 20:58:07 -06:00
|
|
|
}
|