about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/pkgid.rs128
1 files changed, 77 insertions, 51 deletions
diff --git a/src/libsyntax/pkgid.rs b/src/libsyntax/pkgid.rs
index 1e840ca700b..3c10e5199c9 100644
--- a/src/libsyntax/pkgid.rs
+++ b/src/libsyntax/pkgid.rs
@@ -8,10 +8,21 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+/// PkgIds identify crates and include the crate name and optionall a path and
+/// version. In the full form, they look like relative URLs. Example:
+/// `github.com/mozilla/rust#std:1.0` would be a package ID with a path of
+/// `gitub.com/mozilla/rust` and a crate name of `std` with a version of
+/// `1.0`. If no crate name is given after the hash, the name is inferred to
+/// be the last component of the path. If no version is given, it is inferred
+/// to be `0.0`.
 #[deriving(Clone, Eq)]
 pub struct PkgId {
+    /// A path which represents the codes origin. By convention this is the
+    /// URL, without `http://` or `https://` prefix, to the crate's repository
     path: ~str,
+    /// The name of the crate.
     name: ~str,
+    /// The version of the crate.
     version: Option<~str>,
 }
 
@@ -21,62 +32,55 @@ impl ToStr for PkgId {
             None => "0.0",
             Some(ref version) => version.as_slice(),
         };
