{"action":"created","comment":{"url":"https://api.github.com/repos/rust-lang/rust/pulls/comments/23814377","id":23814377,"diff_hunk":"@@ -0,0 +1,2415 @@\n+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT\n+// file at the top-level directory of this distribution and at\n+// http://rust-lang.org/COPYRIGHT.\n+//\n+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or\n+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license\n+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your\n+// option. This file may not be copied, modified, or distributed\n+// except according to those terms.\n+\n+//! Cross-platform path manipulation.\n+//!\n+//! This module provides two types, `PathBuf` and `Path` (akin to `String` and\n+//! `str`), for working with paths abstractly. These types are thin wrappers\n+//! around `OsString` and `OsStr` respectively, meaning that they work directly\n+//! on strings according to the local platform's path syntax.\n+//!\n+//! ## Simple usage\n+//!\n+//! Path manipulation involves both parsing components from slices and building\n+//! new owned paths.\n+//!\n+//! To parse a path, you can create a `Path` slice from a `str`\n+//! slice and start asking questions:\n+//!\n+//! ```rust\n+//! use std::path::Path;\n+//!\n+//! let path = Path::from_str(\"/tmp/foo/bar.txt\");\n+//! let file = path.file_name();\n+//! let extension = path.extension();\n+//! let parent_dir = path.parent();\n+//! ```\n+//!\n+//! To build or modify paths, use `PathBuf`:\n+//!\n+//! ```rust\n+//! use std::path::PathBuf;\n+//!\n+//! let mut path = PathBuf::new();\n+//! path.push(\"c:\\\\\");\n+//! path.push(\"windows\");\n+//! path.push(\"system32\");\n+//! path.set_extension(\"dll\");\n+//! ```\n+//!\n+//! ## Path components and normalization\n+//!\n+//! The path APIs are built around the notion of \"components\", which roughly\n+//! correspond to the substrings between path separators (`/` and, on Windows,\n+//! `\\`). The APIs for path parsing are largely specified in terms of the path's\n+//! components, so it's important to clearly understand how those are determined.\n+//!\n+//! A key aspect of components is that a path can always be reconstructed into\n+//! an equivalent path by putting together its components via `push`. The\n+//! represent the full amount of information that it is possibly useful to know\n+//! about a path.\n+//!\n+//! ### Component types\n+//!\n+//! Components come in several types:\n+//!\n+//! * Normal components are the default: stanard references to files or\n+//! directories. The path `a/b` has two normal components, `a` and `b`.\n+//!\n+//! * A \"current directory\" component, i.e. the `.` character. For example,\n+//! `a/.` has a normal component `a` and a current directory component.\n+//!\n+//! * The \"root diretory\" component, representing a separator that designates\n+//!   starting from root. For example, `/a/b` has a root directory component\n+//!   followed by normal components `a` and `b`.\n+//!\n+//! On Windows, two additional component types come into play:\n+//!\n+//! * Prefix components, of which there is a large variety. For example, `C:`\n+//! and `\\\\server\\share` are prefixes. The path `C:windows` has a prefix\n+//! component `C:` and a normal component `windows`; the path `C:\\windows` has a\n+//! prefix component `C:`, a root directory component, and a normal component\n+//! `windows`.\n+//!\n+//! * Empty components, a special case for so-called \"verbatim\" paths where very\n+//! little normalization is allowed. For example, `\\\\?\\C:\\` has a \"verbatim\"\n+//! prefix `\\\\?\\C:`, a root component, and an empty component (as a way of\n+//! representing the trailing `\\`. Such a trailing `\\` is in fact the only\n+//! situation in which an empty component is produced.\n+//!\n+//! ### Normalization\n+//!\n+//! Aside from splitting on the separator(s), there is a small amount of\n+//! \"normalization\":\n+//!\n+//! * Repeated separators are ignored: `a/b` and `a//b` both have components `a`\n+//!   and `b`.\n+//!\n+//! * Paths ending in a separator are treated as if they has a current directory\n+//!   component at the end (or, in verbatim paths, and empty component).  For\n+//!   example, while `a/b` has components `a` and `b`, the paths `a/b/` and\n+//!   `a/b/.` both have components `a`, `b`, and `.` (current directory).  The\n+//!   reason for this normalization is that `a/b` and `a/b/` are treated\n+//!   differently in some contexts, but `a/b/` and `a/b/.` are always treated\n+//!   the same.\n+//!\n+//! No other normalization takes place by default. In particular, `a/./b/` and\n+//! `a/b` are treated distinctly in terms of components, as are `a/c` and\n+//! `a/b/../c`. Further normalization is possible to build on top of the\n+//! components APIs, and will be included in this library very soon.\n+\n+#![unstable = \"recently added as an implementation of Path Reform\"]\n+\n+use core::prelude::*;\n+\n+use borrow::BorrowFrom;\n+use cmp;\n+use iter;\n+use mem;\n+use ops::{self, Deref};\n+use string::CowString;\n+use string::String;\n+use vec::Vec;\n+\n+use ffi::{OsStr, OsString, AsOsStr};\n+\n+use self::platform::{is_sep, is_verbatim_sep, MAIN_SEP, MAIN_SEP_STR, parse_prefix, Prefix};\n+\n+#[cfg(unix)]\n+#[path = \"sys/unix/mod.rs\"] mod sys;\n+#[cfg(windows)]\n+#[path = \"sys/windows/mod.rs\"] mod sys;\n+\n+////////////////////////////////////////////////////////////////////////////////\n+// GENERAL NOTES\n+////////////////////////////////////////////////////////////////////////////////\n+//\n+// Parsing in this module is done by directly transmuting OsStr to [u8] slices,\n+// taking advantage of the fact that OsStr always encodes ASCII characters\n+// as-is.  Eventually, this transmutation should be replaced by direct uses of\n+// OsStr APIs for parsing, but it will take a while for those to become\n+// available.\n+\n+////////////////////////////////////////////////////////////////////////////////\n+// Platform-specific definitions\n+////////////////////////////////////////////////////////////////////////////////\n+\n+// The following modules give the most basic tools for parsing paths on various\n+// platforms. The bulk of the code is devoted to parsing prefixes on Windows.\n+\n+#[cfg(unix)]\n+mod platform {\n+    use core::prelude::*;\n+    use ffi::OsStr;\n+\n+    #[inline]\n+    pub fn is_sep(b: u8) -> bool {\n+        b == b'/'\n+    }\n+\n+    #[inline]\n+    pub fn is_verbatim_sep(b: u8) -> bool {\n+        b == b'/'\n+    }\n+\n+    pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {\n+        None\n+    }\n+\n+    #[derive(Copy, Clone, PartialEq)]\n+    pub struct Prefix<'a>;\n+\n+    impl<'a> Prefix<'a> {\n+        #[inline]\n+        pub fn len(&self) -> usize { 0 }\n+        #[inline]\n+        pub fn is_verbatim(&self) -> bool { false }\n+        #[inline]\n+        pub fn is_drive(&self) -> bool { false }\n+        #[inline]\n+        pub fn has_implicit_root(&self) -> bool { false }\n+    }\n+\n+    pub const MAIN_SEP: u8 = b'/';\n+    pub const MAIN_SEP_STR: &'static str = \"/\";\n+}\n+\n+#[cfg(windows)]\n+mod platform {\n+    use core::prelude::*;\n+\n+    use super::{Path, os_str_as_u8_slice, u8_slice_as_os_str};\n+    use ffi::OsStr;\n+    use ascii::*;\n+\n+    #[inline]\n+    pub fn is_sep(b: u8) -> bool {\n+        b == b'/' || b == b'\\\\'\n+    }\n+\n+    #[inline]\n+    pub fn is_verbatim_sep(b: u8) -> bool {\n+        b == b'\\\\'\n+    }\n+\n+    pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> {\n+        use self::Prefix::*;\n+        unsafe {\n+            // The unsafety here stems from converting between &OsStr and &[u8]\n+            // and back. This is safe to do because (1) we only look at ASCII\n+            // contents of the encoding and (2) new &OsStr values are produced\n+            // only from ASCII-bounded slices of existing &OsStr values.\n+            let mut path = os_str_as_u8_slice(path);\n+\n+            if path.starts_with(b\"\\\\\\\\\") {\n+                // \\\\\n+                path = &path[2..];\n+                if path.starts_with(b\"?\\\\\") {\n+                    // \\\\?\\\n+                    path = &path[2..];\n+                    if path.starts_with(b\"UNC\\\\\") {\n+                        // \\\\?\\UNC\\server\\share\n+                        path = &path[4..];\n+                        let (server, share) = match parse_two_comps(path, is_verbatim_sep) {\n+                            Some((server, share)) => (u8_slice_as_os_str(server),\n+                                                      u8_slice_as_os_str(share)),\n+                            None => (u8_slice_as_os_str(path),\n+                                     u8_slice_as_os_str(&[])),\n+                        };\n+                        return Some(VerbatimUNC(server, share));\n+                    } else {\n+                        // \\\\?\\path\n+                        let idx = path.position_elem(&b'\\\\');\n+                        if idx == Some(2) && path[1] == b':' {\n+                            let c = path[0];\n+                            if c.is_ascii() && (c as char).is_alphabetic() {\n+                                // \\\\?\\C:\\ path\n+                                let slice = u8_slice_as_os_str(&path[0..1]);\n+                                return Some(VerbatimDisk(slice));\n+                            }\n+                        }\n+                        let slice = &path[.. idx.unwrap_or(path.len())];\n+                        return Some(Verbatim(u8_slice_as_os_str(slice)));\n+                    }\n+                } else if path.starts_with(b\".\\\\\") {\n+                    // \\\\.\\path\n+                    path = &path[2..];\n+                    let slice = &path[.. path.position_elem(&b'\\\\').unwrap_or(path.len())];\n+                    return Some(DeviceNS(u8_slice_as_os_str(slice)));\n+                }\n+                match parse_two_comps(path, is_sep) {\n+                    Some((server, share)) if server.len() > 0 && share.len() > 0 => {\n+                        // \\\\server\\share\n+                        return Some(UNC(u8_slice_as_os_str(server),\n+                                        u8_slice_as_os_str(share)));\n+                    }\n+                    _ => ()\n+                }\n+            } else if path.len() > 1 && path[1] == b':' {\n+                // C:\n+                let c = path[0];\n+                if c.is_ascii() && (c as char).is_alphabetic() {\n+                    return Some(Disk(u8_slice_as_os_str(&path[0..1])));\n+                }\n+            }\n+            return None;\n+        }\n+\n+        fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {\n+            let first = match path.iter().position(|x| f(*x)) {\n+                None => return None,\n+                Some(x) => &path[.. x]\n+            };\n+            path = &path[(first.len()+1)..];\n+            let idx = path.iter().position(|x| f(*x));\n+            let second = &path[.. idx.unwrap_or(path.len())];\n+            Some((first, second))\n+        }\n+    }\n+\n+    #[derive(Copy, Clone, PartialEq)]\n+    pub enum Prefix<'a> {\n+        Verbatim(&'a OsStr),\n+        VerbatimUNC(&'a OsStr, &'a OsStr),\n+        VerbatimDisk(&'a OsStr),\n+        DeviceNS(&'a OsStr),\n+        UNC(&'a OsStr, &'a OsStr),\n+        Disk(&'a OsStr),\n+    }\n+\n+    impl<'a> Prefix<'a> {\n+        #[inline]\n+        pub fn len(&self) -> usize {\n+            use self::Prefix::*;\n+            fn os_str_len(s: &OsStr) -> usize {\n+                unsafe { os_str_as_u8_slice(s).len() }\n+            }\n+            match *self {\n+                Verbatim(x) => 4 + os_str_len(x),\n+                VerbatimUNC(x,y) => 8 + os_str_len(x) +\n+                    if os_str_len(y) > 0 { 1 + os_str_len(y) }\n+                    else { 0 },\n+                VerbatimDisk(_) => 6,\n+                UNC(x,y) => 2 + os_str_len(x) +\n+                    if os_str_len(y) > 0 { 1 + os_str_len(y) }\n+                    else { 0 },\n+                DeviceNS(x) => 4 + os_str_len(x),\n+                Disk(_) => 2\n+            }\n+\n+        }\n+\n+        #[inline]\n+        pub fn is_verbatim(&self) -> bool {\n+            use self::Prefix::*;\n+            match *self {\n+                Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(_, _) => true,\n+                _ => false\n+            }\n+        }\n+\n+        #[inline]\n+        pub fn is_drive(&self) -> bool {\n+            match *self {\n+                Prefix::Disk(_) => true,\n+                _ => false,\n+            }\n+        }\n+\n+        #[inline]\n+        pub fn has_implicit_root(&self) -> bool {\n+            !self.is_drive()\n+        }\n+    }\n+\n+    pub const MAIN_SEP: u8 = b'\\\\';\n+    pub const MAIN_SEP_STR: &'static str = \"\\\\\";\n+}\n+\n+////////////////////////////////////////////////////////////////////////////////\n+// Misc helpers\n+////////////////////////////////////////////////////////////////////////////////\n+\n+// Iterate through `iter` while it matches `prefix`; return `None` if `prefix`\n+// is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving\n+// `iter` after having exhausted `prefix`.\n+fn iter_after<A, I, J>(mut iter: I, mut prefix: J) -> Option<I> where\n+    I: Iterator<Item=A> + Clone, J: Iterator<Item=A>, A: PartialEq\n+{\n+    loop {\n+        let mut iter_next = iter.clone();\n+        match (iter_next.next(), prefix.next()) {\n+            (Some(x), Some(y)) => {\n+                if x != y { return None }\n+            }\n+            (Some(_), None) => return Some(iter),\n+            (None, None) => return Some(iter),\n+            (None, Some(_)) => return None,\n+        }\n+        iter = iter_next;\n+    }\n+}\n+\n+// See note at the top of this module to understand why these are used:\n+unsafe fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {\n+    mem::transmute(s)\n+}\n+unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {\n+    mem::transmute(s)\n+}\n+\n+////////////////////////////////////////////////////////////////////////////////\n+// Cross-platform parsing\n+////////////////////////////////////////////////////////////////////////////////\n+\n+/// Says whether the path ends in a separator character and therefore needs to\n+/// be treated as if it ended with an additional `.`\n+fn has_suffix(s: &[u8], prefix: Option<Prefix>) -> bool {\n+    let (prefix_len, verbatim) = if let Some(p) = prefix {\n+        (p.len(), p.is_verbatim())\n+    } else { (0, false) };\n+    if prefix_len > 0 && prefix_len == s.len() && !verbatim { return true; }\n+    let mut splits = s[prefix_len..].split(|b| is_sep(*b));\n+    let last = splits.next_back().unwrap();\n+    let more = splits.next_back().is_some();\n+    more && last == b\"\"\n+}\n+\n+/// Says whether the first byte after the prefix is a separator.\n+fn has_physical_root(s: &[u8], prefix: Option<Prefix>) -> bool {\n+    let path = if let Some(p) = prefix { &s[p.len()..] } else { s };\n+    path.len() > 0 && is_sep(path[0])\n+}\n+\n+fn parse_single_component(comp: &[u8]) -> Option<Component> {\n+    match comp {\n+        b\".\" => Some(Component::CurDir),\n+        b\"..\" => Some(Component::ParentDir),\n+        b\"\" => None,\n+        _ => Some(Component::Normal(unsafe { Path::from_u8_slice(comp) }))\n+    }\n+}\n+\n+// basic workhorse for splitting stem and extension\n+fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {\n+    unsafe {\n+        if os_str_as_u8_slice(file) == b\"..\" { return (Some(file), None) }\n+\n+        // The unsafety here stems from converting between &OsStr and &[u8]\n+        // and back. This is safe to do because (1) we only look at ASCII\n+        // contents of the encoding and (2) new &OsStr values are produced\n+        // only from ASCII-bounded slices of existing &OsStr values.\n+\n+        let mut iter = os_str_as_u8_slice(file).rsplitn(1, |b| *b == b'.');\n+        let after = iter.next();\n+        let before = iter.next();\n+        if before == Some(b\"\"){\n+            (Some(file), None)\n+        } else {\n+            (before.map(|s| unsafe { u8_slice_as_os_str(s) }),\n+             after.map(|s| unsafe { u8_slice_as_os_str(s) }))\n+        }\n+    }\n+}\n+\n+////////////////////////////////////////////////////////////////////////////////\n+// The core iterators\n+////////////////////////////////////////////////////////////////////////////////\n+\n+/// Component parsing works by a double-ended state machine; the cursors at the\n+/// front and back of the path each keep track of what parts of the path have\n+/// been consumed so far.\n+///\n+/// Going front to back, a path is made up of a prefix, a root component, a body\n+/// (of normal components), and a suffix/emptycomponent (normalized `.` or ``\n+/// for a path ending with the separator)\n+#[derive(Copy, Clone, PartialEq, PartialOrd, Show)]\n+enum State {\n+    Prefix = 0,         // c:\n+    Root = 1,           // /\n+    Body = 2,           // foo/bar/baz\n+    Suffix = 3,         // .\n+    Done = 4,\n+}\n+\n+/// A single component of a path.\n+///\n+/// See the module documentation for an in-depth explanation of components and\n+/// their role in the API.\n+#[derive(Copy, Clone)]\n+pub enum Component<'a> {\n+    /// A Windows path prefix, e.g. `C:` or `\\server\\share`\n+    Prefix(&'a Path),\n+\n+    /// An empty component. Only used on Windows for the last\n+    /// component of paths ending with a separator\n+    Empty,\n+\n+    /// The root directory component, appears after any prefix and before anything else\n+    RootDir,\n+\n+    /// A reference to the current directory, i.e. `.`\n+    CurDir,\n+\n+    /// A reference to the parent directory, i.e. `..`\n+    ParentDir,\n+\n+    /// A normal component, i.e. `a` and `b` in `a/b`\n+    Normal(&'a Path),\n+}\n+\n+impl<'a> Component<'a> {\n+    fn as_path(self) -> &'a Path {\n+        match self {\n+            Component::Prefix(path) => path,\n+            Component::Empty => Path::from_str(\"\"),\n+            Component::RootDir => Path::from_str(MAIN_SEP_STR),\n+            Component::CurDir => Path::from_str(\".\"),\n+            Component::ParentDir => Path::from_str(\"..\"),\n+            Component::Normal(path) => path,\n+        }\n+    }\n+\n+    fn as_os_str(self) -> &'a OsStr {\n+        self.as_path().as_os_str()\n+    }\n+\n+    fn len(self) -> usize {\n+        self.as_path().as_u8_slice().len()\n+    }\n+\n+    // prefix or root?\n+    fn is_prefix_root(self) -> bool {\n+        match self {\n+            Component::Normal(_) => false,\n+            Component::CurDir => false,\n+            Component::ParentDir => false,\n+            _ => true,\n+        }\n+    }\n+}\n+\n+impl<'a> cmp::PartialEq for Component<'a> {\n+    fn eq(&self, other: &Component<'a>) -> bool {\n+        use self::Component::*;\n+\n+        match (*self, *other) {\n+            (Prefix(left), Prefix(right)) => left.as_u8_slice() == right.as_u8_slice(),\n+            (RootDir, RootDir) => true,\n+            (CurDir, CurDir) => true,\n+            (ParentDir, ParentDir) => true,\n+            (Normal(left), Normal(right)) => left.as_u8_slice() == right.as_u8_slice(),\n+            _ => false\n+        }\n+    }\n+}\n+\n+/// The core iterator giving the components of a path.\n+///\n+/// See the module documentation for an in-depth explanation of components and\n+/// their role in the API.\n+#[derive(Clone)]\n+pub struct Components<'a> {\n+    // The path left to parse components from\n+    path: &'a [u8],\n+\n+    // The prefix as it was originally parsed, if any\n+    prefix: Option<Prefix<'a>>,\n+\n+    // true if path *physically* has a root separator; for most Windows\n+    // prefixes, it may have a \"logical\" rootseparator for the purposes of\n+    // normalization, e.g.  \\\\server\\share == \\\\server\\share\\.\n+    has_physical_root: bool,\n+\n+    // The iterator is double-ended, and these two states keep track of what has\n+    // been produced from either end\n+    front: State,\n+    back: State,\n+}\n+\n+/// An iterator over the components of a path, as `OsStr` slices.\n+#[derive(Clone)]\n+pub struct Iter<'a> {\n+    inner: Components<'a>\n+}\n+\n+impl<'a> Components<'a> {\n+    // how long is the prefix, if any?\n+    #[inline]\n+    fn prefix_len(&self) -> usize {\n+        self.prefix.as_ref().map(Prefix::len).unwrap_or(0)\n+    }\n+\n+    #[inline]\n+    fn prefix_verbatim(&self) -> bool {\n+        self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false)\n+    }\n+\n+    /// how much of the prefix is left from the point of view of iteration?\n+    #[inline]\n+    fn prefix_remaining(&self) -> usize {\n+        if self.front == State::Prefix { self.prefix_len() }\n+        else { 0 }\n+    }\n+\n+    fn prefix_and_root(&self) -> usize {\n+        let root = if self.front <= State::Root && self.has_physical_root { 1 } else { 0 };\n+        self.prefix_remaining() + root\n+    }\n+\n+    // is the iteration complete?\n+    #[inline]\n+    fn finished(&self) -> bool {\n+        self.front == State::Done || self.back == State::Done || self.front > self.back\n+    }\n+\n+    #[inline]\n+    fn is_sep(&self, b: u8) -> bool {\n+        if self.prefix_verbatim() {\n+            is_verbatim_sep(b)\n+        } else {\n+            is_sep(b)\n+        }\n+    }\n+\n+    /// Extract a slice corresponding to the portion of the path remaining for iteration.\n+    pub fn as_path(&self) -> &'a Path {\n+        let mut comps = self.clone();\n+        if comps.front == State::Body { comps.trim_left(); }\n+        if comps.back == State::Body { comps.trim_right(); }\n+        if comps.path.is_empty() && comps.front < comps.back && comps.back == State::Suffix {\n+            Component::CurDir.as_path()\n+        } else {\n+            unsafe { Path::from_u8_slice(comps.path) }\n+        }\n+    }\n+\n+    /// Is the *original* path rooted?\n+    fn has_root(&self) -> bool {\n+        if self.has_physical_root { return true }\n+        if let Some(p) = self.prefix {\n+            if p.has_implicit_root() { return true }\n+        }\n+        false\n+    }\n+\n+    // parse a component from the left, saying how many bytes to consume to\n+    // remove the component\n+    fn parse_next_component(&self) -> (usize, Option<Component<'a>>) {\n+        debug_assert!(self.front == State::Body);\n+        let (extra, comp) = match self.path.iter().position(|b| self.is_sep(*b)) {\n+            None => (0, self.path),\n+            Some(i) => (1, &self.path[.. i]),\n+        };\n+        (comp.len() + extra, parse_single_component(comp))\n+    }\n+\n+    // parse a component from the right, saying how many bytes to consume to\n+    // remove the component\n+    fn parse_next_component_back(&self) -> (usize, Option<Component<'a>>) {\n+        debug_assert!(self.back == State::Body);\n+        let start = self.prefix_and_root();\n+        let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep(*b)) {\n+            None => (0, &self.path[start ..]),\n+            Some(i) => (1, &self.path[start + i + 1 ..]),\n+        };\n+        (comp.len() + extra, parse_single_component(comp))\n+    }\n+\n+    // trim away repeated separators (i.e. emtpy components) on the left\n+    fn trim_left(&mut self) {\n+        while !self.path.is_empty() {\n+            let (size, comp) = self.parse_next_component();\n+            if comp.is_some() {\n+                return;\n+            } else {\n+                self.path = &self.path[size ..];\n+            }\n+        }\n+    }\n+\n+    // trim away repeated separators (i.e. emtpy components) on the right\n+    fn trim_right(&mut self) {\n+        while self.path.len() > self.prefix_and_root() {\n+            let (size, comp) = self.parse_next_component_back();\n+            if comp.is_some() {\n+                return;\n+            } else {\n+                self.path = &self.path[.. self.path.len() - size];\n+            }\n+        }\n+    }\n+\n+    /// Examine the next component without consuming it.\n+    pub fn peek(&self) -> Option<Component<'a>> {\n+        self.clone().next()\n+    }\n+}\n+\n+impl<'a> Iter<'a> {\n+    /// Extract a slice corresponding to the portion of the path remaining for iteration.\n+    pub fn as_path(&self) -> &'a Path {\n+        self.inner.as_path()\n+    }\n+}\n+\n+impl<'a> Iterator for Iter<'a> {\n+    type Item = &'a OsStr;\n+\n+    fn next(&mut self) -> Option<&'a OsStr> {\n+        self.inner.next().map(Component::as_os_str)\n+    }\n+}\n+\n+impl<'a> DoubleEndedIterator for Iter<'a> {\n+    fn next_back(&mut self) -> Option<&'a OsStr> {\n+        self.inner.next_back().map(Component::as_os_str)\n+    }\n+}\n+\n+impl<'a> Iterator for Components<'a> {\n+    type Item = Component<'a>;\n+\n+    fn next(&mut self) -> Option<Component<'a>> {\n+        if self.finished() {\n+            None\n+        } else if self.front == State::Prefix {\n+            self.front = State::Root;\n+            if self.prefix_len() > 0 {\n+                debug_assert!(self.prefix_len() <= self.path.len());\n+                let prefix = &self.path[.. self.prefix_len()];\n+                self.path = &self.path[self.prefix_len() .. ];\n+                Some(Component::Prefix(unsafe { Path::from_u8_slice(prefix) }))\n+            } else {\n+                self.next()\n+            }\n+        } else if self.front == State::Root {\n+            self.front = State::Body;\n+            if self.has_physical_root {\n+                debug_assert!(self.path.len() > 0);\n+                self.path = &self.path[1..];\n+                return Some(Component::RootDir)\n+            } else if let Some(p) = self.prefix {\n+                if p.has_implicit_root() && !p.is_verbatim() {\n+                    return Some(Component::RootDir)\n+                }\n+            }\n+            self.next()\n+        } else if self.front == State::Body {\n+            while !self.path.is_empty() {\n+                let (size, comp) = self.parse_next_component();\n+                self.path = &self.path[size ..];\n+                if comp.is_some() { return comp }\n+            }\n+            self.front = State::Suffix;\n+            self.next()\n+        } else { // self.front == State::Suffix\n+            self.front = State::Done;\n+            if self.prefix_verbatim() {\n+                Some(Component::Empty)\n+            } else {\n+                Some(Component::CurDir)\n+            }\n+        }\n+    }\n+}\n+\n+impl<'a> DoubleEndedIterator for Components<'a> {\n+    fn next_back(&mut self) -> Option<Component<'a>> {\n+        if self.finished() {\n+            None\n+        } else if self.back == State::Suffix {\n+            self.back = State::Body;\n+            if self.prefix_verbatim() {\n+                Some(Component::Empty)\n+            } else {\n+                Some(Component::CurDir)\n+            }\n+        } else if self.back == State::Body {\n+            while self.path.len() > self.prefix_and_root() {\n+                let (size, comp) = self.parse_next_component_back();\n+                self.path = &self.path[.. self.path.len() - size];\n+                if comp.is_some() { return comp }\n+            }\n+            self.back = State::Root;\n+            self.next_back()\n+        } else if self.back == State::Root {\n+            self.back = State::Prefix;\n+            if self.has_physical_root {\n+                self.path = &self.path[.. self.path.len() - 1];\n+                return Some(Component::RootDir)\n+            } else if let Some(p) = self.prefix {\n+                if p.has_implicit_root() && !p.is_verbatim() {\n+                    return Some(Component::RootDir)\n+                }\n+            }\n+            self.next_back()\n+        } else { // self.back == State::Prefix\n+            self.back = State::Done;\n+            if self.prefix_len() > 0 {\n+                //debug_assert!(self.path.len() == self.prefix.len());\n+                Some(Component::Prefix(unsafe { Path::from_u8_slice(self.path) }))\n+            } else {\n+                None\n+            }\n+        }\n+    }\n+}\n+\n+fn optional_path(path: &Path) -> Option<&Path> {\n+    if path.as_u8_slice().is_empty() { None } else { Some(path) }\n+}\n+\n+////////////////////////////////////////////////////////////////////////////////\n+// Basic types and traits\n+////////////////////////////////////////////////////////////////////////////////\n+\n+/// An owned, mutable path (akin to `String`).\n+pub struct PathBuf {\n+    inner: OsString\n+}\n+\n+/// A slice of a path (akin to `str`).\n+pub struct Path {\n+    inner: OsStr\n+}\n+\n+/// Freely convertible to a `Path`.\n+pub trait AsPath {\n+    /// Convert to a `Path`.\n+    fn as_path(&self) -> &Path;\n+}\n+\n+impl ops::Deref for PathBuf {\n+    type Target = Path;\n+\n+    fn deref(&self) -> &Path {\n+        unsafe { mem::transmute(&self.inner[]) }\n+    }\n+}\n+\n+impl BorrowFrom<PathBuf> for Path {\n+    fn borrow_from(owned: &PathBuf) -> &Path {\n+        owned.deref()\n+    }\n+}\n+\n+impl AsPath for Path {\n+    fn as_path(&self) -> &Path { self }\n+}\n+\n+impl<T: AsOsStr + ?Sized> AsPath for T {\n+    fn as_path(&self) -> &Path { Path::from_os_str(self.as_os_str()) }\n+}\n+\n+impl Path {\n+    // The following (private!) function allows construction of a path from a u8\n+    // slice, which is only safe when it is known to follow the OsStr encoding.\n+    unsafe fn from_u8_slice(s: &[u8]) -> &Path {\n+        mem::transmute(s)\n+    }\n+    // The following (private!) function reveals the byte encoding used for OsStr.\n+    fn as_u8_slice(&self) -> &[u8] {\n+        unsafe { mem::transmute(self) }\n+    }\n+\n+    /// Directly wrap a `str` slice as a `Path` slice.\n+    ///\n+    /// This is a cost-free conversion.\n+    pub fn from_str(s: &str) -> &Path {\n+        Path::from_os_str(OsStr::from_str(s))\n+    }\n+\n+    /// Directly wrap an `OsStr` slice as a `Path` slice.\n+    ///\n+    /// This is a cost-free conversion.\n+    pub fn from_os_str(s: &OsStr) -> &Path {\n+        unsafe { mem::transmute(s) }\n+    }\n+\n+    /// Extract the underlying `OsStr` slice.\n+    pub fn as_os_str(&self) -> &OsStr {\n+        &self.inner\n+    }\n+\n+    /// Yield a `&str` slice if the `Path` is valid unicode.\n+    ///\n+    /// This conversion may entail doing a check for UTF-8 validity.\n+    pub fn to_str(&self) -> Option<&str> {\n+        self.inner.to_str()\n+    }\n+\n+    /// Convert a `Path` to a `CowString`.\n+    ///\n+    /// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER.\n+    pub fn to_string_lossy(&self) -> CowString {\n+        self.inner.to_string_lossy()\n+    }\n+\n+    /// Convert a `Path` to an owned `PathBuf`.\n+    pub fn to_path_buf(&self) -> PathBuf {\n+        PathBuf::from_path(self)\n+    }\n+\n+    /// A path is *absolute* if it is indepedent of the current directory.\n+    ///\n+    /// * On Unix, a path is absolute if it starts with the root, so\n+    /// `is_absolute` and `has_root` are equivalent.\n+    ///\n+    /// * On Windows, a path is absolute if it has a prefix and starts with the\n+    /// root: `c:\\windows` is absolute, while `c:temp` and `\\temp` are not. In\n+    /// other words, `path.is_absolute() == path.prefix().is_some() && path.has_root()`.\n+    pub fn is_absolute(&self) -> bool {\n+        self.has_root() &&\n+            (cfg!(unix) || self.prefix().is_some())\n+    }\n+\n+    /// A path is *relative* if it is not absolute.\n+    pub fn is_relative(&self) -> bool {\n+        !self.is_absolute()\n+    }\n+\n+    /// Returns the *prefix* of a path, if any.\n+    ///\n+    /// Prefixes are relevant only for Windows paths, and consist of volumes\n+    /// like `C:`, UNC prefixes like `\\\\server`, and others described in more\n+    /// detail in `std::os::windows::PathExt`.\n+    pub fn prefix(&self) -> Option<&Path> {\n+        let iter = self.components();\n+        optional_path(unsafe {\n+            Path::from_u8_slice(\n+                &self.as_u8_slice()[.. iter.prefix_remaining()])\n+        })\n+    }\n+\n+    /// A path has a root if the body of the path begins with the directory separator.\n+    ///\n+    /// * On Unix, a path hss a root if it begins with `/`.\n+    ///\n+    /// * On Windows, a path has a root if it:\n+    ///     * has no prefix and begins with a separator, e.g. `\\\\windows`\n+    ///     * has a prefix followed by a separator, e.g. `c:\\windows` but not `c:windows`\n+    ///     * has any non-disk prefix, e.g. `\\\\server\\share`\n+    pub fn has_root(&self) -> bool {\n+         self.components().has_root()\n+    }\n+\n+    /// The path without its final component.\n+    ///\n+    /// Does nothing, returnning `None` if the path consists of just a prefix\n+    /// and/or root directory reference.\n+    pub fn parent(&self) -> Option<&Path> {\n+        let mut comps = self.components();\n+        let comp = comps.next_back();\n+        let rest = optional_path(comps.as_path());\n+\n+        match (comp, comps.next_back()) {\n+            (Some(Component::CurDir), Some(Component::RootDir)) => None,\n+            (Some(Component::CurDir), Some(Component::Prefix(_))) => None,\n+            (Some(Component::Empty), Some(Component::RootDir)) => None,\n+            (Some(Component::Empty), Some(Component::Prefix(_))) => None,\n+            (Some(Component::Prefix(_)), None) => None,\n+            (Some(Component::RootDir), Some(Component::Prefix(_))) => None,\n+            _ => rest\n+        }\n+    }\n+\n+    /// The final component of the path, if it is a normal file.\n+    ///\n+    /// If the path terminates in `.`, `..`, or consists solely or a root of\n+    /// prefix, `file` will return `None`.\n+    pub fn file_name(&self) -> Option<&OsStr> {\n+        self.components().next_back().and_then(|p| match p {\n+            Component::Normal(p) => Some(p.as_os_str()),\n+            _ => None\n+        })\n+    }\n+\n+    /// Returns a path that, when joined onto `base`, yields `self`.\n+    pub fn relative_from<'a, P: ?Sized>(&'a self, base: &'a P) -> Option<&Path> where\n+        P: AsPath\n+    {\n+        iter_after(self.components(), base.as_path().components()).map(|c| c.as_path())\n+    }\n+\n+    /// Determines whether `base` is a prefix of `self`.\n+    pub fn starts_with<P: ?Sized>(&self, base: &P) -> bool where P: AsPath {\n+        iter_after(self.components(), base.as_path().components()).is_some()\n+    }\n+\n+    /// Determines whether `base` is a suffix of `self`.\n+    pub fn ends_with<P: ?Sized>(&self, child: &P) -> bool where P: AsPath {\n+        iter_after(self.components().rev(), child.as_path().components().rev()).is_some()\n+    }\n+\n+    /// Extract the stem (non-extension) portion of `self.file()`.\n+    ///\n+    /// The stem is:\n+    ///\n+    /// * None, if there is no file name;\n+    /// * The entire file name if there is no embedded `.`;\n+    /// * The entire file name if the file name begins with `.` and has no other `.`s within;\n+    /// * Otherwise, the portion of the file name before the final `.`\n+    pub fn file_stem(&self) -> Option<&OsStr> {\n+        self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after))\n+    }\n+\n+    /// Extract the extension of `self.file()`, if possible.\n+    ///\n+    /// The extension is:\n+    ///\n+    /// * None, if there is no file name;\n+    /// * None, if there is no embedded `.`;\n+    /// * None, if the file name begins with `.` and has no other `.`s within;\n+    /// * Otherwise, the portion of the file name after the final `.`\n+    pub fn extension(&self) -> Option<&OsStr> {\n+        self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after))\n+    }\n+\n+    /// Creates an owned `PathBuf` with `path` adjoined to `self`.\n+    ///\n+    /// See `PathBuf::push` for more details on what it means to adjoin a path.\n+    pub fn join<P: ?Sized>(&self, path: &P) -> PathBuf where P: AsPath {\n+        let mut buf = self.to_path_buf();\n+        buf.push(path);\n+        buf\n+    }\n+\n+    /// Creates an owned `PathBuf` like `self` but with the given file name.\n+    ///\n+    /// See `PathBuf::set_file_name` for more details.\n+    pub fn with_file_name<S: ?Sized>(&self, file_name: &S) -> PathBuf where S: AsOsStr {\n+        let mut buf = self.to_path_buf();\n+        buf.set_file_name(file_name);\n+        buf\n+    }\n+\n+    /// Creates an owned `PathBuf` like `self` but with the given extension.\n+    ///\n+    /// See `PathBuf::set_extension` for more details.\n+    pub fn with_extension<S: ?Sized>(&self, extension: &S) -> PathBuf where S: AsOsStr {\n+        let mut buf = self.to_path_buf();\n+        buf.set_extension(extension);\n+        buf\n+    }\n+\n+    /// Produce an iterator over the components of the path.\n+    pub fn components(&self) -> Components {\n+        let prefix = parse_prefix(self.as_os_str());\n+        Components {\n+            path: self.as_u8_slice(),\n+            prefix: prefix,\n+            has_physical_root: has_physical_root(self.as_u8_slice(), prefix),\n+            front: State::Prefix,\n+            back: if has_suffix(self.as_u8_slice(), prefix) { State::Suffix }\n+                  else { State::Body },\n+        }\n+    }\n+\n+    /// Produce an iterator over the path's components viewed as `OsStr` slices.\n+    pub fn iter(&self) -> Iter {\n+        Iter { inner: self.components() }\n+    }\n+}\n+\n+impl cmp::PartialEq for Path {\n+    fn eq(&self, other: &Path) -> bool {\n+        iter::order::eq(self.components(), other.components())\n+    }\n+}\n+\n+impl PathBuf {\n+    fn as_mut_vec(&mut self) -> &mut Vec<u8> {\n+        unsafe { mem::transmute(self) }\n+    }\n+\n+    /// Allocate an empty `PathBuf`.\n+    pub fn new() -> PathBuf {\n+        PathBuf { inner: OsString::from_str(\"\") }\n+    }\n+\n+    /// Allocate a `PathBuf` with contents `path`.\n+    pub fn from_path(path: &Path) -> PathBuf {\n+        PathBuf { inner: path.as_os_str().to_os_string() }\n+    }\n+\n+    /// Allocate a `PathBuf` with contents `path`.\n+    pub fn from_str(path: &str) -> PathBuf {\n+        PathBuf { inner: OsString::from_str(path) }\n+    }\n+\n+    /// Allocate a `PathBuf` with contents `path`.\n+    pub fn from_os_str(path: &OsStr) -> PathBuf {\n+        PathBuf { inner: path.to_os_string() }\n+    }\n+\n+    /// Extend `self` with `path`.\n+    ///\n+    /// If `path` is absolute, it replaces the current path.\n+    ///\n+    /// On Windows:\n+    ///\n+    /// * if `path` has a root but no prefix (e.g. `\\windows`), it\n+    ///   replaces everything except for the prefix (if any) of `self`.\n+    /// * if `path` has a prefix but no root, it replaces `self.","path":"src/libstd/path.rs","position":1061,"original_position":1061,"commit_id":"43681cc1949761eca78ca0686935594c4cdba33f","original_commit_id":"43681cc1949761eca78ca0686935594c4cdba33f","user":{"login":"retep998","id":666308,"avatar_url":"https://avatars.githubusercontent.com/u/666308?v=3","gravatar_id":"","url":"https://api.github.com/users/retep998","html_url":"https://github.com/retep998","followers_url":"https://api.github.com/users/retep998/followers","following_url":"https://api.github.com/users/retep998/following{/other_user}","gists_url":"https://api.github.com/users/retep998/gists{/gist_id}","starred_url":"https://api.github.com/users/retep998/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/retep998/subscriptions","organizations_url":"https://api.github.com/users/retep998/orgs","repos_url":"https://api.github.com/users/retep998/repos","events_url":"https://api.github.com/users/retep998/events{/privacy}","received_events_url":"https://api.github.com/users/retep998/received_events","type":"User","site_admin":false},"body":"As it turns out, Windows does has drive specific current directories. Unfortunately I cannot find a way to directly get the CWD of a specific drive. Also the CWD is a very racy thing that is shared across threads and may change values on you between calls.\r\nSince methods in winapi treat `C:foo.txt` as an absolute path when joining, we should replicate that behavior.\r\nAny method that actually interacts with the filesystem and is given a `C:foo.txt` will treat it as a path relative to the CWD on that drive. Therefore when normalizing the path based on the filesystem we should probably use `GetFullPathNameW` to get the absolute path. The only other method I can think of involves changing the CWD to the drive of that path, and then getting the CWD, which is undesirable because it changes the CWD for other threads.","created_at":"2015-01-30T00:01:23Z","updated_at":"2015-01-30T00:01:23Z","html_url":"https://github.com/rust-lang/rust/pull/21759#discussion_r23814377","pull_request_url":"https://api.github.com/repos/rust-lang/rust/pulls/21759","_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/comments/23814377"},"html":{"href":"https://github.com/rust-lang/rust/pull/21759#discussion_r23814377"},"pull_request":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/21759"}}},"pull_request":{"url":"https://api.github.com/repos/rust-lang/rust/pulls/21759","id":28321849,"html_url":"https://github.com/rust-lang/rust/pull/21759","diff_url":"https://github.com/rust-lang/rust/pull/21759.diff","patch_url":"https://github.com/rust-lang/rust/pull/21759.patch","issue_url":"https://api.github.com/repos/rust-lang/rust/issues/21759","number":21759,"state":"open","locked":false,"title":"Rename `std::path` to `std::old_path`; introduce new `std::path`","user":{"login":"aturon","id":709807,"avatar_url":"https://avatars.githubusercontent.com/u/709807?v=3","gravatar_id":"","url":"https://api.github.com/users/aturon","html_url":"https://github.com/aturon","followers_url":"https://api.github.com/users/aturon/followers","following_url":"https://api.github.com/users/aturon/following{/other_user}","gists_url":"https://api.github.com/users/aturon/gists{/gist_id}","starred_url":"https://api.github.com/users/aturon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aturon/subscriptions","organizations_url":"https://api.github.com/users/aturon/orgs","repos_url":"https://api.github.com/users/aturon/repos","events_url":"https://api.github.com/users/aturon/events{/privacy}","received_events_url":"https://api.github.com/users/aturon/received_events","type":"User","site_admin":false},"body":"This PR implements [path reform](https://github.com/rust-lang/rfcs/pull/474), and motivation and details for the change can be found there.\r\n\r\nFor convenience, the old path API is being kept as `old_path` for the time being. Updating after this PR is just a matter of changing imports to `old_path` (which is likely not needed, since the prelude entries still export the old path API).\r\n\r\nThis initial PR does not include additional normalization or platform-specific path extensions. These will be done in follow up commits or PRs.\r\n\r\n[breaking-change]\r\n\r\nCloses #20034\r\nCloses #12056\r\nCloses #11594\r\nCloses #14028\r\nCloses #14049\r\nCloses #10035","created_at":"2015-01-29T22:39:27Z","updated_at":"2015-01-30T00:01:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":{"login":"alexcrichton","id":64996,"avatar_url":"https://avatars.githubusercontent.com/u/64996?v=3","gravatar_id":"","url":"https://api.github.com/users/alexcrichton","html_url":"https://github.com/alexcrichton","followers_url":"https://api.github.com/users/alexcrichton/followers","following_url":"https://api.github.com/users/alexcrichton/following{/other_user}","gists_url":"https://api.github.com/users/alexcrichton/gists{/gist_id}","starred_url":"https://api.github.com/users/alexcrichton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexcrichton/subscriptions","organizations_url":"https://api.github.com/users/alexcrichton/orgs","repos_url":"https://api.github.com/users/alexcrichton/repos","events_url":"https://api.github.com/users/alexcrichton/events{/privacy}","received_events_url":"https://api.github.com/users/alexcrichton/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/rust-lang/rust/pulls/21759/commits","review_comments_url":"https://api.github.com/repos/rust-lang/rust/pulls/21759/comments","review_comment_url":"https://api.github.com/repos/rust-lang/rust/pulls/comments/{number}","comments_url":"https://api.github.com/repos/rust-lang/rust/issues/21759/comments","statuses_url":"https://api.github.com/repos/rust-lang/rust/statuses/43681cc1949761eca78ca0686935594c4cdba33f","head":{"label":"aturon:new-path","ref":"new-path","sha":"43681cc1949761eca78ca0686935594c4cdba33f","user":{"login":"aturon","id":709807,"avatar_url":"https://avatars.githubusercontent.com/u/709807?v=3","gravatar_id":"","url":"https://api.github.com/users/aturon","html_url":"https://github.com/aturon","followers_url":"https://api.github.com/users/aturon/followers","following_url":"https://api.github.com/users/aturon/following{/other_user}","gists_url":"https://api.github.com/users/aturon/gists{/gist_id}","starred_url":"https://api.github.com/users/aturon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aturon/subscriptions","organizations_url":"https://api.github.com/users/aturon/orgs","repos_url":"https://api.github.com/users/aturon/repos","events_url":"https://api.github.com/users/aturon/events{/privacy}","received_events_url":"https://api.github.com/users/aturon/received_events","type":"User","site_admin":false},"repo":{"id":19056749,"name":"rust","full_name":"aturon/rust","owner":{"login":"aturon","id":709807,"avatar_url":"https://avatars.githubusercontent.com/u/709807?v=3","gravatar_id":"","url":"https://api.github.com/users/aturon","html_url":"https://github.com/aturon","followers_url":"https://api.github.com/users/aturon/followers","following_url":"https://api.github.com/users/aturon/following{/other_user}","gists_url":"https://api.github.com/users/aturon/gists{/gist_id}","starred_url":"https://api.github.com/users/aturon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aturon/subscriptions","organizations_url":"https://api.github.com/users/aturon/orgs","repos_url":"https://api.github.com/users/aturon/repos","events_url":"https://api.github.com/users/aturon/events{/privacy}","received_events_url":"https://api.github.com/users/aturon/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/aturon/rust","description":"a safe, concurrent, practical language","fork":true,"url":"https://api.github.com/repos/aturon/rust","forks_url":"https://api.github.com/repos/aturon/rust/forks","keys_url":"https://api.github.com/repos/aturon/rust/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aturon/rust/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aturon/rust/teams","hooks_url":"https://api.github.com/repos/aturon/rust/hooks","issue_events_url":"https://api.github.com/repos/aturon/rust/issues/events{/number}","events_url":"https://api.github.com/repos/aturon/rust/events","assignees_url":"https://api.github.com/repos/aturon/rust/assignees{/user}","branches_url":"https://api.github.com/repos/aturon/rust/branches{/branch}","tags_url":"https://api.github.com/repos/aturon/rust/tags","blobs_url":"https://api.github.com/repos/aturon/rust/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aturon/rust/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aturon/rust/git/refs{/sha}","trees_url":"https://api.github.com/repos/aturon/rust/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aturon/rust/statuses/{sha}","languages_url":"https://api.github.com/repos/aturon/rust/languages","stargazers_url":"https://api.github.com/repos/aturon/rust/stargazers","contributors_url":"https://api.github.com/repos/aturon/rust/contributors","subscribers_url":"https://api.github.com/repos/aturon/rust/subscribers","subscription_url":"https://api.github.com/repos/aturon/rust/subscription","commits_url":"https://api.github.com/repos/aturon/rust/commits{/sha}","git_commits_url":"https://api.github.com/repos/aturon/rust/git/commits{/sha}","comments_url":"https://api.github.com/repos/aturon/rust/comments{/number}","issue_comment_url":"https://api.github.com/repos/aturon/rust/issues/comments/{number}","contents_url":"https://api.github.com/repos/aturon/rust/contents/{+path}","compare_url":"https://api.github.com/repos/aturon/rust/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aturon/rust/merges","archive_url":"https://api.github.com/repos/aturon/rust/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aturon/rust/downloads","issues_url":"https://api.github.com/repos/aturon/rust/issues{/number}","pulls_url":"https://api.github.com/repos/aturon/rust/pulls{/number}","milestones_url":"https://api.github.com/repos/aturon/rust/milestones{/number}","notifications_url":"https://api.github.com/repos/aturon/rust/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aturon/rust/labels{/name}","releases_url":"https://api.github.com/repos/aturon/rust/releases{/id}","created_at":"2014-04-23T04:19:14Z","updated_at":"2014-06-21T05:46:22Z","pushed_at":"2015-01-29T22:35:36Z","git_url":"git://github.com/aturon/rust.git","ssh_url":"git@github.com:aturon/rust.git","clone_url":"https://github.com/aturon/rust.git","svn_url":"https://github.com/aturon/rust","homepage":"http://www.rust-lang.org","size":221864,"stargazers_count":0,"watchers_count":0,"language":"Rust","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"rust-lang:master","ref":"master","sha":"265a23320dbeaeca45b889cfea684d71dec1b8e6","user":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars.githubusercontent.com/u/5430905?v=3","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"repo":{"id":724712,"name":"rust","full_name":"rust-lang/rust","owner":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars.githubusercontent.com/u/5430905?v=3","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rust-lang/rust","description":"a safe, concurrent, practical language","fork":false,"url":"https://api.github.com/repos/rust-lang/rust","forks_url":"https://api.github.com/repos/rust-lang/rust/forks","keys_url":"https://api.github.com/repos/rust-lang/rust/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rust-lang/rust/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rust-lang/rust/teams","hooks_url":"https://api.github.com/repos/rust-lang/rust/hooks","issue_events_url":"https://api.github.com/repos/rust-lang/rust/issues/events{/number}","events_url":"https://api.github.com/repos/rust-lang/rust/events","assignees_url":"https://api.github.com/repos/rust-lang/rust/assignees{/user}","branches_url":"https://api.github.com/repos/rust-lang/rust/branches{/branch}","tags_url":"https://api.github.com/repos/rust-lang/rust/tags","blobs_url":"https://api.github.com/repos/rust-lang/rust/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rust-lang/rust/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rust-lang/rust/git/refs{/sha}","trees_url":"https://api.github.com/repos/rust-lang/rust/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rust-lang/rust/statuses/{sha}","languages_url":"https://api.github.com/repos/rust-lang/rust/languages","stargazers_url":"https://api.github.com/repos/rust-lang/rust/stargazers","contributors_url":"https://api.github.com/repos/rust-lang/rust/contributors","subscribers_url":"https://api.github.com/repos/rust-lang/rust/subscribers","subscription_url":"https://api.github.com/repos/rust-lang/rust/subscription","commits_url":"https://api.github.com/repos/rust-lang/rust/commits{/sha}","git_commits_url":"https://api.github.com/repos/rust-lang/rust/git/commits{/sha}","comments_url":"https://api.github.com/repos/rust-lang/rust/comments{/number}","issue_comment_url":"https://api.github.com/repos/rust-lang/rust/issues/comments/{number}","contents_url":"https://api.github.com/repos/rust-lang/rust/contents/{+path}","compare_url":"https://api.github.com/repos/rust-lang/rust/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rust-lang/rust/merges","archive_url":"https://api.github.com/repos/rust-lang/rust/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rust-lang/rust/downloads","issues_url":"https://api.github.com/repos/rust-lang/rust/issues{/number}","pulls_url":"https://api.github.com/repos/rust-lang/rust/pulls{/number}","milestones_url":"https://api.github.com/repos/rust-lang/rust/milestones{/number}","notifications_url":"https://api.github.com/repos/rust-lang/rust/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rust-lang/rust/labels{/name}","releases_url":"https://api.github.com/repos/rust-lang/rust/releases{/id}","created_at":"2010-06-16T20:39:03Z","updated_at":"2015-01-29T23:18:13Z","pushed_at":"2015-01-29T22:54:19Z","git_url":"git://github.com/rust-lang/rust.git","ssh_url":"git@github.com:rust-lang/rust.git","clone_url":"https://github.com/rust-lang/rust.git","svn_url":"https://github.com/rust-lang/rust","homepage":"http://www.rust-lang.org","size":454023,"stargazers_count":9113,"watchers_count":9113,"language":"Rust","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1755,"mirror_url":null,"open_issues_count":2054,"forks":1755,"open_issues":2054,"watchers":9113,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/21759"},"html":{"href":"https://github.com/rust-lang/rust/pull/21759"},"issue":{"href":"https://api.github.com/repos/rust-lang/rust/issues/21759"},"comments":{"href":"https://api.github.com/repos/rust-lang/rust/issues/21759/comments"},"review_comments":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/21759/comments"},"review_comment":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/rust-lang/rust/pulls/21759/commits"},"statuses":{"href":"https://api.github.com/repos/rust-lang/rust/statuses/43681cc1949761eca78ca0686935594c4cdba33f"}}}}