use core::fmt;
#[cfg(feature = "alloc")]
use core::ptr;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::marker::PhantomData;
#[cfg(feature = "std")]
use std::error::Error as StdError;
#[cfg(feature = "alloc")]
use core::mem::{align_of, size_of};
#[cfg(feature = "alloc")]
use self::super::trivial::TriviallyTransmutable;
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Error<'a, S, T> {
    
    Guard(GuardError),
    
    Unaligned(UnalignedError<'a, S, T>),
    
    
    
    
    #[cfg(feature = "alloc")]
    IncompatibleVecTarget(IncompatibleVecTargetError<S, T>),
    
    InvalidValue,
}
impl<'a, S, T> Error<'a, S, T> {
    
    
    
    
    #[cfg(feature = "alloc")]
    pub fn copy(self) -> Result<Vec<T>, Error<'a, S, T>>
        where T: TriviallyTransmutable
    {
        match self {
            Error::Unaligned(e) => Ok(e.copy()),
            Error::IncompatibleVecTarget(e) => Ok(e.copy()),
            e => Err(e),
        }
    }
    
    
    
    
    
    
    
    
    
    #[cfg(feature = "alloc")]
    pub unsafe fn copy_unchecked(self) -> Result<Vec<T>, Error<'a, S, T>> {
        match self {
            Error::Unaligned(e) => Ok(e.copy_unchecked()),
            Error::IncompatibleVecTarget(e) => Ok(e.copy_unchecked()),
            e => Err(e),
        }
    }
    
    
    
    pub fn without_src<'z>(self) -> Error<'z, S, T> {
        match self {
            Error::Unaligned(UnalignedError { source: _, offset, phantom }) => {
                Error::Unaligned(UnalignedError {
                    source: &[],
                    offset: offset,
                    phantom: phantom,
                })
            }
            Error::Guard(e) => Error::Guard(e),
            Error::InvalidValue => Error::InvalidValue,
            #[cfg(feature = "alloc")]
            Error::IncompatibleVecTarget(e) => Error::IncompatibleVecTarget(e),
        }
    }
}
impl<'a, S, T> fmt::Debug for Error<'a, S, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::Guard(e) => write!(f, "Guard({:?})", e),
            Error::Unaligned(e) => write!(f, "Unaligned({:?})", e),
            Error::InvalidValue => f.write_str("InvalidValue"),
            #[cfg(feature = "alloc")]
            Error::IncompatibleVecTarget(_) => f.write_str("IncompatibleVecTarget"),
        }
    }
}
#[cfg(feature = "std")]
#[allow(deprecated)]
impl<'a, S, T> StdError for Error<'a, S, T> {
    fn description(&self) -> &str {
        match self {
            Error::Guard(e) => e.description(),
            Error::Unaligned(e) => e.description(),
            Error::InvalidValue => "invalid target value",
            Error::IncompatibleVecTarget(e) => e.description(),
        }
    }
}
impl<'a, S, T> fmt::Display for Error<'a, S, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::Guard(e) => e.fmt(f),
            Error::Unaligned(e) => e.fmt(f),
            Error::InvalidValue => f.write_str("Invalid target value"),
            #[cfg(feature = "alloc")]
            Error::IncompatibleVecTarget(e) => e.fmt(f),
        }
    }
}
impl<'a, S, T> From<GuardError> for Error<'a, S, T> {
    fn from(o: GuardError) -> Self {
        Error::Guard(o)
    }
}
impl<'a, S, T> From<UnalignedError<'a, S, T>> for Error<'a, S, T> {
    fn from(o: UnalignedError<'a, S, T>) -> Self {
        Error::Unaligned(o)
    }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GuardError {
    
    pub required: usize,
    
    pub actual: usize,
    
    pub reason: ErrorReason,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ErrorReason {
    
    NotEnoughBytes,
    
    
    
    TooManyBytes,
    
    InexactByteCount,
}
#[cfg(feature = "std")]
impl StdError for GuardError {
    fn description(&self) -> &str {
        self.reason.description()
    }
}
impl fmt::Display for GuardError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} (required: {}, actual: {})", self.reason.description(), self.required, self.actual)
    }
}
impl ErrorReason {
    
