| //! Convenient and efficient string argument passing. |
| //! |
| //! This module defines the `Arg` trait and implements it for several common |
| //! string types. This allows users to pass any of these string types directly |
| //! to rustix APIs with string arguments, and it allows rustix to implement |
| //! NUL-termination without the need for copying where possible. |
| |
| use crate::ffi::CStr; |
| use crate::io; |
| #[cfg(feature = "itoa")] |
| use crate::path::DecInt; |
| use crate::path::SMALL_PATH_BUFFER_SIZE; |
| #[cfg(all(feature = "alloc", feature = "itoa"))] |
| use alloc::borrow::ToOwned; |
| use core::mem::MaybeUninit; |
| use core::{ptr, slice, str}; |
| #[cfg(feature = "std")] |
| use std::ffi::{OsStr, OsString}; |
| #[cfg(all(feature = "std", target_os = "hermit"))] |
| use std::os::hermit::ext::ffi::{OsStrExt, OsStringExt}; |
| #[cfg(all(feature = "std", unix))] |
| use std::os::unix::ffi::{OsStrExt, OsStringExt}; |
| #[cfg(all(feature = "std", target_os = "vxworks"))] |
| use std::os::vxworks::ext::ffi::{OsStrExt, OsStringExt}; |
| #[cfg(all(feature = "std", target_os = "wasi"))] |
| use std::os::wasi::ffi::{OsStrExt, OsStringExt}; |
| #[cfg(feature = "std")] |
| use std::path::{Component, Components, Iter, Path, PathBuf}; |
| #[cfg(feature = "alloc")] |
| use {crate::ffi::CString, alloc::borrow::Cow}; |
| #[cfg(feature = "alloc")] |
| use {alloc::string::String, alloc::vec::Vec}; |
| |
| /// A trait for passing path arguments. |
| /// |
| /// This is similar to [`AsRef`]`<`[`Path`]`>`, but is implemented for more |
| /// kinds of strings and can convert into more kinds of strings. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # #[cfg(any(feature = "fs", feature = "net"))] |
| /// use rustix::ffi::CStr; |
| /// use rustix::io; |
| /// # #[cfg(any(feature = "fs", feature = "net"))] |
| /// use rustix::path::Arg; |
| /// |
| /// # #[cfg(any(feature = "fs", feature = "net"))] |
| /// pub fn touch<P: Arg>(path: P) -> io::Result<()> { |
| /// let path = path.into_c_str()?; |
| /// _touch(&path) |
| /// } |
| /// |
| /// # #[cfg(any(feature = "fs", feature = "net"))] |
| /// fn _touch(path: &CStr) -> io::Result<()> { |
| /// // implementation goes here |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// Users can then call `touch("foo")`, `touch(cstr!("foo"))`, |
| /// `touch(Path::new("foo"))`, or many other things. |
| /// |
| /// [`AsRef`]: std::convert::AsRef |
| pub trait Arg { |
| /// Returns a view of this string as a string slice. |
| fn as_str(&self) -> io::Result<&str>; |
| |
| /// Returns a potentially-lossy rendering of this string as a |
| /// `Cow<'_, str>`. |
| #[cfg(feature = "alloc")] |
| fn to_string_lossy(&self) -> Cow<'_, str>; |
| |
| /// Returns a view of this string as a maybe-owned [`CStr`]. |
| #[cfg(feature = "alloc")] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>>; |
| |
| /// Consumes `self` and returns a view of this string as a maybe-owned |
| /// [`CStr`]. |
| #[cfg(feature = "alloc")] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b; |
| |
| /// Runs a closure with `self` passed in as a `&CStr`. |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>; |
| } |
| |
| impl Arg for &str { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| Ok(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| Cow::Borrowed(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl Arg for &String { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| Ok(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| Cow::Borrowed(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(String::as_str(self)).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| self.as_str().into_c_str() |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl Arg for String { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| Ok(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| Cow::Borrowed(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_str()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl Arg for &OsStr { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| OsStr::to_string_lossy(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl Arg for &OsString { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| OsString::as_os_str(self).to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| self.as_os_str().to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(OsString::as_os_str(self).as_bytes()) |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| self.as_os_str().into_c_str() |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl Arg for OsString { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| self.as_os_str().to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f(&CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl Arg for &Path { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| Path::to_string_lossy(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_os_str().as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl Arg for &PathBuf { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| PathBuf::as_path(self) |
| .as_os_str() |
| .to_str() |
| .ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| self.as_path().to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(PathBuf::as_path(self).as_os_str().as_bytes()) |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| self.as_path().into_c_str() |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_os_str().as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl Arg for PathBuf { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| self.as_os_str().to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self.into_os_string().into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f( |
| &CString::new(self.into_os_string().into_vec()) |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| ) |
| } |
| } |
| |
| impl Arg for &CStr { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| CStr::to_string_lossy(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Borrowed(self)) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Borrowed(self)) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f(self) |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl Arg for &CString { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| unimplemented!() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| unimplemented!() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Borrowed(self)) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Borrowed(self)) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f(self) |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl Arg for CString { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| CStr::to_string_lossy(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Borrowed(self)) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned(self)) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f(&self) |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl<'a> Arg for Cow<'a, str> { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| Ok(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| Cow::Borrowed(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_ref()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| match self { |
| Cow::Owned(s) => CString::new(s), |
| Cow::Borrowed(s) => CString::new(s), |
| } |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| #[cfg(feature = "alloc")] |
| impl<'a> Arg for Cow<'a, OsStr> { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| (**self).to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| (**self).to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| match self { |
| Cow::Owned(os) => CString::new(os.into_vec()), |
| Cow::Borrowed(os) => CString::new(os.as_bytes()), |
| } |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl<'a> Arg for Cow<'a, CStr> { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| let borrow: &CStr = core::borrow::Borrow::borrow(self); |
| borrow.to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Borrowed(self)) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(self) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f(&self) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl<'a> Arg for Component<'a> { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| self.as_os_str().to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_os_str().as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl<'a> Arg for Components<'a> { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.as_path().to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| self.as_path().to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_path().as_os_str().as_bytes()) |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self.as_path().as_os_str().as_bytes()) |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_path().as_os_str().as_bytes(), f) |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| impl<'a> Arg for Iter<'a> { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| self.as_path().to_str().ok_or(io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| self.as_path().to_string_lossy() |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_path().as_os_str().as_bytes()) |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self.as_path().as_os_str().as_bytes()) |
| .map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self.as_path().as_os_str().as_bytes(), f) |
| } |
| } |
| |
| impl Arg for &[u8] { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| String::from_utf8_lossy(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self, f) |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl Arg for &Vec<u8> { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| String::from_utf8_lossy(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| with_c_str(self, f) |
| } |
| } |
| |
| #[cfg(feature = "alloc")] |
| impl Arg for Vec<u8> { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| String::from_utf8_lossy(self) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Owned( |
| CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned( |
| CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
| )) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?) |
| } |
| } |
| |
| #[cfg(feature = "itoa")] |
| impl Arg for DecInt { |
| #[inline] |
| fn as_str(&self) -> io::Result<&str> { |
| Ok(self.as_str()) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn to_string_lossy(&self) -> Cow<'_, str> { |
| Cow::Borrowed(self.as_str()) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
| Ok(Cow::Borrowed(self.as_c_str())) |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
| where |
| Self: 'b, |
| { |
| Ok(Cow::Owned(self.as_c_str().to_owned())) |
| } |
| |
| #[inline] |
| fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
| where |
| Self: Sized, |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| f(self.as_c_str()) |
| } |
| } |
| |
| /// Runs a closure with `bytes` passed in as a `&CStr`. |
| #[allow(unsafe_code, clippy::int_plus_one)] |
| #[inline] |
| fn with_c_str<T, F>(bytes: &[u8], f: F) -> io::Result<T> |
| where |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| // Most paths are less than `SMALL_PATH_BUFFER_SIZE` long. The rest can go |
| // through the dynamic allocation path. If you're opening many files in a |
| // directory with a long path, consider opening the directory and using |
| // `openat` to open the files under it, which will avoid this, and is often |
| // faster in the OS as well. |
| |
| // Test with >= so that we have room for the trailing NUL. |
| if bytes.len() >= SMALL_PATH_BUFFER_SIZE { |
| return with_c_str_slow_path(bytes, f); |
| } |
| |
| // Taken from |
| // <https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs> |
| let mut buf = MaybeUninit::<[u8; SMALL_PATH_BUFFER_SIZE]>::uninit(); |
| let buf_ptr = buf.as_mut_ptr().cast::<u8>(); |
| |
| // This helps test our safety condition below. |
| debug_assert!(bytes.len() + 1 <= SMALL_PATH_BUFFER_SIZE); |
| |
| // SAFETY: `bytes.len() < SMALL_PATH_BUFFER_SIZE` which means we have space |
| // for `bytes.len() + 1` u8s: |
| unsafe { |
| ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); |
| buf_ptr.add(bytes.len()).write(0); |
| } |
| |
| // SAFETY: We just wrote the bytes above and they will remain valid for the |
| // duration of `f` b/c buf doesn't get dropped until the end of the |
| // function. |
| match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) { |
| Ok(s) => f(s), |
| Err(_) => Err(io::Errno::INVAL), |
| } |
| } |
| |
| /// The slow path which handles any length. In theory OS's only support up to |
| /// `PATH_MAX`, but we let the OS enforce that. |
| #[allow(unsafe_code, clippy::int_plus_one)] |
| #[cold] |
| fn with_c_str_slow_path<T, F>(bytes: &[u8], f: F) -> io::Result<T> |
| where |
| F: FnOnce(&CStr) -> io::Result<T>, |
| { |
| #[cfg(feature = "alloc")] |
| { |
| f(&CString::new(bytes).map_err(|_cstr_err| io::Errno::INVAL)?) |
| } |
| |
| #[cfg(not(feature = "alloc"))] |
| { |
| #[cfg(libc)] |
| const LARGE_PATH_BUFFER_SIZE: usize = libc::PATH_MAX as usize; |
| #[cfg(linux_raw)] |
| const LARGE_PATH_BUFFER_SIZE: usize = linux_raw_sys::general::PATH_MAX as usize; |
| |
| // Taken from |
| // <https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs> |
| let mut buf = MaybeUninit::<[u8; LARGE_PATH_BUFFER_SIZE]>::uninit(); |
| let buf_ptr = buf.as_mut_ptr().cast::<u8>(); |
| |
| // This helps test our safety condition below. |
| if bytes.len() + 1 > LARGE_PATH_BUFFER_SIZE { |
| return Err(io::Errno::NAMETOOLONG); |
| } |
| |
| // SAFETY: `bytes.len() < LARGE_PATH_BUFFER_SIZE` which means we have |
| // space for `bytes.len() + 1` u8s: |
| unsafe { |
| ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); |
| buf_ptr.add(bytes.len()).write(0); |
| } |
| |
| // SAFETY: We just wrote the bytes above and they will remain valid for |
| // the duration of `f` b/c buf doesn't get dropped until the end of the |
| // function. |
| match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) |
| { |
| Ok(s) => f(s), |
| Err(_) => Err(io::Errno::INVAL), |
| } |
| } |
| } |