Skip to content

NixPath -> NixString and Error -> Errno #230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions src/cstr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use std::ffi::{OsStr, OsString, CString, NulError};
use std::path::{Path, PathBuf};
use std::os::unix::ffi::{OsStrExt, OsStringExt};

pub trait ToCString {
fn to_cstring(&self) -> Result<CString, NulError>;
fn into_cstring(self) -> Result<CString, NulError> where Self: Sized { unimplemented!() }
}

impl ToCString for [u8] {
fn to_cstring(&self) -> Result<CString, NulError> {
CString::new(self)
}
}

impl ToCString for Vec<u8> {
fn to_cstring(&self) -> Result<CString, NulError> {
ToCString::to_cstring(&**self)
}

fn into_cstring(self) -> Result<CString, NulError> {
CString::new(self)
}
}

impl ToCString for str {
fn to_cstring(&self) -> Result<CString, NulError> {
CString::new(self.as_bytes())
}
}

impl ToCString for String {
fn to_cstring(&self) -> Result<CString, NulError> {
ToCString::to_cstring(&**self)
}

fn into_cstring(self) -> Result<CString, NulError> {
CString::new(self.into_bytes())
}
}

impl ToCString for OsStr {
fn to_cstring(&self) -> Result<CString, NulError> {
CString::new(self.as_bytes())
}
}

impl ToCString for OsString {
fn to_cstring(&self) -> Result<CString, NulError> {
ToCString::to_cstring(&**self)
}

fn into_cstring(self) -> Result<CString, NulError> {
CString::new(self.into_vec())
}
}

impl ToCString for Path {
fn to_cstring(&self) -> Result<CString, NulError> {
ToCString::to_cstring(self.as_os_str())
}
}

impl ToCString for PathBuf {
fn to_cstring(&self) -> Result<CString, NulError> {
ToCString::to_cstring(self.as_os_str())
}

fn into_cstring(self) -> Result<CString, NulError> {
ToCString::into_cstring(self.into_os_string())
}
}

// TODO: allow this in consts/statics
#[macro_export]
macro_rules! cstr {
($s:expr) => {
unsafe { ::std::ffi::CStr::from_ptr(concat!($s, "\0").as_ptr() as *const _) }
}
}
7 changes: 4 additions & 3 deletions src/errno.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use libc::c_int;
use std::{fmt, io, error};
use {Error, Result};
use std::{fmt, io, error, result};

pub use self::consts::*;
pub use self::consts::Errno::*;
Expand Down Expand Up @@ -74,7 +73,7 @@ impl Errno {
/// should not be used when `-1` is not the errno sentinel value.
pub fn result<S: ErrnoSentinel + PartialEq<S>>(value: S) -> Result<S> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth documenting that when implementing new functions this should only be used if the sentinel is actually -1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I'd like it to work for both, but the design I had in mind requires method default type parameters, which aren't ready yet.

if value == S::sentinel() {
Err(Error::Sys(Self::last()))
Err(Self::last())
} else {
Ok(value)
}
Expand Down Expand Up @@ -117,6 +116,8 @@ impl From<Errno> for io::Error {
}
}

pub type Result<T> = result::Result<T, Errno>;

fn last() -> Errno {
Errno::from_i32(errno())
}
Expand Down
10 changes: 5 additions & 5 deletions src/fcntl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use {Errno, Result, NixPath};
use {Errno, Result, NixString};
use libc::{c_int, c_uint};
use sys::stat::Mode;
use std::os::unix::io::RawFd;
Expand Down Expand Up @@ -96,10 +96,10 @@ mod ffi {
}
}

pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
let fd = try!(path.with_nix_path(|cstr| {
unsafe { ffi::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
}));
pub fn open<P: NixString>(path: P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
let fd = unsafe {
ffi::open(path.as_ref().as_ptr(), oflag.bits(), mode.bits() as c_uint)
};

Errno::result(fd)
}
Expand Down
191 changes: 7 additions & 184 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ extern crate nix_test as nixtest;

// Re-exports
pub use libc::{c_int, c_void};
pub use errno::Errno;
pub use errno::{Errno, Result};
pub use nix_string::NixString;

mod nix_string;

#[macro_use]
pub mod cstr;

pub mod errno;
pub mod features;
Expand All @@ -42,186 +48,3 @@ pub mod sched;

pub mod sys;
pub mod unistd;

/*
*
* ===== Result / Error =====
*
*/

use libc::c_char;
use std::{ptr, result};
use std::ffi::{CStr, OsStr};
use std::path::{Path, PathBuf};
use std::os::unix::ffi::OsStrExt;
use std::io;
use std::fmt;
use std::error;
use libc::PATH_MAX;

pub type Result<T> = result::Result<T, Error>;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Error {
Sys(errno::Errno),
InvalidPath,
}

impl Error {
pub fn from_errno(errno: errno::Errno) -> Error {
Error::Sys(errno)
}

pub fn last() -> Error {
Error::Sys(errno::Errno::last())
}

pub fn invalid_argument() -> Error {
Error::Sys(errno::EINVAL)
}

pub fn errno(&self) -> errno::Errno {
match *self {
Error::Sys(errno) => errno,
Error::InvalidPath => errno::Errno::EINVAL,
}
}
}

impl From<errno::Errno> for Error {
fn from(errno: errno::Errno) -> Error { Error::from_errno(errno) }
}

impl error::Error for Error {
fn description(&self) -> &str {
match self {
&Error::InvalidPath => "Invalid path",
&Error::Sys(ref errno) => errno.desc(),
}
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Error::InvalidPath => write!(f, "Invalid path"),
&Error::Sys(errno) => write!(f, "{:?}: {}", errno, errno.desc()),
}
}
}

impl From<Error> for io::Error {
fn from(err: Error) -> Self {
match err {
Error::InvalidPath => io::Error::new(io::ErrorKind::InvalidInput, err),
Error::Sys(errno) => io::Error::from_raw_os_error(errno as i32),
}
}
}

pub trait NixPath {
fn len(&self) -> usize;

fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where F: FnOnce(&CStr) -> T;
}

impl NixPath for str {
fn len(&self) -> usize {
NixPath::len(OsStr::new(self))
}

fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where F: FnOnce(&CStr) -> T {
OsStr::new(self).with_nix_path(f)
}
}

impl NixPath for OsStr {
fn len(&self) -> usize {
self.as_bytes().len()
}

fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where F: FnOnce(&CStr) -> T {
self.as_bytes().with_nix_path(f)
}
}

impl NixPath for CStr {
fn len(&self) -> usize {
self.to_bytes().len()
}

fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where F: FnOnce(&CStr) -> T {
// Equivalence with the [u8] impl.
if self.len() >= PATH_MAX as usize {
return Err(Error::InvalidPath);
}

Ok(f(self))
}
}

impl NixPath for [u8] {
fn len(&self) -> usize {
self.len()
}

fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where F: FnOnce(&CStr) -> T {
let mut buf = [0u8; PATH_MAX as usize];

if self.len() >= PATH_MAX as usize {
return Err(Error::InvalidPath);
}

match self.iter().position(|b| *b == 0) {
Some(_) => Err(Error::InvalidPath),
None => {
unsafe {
// TODO: Replace with bytes::copy_memory. rust-lang/rust#24028
ptr::copy_nonoverlapping(self.as_ptr(), buf.as_mut_ptr(), self.len());
Ok(f(CStr::from_ptr(buf.as_ptr() as *const c_char)))
}

}
}
}
}

impl NixPath for Path {
fn len(&self) -> usize {
NixPath::len(self.as_os_str())
}

fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
self.as_os_str().with_nix_path(f)
}
}

impl NixPath for PathBuf {
fn len(&self) -> usize {
NixPath::len(self.as_os_str())
}

fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
self.as_os_str().with_nix_path(f)
}
}

/// Treats `None` as an empty string.
impl<'a, NP: ?Sized + NixPath> NixPath for Option<&'a NP> {
fn len(&self) -> usize {
self.map_or(0, NixPath::len)
}

fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
if let Some(nix_path) = *self {
nix_path.with_nix_path(f)
} else {
unsafe { CStr::from_ptr("\0".as_ptr() as *const _).with_nix_path(f) }
}
}
}
Loading