diff --git a/doc/rust.md b/doc/rust.md index aa4ce002e021b..4e2868a764fb5 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -790,7 +790,7 @@ extern mod extra; // equivalent to: extern mod extra = "extra"; extern mod rustextra = "extra"; // linking to 'extra' under another name -extern mod foo = "some/where/foo#1.0"; // a full package ID for rustpkg +extern mod foo = "some/where/rust-foo#foo:1.0"; // a full package ID for rustpkg ~~~~ ##### Use declarations diff --git a/src/libsyntax/pkgid.rs b/src/libsyntax/pkgid.rs index 1e840ca700bd1..3c10e5199c926 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 { - 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] @@ -135,12 +139,18 @@ fn absolute_path() { assert!(pkgid.is_none()); } +#[test] +fn path_ends_with_slash() { + let pkgid: Option = 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"); +}