@ -71,9 +71,197 @@ mod raw {
// Errno error handling
//TODO: Move all errno related stuff into a submodule inside `srd/loli/` to un-clutter this file; and add a `RawErrno`-like struct for errors that are *just* an errno value (that implements `SystemError`)
/// A raw `errno` value
pub type RawErrno = raw ::c_int ;
/// Default max buffer size for formatting `errno` message codes.
///
/// This is the size used by glibc's `perror()`. It should cover all internationalised error messages.
pub const DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE : usize = 1024 ;
/// Default buffer size short `errno` message code strings.
///
/// # Internationalisation
/// This size covers all current English `errno` messages. However, if the program is ran with a non-ASCII locale, the size may exceed this by far. See `DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE` for those cases.
pub const DEFAULT_ASCII_ERRNO_MESSAGE_BUFFER_SIZE : usize = 64 ;
/// An error type which also contains an `errno` code.
pub trait SystemError : std ::error ::Error
{
fn code ( & self ) -> RawErrno ;
#[ inline(always) ]
fn fmt_message ( & self , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
errno_fmt_message ::< 1024 > ( self . code ( ) , f )
}
#[ inline ]
fn get_message ( & self ) -> ErrnoFormatter
{
ErrnoFormatter ::new_default ( self . code ( ) )
}
#[ inline ]
fn message ( & self ) -> String
{
self . get_message ( ) . to_string ( )
}
}
/// Extension trait for `SystemError` types.
///
/// # Specific buffered sizes
/// Provides user-configurable buffer sized methods for message formatting
pub trait SystemErrorExt : SystemError
{
fn fmt_message_with_buffer < const BUFFER_SIZE : usize > ( & self , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result ;
fn get_message_with_buffer < const BUFFER_SIZE : usize > ( & self ) -> ErrnoFormatter < BUFFER_SIZE > ;
fn message_with_buffer < const BUFFER_SIZE : usize > ( & self ) -> String ;
}
impl < T : ? Sized > SystemErrorExt for T
where T : SystemError
{
#[ inline ]
fn fmt_message_with_buffer < const BUFFER_SIZE : usize > ( & self , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
errno_fmt_message ::< BUFFER_SIZE > ( self . code ( ) , f )
}
#[ inline ]
fn get_message_with_buffer < const BUFFER_SIZE : usize > ( & self ) -> ErrnoFormatter < BUFFER_SIZE >
{
ErrnoFormatter ::< BUFFER_SIZE > ::new ( self . code ( ) )
}
#[ inline ]
fn message_with_buffer < const BUFFER_SIZE : usize > ( & self ) -> String
{
ErrnoFormatter ::< BUFFER_SIZE > ::new ( self . code ( ) ) . to_string ( )
}
}
/// String formatter for an `errno` value.
///
/// # `MESSAGE_BUFFER_SIZE`
/// This structure contains only the `errno` code. The `MESSAGE_BUFFER_SIZE` const generic is used as a max size for formatting the error messages.
/// It **must** be above 0.
#[ derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy) ]
#[ repr(transparent) ]
pub struct ErrnoFormatter < const MESSAGE_BUFFER_SIZE : usize = DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE > ( RawErrno ) ;
//TODO: Document the following
impl ErrnoFormatter < DEFAULT_ASCII_ERRNO_MESSAGE_BUFFER_SIZE >
{
#[ inline(always) ]
pub const fn new_ascii ( code : RawErrno ) -> Self
{
Self ( code )
}
}
impl ErrnoFormatter < DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE >
{
#[ inline(always) ]
pub const fn new_default ( code : RawErrno ) -> Self
{
Self ( code )
}
}
impl < const BUFF_SIZE : usize > ErrnoFormatter < BUFF_SIZE >
{
#[ inline(always) ]
pub const fn new ( code : RawErrno ) -> Self
{
Self ( code )
}
#[ inline ]
pub const fn raw ( self ) -> RawErrno
{
self . 0
}
#[ inline ]
pub const fn buffer_size ( self ) -> usize
{
BUFF_SIZE
}
#[ inline ]
pub const fn with_buffer_size < const NBUFF_SIZE : usize > ( self ) -> ErrnoFormatter < NBUFF_SIZE >
{
ErrnoFormatter ::< NBUFF_SIZE > ::new ( self . 0 )
}
pub fn try_to_string ( & self ) -> Result < String , Result < UString , RawErrno > >
{
match self . try_to_raw_string ( ) {
Ok ( string ) = > string . into_string ( ) . map_err ( | us | Ok ( us ) ) ,
Err ( en ) = > Err ( Err ( en ) ) ,
}
}
#[ inline ]
pub fn try_to_raw_string ( & self ) -> Result < UString , RawErrno >
{
errno_ustring_buffer ::< BUFF_SIZE > ( self . 0 )
}
#[ inline ]
pub fn try_to_c_string ( & self ) -> Result < CString , RawErrno >
{
errno_cstring_buffer ::< BUFF_SIZE > ( self . 0 )
}
#[ inline ]
pub fn to_raw_buffer ( & self ) -> Result < [ u8 ; BUFF_SIZE ] , RawErrno >
{
errno_raw_buffer ( self . 0 )
}
}
impl < const BUFF_SIZE : usize > fmt ::Display for ErrnoFormatter < BUFF_SIZE >
{
#[ inline ]
fn fmt ( & self , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
errno_fmt_message ::< BUFF_SIZE > ( self . 0 , f )
}
}
fn errno_fmt_message < const BUFF_SIZE : usize > ( code : RawErrno , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
if let Ok ( buffer ) = errno_raw_buffer ::< BUFF_SIZE > ( code )
{
if let Some ( first ) = buffer . split ( | & b | b = = 0 ) . next ( )
{
f . write_str ( String ::from_utf8_lossy ( first ) . as_ref ( ) )
} else {
#[ cold ]
#[ inline(never) ] //XXX: Should we noinline this cold path? I think we should.
fn _failed_path ( code : RawErrno , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
write! ( f , "<!unknown: empty message for errno {}>" , code )
}
_failed_path ( code , f )
}
} else {
#[ cold ]
#[ inline(never) ]
fn _failed_path ( code : RawErrno , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
write! ( f , "<!unknown: message extraction failed for errno {}>" , code )
}
_failed_path ( code , f )
}
}
fn errno_raw_buffer < const BUFF_SIZE : usize > ( errno : RawErrno ) -> Result < [ u8 ; BUFF_SIZE ] , RawErrno >
{
use std ::mem ::MaybeUninit ;
@ -121,11 +309,11 @@ fn errno_lossy_string_buffer<const BUFF_SIZE: usize>(errno: RawErrno) -> Result<
#[ derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord) ]
pub struct DupError ( RawErrno ) ;
impl DupError
impl SystemError for DupError
{
/// The `errno` code for this error.
#[ inline ]
pub fn code ( & self ) -> RawErrno
fn code ( & self ) -> RawErrno
{
self . 0
}
@ -136,16 +324,8 @@ impl fmt::Display for DupError
{
fn fmt ( & self , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
write! ( f , "dup() failed with " ) ? ;
if let Ok ( buffer ) = errno_raw_buffer ::< 1024 > ( self . 0 ) {
let string = String ::from_utf8_lossy ( buffer . split ( | & b | b = = 0 )
. next ( )
. unwrap_or ( b" <empty error message buffer> " ) ) ;
write! ( f , "{}: {}" , self . 0 , string )
} else {
write! ( f , "{} <failed to extract code message at 1024 bytes buffer size>" , self . 0 )
}
write! ( f , "dup() failed with {}: " , self . 0 ) ? ;
self . fmt_message ( f )
}
}
@ -199,6 +379,8 @@ fn errno_if_nz(rv: raw::c_int) -> Result<(), RawErrno>
// Wrapper traits
//TODO: Move wrapper traits into a seperate module in src/loli/ to unclutter this file
/// Extension methods for `dup()`ing file descriptors
pub trait DupExt : Sized
{
@ -233,7 +415,10 @@ where T: FromRawFd + AsRawFd
}
}
//TODO: Document the following, and make it work like CStr/CString
//TODO: Document the following, and make sure it works like CStr/CString (minus the nul-termination bs)
//TODO: Move UStr/UString and related stuffs into a seperate module in src/loli/ to unclutter this file
/// A reference to an unmanaged string that may contain non-utf8 characters.
///
@ -422,6 +607,14 @@ impl UString
} )
}
pub fn try_into_string ( self ) -> Result < String , std ::string ::FromUtf8Error >
{
let vec = Vec ::from ( self . 0 ) ;
String ::from_utf8 ( vec ) /* .map_err(|e| {
Self ( e . into_bytes ( ) . into ( ) )
} ) * /
}
/// Allocate a new owned `CString` from this nul-terminated unmanaged string.
///
/// # Safety
@ -486,8 +679,9 @@ fn deref(&self) -> &Self::Target {
& self . 0 [ .. ]
}
}
XXX : Is DerefMut needed , too ?
XXX : What about AsMut ?
XXX : What about AsMut ? I think those two are unsafe , especially if the & mut UStr comes from an & mut str . There are unsafe methods for this anyway .
* /
impl AsRef < UStr > for UString
@ -510,7 +704,7 @@ impl Deref for UString
}
}
// XXX: Should we allow mutation of the `ustr` here? I think so, because we don't allow mutation of the underlying [u8]
// Should we allow mutation of the `ustr` here? I think so, because we don't allow mutation of the underlying [u8]
impl AsMut < UStr > for UString
{
#[ inline(always) ]
@ -527,6 +721,91 @@ impl DerefMut for UString
}
}
// UStr/ing <--?> CStr/ing
impl From < CString > for UString
{
#[ inline ]
fn from ( from : CString ) -> Self
{
Self ::from_c_string ( from )
}
}
impl < ' a > From < & ' a CStr > for & ' a UStr
{
#[ inline ]
fn from ( from : & ' a CStr ) -> Self
{
UStr ::from_c_str ( from )
}
}
impl TryFrom < UString > for CString
{
type Error = std ::ffi ::FromVecWithNulError ;
#[ inline ]
fn try_from ( from : UString ) -> Result < Self , Self ::Error >
{
from . into_c_string ( )
}
}
impl < ' a > TryFrom < & ' a UStr > for & ' a CStr
{
type Error = std ::ffi ::FromBytesWithNulError ;
#[ inline ]
fn try_from ( from : & ' a UStr ) -> Result < Self , Self ::Error >
{
from . as_c_str ( )
}
}
// UStr/ing <--?> str/ing
impl From < String > for UString
{
#[ inline ]
fn from ( from : String ) -> Self
{
Self ::new ( from . into_bytes ( ) )
}
}
impl < ' a > From < & ' a str > for & ' a UStr
{
#[ inline ]
fn from ( from : & ' a str ) -> Self
{
UStr ::new ( from . as_bytes ( ) )
}
}
// No From<&'_ mut str> for &'_ mut UStr: The bytes belonging to the &'_ str may be modified in safe code if this were allowed.
impl TryFrom < UString > for String
{
type Error = std ::string ::FromUtf8Error ;
#[ inline ]
fn try_from ( from : UString ) -> Result < Self , Self ::Error >
{
from . try_into_string ( )
}
}
impl < ' a > TryFrom < & ' a UStr > for & ' a str
{
type Error = std ::str ::Utf8Error ;
#[ inline ]
fn try_from ( from : & ' a UStr ) -> Result < Self , Self ::Error >
{
from . to_str ( )
}
}
#[ cfg(test) ]
mod tests
{