    pub fn description(self) -> &'static str {
        match self {
            ErrorReason::NotEnoughBytes => "Not enough bytes to fill type",
            ErrorReason::TooManyBytes => "Too many bytes for type",
            ErrorReason::InexactByteCount => "Not exactly the amount of bytes for type",
        }
    }
}
#[cfg(feature = "alloc")]
unsafe fn copy_to_vec_unchecked<S, T>(data: &[S]) -> Vec<T> {
    let len = data.len() * size_of::<S>() / size_of::<T>();
    let mut out = Vec::with_capacity(len);
    ptr::copy_nonoverlapping(data.as_ptr() as *const u8, out.as_mut_ptr() as *mut u8, len * size_of::<T>());
    out.set_len(len);
    out
}
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct UnalignedError<'a, S, T> {
    
    
    pub offset: usize,
    
    pub source: &'a [S],
    phantom: PhantomData<T>,
}
impl<'a, S, T> UnalignedError<'a, S, T> {
    pub fn new(offset: usize, source: &'a [S]) -> Self {
        UnalignedError {
            offset: offset,
            source: source,
            phantom: PhantomData,
        }
    }
    
    
    
    
    
    
    
    
    #[cfg(feature = "alloc")]
    pub unsafe fn copy_unchecked(&self) -> Vec<T> {
        copy_to_vec_unchecked::<S, T>(self.source)
    }
    
    
    
    
    #[cfg(feature = "alloc")]
    pub fn copy(&self) -> Vec<T>
        where T: TriviallyTransmutable
    {
        unsafe {
            
            self.copy_unchecked()
        }
    }
}
impl<'a, S, T> fmt::Debug for UnalignedError<'a, S, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        
        
        struct Source {
            len: usize,
        }
        impl fmt::Debug for Source {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.debug_struct("&[S]")
                    .field("len", &self.len)
                    .finish()
            }
        }
        f.debug_struct("UnalignedError")
            .field("offset", &self.offset)
            .field("source", &Source { len: self.source.len() })
            .finish()
    }
}
#[cfg(feature = "std")]
impl<'a, S, T> StdError for UnalignedError<'a, S, T> {
    fn description(&self) -> &str {
        "data is unaligned"
    }
}
impl<'a, S, T> fmt::Display for UnalignedError<'a, S, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "data is unaligned (off by {} bytes)", self.offset)
    }
}
#[cfg(feature = "alloc")]
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct IncompatibleVecTargetError<S, T> {
    
    pub vec: Vec<S>,
    
    target: PhantomData<T>,
}
#[cfg(feature = "alloc")]
impl<S, T> IncompatibleVecTargetError<S, T> {
    
    pub fn new(vec: Vec<S>) -> Self {
        IncompatibleVecTargetError {
            vec: vec,
            target: PhantomData,
        }
    }
    
    
    
    
    
    
    
    
    pub unsafe fn copy_unchecked(&self) -> Vec<T> {
        copy_to_vec_unchecked::<S, T>(&self.vec)
    }
    
    
    
    pub fn copy(&self) -> Vec<T>
        where T: TriviallyTransmutable
    {
        unsafe {
            
            self.copy_unchecked()
        }
    }
}
#[cfg(feature = "alloc")]
impl<'a, S, T> From<IncompatibleVecTargetError<S, T>> for Error<'a, S, T> {
    fn from(e: IncompatibleVecTargetError<S, T>) -> Self {
        Error::IncompatibleVecTarget(e)
    }
}
#[cfg(feature = "alloc")]
impl<S, T> fmt::Debug for IncompatibleVecTargetError<S, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("IncompatibleVecTargetError")
            .field("size_of<S>", &size_of::<S>())
            .field("align_of<S>", &align_of::<S>())
            .field("size_of<T>", &size_of::<T>())
            .field("align_of<T>", &align_of::<T>())
            .finish()
    }
}
#[cfg(feature = "std")]
impl<S, T> StdError for IncompatibleVecTargetError<S, T> {
    fn description(&self) -> &str {
        "incompatible target type"
    }
}
#[cfg(feature = "alloc")]
impl<S, T> fmt::Display for IncompatibleVecTargetError<S, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "incompatible target type (size: {}, align: {}) for transmutation from source (size: {}, align: {})",
               size_of::<T>(),
               align_of::<T>(),
               size_of::<S>(),
               align_of::<S>())
    }
}