]> git.lizzy.rs Git - rust.git/commitdiff
Rename PkgId to CrateId
authorLuis de Bethencourt <luis@debethencourt.com>
Sat, 28 Dec 2013 17:16:48 +0000 (12:16 -0500)
committerLuis de Bethencourt <luis@debethencourt.com>
Sun, 29 Dec 2013 20:25:32 +0000 (15:25 -0500)
13 files changed:
src/librustc/back/link.rs
src/librustc/metadata/common.rs
src/librustc/metadata/creader.rs
src/librustc/metadata/decoder.rs
src/librustc/metadata/encoder.rs
src/librustc/metadata/loader.rs
src/librustc/middle/trans/base.rs
src/librustc/middle/trans/debuginfo.rs
src/librustc/middle/trans/expr.rs
src/libsyntax/attr.rs
src/libsyntax/crateid.rs [new file with mode: 0644]
src/libsyntax/lib.rs
src/libsyntax/pkgid.rs [deleted file]

index cf4ed099cf26bc9f936476a980c3d7364acbb7ac..786c05de20465258a4d5f0b608c09854578032d4 100644 (file)
@@ -41,7 +41,7 @@
 use syntax::ast_map::{path, path_mod, path_name, path_pretty_name};
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
-use syntax::pkgid::PkgId;
+use syntax::crateid::CrateId;
 
 #[deriving(Clone, Eq)]
 pub enum output_type {
@@ -444,13 +444,13 @@ unsafe fn populate_llvm_passes(fpm: lib::llvm::PassManagerRef,
  *
  * So here is what we do:
  *
- *  - Consider the package id; every crate has one (specified with pkgid
+ *  - Consider the package id; every crate has one (specified with crate_id
  *    attribute).  If a package id isn't provided explicitly, we infer a
  *    versionless one from the output name. The version will end up being 0.0
  *    in this case. CNAME and CVERS are taken from this package id. For
  *    example, github.com/mozilla/CNAME#CVERS.
  *
- *  - Define CMH as SHA256(pkgid).
+ *  - Define CMH as SHA256(crateid).
  *
  *  - Define CMH8 as the first 8 characters of CMH.
  *
@@ -469,9 +469,9 @@ pub fn build_link_meta(sess: Session,
                        symbol_hasher: &mut Sha256)
                        -> LinkMeta {
     // This calculates CMH as defined above
-    fn crate_hash(symbol_hasher: &mut Sha256, pkgid: &PkgId) -> @str {
+    fn crate_hash(symbol_hasher: &mut Sha256, crateid: &CrateId) -> @str {
         symbol_hasher.reset();
-        symbol_hasher.input_str(pkgid.to_str());
+        symbol_hasher.input_str(crateid.to_str());
         truncated_hash_result(symbol_hasher).to_managed()
     }
 
@@ -487,10 +487,10 @@ fn crate_hash(symbol_hasher: &mut Sha256, pkgid: &PkgId) -> @str {
         Some(s) => s,
     };
 
-    let hash = crate_hash(symbol_hasher, &pkgid);
+    let hash = crate_hash(symbol_hasher, &crateid);
 
     LinkMeta {
-        pkgid: pkgid,
+        crateid: crateid,
         crate_hash: hash,
     }
 }
@@ -509,7 +509,7 @@ pub fn symbol_hash(tcx: ty::ctxt,
     // to be independent of one another in the crate.
 
     symbol_hasher.reset();
-    symbol_hasher.input_str(link_meta.pkgid.name);
+    symbol_hasher.input_str(link_meta.crateid.name);
     symbol_hasher.input_str("-");
     symbol_hasher.input_str(link_meta.crate_hash);
     symbol_hasher.input_str("-");
@@ -669,7 +669,7 @@ pub fn mangle_exported_name(ccx: &CrateContext,
     let hash = get_symbol_hash(ccx, t);
     return exported_name(ccx.sess, path,
                          hash,
-                         ccx.link_meta.pkgid.version_or_default());
+                         ccx.link_meta.crateid.version_or_default());
 }
 
 pub fn mangle_internal_name_by_type_only(ccx: &CrateContext,
@@ -710,9 +710,9 @@ pub fn mangle_internal_name_by_path(ccx: &CrateContext, path: path) -> ~str {
 
 pub fn output_lib_filename(lm: &LinkMeta) -> ~str {
     format!("{}-{}-{}",
-            lm.pkgid.name,
+            lm.crateid.name,
             lm.crate_hash.slice_chars(0, 8),
-            lm.pkgid.version_or_default())
+            lm.crateid.version_or_default())
 }
 
 pub fn get_cc_prog(sess: Session) -> ~str {
index bd6794b1d9ff76c92187587fcefa397e57174914..8b3d5062e15190c980f6d9d68f54b3f4f2b8f5db 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use std::cast;
-use syntax::pkgid::PkgId;
+use syntax::crateid::CrateId;
 
 // EBML enum definitions and utils shared by the encoder and decoder
 
@@ -206,6 +206,6 @@ pub fn from_uint(value : uint) -> Option<astencode_tag> {
 
 #[deriving(Clone)]
 pub struct LinkMeta {
-    pkgid: PkgId,
+    crateid: CrateId,
     crate_hash: @str,
 }
index 2c32c343ba86d8f66ec450c499f8833a60ca25ec..8fa1e5563947f3693a7702731604eca5246c9be0 100644 (file)
@@ -25,7 +25,7 @@
 use syntax::diagnostic::span_handler;
 use syntax::parse::token;
 use syntax::parse::token::ident_interner;
-use syntax::pkgid::PkgId;
+use syntax::crateid::CrateId;
 use syntax::visit;
 
 // Traverses an AST, reading all the information about use'd crates and extern
@@ -73,7 +73,7 @@ struct cache_entry {
     cnum: ast::CrateNum,
     span: Span,
     hash: @str,
-    pkgid: PkgId,
+    crateid: CrateId,
 }
 
 fn dump_crates(crate_cache: &[cache_entry]) {
@@ -89,10 +89,10 @@ fn warn_if_multiple_versions(e: &mut Env,
                              diag: @mut span_handler,
                              crate_cache: &[cache_entry]) {
     if crate_cache.len() != 0u {
-        let name = crate_cache[crate_cache.len() - 1].pkgid.name.clone();
+        let name = crate_cache[crate_cache.len() - 1].crateid.name.clone();
 
         let (matches, non_matches) = crate_cache.partitioned(|entry|
-            name == entry.pkgid.name);
+            name == entry.crateid.name);
 
         assert!(!matches.is_empty());
 
@@ -101,7 +101,7 @@ fn warn_if_multiple_versions(e: &mut Env,
                 format!("using multiple versions of crate `{}`", name));
             for match_ in matches.iter() {
                 diag.span_note(match_.span, "used here");
-                loader::note_pkgid_attr(diag, &match_.pkgid);
+                loader::note_crateid_attr(diag, &match_.crateid);
             }
         }
 
@@ -138,7 +138,7 @@ fn visit_view_item(e: &mut Env, i: &ast::view_item) {
                  ident, path_opt);
           let (name, version) = match path_opt {
               Some((path_str, _)) => {
-                  let crateid: Option<PkgId> = from_str(path_str);
+                  let crateid: Option<CrateId> = from_str(path_str);
                   match crateid {
                       None => (@"", @""),
                       Some(crateid) => {
@@ -245,12 +245,12 @@ fn visit_item(e: &Env, i: @ast::item) {
 fn existing_match(e: &Env, name: @str, version: @str, hash: &str) -> Option<ast::CrateNum> {
     let crate_cache = e.crate_cache.borrow();
     for c in crate_cache.get().iter() {
-        let pkgid_version = match c.pkgid.version {
+        let crateid_version = match c.crateid.version {
             None => @"0.0",
             Some(ref ver) => ver.to_managed(),
         };
-        if (name.is_empty() || c.pkgid.name.to_managed() == name) &&
-            (version.is_empty() || pkgid_version == version) &&
+        if (name.is_empty() || c.crateid.name.to_managed() == name) &&
+            (version.is_empty() || crateid_version == version) &&
             (hash.is_empty() || c.hash.as_slice() == hash) {
             return Some(c.cnum);
         }
@@ -293,7 +293,7 @@ fn resolve_crate(e: &mut Env,
                 cnum: cnum,
                 span: span,
                 hash: hash,
-                pkgid: pkgid,
+                crateid: crateid,
             });
         }
         e.next_crate_num += 1;
index a409887575a5e275efc4e19f8c7d7cc8a584ce7a..b4c19c771dc0aacb2c8e112a37e794f0b1ee9ffd 100644 (file)
@@ -1175,7 +1175,7 @@ pub fn get_crate_vers(data: &[u8]) -> @str {
     let attrs = decoder::get_crate_attributes(data);
     match attr::find_crateid(attrs) {
         None => @"0.0",
-        Some(pkgid) => pkgid.version_or_default().to_managed(),
+        Some(crateid) => crateid.version_or_default().to_managed(),
     }
 }
 
index 7bd87f5dff816148e04f3de621c3bfa5b3c1cef8..73b0ac46cbda96cffe978eea5f3bfd468c02ff15 100644 (file)
@@ -1559,19 +1559,19 @@ fn encode_attributes(ebml_w: &mut writer::Encoder, attrs: &[Attribute]) {
     ebml_w.end_tag();
 }
 
-// So there's a special crate attribute called 'pkgid' which defines the
+// So there's a special crate attribute called 'crate_id' which defines the
 // metadata that Rust cares about for linking crates. If the user didn't
 // provide it we will throw it in anyway with a default value.
 fn synthesize_crate_attrs(ecx: &EncodeContext,
                           crate: &Crate) -> ~[Attribute] {
 
-    fn synthesize_pkgid_attr(ecx: &EncodeContext) -> Attribute {
-        assert!(!ecx.link_meta.pkgid.name.is_empty());
+    fn synthesize_crateid_attr(ecx: &EncodeContext) -> Attribute {
+        assert!(!ecx.link_meta.crateid.name.is_empty());
 
         attr::mk_attr(
             attr::mk_name_value_item_str(
                 @"crate_id",
-                ecx.link_meta.pkgid.to_str().to_managed()))
+                ecx.link_meta.crateid.to_str().to_managed()))
     }
 
     let mut attrs = ~[];
@@ -1580,7 +1580,7 @@ fn synthesize_pkgid_attr(ecx: &EncodeContext) -> Attribute {
             attrs.push(*attr);
         }
     }
-    attrs.push(synthesize_pkgid_attr(ecx));
+    attrs.push(synthesize_crateid_attr(ecx));
 
     attrs
 }
index 7c6be22208eb77793c5ce0fae854fa1617e11087..481a43d1cedd2581b063b72c602dceaf941c5cd9 100644 (file)
@@ -21,7 +21,7 @@
 use syntax::codemap::Span;
 use syntax::diagnostic::span_handler;
 use syntax::parse::token::ident_interner;
-use syntax::pkgid::PkgId;
+use syntax::crateid::CrateId;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 
@@ -112,7 +112,7 @@ fn find_library_crate(&self) -> Option<Library> {
                             Some(cvec) =>
                                 if crate_matches(cvec.as_slice(), self.name,
                                                  self.version, self.hash) {
-                                    debug!("found {} with matching pkgid",
+                                    debug!("found {} with matching crate_id",
                                            path.display());
                                     let (rlib, dylib) = if file.ends_with(".rlib") {
                                         (Some(path.clone()), None)
@@ -126,7 +126,7 @@ fn find_library_crate(&self) -> Option<Library> {
                                     });
                                     FileMatches
                                 } else {
-                                    debug!("skipping {}, pkgid doesn't match",
+                                    debug!("skipping {}, crate_id doesn't match",
                                            path.display());
                                     FileDoesntMatch
                                 },
@@ -167,8 +167,8 @@ fn find_library_crate(&self) -> Option<Library> {
                     let attrs = decoder::get_crate_attributes(data);
                     match attr::find_crateid(attrs) {
                         None => {}
-                        Some(pkgid) => {
-                            note_pkgid_attr(self.sess.diagnostic(), &pkgid);
+                        Some(crateid) => {
+                            note_crateid_attr(self.sess.diagnostic(), &crateid);
                         }
                     }
                 }
@@ -231,9 +231,9 @@ fn dylibname(&self) -> (&'static str, &'static str) {
     }
 }
 
-pub fn note_pkgid_attr(diag: @mut span_handler,
-                       pkgid: &PkgId) {
-    diag.handler().note(format!("pkgid: {}", pkgid.to_str()));
+pub fn note_crateid_attr(diag: @mut span_handler,
+                       crateid: &CrateId) {
+    diag.handler().note(format!("crate_id: {}", crateid.to_str()));
 }
 
 fn crate_matches(crate_data: &[u8],
@@ -243,13 +243,13 @@ fn crate_matches(crate_data: &[u8],
     let attrs = decoder::get_crate_attributes(crate_data);
     match attr::find_crateid(attrs) {
         None => false,
-        Some(pkgid) => {
+        Some(crateid) => {
             if !hash.is_empty() {
                 let chash = decoder::get_crate_hash(crate_data);
                 if chash != hash { return false; }
             }
-            name == pkgid.name.to_managed() &&
-                (version.is_empty() || version == pkgid.version_or_default().to_managed())
+            name == crateid.name.to_managed() &&
+                (version.is_empty() || version == crateid.version_or_default().to_managed())
         }
     }
 }
index 0b2ee710e991324e011b552ddc82f303469f80c5..ce953ab3ab70f7d26ad4e2a3f5f83d189e1ea5fd 100644 (file)
@@ -3048,8 +3048,8 @@ pub fn decl_crate_map(sess: session::Session, mapmeta: LinkMeta,
     let sym_name = if is_top {
         ~"_rust_crate_map_toplevel"
     } else {
-        symname(sess, "_rust_crate_map_" + mapmeta.pkgid.name, mapmeta.crate_hash,
-                mapmeta.pkgid.version_or_default())
+        symname(sess, "_rust_crate_map_" + mapmeta.crateid.name, mapmeta.crate_hash,
+                mapmeta.crateid.version_or_default())
     };
 
     let slicetype = Type::struct_([int_type, int_type], false);
@@ -3168,8 +3168,8 @@ pub fn write_metadata(cx: &CrateContext, crate: &ast::Crate) -> ~[u8] {
                         flate::deflate_bytes(metadata);
     let llmeta = C_bytes(compressed);
     let llconst = C_struct([llmeta], false);
-    let name = format!("rust_metadata_{}_{}_{}", cx.link_meta.pkgid.name,
-                       cx.link_meta.pkgid.version_or_default(), cx.link_meta.crate_hash);
+    let name = format!("rust_metadata_{}_{}_{}", cx.link_meta.crateid.name,
+                       cx.link_meta.crateid.version_or_default(), cx.link_meta.crate_hash);
     let llglobal = name.with_c_str(|buf| {
         unsafe {
             llvm::LLVMAddGlobal(cx.metadata_llmod, val_ty(llconst).to_ref(), buf)
@@ -3205,7 +3205,7 @@ pub fn trans_crate(sess: session::Session,
     // crashes if the module identifer is same as other symbols
     // such as a function name in the module.
     // 1. http://llvm.org/bugs/show_bug.cgi?id=11479
-    let llmod_id = link_meta.pkgid.name.clone() + ".rc";
+    let llmod_id = link_meta.crateid.name.clone() + ".rc";
 
     let ccx = @CrateContext::new(sess,
                                      llmod_id,
index d81eaf8fab87565527d3d7ca145462ee47265273..3e9aae4730e34cb6008f1c3fff0d93cd8df0d9e5 100644 (file)
@@ -2827,7 +2827,7 @@ fn namespace_for_item(cx: &CrateContext,
 
         if def_id.crate == ast::LOCAL_CRATE {
             // prepend crate name if not already present
-            let crate_namespace_ident = token::str_to_ident(cx.link_meta.pkgid.name);
+            let crate_namespace_ident = token::str_to_ident(cx.link_meta.crateid.name);
             item_path.insert(0, ast_map::path_mod(crate_namespace_ident));
         }
 
index bf7a0d6039bdc92c51561428ca7a6fd56960fdba..9fcd0f458b3b5ae62afe65e81635df2b47b53147 100644 (file)
@@ -1855,7 +1855,7 @@ pub fn trans_log_level(bcx: @Block) -> DatumBlock {
                 Some(&src) => {
                     ccx.sess.cstore.get_crate_data(src.crate).name
                 }
-                None => ccx.link_meta.pkgid.name.to_managed(),
+                None => ccx.link_meta.crateid.name.to_managed(),
             };
         };
         let mut modpath = ~[path_mod(ccx.sess.ident_of(srccrate))];
index 0bd457b254c128d34daf6d58a0258b9abedadccc..f2526c29b9a802b7adf47cf93470670f5d8cabec 100644 (file)
@@ -16,7 +16,7 @@
 use codemap::BytePos;
 use diagnostic::span_handler;
 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
-use pkgid::PkgId;
+use crateid::CrateId;
 
 use std::hashmap::HashSet;
 
diff --git a/src/libsyntax/crateid.rs b/src/libsyntax/crateid.rs
new file mode 100644 (file)
index 0000000..0bb1eec
--- /dev/null
@@ -0,0 +1,186 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// CrateIds identify crates and include the crate name and optionally 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 CrateId {
+    /// 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>,
+}
+
+impl ToStr for CrateId {
+    fn to_str(&self) -> ~str {
+        let version = match self.version {
+            None => "0.0",
+            Some(ref version) => version.as_slice(),
+        };
+        if self.path == self.name || self.path.ends_with(format!("/{}", self.name)) {
+            format!("{}\\#{}", self.path, version)
+        } else {
+            format!("{}\\#{}:{}", self.path, self.name, version)
+        }
+    }
+}
+
+impl FromStr for CrateId {
+    fn from_str(s: &str) -> Option<CrateId> {
+        let pieces: ~[&str] = s.splitn('#', 1).collect();
+        let path = pieces[0].to_owned();
+
+        if path.starts_with("/") || path.ends_with("/") ||
+            path.starts_with(".") || path.is_empty() {
+            return None;
+        }
+
+        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(CrateId {
+            path: path,
+            name: name,
+            version: version,
+        })
+    }
+}
+
+impl CrateId {
+    pub fn version_or_default<'a>(&'a self) -> &'a str {
+        match self.version {
+            None => "0.0",
+            Some(ref version) => version.as_slice(),
+        }
+    }
+}
+
+#[test]
+fn bare_name() {
+    let crateid: CrateId = from_str("foo").expect("valid crateid");
+    assert_eq!(crateid.name, ~"foo");
+    assert_eq!(crateid.version, None);
+    assert_eq!(crateid.path, ~"foo");
+}
+
+#[test]
+fn bare_name_single_char() {
+    let crateid: CrateId = from_str("f").expect("valid crateid");
+    assert_eq!(crateid.name, ~"f");
+    assert_eq!(crateid.version, None);
+    assert_eq!(crateid.path, ~"f");
+}
+
+#[test]
+fn empty_crateid() {
+    let crateid: Option<CrateId> = from_str("");
+    assert!(crateid.is_none());
+}
+
+#[test]
+fn simple_path() {
+    let crateid: CrateId = from_str("example.com/foo/bar").expect("valid crateid");
+    assert_eq!(crateid.name, ~"bar");
+    assert_eq!(crateid.version, None);
+    assert_eq!(crateid.path, ~"example.com/foo/bar");
+}
+
+#[test]
+fn simple_version() {
+    let crateid: CrateId = from_str("foo#1.0").expect("valid crateid");
+    assert_eq!(crateid.name, ~"foo");
+    assert_eq!(crateid.version, Some(~"1.0"));
+    assert_eq!(crateid.path, ~"foo");
+}
+
+#[test]
+fn absolute_path() {
+    let crateid: Option<CrateId> = from_str("/foo/bar");
+    assert!(crateid.is_none());
+}
+
+#[test]
+fn path_ends_with_slash() {
+    let crateid: Option<CrateId> = from_str("foo/bar/");
+    assert!(crateid.is_none());
+}
+
+#[test]
+fn path_and_version() {
+    let crateid: CrateId = from_str("example.com/foo/bar#1.0").expect("valid crateid");
+    assert_eq!(crateid.name, ~"bar");
+    assert_eq!(crateid.version, Some(~"1.0"));
+    assert_eq!(crateid.path, ~"example.com/foo/bar");
+}
+
+#[test]
+fn single_chars() {
+    let crateid: CrateId = from_str("a/b#1").expect("valid crateid");
+    assert_eq!(crateid.name, ~"b");
+    assert_eq!(crateid.version, Some(~"1"));
+    assert_eq!(crateid.path, ~"a/b");
+}
+
+#[test]
+fn missing_version() {
+    let crateid: CrateId = from_str("foo#").expect("valid crateid");
+    assert_eq!(crateid.name, ~"foo");
+    assert_eq!(crateid.version, None);
+    assert_eq!(crateid.path, ~"foo");
+}
+
+#[test]
+fn path_and_name() {
+    let crateid: CrateId = from_str("foo/rust-bar#bar:1.0").expect("valid crateid");
+    assert_eq!(crateid.name, ~"bar");
+    assert_eq!(crateid.version, Some(~"1.0"));
+    assert_eq!(crateid.path, ~"foo/rust-bar");
+}
+
+#[test]
+fn empty_name() {
+    let crateid: CrateId = from_str("foo/bar#:1.0").expect("valid crateid");
+    assert_eq!(crateid.name, ~"bar");
+    assert_eq!(crateid.version, Some(~"1.0"));
+    assert_eq!(crateid.path, ~"foo/bar");
+}
index a8c5ba0153db9717f664ace87dc90d32c032346c..9631849235e8628d3253f078084acc6b5e730db5 100644 (file)
@@ -47,7 +47,7 @@ pub mod syntax {
 
 
 pub mod parse;
-pub mod pkgid;
+pub mod crateid;
 
 pub mod print {
     pub mod pp;
diff --git a/src/libsyntax/pkgid.rs b/src/libsyntax/pkgid.rs
deleted file mode 100644 (file)
index 3c10e51..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// 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>,
-}
-
-impl ToStr for PkgId {
-    fn to_str(&self) -> ~str {
-        let version = match self.version {
-            None => "0.0",
-            Some(ref version) => version.as_slice(),
-        };
-        if self.path == self.name || self.path.ends_with(format!("/{}", self.name)) {
-            format!("{}\\#{}", self.path, version)
-        } else {
-            format!("{}\\#{}:{}", self.path, self.name, version)
-        }
-    }
-}
-
-impl FromStr for PkgId {
-    fn from_str(s: &str) -> Option<PkgId> {
-        let pieces: ~[&str] = s.splitn('#', 1).collect();
-        let path = pieces[0].to_owned();
-
-        if path.starts_with("/") || path.ends_with("/") ||
-            path.starts_with(".") || path.is_empty() {
-            return None;
-        }
-
-        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,
-            name: name,
-            version: version,
-        })
-    }
-}
-
-impl PkgId {
-    pub fn version_or_default<'a>(&'a self) -> &'a str {
-        match self.version {
-            None => "0.0",
-            Some(ref version) => version.as_slice(),
-        }
-    }
-}
-
-#[test]
-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, ~"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, ~"f");
-}
-
-#[test]
-fn empty_pkgid() {
-    let pkgid: Option<PkgId> = from_str("");
-    assert!(pkgid.is_none());
-}
-
-#[test]
-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/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, ~"foo");
-}
-
-#[test]
-fn absolute_path() {
-    let pkgid: Option<PkgId> = from_str("/foo/bar");
-    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/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/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, ~"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");
-}