-        if self.path.is_empty() {
-            format!("{}\\#{}", self.name, version)
+        if self.path == self.name || self.path.ends_with(format!("/{}", self.name)) {
+            format!("{}\\#{}", self.path, version)
         } else {
-            format!("{}/{}\\#{}", self.path, self.name, version)
+            format!("{}\\#{}:{}", self.path, self.name, version)
         }
     }
 }
 
 impl FromStr for PkgId {
     fn from_str(s: &str) -> Option<PkgId> {
-        let hash_idx = match s.find('#') {
-            None => s.len(),
-            Some(idx) => idx,
-        };
-        let prefix = s.slice_to(hash_idx);
-        let name_idx = match prefix.rfind('/') {
-            None => 0,
-            Some(idx) => idx + 1,
-        };
-        if name_idx >= prefix.len() {
-            return None;
-        }
-        let name = prefix.slice_from(name_idx);
-        if name.len() <= 0 {
-            return None;
-        }
+        let pieces: ~[&str] = s.splitn('#', 1).collect();
+        let path = pieces[0].to_owned();
 
-        let path = if name_idx == 0 {
-            ""
-        } else {
-            prefix.slice_to(name_idx - 1)
-        };
-        let check_path = Path::new(path);
-        if !check_path.is_relative() {
+        if path.starts_with("/") || path.ends_with("/") ||
+            path.starts_with(".") || path.is_empty() {
             return None;
         }
 
-        let version = match s.find('#') {
-            None => None,
-            Some(idx) => {
-                if idx >= s.len() {
-                    None
-                } else {
-                    let v = s.slice_from(idx + 1);
-                    if v.is_empty() {
-                        None
-                    } else {
-                        Some(v.to_owned())
-                    }
-                }
-            }
+        let path_pieces: ~[&str] = path.rsplitn('/', 1).collect();
+        let inferred_name = path_pieces[0];
+
+        let (name, version) = if pieces.len() == 1 {
+            (inferred_name.to_owned(), None)
+        } else {
+            let hash_pieces: ~[&str] = pieces[1].splitn(':', 1).collect();
+            let (hash_name, hash_version) = if hash_pieces.len() == 1 {
+                ("", hash_pieces[0])
+            } else {
+                (hash_pieces[0], hash_pieces[1])
+            };
+
+            let name = if !hash_name.is_empty() {
+                hash_name.to_owned()
+            } else {
+                inferred_name.to_owned()
+            };
+
+            let version = if !hash_version.is_empty() {
+                Some(hash_version.to_owned())
+            } else {
+                None
+            };
+
+            (name, version)
         };
 
-        Some(PkgId{
-            path: path.to_owned(),
-            name: name.to_owned(),
+        Some(PkgId {
+            path: path,
+            name: name,
             version: version,
         })
     }
@@ -96,7 +100,7 @@ fn bare_name() {
     let pkgid: PkgId = from_str("foo").expect("valid pkgid");
     assert_eq!(pkgid.name, ~"foo");
     assert_eq!(pkgid.version, None);
-    assert_eq!(pkgid.path, ~"");
+    assert_eq!(pkgid.path, ~"foo");
 }
 
 #[test]
@@ -104,7 +108,7 @@ fn bare_name_single_char() {
     let pkgid: PkgId = from_str("f").expect("valid pkgid");
     assert_eq!(pkgid.name, ~"f");
     assert_eq!(pkgid.version, None);
-    assert_eq!(pkgid.path, ~"");
+    assert_eq!(pkgid.path, ~"f");
 }
 
 #[test]
@@ -118,7 +122,7 @@ fn simple_path() {
     let pkgid: PkgId = from_str("example.com/foo/bar").expect("valid pkgid");
     assert_eq!(pkgid.name, ~"bar");
     assert_eq!(pkgid.version, None);
-    assert_eq!(pkgid.path, ~"example.com/foo");
+    assert_eq!(pkgid.path, ~"example.com/foo/bar");
 }
 
 #[test]
@@ -126,7 +130,7 @@ fn simple_version() {
     let pkgid: PkgId = from_str("foo#1.0").expect("valid pkgid");
     assert_eq!(pkgid.name, ~"foo");
     assert_eq!(pkgid.version, Some(~"1.0"));
-    assert_eq!(pkgid.path, ~"");
+    assert_eq!(pkgid.path, ~"foo");
 }
 
 #[test]
@@ -136,11 +140,17 @@ fn absolute_path() {
 }
 
 #[test]
+fn path_ends_with_slash() {
+    let pkgid: Option<PkgId> = from_str("foo/bar/");
+    assert!(pkgid.is_none());
+}
+
+#[test]
 fn path_and_version() {
     let pkgid: PkgId = from_str("example.com/foo/bar#1.0").expect("valid pkgid");
     assert_eq!(pkgid.name, ~"bar");
     assert_eq!(pkgid.version, Some(~"1.0"));
-    assert_eq!(pkgid.path, ~"example.com/foo");
+    assert_eq!(pkgid.path, ~"example.com/foo/bar");
 }
 
 #[test]
@@ -148,7 +158,7 @@ fn single_chars() {
     let pkgid: PkgId = from_str("a/b#1").expect("valid pkgid");
     assert_eq!(pkgid.name, ~"b");
     assert_eq!(pkgid.version, Some(~"1"));
-    assert_eq!(pkgid.path, ~"a");
+    assert_eq!(pkgid.path, ~"a/b");
 }
 
 #[test]
@@ -156,5 +166,21 @@ fn missing_version() {
     let pkgid: PkgId = from_str("foo#").expect("valid pkgid");
     assert_eq!(pkgid.name, ~"foo");
     assert_eq!(pkgid.version, None);
-    assert_eq!(pkgid.path, ~"");
-}
\ No newline at end of file
+    assert_eq!(pkgid.path, ~"foo");
+}
+
+#[test]
+fn path_and_name() {
+    let pkgid: PkgId = from_str("foo/rust-bar#bar:1.0").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"bar");
+    assert_eq!(pkgid.version, Some(~"1.0"));
+    assert_eq!(pkgid.path, ~"foo/rust-bar");
+}
+
+#[test]
+fn empty_name() {
+    let pkgid: PkgId = from_str("foo/bar#:1.0").expect("valid pkgid");
+    assert_eq!(pkgid.name, ~"bar");
+    assert_eq!(pkgid.version, Some(~"1.0"));
+    assert_eq!(pkgid.path, ~"foo/bar");
+}