1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 /// CrateIds identify crates and include the crate name and optionally a path
14 /// and version. In the full form, they look like relative URLs. Example:
15 /// `github.com/mozilla/rust#std:1.0` would be a package ID with a path of
16 /// `github.com/mozilla/rust` and a crate name of `std` with a version of
17 /// `1.0`. If no crate name is given after the hash, the name is inferred to
18 /// be the last component of the path. If no version is given, it is inferred
21 use std::from_str::FromStr;
23 #[deriving(Clone, PartialEq)]
25 /// A path which represents the codes origin. By convention this is the
26 /// URL, without `http://` or `https://` prefix, to the crate's repository
28 /// The name of the crate.
30 /// The version of the crate.
31 pub version: Option<String>,
34 impl fmt::Show for CrateId {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 try!(write!(f, "{}", self.path));
38 let version = match self.version {
40 Some(ref version) => version.as_slice(),
42 if self.path == self.name ||
45 .ends_with(format!("/{}", self.name).as_slice()) {
46 write!(f, "\\#{}", version)
48 write!(f, "\\#{}:{}", self.name, version)
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 try!(write!(f, "{}", self.path));
54 let version = match self.version {
56 Some(ref version) => version.as_slice(),
58 if self.path == self.name ||
61 .ends_with(format!("/{}", self.name).as_slice()) {
62 write!(f, "#{}", version)
64 write!(f, "#{}:{}", self.name, version)
69 impl FromStr for CrateId {
70 fn from_str(s: &str) -> Option<CrateId> {
71 let pieces: Vec<&str> = s.splitn('#', 1).collect();
72 let path = pieces.get(0).to_string();
74 if path.as_slice().starts_with("/") || path.as_slice().ends_with("/") ||
75 path.as_slice().starts_with(".") || path.is_empty() {
79 let path_pieces: Vec<&str> = path.as_slice()
82 let inferred_name = *path_pieces.get(0);
84 let (name, version) = if pieces.len() == 1 {
85 (inferred_name.to_string(), None)
87 let hash_pieces: Vec<&str> = pieces.get(1)
90 let (hash_name, hash_version) = if hash_pieces.len() == 1 {
91 ("", *hash_pieces.get(0))
93 (*hash_pieces.get(0), *hash_pieces.get(1))
96 let name = if !hash_name.is_empty() {
99 inferred_name.to_string()
102 let version = if !hash_version.is_empty() {
103 if hash_version == "0.0" {
106 Some(hash_version.to_string())
116 path: path.to_string(),
124 pub fn version_or_default<'a>(&'a self) -> &'a str {
127 Some(ref version) => version.as_slice(),
131 pub fn short_name_with_version(&self) -> String {
132 (format!("{}-{}", self.name, self.version_or_default())).to_string()
135 pub fn matches(&self, other: &CrateId) -> bool {
136 // FIXME: why does this not match on `path`?
137 if self.name != other.name { return false }
138 match (&self.version, &other.version) {
139 (&Some(ref v1), &Some(ref v2)) => v1 == v2,
147 let crateid: CrateId = from_str("foo").expect("valid crateid");
148 assert_eq!(crateid.name, "foo".to_string());
149 assert_eq!(crateid.version, None);
150 assert_eq!(crateid.path, "foo".to_string());
154 fn bare_name_single_char() {
155 let crateid: CrateId = from_str("f").expect("valid crateid");
156 assert_eq!(crateid.name, "f".to_string());
157 assert_eq!(crateid.version, None);
158 assert_eq!(crateid.path, "f".to_string());
163 let crateid: Option<CrateId> = from_str("");
164 assert!(crateid.is_none());
169 let crateid: CrateId = from_str("example.com/foo/bar").expect("valid crateid");
170 assert_eq!(crateid.name, "bar".to_string());
171 assert_eq!(crateid.version, None);
172 assert_eq!(crateid.path, "example.com/foo/bar".to_string());
176 fn simple_version() {
177 let crateid: CrateId = from_str("foo#1.0").expect("valid crateid");
178 assert_eq!(crateid.name, "foo".to_string());
179 assert_eq!(crateid.version, Some("1.0".to_string()));
180 assert_eq!(crateid.path, "foo".to_string());
185 let crateid: Option<CrateId> = from_str("/foo/bar");
186 assert!(crateid.is_none());
190 fn path_ends_with_slash() {
191 let crateid: Option<CrateId> = from_str("foo/bar/");
192 assert!(crateid.is_none());
196 fn path_and_version() {
197 let crateid: CrateId = from_str("example.com/foo/bar#1.0").expect("valid crateid");
198 assert_eq!(crateid.name, "bar".to_string());
199 assert_eq!(crateid.version, Some("1.0".to_string()));
200 assert_eq!(crateid.path, "example.com/foo/bar".to_string());
205 let crateid: CrateId = from_str("a/b#1").expect("valid crateid");
206 assert_eq!(crateid.name, "b".to_string());
207 assert_eq!(crateid.version, Some("1".to_string()));
208 assert_eq!(crateid.path, "a/b".to_string());
212 fn missing_version() {
213 let crateid: CrateId = from_str("foo#").expect("valid crateid");
214 assert_eq!(crateid.name, "foo".to_string());
215 assert_eq!(crateid.version, None);
216 assert_eq!(crateid.path, "foo".to_string());
221 let crateid: CrateId = from_str("foo/rust-bar#bar:1.0").expect("valid crateid");
222 assert_eq!(crateid.name, "bar".to_string());
223 assert_eq!(crateid.version, Some("1.0".to_string()));
224 assert_eq!(crateid.path, "foo/rust-bar".to_string());
229 let crateid: CrateId = from_str("foo/bar#:1.0").expect("valid crateid");
230 assert_eq!(crateid.name, "bar".to_string());
231 assert_eq!(crateid.version, Some("1.0".to_string()));
232 assert_eq!(crateid.path, "foo/bar".to_string());