]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/crateid.rs
Fix typo "gihub" in libsyntax/crateid.
[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     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36         try!(write!(f, "{}", 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
43                     .as_slice()
44                     .ends_with(format!("/{}", self.name).as_slice()) {
45             write!(f, "\\#{}", version)
46         } else {
47             write!(f, "\\#{}:{}", self.name, version)
48         }
49     }
50 }
51
52 impl FromStr for CrateId {
53     fn from_str(s: &str) -> Option<CrateId> {
54         let pieces: Vec<&str> = s.splitn('#', 1).collect();
55         let path = pieces.get(0).to_string();
56
57         if path.as_slice().starts_with("/") || path.as_slice().ends_with("/") ||
58             path.as_slice().starts_with(".") || path.is_empty() {
59             return None;
60         }
61
62         let path_pieces: Vec<&str> = path.as_slice()
63                                          .rsplitn('/', 1)
64                                          .collect();
65         let inferred_name = *path_pieces.get(0);
66
67         let (name, version) = if pieces.len() == 1 {
68             (inferred_name.to_string(), None)
69         } else {
70             let hash_pieces: Vec<&str> = pieces.get(1)
71                                                .splitn(':', 1)
72                                                .collect();
73             let (hash_name, hash_version) = if hash_pieces.len() == 1 {
74                 ("", *hash_pieces.get(0))
75             } else {
76                 (*hash_pieces.get(0), *hash_pieces.get(1))
77             };
78
79             let name = if !hash_name.is_empty() {
80                 hash_name.to_string()
81             } else {
82                 inferred_name.to_string()
83             };
84
85             let version = if !hash_version.is_empty() {
86                 if hash_version == "0.0" {
87                     None
88                 } else {
89                     Some(hash_version.to_string())
90                 }
91             } else {
92                 None
93             };
94
95             (name, version)
96         };
97
98         Some(CrateId {
99             path: path.to_string(),
100             name: name,
101             version: version,
102         })
103     }
104 }
105
106 impl CrateId {
107     pub fn version_or_default<'a>(&'a self) -> &'a str {
108         match self.version {
109             None => "0.0",
110             Some(ref version) => version.as_slice(),
111         }
112     }
113
114     pub fn short_name_with_version(&self) -> String {
115         (format!("{}-{}", self.name, self.version_or_default())).to_string()
116     }
117
118     pub fn matches(&self, other: &CrateId) -> bool {
119         // FIXME: why does this not match on `path`?
120         if self.name != other.name { return false }
121         match (&self.version, &other.version) {
122             (&Some(ref v1), &Some(ref v2)) => v1 == v2,
123             _ => true,
124         }
125     }
126 }
127
128 #[test]
129 fn bare_name() {
130     let crateid: CrateId = from_str("foo").expect("valid crateid");
131     assert_eq!(crateid.name, "foo".to_string());
132     assert_eq!(crateid.version, None);
133     assert_eq!(crateid.path, "foo".to_string());
134 }
135
136 #[test]
137 fn bare_name_single_char() {
138     let crateid: CrateId = from_str("f").expect("valid crateid");
139     assert_eq!(crateid.name, "f".to_string());
140     assert_eq!(crateid.version, None);
141     assert_eq!(crateid.path, "f".to_string());
142 }
143
144 #[test]
145 fn empty_crateid() {
146     let crateid: Option<CrateId> = from_str("");
147     assert!(crateid.is_none());
148 }
149
150 #[test]
151 fn simple_path() {
152     let crateid: CrateId = from_str("example.com/foo/bar").expect("valid crateid");
153     assert_eq!(crateid.name, "bar".to_string());
154     assert_eq!(crateid.version, None);
155     assert_eq!(crateid.path, "example.com/foo/bar".to_string());
156 }
157
158 #[test]
159 fn simple_version() {
160     let crateid: CrateId = from_str("foo#1.0").expect("valid crateid");
161     assert_eq!(crateid.name, "foo".to_string());
162     assert_eq!(crateid.version, Some("1.0".to_string()));
163     assert_eq!(crateid.path, "foo".to_string());
164 }
165
166 #[test]
167 fn absolute_path() {
168     let crateid: Option<CrateId> = from_str("/foo/bar");
169     assert!(crateid.is_none());
170 }
171
172 #[test]
173 fn path_ends_with_slash() {
174     let crateid: Option<CrateId> = from_str("foo/bar/");
175     assert!(crateid.is_none());
176 }
177
178 #[test]
179 fn path_and_version() {
180     let crateid: CrateId = from_str("example.com/foo/bar#1.0").expect("valid crateid");
181     assert_eq!(crateid.name, "bar".to_string());
182     assert_eq!(crateid.version, Some("1.0".to_string()));
183     assert_eq!(crateid.path, "example.com/foo/bar".to_string());
184 }
185
186 #[test]
187 fn single_chars() {
188     let crateid: CrateId = from_str("a/b#1").expect("valid crateid");
189     assert_eq!(crateid.name, "b".to_string());
190     assert_eq!(crateid.version, Some("1".to_string()));
191     assert_eq!(crateid.path, "a/b".to_string());
192 }
193
194 #[test]
195 fn missing_version() {
196     let crateid: CrateId = from_str("foo#").expect("valid crateid");
197     assert_eq!(crateid.name, "foo".to_string());
198     assert_eq!(crateid.version, None);
199     assert_eq!(crateid.path, "foo".to_string());
200 }
201
202 #[test]
203 fn path_and_name() {
204     let crateid: CrateId = from_str("foo/rust-bar#bar:1.0").expect("valid crateid");
205     assert_eq!(crateid.name, "bar".to_string());
206     assert_eq!(crateid.version, Some("1.0".to_string()));
207     assert_eq!(crateid.path, "foo/rust-bar".to_string());
208 }
209
210 #[test]
211 fn empty_name() {
212     let crateid: CrateId = from_str("foo/bar#:1.0").expect("valid crateid");
213     assert_eq!(crateid.name, "bar".to_string());
214     assert_eq!(crateid.version, Some("1.0".to_string()));
215     assert_eq!(crateid.path, "foo/bar".to_string());
216 }