]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/crateid.rs
d5e0a4e80d4480d59b2c63844ccd0f34d12989e1
[rust.git] / src / libsyntax / crateid.rs
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.
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 /// `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
19 /// to be `0.0`.
20
21 use std::from_str::FromStr;
22
23 #[deriving(Clone, PartialEq)]
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: String,
28     /// The name of the crate.
29     pub name: String,
30     /// The version of the crate.
31     pub version: Option<String>,
32 }
33
34 impl fmt::Show for CrateId {
35     #[cfg(stage0)]
36     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37         try!(write!(f, "{}", self.path));
38         let version = match self.version {
39             None => "0.0",
40             Some(ref version) => version.as_slice(),
41         };
42         if self.path == self.name ||
43                 self.path
44                     .as_slice()
45                     .ends_with(format!("/{}", self.name).as_slice()) {
46             write!(f, "\\#{}", version)
47         } else {
48             write!(f, "\\#{}:{}", self.name, version)
49         }
50     }
51     #[cfg(not(stage0))]
52     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53         try!(write!(f, "{}", self.path));
54         let version = match self.version {
55             None => "0.0",
56             Some(ref version) => version.as_slice(),
57         };
58         if self.path == self.name ||
59                 self.path
60                     .as_slice()
61                     .ends_with(format!("/{}", self.name).as_slice()) {
62             write!(f, "#{}", version)
63         } else {
64             write!(f, "#{}:{}", self.name, version)
65         }
66     }
67 }
68
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();
73
74         if path.as_slice().starts_with("/") || path.as_slice().ends_with("/") ||
75             path.as_slice().starts_with(".") || path.is_empty() {
76             return None;
77         }
78
79         let path_pieces: Vec<&str> = path.as_slice()
80                                          .rsplitn('/', 1)
81                                          .collect();
82         let inferred_name = *path_pieces.get(0);
83
84         let (name, version) = if pieces.len() == 1 {
85             (inferred_name.to_string(), None)
86         } else {
87             let hash_pieces: Vec<&str> = pieces.get(1)
88                                                .splitn(':', 1)
89                                                .collect();
90             let (hash_name, hash_version) = if hash_pieces.len() == 1 {
91                 ("", *hash_pieces.get(0))
92             } else {
93                 (*hash_pieces.get(0), *hash_pieces.get(1))
94             };
95
96             let name = if !hash_name.is_empty() {
97                 hash_name.to_string()
98             } else {
99                 inferred_name.to_string()
100             };
101
102             let version = if !hash_version.is_empty() {
103                 if hash_version == "0.0" {
104                     None
105                 } else {
106                     Some(hash_version.to_string())
107                 }
108             } else {
109                 None
110             };
111
112             (name, version)
113         };
114
115         Some(CrateId {
116             path: path.to_string(),
117             name: name,
118             version: version,
119         })
120     }
121 }
122
123 impl CrateId {
124     pub fn version_or_default<'a>(&'a self) -> &'a str {
125         match self.version {
126             None => "0.0",
127             Some(ref version) => version.as_slice(),
128         }
129     }
130
131     pub fn short_name_with_version(&self) -> String {
132         (format!("{}-{}", self.name, self.version_or_default())).to_string()
133     }
134
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,
140             _ => true,
141         }
142     }
143 }
144
145 #[test]
146 fn bare_name() {
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());
151 }
152
153 #[test]
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());
159 }
160
161 #[test]
162 fn empty_crateid() {
163     let crateid: Option<CrateId> = from_str("");
164     assert!(crateid.is_none());
165 }
166
167 #[test]
168 fn simple_path() {
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());
173 }
174
175 #[test]
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());
181 }
182
183 #[test]
184 fn absolute_path() {
185     let crateid: Option<CrateId> = from_str("/foo/bar");
186     assert!(crateid.is_none());
187 }
188
189 #[test]
190 fn path_ends_with_slash() {
191     let crateid: Option<CrateId> = from_str("foo/bar/");
192     assert!(crateid.is_none());
193 }
194
195 #[test]
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());
201 }
202
203 #[test]
204 fn single_chars() {
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());
209 }
210
211 #[test]
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());
217 }
218
219 #[test]
220 fn path_and_name() {
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());
225 }
226
227 #[test]
228 fn empty_name() {
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());
233 }