Skip to content

Commit

Permalink
Change pkgid parser to allow overriding the inferred crate name.
Browse files Browse the repository at this point in the history
Previously the a pkgid of `foo/rust-bar#1.0` implied a crate name of
`rust-bar` and didn't allow this to be overridden. Now you can override the
inferred crate name with `foo/rust-bar#bar:1.0`.
  • Loading branch information
metajack committed Dec 17, 2013
1 parent 000cda6 commit 66e049e
Showing 1 changed file with 77 additions and 51 deletions.
128 changes: 77 additions & 51 deletions src/libsyntax/pkgid.rs
Expand Up @@ -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>,
}

Expand All @@ -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,
})
}
Expand All @@ -96,15 +100,15 @@ 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]
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]
Expand All @@ -118,15 +122,15 @@ 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]
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]
Expand All @@ -135,26 +139,48 @@ fn absolute_path() {
assert!(pkgid.is_none());
}

#[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]
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]
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, ~"");
}
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");
}

0 comments on commit 66e049e

Please sign in to comment.