(index<- ) ./libsyntax/crateid.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
1 // Copyright 2013 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.
4 //
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.
10
11 use std::fmt;
12
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 /// `gitub.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
19 /// to be `0.0`.
20
21 use std::from_str::FromStr;
22
23 #[deriving(Clone, Eq)]
24 pub struct CrateId {
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
27 pub path: StrBuf,
28 /// The name of the crate.
29 pub name: StrBuf,
30 /// The version of the crate.
31 pub version: Option<StrBuf>,
32 }
33
34 impl fmt::Show for CrateId {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 try!(write!(f.buf, "{}", self.path));
37 let version = match self.version {
38 None => "0.0",
39 Some(ref version) => version.as_slice(),
40 };
41 if self.path == self.name ||
42 self.path.as_slice().ends_with(format!("/{}", self.name)) {
43 write!(f.buf, "\\#{}", version)
44 } else {
45 write!(f.buf, "\\#{}:{}", self.name, version)
46 }
47 }
48 }
49
50 impl FromStr for CrateId {
51 fn from_str(s: &str) -> Option<CrateId> {
52 let pieces: Vec<&str> = s.splitn('#', 1).collect();
53 let path = pieces.get(0).to_owned();
54
55 if path.starts_with("/") || path.ends_with("/") ||
56 path.starts_with(".") || path.is_empty() {
57 return None;
58 }
59
60 let path_pieces: Vec<&str> = path.rsplitn('/', 1).collect();
61 let inferred_name = *path_pieces.get(0);
62
63 let (name, version) = if pieces.len() == 1 {
64 (inferred_name.to_strbuf(), None)
65 } else {
66 let hash_pieces: Vec<&str> = pieces.get(1)
67 .splitn(':', 1)
68 .collect();
69 let (hash_name, hash_version) = if hash_pieces.len() == 1 {
70 ("", *hash_pieces.get(0))
71 } else {
72 (*hash_pieces.get(0), *hash_pieces.get(1))
73 };
74
75 let name = if !hash_name.is_empty() {
76 hash_name.to_strbuf()
77 } else {
78 inferred_name.to_strbuf()
79 };
80
81 let version = if !hash_version.is_empty() {
82 if hash_version == "0.0" {
83 None
84 } else {
85 Some(hash_version.to_strbuf())
86 }
87 } else {
88 None
89 };
90
91 (name, version)
92 };
93
94 Some(CrateId {
95 path: path.to_strbuf(),
96 name: name,
97 version: version,
98 })
99 }
100 }
101
102 impl CrateId {
103 pub fn version_or_default<'a>(&'a self) -> &'a str {
104 match self.version {
105 None => "0.0",
106 Some(ref version) => version.as_slice(),
107 }
108 }
109
110 pub fn short_name_with_version(&self) -> StrBuf {
111 (format!("{}-{}", self.name, self.version_or_default())).to_strbuf()
112 }
113
114 pub fn matches(&self, other: &CrateId) -> bool {
115 // FIXME: why does this not match on `path`?
116 if self.name != other.name { return false }
117 match (&self.version, &other.version) {
118 (&Some(ref v1), &Some(ref v2)) => v1 == v2,
119 _ => true,
120 }
121 }
122 }
123
124 #[test]
125 fn bare_name() {
126 let crateid: CrateId = from_str("foo").expect("valid crateid");
127 assert_eq!(crateid.name, "foo".to_strbuf());
128 assert_eq!(crateid.version, None);
129 assert_eq!(crateid.path, "foo".to_strbuf());
130 }
131
132 #[test]
133 fn bare_name_single_char() {
134 let crateid: CrateId = from_str("f").expect("valid crateid");
135 assert_eq!(crateid.name, "f".to_strbuf());
136 assert_eq!(crateid.version, None);
137 assert_eq!(crateid.path, "f".to_strbuf());
138 }
139
140 #[test]
141 fn empty_crateid() {
142 let crateid: Option<CrateId> = from_str("");
143 assert!(crateid.is_none());
144 }
145
146 #[test]
147 fn simple_path() {
148 let crateid: CrateId = from_str("example.com/foo/bar").expect("valid crateid");
149 assert_eq!(crateid.name, "bar".to_strbuf());
150 assert_eq!(crateid.version, None);
151 assert_eq!(crateid.path, "example.com/foo/bar".to_strbuf());
152 }
153
154 #[test]
155 fn simple_version() {
156 let crateid: CrateId = from_str("foo#1.0").expect("valid crateid");
157 assert_eq!(crateid.name, "foo".to_strbuf());
158 assert_eq!(crateid.version, Some("1.0".to_strbuf()));
159 assert_eq!(crateid.path, "foo".to_strbuf());
160 }
161
162 #[test]
163 fn absolute_path() {
164 let crateid: Option<CrateId> = from_str("/foo/bar");
165 assert!(crateid.is_none());
166 }
167
168 #[test]
169 fn path_ends_with_slash() {
170 let crateid: Option<CrateId> = from_str("foo/bar/");
171 assert!(crateid.is_none());
172 }
173
174 #[test]
175 fn path_and_version() {
176 let crateid: CrateId = from_str("example.com/foo/bar#1.0").expect("valid crateid");
177 assert_eq!(crateid.name, "bar".to_strbuf());
178 assert_eq!(crateid.version, Some("1.0".to_strbuf()));
179 assert_eq!(crateid.path, "example.com/foo/bar".to_strbuf());
180 }
181
182 #[test]
183 fn single_chars() {
184 let crateid: CrateId = from_str("a/b#1").expect("valid crateid");
185 assert_eq!(crateid.name, "b".to_strbuf());
186 assert_eq!(crateid.version, Some("1".to_strbuf()));
187 assert_eq!(crateid.path, "a/b".to_strbuf());
188 }
189
190 #[test]
191 fn missing_version() {
192 let crateid: CrateId = from_str("foo#").expect("valid crateid");
193 assert_eq!(crateid.name, "foo".to_strbuf());
194 assert_eq!(crateid.version, None);
195 assert_eq!(crateid.path, "foo".to_strbuf());
196 }
197
198 #[test]
199 fn path_and_name() {
200 let crateid: CrateId = from_str("foo/rust-bar#bar:1.0").expect("valid crateid");
201 assert_eq!(crateid.name, "bar".to_strbuf());
202 assert_eq!(crateid.version, Some("1.0".to_strbuf()));
203 assert_eq!(crateid.path, "foo/rust-bar".to_strbuf());
204 }
205
206 #[test]
207 fn empty_name() {
208 let crateid: CrateId = from_str("foo/bar#:1.0").expect("valid crateid");
209 assert_eq!(crateid.name, "bar".to_strbuf());
210 assert_eq!(crateid.version, Some("1.0".to_strbuf()));
211 assert_eq!(crateid.path, "foo/bar".to_strbuf());
212 }