| // Copyright 2015 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. |
| |
| use borrow::{Borrow, Cow, ToOwned}; |
| use fmt::{self, Debug}; |
| use mem; |
| use string::String; |
| use ops; |
| use cmp; |
| use hash::{Hash, Hasher}; |
| use vec::Vec; |
| |
| use sys::os_str::{Buf, Slice}; |
| use sys_common::{AsInner, IntoInner, FromInner}; |
| |
| /// A type that can represent owned, mutable platform-native strings, but is |
| /// cheaply inter-convertible with Rust strings. |
| /// |
| /// The need for this type arises from the fact that: |
| /// |
| /// * On Unix systems, strings are often arbitrary sequences of non-zero |
| /// bytes, in many cases interpreted as UTF-8. |
| /// |
| /// * On Windows, strings are often arbitrary sequences of non-zero 16-bit |
| /// values, interpreted as UTF-16 when it is valid to do so. |
| /// |
| /// * In Rust, strings are always valid UTF-8, but may contain zeros. |
| /// |
| /// `OsString` and `OsStr` bridge this gap by simultaneously representing Rust |
| /// and platform-native string values, and in particular allowing a Rust string |
| /// to be converted into an "OS" string with no cost. |
| #[derive(Clone)] |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub struct OsString { |
| inner: Buf |
| } |
| |
| /// Slices into OS strings (see `OsString`). |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub struct OsStr { |
| inner: Slice |
| } |
| |
| impl OsString { |
| /// Constructs a new empty `OsString`. |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn new() -> OsString { |
| OsString { inner: Buf::from_string(String::new()) } |
| } |
| |
| #[cfg(unix)] |
| fn _from_bytes(vec: Vec<u8>) -> Option<OsString> { |
| use os::unix::ffi::OsStringExt; |
| Some(OsString::from_vec(vec)) |
| } |
| |
| #[cfg(windows)] |
| fn _from_bytes(vec: Vec<u8>) -> Option<OsString> { |
| String::from_utf8(vec).ok().map(OsString::from) |
| } |
| |
| /// Converts to an `OsStr` slice. |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn as_os_str(&self) -> &OsStr { |
| self |
| } |
| |
| /// Converts the `OsString` into a `String` if it contains valid Unicode data. |
| /// |
| /// On failure, ownership of the original `OsString` is returned. |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn into_string(self) -> Result<String, OsString> { |
| self.inner.into_string().map_err(|buf| OsString { inner: buf} ) |
| } |
| |
| /// Extends the string with the given `&OsStr` slice. |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn push<T: AsRef<OsStr>>(&mut self, s: T) { |
| self.inner.push_slice(&s.as_ref().inner) |
| } |
| |
| /// Creates a new `OsString` with the given capacity. |
| /// |
| /// The string will be able to hold exactly `capacity` lenth units of other |
| /// OS strings without reallocating. If `capacity` is 0, the string will not |
| /// allocate. |
| /// |
| /// See main `OsString` documentation information about encoding. |
| #[stable(feature = "osstring_simple_functions", since = "1.9.0")] |
| pub fn with_capacity(capacity: usize) -> OsString { |
| OsString { |
| inner: Buf::with_capacity(capacity) |
| } |
| } |
| |
| /// Truncates the `OsString` to zero length. |
| #[stable(feature = "osstring_simple_functions", since = "1.9.0")] |
| pub fn clear(&mut self) { |
| self.inner.clear() |
| } |
| |
| /// Returns the capacity this `OsString` can hold without reallocating. |
| /// |
| /// See `OsString` introduction for information about encoding. |
| #[stable(feature = "osstring_simple_functions", since = "1.9.0")] |
| pub fn capacity(&self) -> usize { |
| self.inner.capacity() |
| } |
| |
| /// Reserves capacity for at least `additional` more capacity to be inserted |
| /// in the given `OsString`. |
| /// |
| /// The collection may reserve more space to avoid frequent reallocations. |
| #[stable(feature = "osstring_simple_functions", since = "1.9.0")] |
| pub fn reserve(&mut self, additional: usize) { |
| self.inner.reserve(additional) |
| } |
| |
| /// Reserves the minimum capacity for exactly `additional` more capacity to |
| /// be inserted in the given `OsString`. Does nothing if the capacity is |
| /// already sufficient. |
| /// |
| /// Note that the allocator may give the collection more space than it |
| /// requests. Therefore capacity can not be relied upon to be precisely |
| /// minimal. Prefer reserve if future insertions are expected. |
| #[stable(feature = "osstring_simple_functions", since = "1.9.0")] |
| pub fn reserve_exact(&mut self, additional: usize) { |
| self.inner.reserve_exact(additional) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl From<String> for OsString { |
| fn from(s: String) -> OsString { |
| OsString { inner: Buf::from_string(s) } |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl<'a, T: ?Sized + AsRef<OsStr>> From<&'a T> for OsString { |
| fn from(s: &'a T) -> OsString { |
| s.as_ref().to_os_string() |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl ops::Index<ops::RangeFull> for OsString { |
| type Output = OsStr; |
| |
| #[inline] |
| fn index(&self, _index: ops::RangeFull) -> &OsStr { |
| OsStr::from_inner(self.inner.as_slice()) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl ops::Deref for OsString { |
| type Target = OsStr; |
| |
| #[inline] |
| fn deref(&self) -> &OsStr { |
| &self[..] |
| } |
| } |
| |
| #[stable(feature = "osstring_default", since = "1.9.0")] |
| impl Default for OsString { |
| #[inline] |
| fn default() -> OsString { |
| OsString::new() |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Debug for OsString { |
| fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| fmt::Debug::fmt(&**self, formatter) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialEq for OsString { |
| fn eq(&self, other: &OsString) -> bool { |
| &**self == &**other |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialEq<str> for OsString { |
| fn eq(&self, other: &str) -> bool { |
| &**self == other |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialEq<OsString> for str { |
| fn eq(&self, other: &OsString) -> bool { |
| &**other == self |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Eq for OsString {} |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialOrd for OsString { |
| #[inline] |
| fn partial_cmp(&self, other: &OsString) -> Option<cmp::Ordering> { |
| (&**self).partial_cmp(&**other) |
| } |
| #[inline] |
| fn lt(&self, other: &OsString) -> bool { &**self < &**other } |
| #[inline] |
| fn le(&self, other: &OsString) -> bool { &**self <= &**other } |
| #[inline] |
| fn gt(&self, other: &OsString) -> bool { &**self > &**other } |
| #[inline] |
| fn ge(&self, other: &OsString) -> bool { &**self >= &**other } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialOrd<str> for OsString { |
| #[inline] |
| fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> { |
| (&**self).partial_cmp(other) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Ord for OsString { |
| #[inline] |
| fn cmp(&self, other: &OsString) -> cmp::Ordering { |
| (&**self).cmp(&**other) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Hash for OsString { |
| #[inline] |
| fn hash<H: Hasher>(&self, state: &mut H) { |
| (&**self).hash(state) |
| } |
| } |
| |
| impl OsStr { |
| /// Coerces into an `OsStr` slice. |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr { |
| s.as_ref() |
| } |
| |
| fn from_inner(inner: &Slice) -> &OsStr { |
| unsafe { mem::transmute(inner) } |
| } |
| |
| /// Yields a `&str` slice if the `OsStr` is valid Unicode. |
| /// |
| /// This conversion may entail doing a check for UTF-8 validity. |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn to_str(&self) -> Option<&str> { |
| self.inner.to_str() |
| } |
| |
| /// Converts an `OsStr` to a `Cow<str>`. |
| /// |
| /// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER. |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn to_string_lossy(&self) -> Cow<str> { |
| self.inner.to_string_lossy() |
| } |
| |
| /// Copies the slice into an owned `OsString`. |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn to_os_string(&self) -> OsString { |
| OsString { inner: self.inner.to_owned() } |
| } |
| |
| /// Checks whether the `OsStr` is empty. |
| #[stable(feature = "osstring_simple_functions", since = "1.9.0")] |
| pub fn is_empty(&self) -> bool { |
| self.inner.inner.is_empty() |
| } |
| |
| /// Returns the length of this `OsStr`. |
| /// |
| /// Note that this does **not** return the number of bytes in this string |
| /// as, for example, OS strings on Windows are encoded as a list of `u16` |
| /// rather than a list of bytes. This number is simply useful for passing to |
| /// other methods like `OsString::with_capacity` to avoid reallocations. |
| /// |
| /// See `OsStr` introduction for more information about encoding. |
| #[stable(feature = "osstring_simple_functions", since = "1.9.0")] |
| pub fn len(&self) -> usize { |
| self.inner.inner.len() |
| } |
| |
| /// Gets the underlying byte representation. |
| /// |
| /// Note: it is *crucial* that this API is private, to avoid |
| /// revealing the internal, platform-specific encodings. |
| fn bytes(&self) -> &[u8] { |
| unsafe { mem::transmute(&self.inner) } |
| } |
| } |
| |
| #[stable(feature = "osstring_default", since = "1.9.0")] |
| impl<'a> Default for &'a OsStr { |
| #[inline] |
| fn default() -> &'a OsStr { |
| OsStr::new("") |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialEq for OsStr { |
| fn eq(&self, other: &OsStr) -> bool { |
| self.bytes().eq(other.bytes()) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialEq<str> for OsStr { |
| fn eq(&self, other: &str) -> bool { |
| *self == *OsStr::new(other) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialEq<OsStr> for str { |
| fn eq(&self, other: &OsStr) -> bool { |
| *other == *OsStr::new(self) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Eq for OsStr {} |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialOrd for OsStr { |
| #[inline] |
| fn partial_cmp(&self, other: &OsStr) -> Option<cmp::Ordering> { |
| self.bytes().partial_cmp(other.bytes()) |
| } |
| #[inline] |
| fn lt(&self, other: &OsStr) -> bool { self.bytes().lt(other.bytes()) } |
| #[inline] |
| fn le(&self, other: &OsStr) -> bool { self.bytes().le(other.bytes()) } |
| #[inline] |
| fn gt(&self, other: &OsStr) -> bool { self.bytes().gt(other.bytes()) } |
| #[inline] |
| fn ge(&self, other: &OsStr) -> bool { self.bytes().ge(other.bytes()) } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl PartialOrd<str> for OsStr { |
| #[inline] |
| fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> { |
| self.partial_cmp(OsStr::new(other)) |
| } |
| } |
| |
| // FIXME (#19470): cannot provide PartialOrd<OsStr> for str until we |
| // have more flexible coherence rules. |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Ord for OsStr { |
| #[inline] |
| fn cmp(&self, other: &OsStr) -> cmp::Ordering { self.bytes().cmp(other.bytes()) } |
| } |
| |
| macro_rules! impl_cmp { |
| ($lhs:ty, $rhs: ty) => { |
| #[stable(feature = "cmp_os_str", since = "1.8.0")] |
| impl<'a, 'b> PartialEq<$rhs> for $lhs { |
| #[inline] |
| fn eq(&self, other: &$rhs) -> bool { <OsStr as PartialEq>::eq(self, other) } |
| } |
| |
| #[stable(feature = "cmp_os_str", since = "1.8.0")] |
| impl<'a, 'b> PartialEq<$lhs> for $rhs { |
| #[inline] |
| fn eq(&self, other: &$lhs) -> bool { <OsStr as PartialEq>::eq(self, other) } |
| } |
| |
| #[stable(feature = "cmp_os_str", since = "1.8.0")] |
| impl<'a, 'b> PartialOrd<$rhs> for $lhs { |
| #[inline] |
| fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> { |
| <OsStr as PartialOrd>::partial_cmp(self, other) |
| } |
| } |
| |
| #[stable(feature = "cmp_os_str", since = "1.8.0")] |
| impl<'a, 'b> PartialOrd<$lhs> for $rhs { |
| #[inline] |
| fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> { |
| <OsStr as PartialOrd>::partial_cmp(self, other) |
| } |
| } |
| } |
| } |
| |
| impl_cmp!(OsString, OsStr); |
| impl_cmp!(OsString, &'a OsStr); |
| impl_cmp!(Cow<'a, OsStr>, OsStr); |
| impl_cmp!(Cow<'a, OsStr>, &'b OsStr); |
| impl_cmp!(Cow<'a, OsStr>, OsString); |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Hash for OsStr { |
| #[inline] |
| fn hash<H: Hasher>(&self, state: &mut H) { |
| self.bytes().hash(state) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Debug for OsStr { |
| fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| self.inner.fmt(formatter) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl Borrow<OsStr> for OsString { |
| fn borrow(&self) -> &OsStr { &self[..] } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl ToOwned for OsStr { |
| type Owned = OsString; |
| fn to_owned(&self) -> OsString { self.to_os_string() } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl AsRef<OsStr> for OsStr { |
| fn as_ref(&self) -> &OsStr { |
| self |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl AsRef<OsStr> for OsString { |
| fn as_ref(&self) -> &OsStr { |
| self |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl AsRef<OsStr> for str { |
| fn as_ref(&self) -> &OsStr { |
| OsStr::from_inner(Slice::from_str(self)) |
| } |
| } |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl AsRef<OsStr> for String { |
| fn as_ref(&self) -> &OsStr { |
| (&**self).as_ref() |
| } |
| } |
| |
| impl FromInner<Buf> for OsString { |
| fn from_inner(buf: Buf) -> OsString { |
| OsString { inner: buf } |
| } |
| } |
| |
| impl IntoInner<Buf> for OsString { |
| fn into_inner(self) -> Buf { |
| self.inner |
| } |
| } |
| |
| impl AsInner<Slice> for OsStr { |
| fn as_inner(&self) -> &Slice { |
| &self.inner |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use sys_common::{AsInner, IntoInner}; |
| |
| #[test] |
| fn test_os_string_with_capacity() { |
| let os_string = OsString::with_capacity(0); |
| assert_eq!(0, os_string.inner.into_inner().capacity()); |
| |
| let os_string = OsString::with_capacity(10); |
| assert_eq!(10, os_string.inner.into_inner().capacity()); |
| |
| let mut os_string = OsString::with_capacity(0); |
| os_string.push("abc"); |
| assert!(os_string.inner.into_inner().capacity() >= 3); |
| } |
| |
| #[test] |
| fn test_os_string_clear() { |
| let mut os_string = OsString::from("abc"); |
| assert_eq!(3, os_string.inner.as_inner().len()); |
| |
| os_string.clear(); |
| assert_eq!(&os_string, ""); |
| assert_eq!(0, os_string.inner.as_inner().len()); |
| } |
| |
| #[test] |
| fn test_os_string_capacity() { |
| let os_string = OsString::with_capacity(0); |
| assert_eq!(0, os_string.capacity()); |
| |
| let os_string = OsString::with_capacity(10); |
| assert_eq!(10, os_string.capacity()); |
| |
| let mut os_string = OsString::with_capacity(0); |
| os_string.push("abc"); |
| assert!(os_string.capacity() >= 3); |
| } |
| |
| #[test] |
| fn test_os_string_reserve() { |
| let mut os_string = OsString::new(); |
| assert_eq!(os_string.capacity(), 0); |
| |
| os_string.reserve(2); |
| assert!(os_string.capacity() >= 2); |
| |
| for _ in 0..16 { |
| os_string.push("a"); |
| } |
| |
| assert!(os_string.capacity() >= 16); |
| os_string.reserve(16); |
| assert!(os_string.capacity() >= 32); |
| |
| os_string.push("a"); |
| |
| os_string.reserve(16); |
| assert!(os_string.capacity() >= 33) |
| } |
| |
| #[test] |
| fn test_os_string_reserve_exact() { |
| let mut os_string = OsString::new(); |
| assert_eq!(os_string.capacity(), 0); |
| |
| os_string.reserve_exact(2); |
| assert!(os_string.capacity() >= 2); |
| |
| for _ in 0..16 { |
| os_string.push("a"); |
| } |
| |
| assert!(os_string.capacity() >= 16); |
| os_string.reserve_exact(16); |
| assert!(os_string.capacity() >= 32); |
| |
| os_string.push("a"); |
| |
| os_string.reserve_exact(16); |
| assert!(os_string.capacity() >= 33) |
| } |
| |
| #[test] |
| fn test_os_string_default() { |
| let os_string: OsString = Default::default(); |
| assert_eq!("", &os_string); |
| } |
| |
| #[test] |
| fn test_os_str_is_empty() { |
| let mut os_string = OsString::new(); |
| assert!(os_string.is_empty()); |
| |
| os_string.push("abc"); |
| assert!(!os_string.is_empty()); |
| |
| os_string.clear(); |
| assert!(os_string.is_empty()); |
| } |
| |
| #[test] |
| fn test_os_str_len() { |
| let mut os_string = OsString::new(); |
| assert_eq!(0, os_string.len()); |
| |
| os_string.push("abc"); |
| assert_eq!(3, os_string.len()); |
| |
| os_string.clear(); |
| assert_eq!(0, os_string.len()); |
| } |
| |
| #[test] |
| fn test_os_str_default() { |
| let os_str: &OsStr = Default::default(); |
| assert_eq!("", os_str); |
| } |
| } |