88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
11+ /// PkgIds identify crates and include the crate name and optionall a path and
12+ /// version. In the full form, they look like relative URLs. Example:
13+ /// `github.com/mozilla/rust#std:1.0` would be a package ID with a path of
14+ /// `gitub.com/mozilla/rust` and a crate name of `std` with a version of
15+ /// `1.0`. If no crate name is given after the hash, the name is inferred to
16+ /// be the last component of the path. If no version is given, it is inferred
17+ /// to be `0.0`.
1118#[ deriving( Clone , Eq ) ]
1219pub struct PkgId {
20+ /// A path which represents the codes origin. By convention this is the
21+ /// URL, without `http://` or `https://` prefix, to the crate's repository
1322 path : ~str ,
23+ /// The name of the crate.
1424 name : ~str ,
25+ /// The version of the crate.
1526 version : Option < ~str > ,
1627}
1728
@@ -21,62 +32,55 @@ impl ToStr for PkgId {
2132 None => "0.0" ,
2233 Some ( ref version) => version. as_slice ( ) ,
2334 } ;
24- if self . path . is_empty ( ) {
25- format ! ( "{}\\ #{}" , self . name , version)
35+ if self . path == self . name || self . path . ends_with ( format ! ( "/{}" , self . name ) ) {
36+ format ! ( "{}\\ #{}" , self . path , version)
2637 } else {
27- format ! ( "{}/{} \\ #{}" , self . path, self . name, version)
38+ format ! ( "{}\\ #{}: {}" , self . path, self . name, version)
2839 }
2940 }
3041}
3142
3243impl FromStr for PkgId {
3344 fn from_str ( s : & str ) -> Option < PkgId > {
34- let hash_idx = match s. find ( '#' ) {
35- None => s. len ( ) ,
36- Some ( idx) => idx,
37- } ;
38- let prefix = s. slice_to ( hash_idx) ;
39- let name_idx = match prefix. rfind ( '/' ) {
40- None => 0 ,
41- Some ( idx) => idx + 1 ,
42- } ;
43- if name_idx >= prefix. len ( ) {
44- return None ;
45- }
46- let name = prefix. slice_from ( name_idx) ;
47- if name. len ( ) <= 0 {
48- return None ;
49- }
45+ let pieces: ~[ & str ] = s. splitn ( '#' , 1 ) . collect ( ) ;
46+ let path = pieces[ 0 ] . to_owned ( ) ;
5047
51- let path = if name_idx == 0 {
52- ""
53- } else {
54- prefix. slice_to ( name_idx - 1 )
55- } ;
56- let check_path = Path :: new ( path) ;
57- if !check_path. is_relative ( ) {
48+ if path. starts_with ( "/" ) || path. ends_with ( "/" ) ||
49+ path. starts_with ( "." ) || path. is_empty ( ) {
5850 return None ;
5951 }
6052
61- let version = match s. find ( '#' ) {
62- None => None ,
63- Some ( idx) => {
64- if idx >= s. len ( ) {
65- None
66- } else {
67- let v = s. slice_from ( idx + 1 ) ;
68- if v. is_empty ( ) {
69- None
70- } else {
71- Some ( v. to_owned ( ) )
72- }
73- }
74- }
53+ let path_pieces: ~[ & str ] = path. rsplitn ( '/' , 1 ) . collect ( ) ;
54+ let inferred_name = path_pieces[ 0 ] ;
55+
56+ let ( name, version) = if pieces. len ( ) == 1 {
57+ ( inferred_name. to_owned ( ) , None )
58+ } else {
59+ let hash_pieces: ~[ & str ] = pieces[ 1 ] . splitn ( ':' , 1 ) . collect ( ) ;
60+ let ( hash_name, hash_version) = if hash_pieces. len ( ) == 1 {
61+ ( "" , hash_pieces[ 0 ] )
62+ } else {
63+ ( hash_pieces[ 0 ] , hash_pieces[ 1 ] )
64+ } ;
65+
66+ let name = if !hash_name. is_empty ( ) {
67+ hash_name. to_owned ( )
68+ } else {
69+ inferred_name. to_owned ( )
70+ } ;
71+
72+ let version = if !hash_version. is_empty ( ) {
73+ Some ( hash_version. to_owned ( ) )
74+ } else {
75+ None
76+ } ;
77+
78+ ( name, version)
7579 } ;
7680
77- Some ( PkgId {
78- path : path. to_owned ( ) ,
79- name : name. to_owned ( ) ,
81+ Some ( PkgId {
82+ path : path,
83+ name : name,
8084 version : version,
8185 } )
8286 }
@@ -96,15 +100,15 @@ fn bare_name() {
96100 let pkgid: PkgId = from_str ( "foo" ) . expect ( "valid pkgid" ) ;
97101 assert_eq ! ( pkgid. name, ~"foo");
98102 assert_eq!(pkgid.version, None);
99- assert_eq!(pkgid.path, ~" ");
103+ assert_eq!(pkgid.path, ~" foo ");
100104}
101105
102106#[test]
103107fn bare_name_single_char() {
104108 let pkgid: PkgId = from_str(" f").expect(" valid pkgid");
105109 assert_eq!(pkgid.name, ~" f");
106110 assert_eq!(pkgid.version, None);
107- assert_eq!(pkgid.path, ~" ");
111+ assert_eq!(pkgid.path, ~" f ");
108112}
109113
110114#[test]
@@ -118,15 +122,15 @@ fn simple_path() {
118122 let pkgid: PkgId = from_str(" example. com/foo/bar").expect(" valid pkgid");
119123 assert_eq!(pkgid.name, ~" bar");
120124 assert_eq!(pkgid.version, None);
121- assert_eq!(pkgid.path, ~" example. com/foo");
125+ assert_eq!(pkgid.path, ~" example. com/foo/bar ");
122126}
123127
124128#[test]
125129fn simple_version() {
126130 let pkgid: PkgId = from_str(" foo#1.0 ").expect(" valid pkgid");
127131 assert_eq!(pkgid.name, ~" foo");
128132 assert_eq!(pkgid.version, Some(~" 1.0 "));
129- assert_eq!(pkgid.path, ~" ");
133+ assert_eq!(pkgid.path, ~" foo ");
130134}
131135
132136#[test]
@@ -135,26 +139,48 @@ fn absolute_path() {
135139 assert!(pkgid.is_none());
136140}
137141
142+ #[test]
143+ fn path_ends_with_slash() {
144+ let pkgid: Option<PkgId> = from_str(" foo/bar/");
145+ assert!(pkgid.is_none());
146+ }
147+
138148#[test]
139149fn path_and_version() {
140150 let pkgid: PkgId = from_str(" example. com/foo/bar#1.0 ").expect(" valid pkgid");
141151 assert_eq!(pkgid.name, ~" bar");
142152 assert_eq!(pkgid.version, Some(~" 1.0 "));
143- assert_eq!(pkgid.path, ~" example. com/foo");
153+ assert_eq!(pkgid.path, ~" example. com/foo/bar ");
144154}
145155
146156#[test]
147157fn single_chars() {
148158 let pkgid: PkgId = from_str(" a/b#1 ").expect(" valid pkgid");
149159 assert_eq!(pkgid.name, ~" b");
150160 assert_eq!(pkgid.version, Some(~" 1 "));
151- assert_eq!(pkgid.path, ~" a");
161+ assert_eq!(pkgid.path, ~" a/ b ");
152162}
153163
154164#[test]
155165fn missing_version() {
156166 let pkgid: PkgId = from_str(" foo#").expect(" valid pkgid");
157167 assert_eq!(pkgid.name, ~" foo");
158168 assert_eq!(pkgid.version, None);
159- assert_eq!(pkgid.path, ~" " ) ;
160- }
169+ assert_eq!(pkgid.path, ~" foo");
170+ }
171+
172+ #[test]
173+ fn path_and_name() {
174+ let pkgid: PkgId = from_str(" foo/rust-bar#bar: 1.0 ").expect(" valid pkgid");
175+ assert_eq!(pkgid.name, ~" bar");
176+ assert_eq!(pkgid.version, Some(~" 1.0 "));
177+ assert_eq!(pkgid.path, ~" foo/rust-bar");
178+ }
179+
180+ #[test]
181+ fn empty_name() {
182+ let pkgid: PkgId = from_str(" foo/bar#: 1.0 ").expect(" valid pkgid");
183+ assert_eq!(pkgid.name, ~" bar");
184+ assert_eq!(pkgid.version, Some(~" 1.0 "));
185+ assert_eq!(pkgid.path, ~" foo/bar" ) ;
186+ }
0 commit comments