<commit_before>//! Contains detailed error representation.

use std::error::Error;
use std::fmt;
use std::io;

use crate::color::ExtendedColorType;
use crate::image::ImageFormat;

/// The generic error type for image operations.
///
/// This high level enum is allows, by variant matching, a rough separation of concerns between
/// underlying IO, the caller, format specifications, and the `image` implementation.
#[derive(Debug)]
pub enum NewImageError {
    /// An error was encountered while decoding.
    ///
    /// This means that the input data did not conform to the specification of some image format,
    /// or that no format could be determined, or that it did not match format specific
    /// requirements set by the caller.
    Decoding(DecodingError),

    /// An error was encountered while encoding.
    ///
    /// The input image can not be encoded with the chosen format, for example because the
    /// specification has no representation for its color space or because a necessary conversion
    /// is ambiguous. In some cases it might also happen that the dimensions can not be used with
    /// the format.
    Encoding(EncodingError),

    /// An error was encountered in inputs arguments.
    ///
    /// This is a catch-all case for strictly internal operations such as scaling, conversions,
    /// etc. that involve no external format specifications.
    Parameter(ParameterError),

    /// Completing the operation would have required more resources than allowed.
    ///
    /// Errors of this type are limits set by the user or environment, *not* inherent in a specific
    /// format or operation that was executed.
    Limits(LimitError),

    /// An operation can not be completed by the chosen abstraction.
    ///
    /// This means that it might be possible for the operation to succeed in general but
    /// * it requires a disabled feature,
    /// * the implementation does not yet exist, or
    /// * no abstraction for a lower level could be found.
    Unsupported(UnsupportedError),

    /// An error occurred while interacting with the environment.
    IoError(io::Error),
}

/// An enumeration of Image errors
#[derive(Debug)]
pub enum ImageError {
    /// The Image is not formatted properly
    FormatError(String),

    /// The Image's dimensions are either too small or too large
    DimensionError,

    /// The Decoder does not support this image format
    UnsupportedError(String),

    /// The Decoder does not support this color type
    UnsupportedColor(ExtendedColorType),

    /// Not enough data was provided to the Decoder
    /// to decode the image
    NotEnoughData,

    /// An I/O Error occurred while decoding the image
    IoError(io::Error),

    /// The end of the image has been reached
    ImageEnd,

    /// There is not enough memory to complete the given operation
    InsufficientMemory,
}

/// The implementation for an operation was not provided.
///
/// See the variant [`Unsupported`] for more documentation.
///
/// [`Unsupported`]: struct.ImageError.html#variant.Unsupported
#[derive(Debug)]
pub struct UnsupportedError {
    format: ImageFormatHint,
    kind: UnsupportedErrorKind,
}

/// Details what feature is not supported.
#[derive(Clone, Debug, Hash, PartialEq)]
pub enum UnsupportedErrorKind {
    /// The required color type can not be handled.
    Color(ExtendedColorType),
    /// An image format is not supported.
    Format(ImageFormatHint),
    Feature,
    #[doc(hidden)]
    __NonExhaustive,
}

#[derive(Debug)]
pub struct EncodingError {
    format: ImageFormatHint,
    message: String,
    underlying: Option<Box<dyn Error>>,
}

#[derive(Debug)]
pub struct ParameterError {
    kind: ParameterErrorKind,
    underlying: Option<Box<dyn Error>>,
}

/// Details how a parameter is malformed.
#[derive(Clone, Debug, Hash, PartialEq)]
pub enum ParameterErrorKind {
    /// Repeated an operation for which error that could not be cloned was emitted already.
    FailedAlready,
    /// The dimensions passed are wrong.
    DimensionMismatch,
    /// A string describing the parameter.
    /// This is discouraged and is likely to get deprecated (but not removed).
    Generic(String),
    #[doc(hidden)]
    __NonExhaustive,
}

#[derive(Debug)]
pub struct DecodingError {
    format: ImageFormatHint,
    message: String,
    underlying: Option<Box<dyn Error>>,
}

#[derive(Debug)]
pub struct LimitError {
    kind: LimitErrorKind,
    // do we need an underlying error?
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[allow(missing_copy_implementations)] // Might be non-Copy in the future.
pub enum LimitErrorKind {
    DimensionError,
    InsufficientMemory,
    #[doc(hidden)]
    __NonExhaustive,
}

/// A best effort representation for image formats.
#[derive(Clone, Debug, Hash, PartialEq)]
pub enum ImageFormatHint {
    /// The format is known exactly.
    Exact(ImageFormat),

    /// The format can be identified by a name.
    Name(String),

    /// A common path extension for the format is known.
    PathExtension(std::path::PathBuf),

    /// The format is not known or could not be determined.
    Unknown,

    #[doc(hidden)]
    __NonExhaustive,
}

impl UnsupportedError {
    pub fn kind(&self) -> UnsupportedErrorKind {
        self.kind.clone()
    }

    pub fn format_hint(&self) -> ImageFormatHint {
        self.format.clone()
    }
}

impl DecodingError {
    pub fn format_hint(&self) -> ImageFormatHint {
        self.format.clone()
    }
}

impl EncodingError {
    pub fn format_hint(&self) -> ImageFormatHint {
        self.format.clone()
    }
}

impl LimitError {
    pub fn kind(&self) -> LimitErrorKind {
        self.kind.clone()
    }
}

impl fmt::Display for ImageError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ImageError::FormatError(ref e) => write!(fmt, "Format error: {}", e),
            ImageError::DimensionError => write!(
                fmt,
                "The Image's dimensions are either too \
                 small or too large"
            ),
            ImageError::UnsupportedError(ref f) => write!(
                fmt,
                "The Decoder does not support the \
                 image format `{}`",
                f
            ),
            ImageError::UnsupportedColor(ref c) => write!(
                fmt,
                "The decoder does not support \
                 the color type `{:?}`",
                c
            ),
            ImageError::NotEnoughData => write!(
                fmt,
                "Not enough data was provided to the \
                 Decoder to decode the image"
            ),
            ImageError::IoError(ref e) => e.fmt(fmt),
            ImageError::ImageEnd => write!(fmt, "The end of the image has been reached"),
            ImageError::InsufficientMemory => write!(fmt, "Insufficient memory"),
        }
    }
}

impl Error for ImageError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match *self {
            ImageError::IoError(ref e) => Some(e),
            _ => None,
        }
    }
}

impl From<io::Error> for ImageError {
    fn from(err: io::Error) -> ImageError {
        ImageError::IoError(err)
    }
}

/// Result of an image decoding/encoding process
pub type ImageResult<T> = Result<T, ImageError>;

impl fmt::Display for NewImageError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            NewImageError::IoError(err) => err.fmt(fmt),
            NewImageError::Decoding(err) => err.fmt(fmt),
            NewImageError::Encoding(err) => err.fmt(fmt),
            NewImageError::Parameter(err) => err.fmt(fmt),
            NewImageError::Limits(err) => err.fmt(fmt),
            NewImageError::Unsupported(err) => err.fmt(fmt),
        }
    }
}

impl fmt::Display for UnsupportedError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self.kind {
            UnsupportedErrorKind::Format(ImageFormatHint::Unknown) => write!(
                fmt,
                "The image format could not be determined",
            ),
            UnsupportedErrorKind::Format(format @ ImageFormatHint::PathExtension(_)) => write!(
                fmt,
                "The file extension {} was not recognized as an image format",
                format,
            ),
            UnsupportedErrorKind::Format(format) => write!(
                fmt,
                "The image format {} is not supported",
                format,
            ),
            UnsupportedErrorKind::Color(color) => write!(
                fmt,
                "The decoder for {} does not support the color type `{:?}`",
                self.format,
                color,
            ),
            UnsupportedErrorKind::Feature => unimplemented!(),
            UnsupportedErrorKind::__NonExhaustive => unreachable!()
        }
    }
}

impl Error for UnsupportedError { }

impl fmt::Display for ParameterError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self.kind {
            ParameterErrorKind::DimensionMismatch => write!(
                fmt,
                "The Image's dimensions are either too \
                 small or too large"
            ),
            ParameterErrorKind::FailedAlready => write!(
                fmt,
                "The end the image stream has been reached due to a previous error"
            ),
            ParameterErrorKind::Generic(message) => write!(
                fmt,
                "The parameter is malformed: {}",
                message,
            ),
            ParameterErrorKind::__NonExhaustive => unreachable!(),
        }?;

        if let Some(underlying) = &self.underlying {
            write!(fmt, "\n{}", underlying)?;
        }

        Ok(())
    }
}

impl Error for ParameterError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.underlying.as_ref().map(|b| &**b)
    }
}

impl fmt::Display for EncodingError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self.underlying {
            Some(underlying) => write!(
                fmt,
                "Format error encoding {}: {}\n{}",
                self.format,
                self.message,
                underlying,
            ),
            None => write!(
                fmt,
                "Format error encoding {}: {}",
                self.format,
                self.message,
            ),
        }
    }
}

impl Error for EncodingError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.underlying.as_ref().map(|b| &**b)
    }
}

impl fmt::Display for DecodingError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self.underlying {
            Some(underlying) => write!(
                fmt,
                "Format error decoding {}: {}\n{}",
                self.format,
                self.message,
                underlying,
            ),
            None => write!(
                fmt,
                "Format error decoding {}: {}",
                self.format,
                self.message,
            ),
        }
    }
}

impl Error for DecodingError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.underlying.as_ref().map(|b| &**b)
    }
}

impl fmt::Display for LimitError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.kind {
            LimitErrorKind::InsufficientMemory => write!(fmt, "Insufficient memory"),
            LimitErrorKind::DimensionError => write!(fmt, "Image is too large"),
            LimitErrorKind::__NonExhaustive => unreachable!(),
        }
    }
}

impl Error for LimitError { }

impl fmt::Display for ImageFormatHint {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            ImageFormatHint::Exact(format) => write!(fmt, "{:?}", format),
            ImageFormatHint::Name(name) => write!(fmt, "`{}`", name),
            ImageFormatHint::PathExtension(ext) => write!(fmt, "`.{:?}`", ext),
            ImageFormatHint::Unknown => write!(fmt, "`Unknown`"),
            ImageFormatHint::__NonExhaustive => unreachable!(),
        }
    }
}
<commit_msg>Add utility constructors for new error type<commit_after>//! Contains detailed error representation.

use std::error::Error;
use std::fmt;
use std::io;

use crate::color::ExtendedColorType;
use crate::image::ImageFormat;

/// The generic error type for image operations.
///
/// This high level enum is allows, by variant matching, a rough separation of concerns between
/// underlying IO, the caller, format specifications, and the `image` implementation.
#[derive(Debug)]
pub enum NewImageError {
    /// An error was encountered while decoding.
    ///
    /// This means that the input data did not conform to the specification of some image format,
    /// or that no format could be determined, or that it did not match format specific
    /// requirements set by the caller.
    Decoding(DecodingError),

    /// An error was encountered while encoding.
    ///
    /// The input image can not be encoded with the chosen format, for example because the
    /// specification has no representation for its color space or because a necessary conversion
    /// is ambiguous. In some cases it might also happen that the dimensions can not be used with
    /// the format.
    Encoding(EncodingError),

    /// An error was encountered in inputs arguments.
    ///
    /// This is a catch-all case for strictly internal operations such as scaling, conversions,
    /// etc. that involve no external format specifications.
    Parameter(ParameterError),

    /// Completing the operation would have required more resources than allowed.
    ///
    /// Errors of this type are limits set by the user or environment, *not* inherent in a specific
    /// format or operation that was executed.
    Limits(LimitError),

    /// An operation can not be completed by the chosen abstraction.
    ///
    /// This means that it might be possible for the operation to succeed in general but
    /// * it requires a disabled feature,
    /// * the implementation does not yet exist, or
    /// * no abstraction for a lower level could be found.
    Unsupported(UnsupportedError),

    /// An error occurred while interacting with the environment.
    IoError(io::Error),
}

/// An enumeration of Image errors
#[derive(Debug)]
pub enum ImageError {
    /// The Image is not formatted properly
    FormatError(String),

    /// The Image's dimensions are either too small or too large
    DimensionError,

    /// The Decoder does not support this image format
    UnsupportedError(String),

    /// The Decoder does not support this color type
    UnsupportedColor(ExtendedColorType),

    /// Not enough data was provided to the Decoder
    /// to decode the image
    NotEnoughData,

    /// An I/O Error occurred while decoding the image
    IoError(io::Error),

    /// The end of the image has been reached
    ImageEnd,

    /// There is not enough memory to complete the given operation
    InsufficientMemory,
}

/// The implementation for an operation was not provided.
///
/// See the variant [`Unsupported`] for more documentation.
///
/// [`Unsupported`]: struct.ImageError.html#variant.Unsupported
#[derive(Debug)]
pub struct UnsupportedError {
    format: ImageFormatHint,
    kind: UnsupportedErrorKind,
}

/// Details what feature is not supported.
#[derive(Clone, Debug, Hash, PartialEq)]
pub enum UnsupportedErrorKind {
    /// The required color type can not be handled.
    Color(ExtendedColorType),
    /// An image format is not supported.
    Format(ImageFormatHint),
    /// Some feature specified by string.
    /// This is discouraged and is likely to get deprecated (but not removed).
    GenericFeature(String),
    #[doc(hidden)]
    __NonExhaustive,
}

#[derive(Debug)]
pub struct EncodingError {
    format: ImageFormatHint,
    message: String,
    underlying: Option<Box<dyn Error>>,
}

#[derive(Debug)]
pub struct ParameterError {
    kind: ParameterErrorKind,
    underlying: Option<Box<dyn Error>>,
}

/// Details how a parameter is malformed.
#[derive(Clone, Debug, Hash, PartialEq)]
pub enum ParameterErrorKind {
    /// Repeated an operation for which error that could not be cloned was emitted already.
    FailedAlready,
    /// The dimensions passed are wrong.
    DimensionMismatch,
    /// A string describing the parameter.
    /// This is discouraged and is likely to get deprecated (but not removed).
    Generic(String),
    #[doc(hidden)]
    __NonExhaustive,
}

#[derive(Debug)]
pub struct DecodingError {
    format: ImageFormatHint,
    message: String,
    underlying: Option<Box<dyn Error>>,
}

#[derive(Debug)]
pub struct LimitError {
    kind: LimitErrorKind,
    // do we need an underlying error?
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[allow(missing_copy_implementations)] // Might be non-Copy in the future.
pub enum LimitErrorKind {
    DimensionError,
    InsufficientMemory,
    #[doc(hidden)]
    __NonExhaustive,
}

/// A best effort representation for image formats.
#[derive(Clone, Debug, Hash, PartialEq)]
pub enum ImageFormatHint {
    /// The format is known exactly.
    Exact(ImageFormat),

    /// The format can be identified by a name.
    Name(String),

    /// A common path extension for the format is known.
    PathExtension(std::path::PathBuf),

    /// The format is not known or could not be determined.
    Unknown,

    #[doc(hidden)]
    __NonExhaustive,
}

#[allow(non_upper_case_globals)]
#[allow(non_snake_case)]
impl NewImageError {
    pub(crate) const InsufficientMemory: Self =
        NewImageError::Limits(LimitError {
            kind: LimitErrorKind::InsufficientMemory,
        });

    pub(crate) const DimensionError: Self =
        NewImageError::Parameter(ParameterError {
            kind: ParameterErrorKind::DimensionMismatch,
            underlying: None,
        });

    pub(crate) const ImageEnd: Self =
        NewImageError::Parameter(ParameterError {
            kind: ParameterErrorKind::FailedAlready,
            underlying: None,
        });

    pub(crate) fn UnsupportedError(message: String) -> Self {
        NewImageError::Unsupported(UnsupportedError::legacy_from_string(message))
    }

    pub(crate) fn UnsupportedColor(color: ExtendedColorType) -> Self {
        NewImageError::Unsupported(UnsupportedError::new(
            ImageFormatHint::Unknown,
            UnsupportedErrorKind::Color(color),
        ))
    }

    pub(crate) fn FormatError(message: String) -> Self {
        NewImageError::Decoding(DecodingError::legacy_from_string(message))
    }
}

impl UnsupportedError {
    pub fn new(format: ImageFormatHint, kind: UnsupportedErrorKind) -> Self {
        UnsupportedError {
            format,
            kind,
        }
    }

    pub(crate) fn legacy_from_string(message: String) -> Self {
        UnsupportedError {
            format: ImageFormatHint::Unknown,
            kind: UnsupportedErrorKind::GenericFeature(message),
        }
    }

    pub fn kind(&self) -> UnsupportedErrorKind {
        self.kind.clone()
    }

    pub fn format_hint(&self) -> ImageFormatHint {
        self.format.clone()
    }
}

impl DecodingError {
    pub fn format_hint(&self) -> ImageFormatHint {
        self.format.clone()
    }

    pub(crate) fn legacy_from_string(message: String) -> Self {
        DecodingError {
            format: ImageFormatHint::Unknown,
            message,
            underlying: None,
        }
    }
}

impl EncodingError {
    pub fn format_hint(&self) -> ImageFormatHint {
        self.format.clone()
    }
}

impl LimitError {
    pub fn kind(&self) -> LimitErrorKind {
        self.kind.clone()
    }
}

impl From<io::Error> for NewImageError {
    fn from(err: io::Error) -> NewImageError {
        NewImageError::IoError(err)
    }
}

impl From<ImageFormat> for ImageFormatHint {
    fn from(format: ImageFormat) -> Self {
        ImageFormatHint::Exact(format)
    }
}

impl From<&'_ std::path::Path> for ImageFormatHint {
    fn from(path: &'_ std::path::Path) -> Self {
        match path.extension() {
            Some(ext) => ImageFormatHint::PathExtension(ext.into()),
            None => ImageFormatHint::Unknown,
        }
    }
}

impl From<ImageFormatHint> for UnsupportedError {
    fn from(hint: ImageFormatHint) -> Self {
        UnsupportedError {
            format: hint.clone(),
            kind: UnsupportedErrorKind::Format(hint),
        }
    }
}

impl fmt::Display for ImageError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ImageError::FormatError(ref e) => write!(fmt, "Format error: {}", e),
            ImageError::DimensionError => write!(
                fmt,
                "The Image's dimensions are either too \
                 small or too large"
            ),
            ImageError::UnsupportedError(ref f) => write!(
                fmt,
                "The Decoder does not support the \
                 image format `{}`",
                f
            ),
            ImageError::UnsupportedColor(ref c) => write!(
                fmt,
                "The decoder does not support \
                 the color type `{:?}`",
                c
            ),
            ImageError::NotEnoughData => write!(
                fmt,
                "Not enough data was provided to the \
                 Decoder to decode the image"
            ),
            ImageError::IoError(ref e) => e.fmt(fmt),
            ImageError::ImageEnd => write!(fmt, "The end of the image has been reached"),
            ImageError::InsufficientMemory => write!(fmt, "Insufficient memory"),
        }
    }
}

impl Error for ImageError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match *self {
            ImageError::IoError(ref e) => Some(e),
            _ => None,
        }
    }
}

impl From<io::Error> for ImageError {
    fn from(err: io::Error) -> ImageError {
        ImageError::IoError(err)
    }
}

/// Result of an image decoding/encoding process
pub type ImageResult<T> = Result<T, ImageError>;

/// Result of an image decoding/encoding process
pub(crate) type NewImageResult<T> = Result<T, NewImageError>;

impl fmt::Display for NewImageError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            NewImageError::IoError(err) => err.fmt(fmt),
            NewImageError::Decoding(err) => err.fmt(fmt),
            NewImageError::Encoding(err) => err.fmt(fmt),
            NewImageError::Parameter(err) => err.fmt(fmt),
            NewImageError::Limits(err) => err.fmt(fmt),
            NewImageError::Unsupported(err) => err.fmt(fmt),
        }
    }
}

impl fmt::Display for UnsupportedError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self.kind {
            UnsupportedErrorKind::Format(ImageFormatHint::Unknown) => write!(
                fmt,
                "The image format could not be determined",
            ),
            UnsupportedErrorKind::Format(format @ ImageFormatHint::PathExtension(_)) => write!(
                fmt,
                "The file extension {} was not recognized as an image format",
                format,
            ),
            UnsupportedErrorKind::Format(format) => write!(
                fmt,
                "The image format {} is not supported",
                format,
            ),
            UnsupportedErrorKind::Color(color) => write!(
                fmt,
                "The decoder for {} does not support the color type `{:?}`",
                self.format,
                color,
            ),
            UnsupportedErrorKind::GenericFeature(message) => {
                match &self.format {
                    ImageFormatHint::Unknown => write!(
                        fmt,
                        "The decoder does not support the format feature {}",
                        message,
                    ),
                    other => write!(
                        fmt,
                        "The decoder for {} does not support the format features {}",
                        other,
                        message,
                    ),
                }
            },
            UnsupportedErrorKind::__NonExhaustive => unreachable!()
        }
    }
}

impl Error for UnsupportedError { }

impl fmt::Display for ParameterError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self.kind {
            ParameterErrorKind::DimensionMismatch => write!(
                fmt,
                "The Image's dimensions are either too \
                 small or too large"
            ),
            ParameterErrorKind::FailedAlready => write!(
                fmt,
                "The end the image stream has been reached due to a previous error"
            ),
            ParameterErrorKind::Generic(message) => write!(
                fmt,
                "The parameter is malformed: {}",
                message,
            ),
            ParameterErrorKind::__NonExhaustive => unreachable!(),
        }?;

        if let Some(underlying) = &self.underlying {
            write!(fmt, "\n{}", underlying)?;
        }

        Ok(())
    }
}

impl Error for ParameterError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.underlying.as_ref().map(|b| &**b)
    }
}

impl fmt::Display for EncodingError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self.underlying {
            Some(underlying) => write!(
                fmt,
                "Format error encoding {}: {}\n{}",
                self.format,
                self.message,
                underlying,
            ),
            None => write!(
                fmt,
                "Format error encoding {}: {}",
                self.format,
                self.message,
            ),
        }
    }
}

impl Error for EncodingError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.underlying.as_ref().map(|b| &**b)
    }
}

impl fmt::Display for DecodingError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self.underlying {
            Some(underlying) => write!(
                fmt,
                "Format error decoding {}: {}\n{}",
                self.format,
                self.message,
                underlying,
            ),
            None => write!(
                fmt,
                "Format error decoding {}: {}",
                self.format,
                self.message,
            ),
        }
    }
}

impl Error for DecodingError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.underlying.as_ref().map(|b| &**b)
    }
}

impl fmt::Display for LimitError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.kind {
            LimitErrorKind::InsufficientMemory => write!(fmt, "Insufficient memory"),
            LimitErrorKind::DimensionError => write!(fmt, "Image is too large"),
            LimitErrorKind::__NonExhaustive => unreachable!(),
        }
    }
}

impl Error for LimitError { }

impl fmt::Display for ImageFormatHint {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            ImageFormatHint::Exact(format) => write!(fmt, "{:?}", format),
            ImageFormatHint::Name(name) => write!(fmt, "`{}`", name),
            ImageFormatHint::PathExtension(ext) => write!(fmt, "`.{:?}`", ext),
            ImageFormatHint::Unknown => write!(fmt, "`Unknown`"),
            ImageFormatHint::__NonExhaustive => unreachable!(),
        }
    }
}
<|endoftext|>


<commit_before>use ffi::h5::herr_t;
use ffi::h5e::{H5Ewalk2, H5Eget_msg, H5E_error2_t, H5Eset_auto2, H5Eget_current_stack,
               H5Eclose_stack, H5E_WALK_DOWNWARD, H5E_DEFAULT};

use util::{get_h5_str, string_from_cstr};

use std::ptr;
use std::fmt;
use std::ops::Index;
use std::error::Error as BaseError;

use libc::{c_uint, c_void};
use num::{Integer, Zero, Bounded};

pub struct ErrorFrame {
    desc: String,
    func: String,
    major: String,
    minor: String,
    description: String,
}

impl ErrorFrame {
    pub fn new<S1, S2, S3, S4>(desc: S1, func: S2, major: S3, minor: S4) -> ErrorFrame
              where S1: Into<String>, S2: Into<String>, S3: Into<String>, S4: Into<String> {
        let (s_func, s_desc) = (func.into(), desc.into());
        let description = format!("{}(): {}", s_func.clone(), s_desc.clone());
        ErrorFrame {
            desc: s_desc,
            func: s_func,
            major: major.into(),
            minor: minor.into(),
            description: description,
        }
    }

    pub fn description(&self) -> &str {
        self.description.as_ref()
    }

    pub fn detail(&self) -> Option<String> {
        Some(format!("Error in {}(): {} [{}: {}]",
             self.func, self.desc, self.major, self.minor).clone())
    }
}

pub fn silence_errors() {
    h5lock!(H5Eset_auto2(H5E_DEFAULT, None, ptr::null_mut()));
}

pub struct ErrorStack {
    frames: Vec<ErrorFrame>
}

impl Index<usize> for ErrorStack {
    type Output = ErrorFrame;

    fn index<'a>(&'a self, index: usize) -> &'a ErrorFrame {
        &self.frames[index]
    }
}

impl ErrorStack {
    // This low-level function is not thread-safe and has to be synchronized by the user
    pub fn query() -> Result<Option<ErrorStack>> {
        struct CallbackData {
            stack: ErrorStack,
            err: Option<Error>,
        }

        extern fn callback(_: c_uint, err_desc: *const H5E_error2_t, data: *mut c_void) -> herr_t {
            unsafe {
                let data = &mut *(data as *mut CallbackData);
                if data.err.is_some() {
                    return 0;
                }
                let closure = |e: H5E_error2_t| -> Result<ErrorFrame> {
                    let (desc, func) = (string_from_cstr(e.desc), string_from_cstr(e.func_name));
                    let major = try!(get_h5_str(|m, s| {
                        H5Eget_msg(e.maj_num, ptr::null_mut(), m, s)
                    }));
                    let minor = try!(get_h5_str(|m, s| {
                        H5Eget_msg(e.min_num, ptr::null_mut(), m, s)
                    }));
                    Ok(ErrorFrame::new(desc, func, major, minor))
                };
                match closure(*err_desc) {
                    Ok(frame) => { data.stack.push(frame); 0 },
                    Err(err)  => { data.err = Some(From::from(err)); 0 }
                }
            }
        }

        let mut data = CallbackData { stack: ErrorStack::new(), err: None };
        let data_ptr: *mut c_void = &mut data as *mut _ as *mut c_void;

        // known HDF5 bug: H5Eget_msg() may corrupt the current stack, so we copy it first
        unsafe {
            let stack_id = H5Eget_current_stack();
            ensure!(stack_id >= 0, "failed to copy the current error stack");
            H5Ewalk2(stack_id, H5E_WALK_DOWNWARD, Some(callback), data_ptr);
            H5Eclose_stack(stack_id)
        };

        match (data.err, data.stack.is_empty()) {
            (Some(err), _) => Err(err),
            (None, false)  => Ok(Some(data.stack)),
            (None, true)   => Ok(None),
        }
    }

    pub fn new() -> ErrorStack {
        ErrorStack { frames: Vec::new() }
    }

    pub fn len(&self) -> usize {
        self.frames.len()
    }

    pub fn push(&mut self, frame: ErrorFrame) {
        self.frames.push(frame)
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn top(&self) -> Option<&ErrorFrame> {
        match !self.is_empty() {
            false => None,
            true  => Some(&self.frames[0]),
        }
    }

    pub fn description(&self) -> &str {
        match self.top() {
            None        => "unknown library error",
            Some(frame) => frame.description(),
        }
    }

    pub fn detail(&self) -> Option<String> {
        match self.top() {
            None        => None,
            Some(frame) => frame.detail(),
        }
    }
}

pub enum Error {
    LibraryError(ErrorStack),
    InternalError(String),
}

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

impl Error {
    pub fn query() -> Option<Error> {
        match ErrorStack::query() {
            Err(err)        => Some(From::from(err)),
            Ok(Some(stack)) => Some(Error::LibraryError(stack)),
            Ok(None)        => None,
        }
    }

    pub fn description(&self) -> &str {
        match *self {
            Error::InternalError(ref desc) => desc.as_ref(),
            Error::LibraryError(ref stack) => stack.description(),
        }
    }
}

impl<S> From<S> for Error where S: Into<String> {
    fn from(desc: S) -> Error {
        Error::InternalError(desc.into())
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            Error::InternalError(ref desc) => desc.fmt(formatter),
            Error::LibraryError(ref stack) => stack.detail().fmt(formatter),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        self.description().fmt(formatter)
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        self.description()
    }
}

pub fn h5check<T>(value: T) -> Result<T> where T: Integer + Zero + Bounded,
{
    let min_value: T = Bounded::min_value();
    let zero:      T = Zero::zero();

    let maybe_error = if min_value < zero {
        value < zero
    } else {
        value == zero
    };

    match maybe_error {
        false => Ok(value),
        true  => match Error::query() {
            None       => Ok(value),
            Some(err)  => Err(From::from(err)),
        },
    }
}

#[cfg(test)]
mod tests {
    use ffi::h5::herr_t;
    use ffi::h5p::{H5Pcreate, H5Pclose};
    use globals::H5P_ROOT;
    use super::{ErrorStack, Result, silence_errors};

    #[test]
    pub fn test_error_stack() {
        silence_errors();

        let result_no_error = h5lock!({
            let plist_id = H5Pcreate(*H5P_ROOT);
            H5Pclose(plist_id);
            ErrorStack::query()
        });
        assert!(result_no_error.ok().unwrap().is_none());

        let result_error = h5lock!({
            let plist_id = H5Pcreate(*H5P_ROOT);
            H5Pclose(plist_id);
            H5Pclose(plist_id);
            ErrorStack::query()
        });
        let stack = result_error.ok().unwrap().unwrap();
        assert_eq!(stack.description(), "H5Pclose(): can't close");
        assert_eq!(&stack.detail().unwrap(),
                   "Error in H5Pclose(): can't close [Property lists: Unable to free object]");

        assert!(stack.len() >= 2 && stack.len() <= 3); // depending on HDF5 version
        assert!(!stack.is_empty());

        assert_eq!(stack[0].description(), "H5Pclose(): can't close");
        assert_eq!(&stack[0].detail().unwrap(),
                   "Error in H5Pclose(): can't close \
                    [Property lists: Unable to free object]");

        assert_eq!(stack[stack.len() - 1].description(), "H5I_dec_ref(): can't locate ID");
        assert_eq!(&stack[stack.len() - 1].detail().unwrap(),
                   "Error in H5I_dec_ref(): can't locate ID \
                    [Object atom: Unable to find atom information (already closed?)]");

        let empty_stack = ErrorStack::new();
        assert!(empty_stack.is_empty());
        assert_eq!(empty_stack.len(), 0);
    }

    #[test]
    pub fn test_h5call() {
        silence_errors();

        let result_no_error = h5call!({
            let plist_id = H5Pcreate(*H5P_ROOT);
            H5Pclose(plist_id)
        });
        assert!(result_no_error.is_ok());

        let result_error = h5call!({
            let plist_id = H5Pcreate(*H5P_ROOT);
            H5Pclose(plist_id);
            H5Pclose(plist_id)
        });
        assert!(result_error.is_err());
    }

    #[test]
    pub fn test_h5try() {
        silence_errors();

        fn f1() -> Result<herr_t> {
            let plist_id = h5try!(H5Pcreate(*H5P_ROOT));
            h5try!(H5Pclose(plist_id));
            Ok(100)
        }

        let result1 = f1();
        assert!(result1.is_ok());
        assert_eq!(result1.ok().unwrap(), 100);

        fn f2() -> Result<herr_t> {
            let plist_id = h5try!(H5Pcreate(*H5P_ROOT));
            h5try!(H5Pclose(plist_id));
            h5try!(H5Pclose(plist_id));
            Ok(100)
        }

        let result2 = f2();
        assert!(result2.is_err());
    }
}
<commit_msg>Improve error formatting (now the top and bottom frames are printed if possible)<commit_after>use ffi::h5::herr_t;
use ffi::h5e::{H5Ewalk2, H5Eget_msg, H5E_error2_t, H5Eset_auto2, H5Eget_current_stack,
               H5Eclose_stack, H5E_WALK_DOWNWARD, H5E_DEFAULT};

use util::{get_h5_str, string_from_cstr};

use std::ptr;
use std::fmt;
use std::ops::Index;
use std::error::Error as BaseError;

use libc::{c_uint, c_void};
use num::{Integer, Zero, Bounded};

pub struct ErrorFrame {
    desc: String,
    func: String,
    major: String,
    minor: String,
    description: String,
}

impl ErrorFrame {
    pub fn new<S1, S2, S3, S4>(desc: S1, func: S2, major: S3, minor: S4) -> ErrorFrame
              where S1: Into<String>, S2: Into<String>, S3: Into<String>, S4: Into<String> {
        let (s_func, s_desc) = (func.into(), desc.into());
        let description = format!("{}(): {}", s_func.clone(), s_desc.clone());
        ErrorFrame {
            desc: s_desc,
            func: s_func,
            major: major.into(),
            minor: minor.into(),
            description: description,
        }
    }

    pub fn desc(&self) -> &str {
        self.desc.as_ref()
    }

    pub fn description(&self) -> &str {
        self.description.as_ref()
    }

    pub fn detail(&self) -> Option<String> {
        Some(format!("Error in {}(): {} [{}: {}]",
             self.func, self.desc, self.major, self.minor).clone())
    }
}

pub fn silence_errors() {
    h5lock!(H5Eset_auto2(H5E_DEFAULT, None, ptr::null_mut()));
}

pub struct ErrorStack {
    frames: Vec<ErrorFrame>,
    description: Option<String>,
}

impl Index<usize> for ErrorStack {
    type Output = ErrorFrame;

    fn index<'a>(&'a self, index: usize) -> &'a ErrorFrame {
        &self.frames[index]
    }
}

impl ErrorStack {
    // This low-level function is not thread-safe and has to be synchronized by the user
    pub fn query() -> Result<Option<ErrorStack>> {
        struct CallbackData {
            stack: ErrorStack,
            err: Option<Error>,
        }

        extern fn callback(_: c_uint, err_desc: *const H5E_error2_t, data: *mut c_void) -> herr_t {
            unsafe {
                let data = &mut *(data as *mut CallbackData);
                if data.err.is_some() {
                    return 0;
                }
                let closure = |e: H5E_error2_t| -> Result<ErrorFrame> {
                    let (desc, func) = (string_from_cstr(e.desc), string_from_cstr(e.func_name));
                    let major = try!(get_h5_str(|m, s| {
                        H5Eget_msg(e.maj_num, ptr::null_mut(), m, s)
                    }));
                    let minor = try!(get_h5_str(|m, s| {
                        H5Eget_msg(e.min_num, ptr::null_mut(), m, s)
                    }));
                    Ok(ErrorFrame::new(desc, func, major, minor))
                };
                match closure(*err_desc) {
                    Ok(frame) => { data.stack.push(frame); 0 },
                    Err(err)  => { data.err = Some(From::from(err)); 0 }
                }
            }
        }

        let mut data = CallbackData { stack: ErrorStack::new(), err: None };
        let data_ptr: *mut c_void = &mut data as *mut _ as *mut c_void;

        // known HDF5 bug: H5Eget_msg() may corrupt the current stack, so we copy it first
        unsafe {
            let stack_id = H5Eget_current_stack();
            ensure!(stack_id >= 0, "failed to copy the current error stack");
            H5Ewalk2(stack_id, H5E_WALK_DOWNWARD, Some(callback), data_ptr);
            H5Eclose_stack(stack_id)
        };

        match (data.err, data.stack.is_empty()) {
            (Some(err), _) => Err(err),
            (None, false)  => Ok(Some(data.stack)),
            (None, true)   => Ok(None),
        }
    }

    pub fn new() -> ErrorStack {
        ErrorStack { frames: Vec::new(), description: None }
    }

    pub fn len(&self) -> usize {
        self.frames.len()
    }

    pub fn push(&mut self, frame: ErrorFrame) {
        self.frames.push(frame);
        if self.len() >= 1 {
            let top_desc = self.frames[0].description().to_string();
            if self.len() == 1 {
                self.description = Some(top_desc);
            } else {
                self.description = Some(format!("{}: {}", top_desc,
                                                self.frames[self.len() - 1].desc()));
            }
        }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn top(&self) -> Option<&ErrorFrame> {
        match !self.is_empty() {
            false => None,
            true  => Some(&self.frames[0]),
        }
    }

    pub fn description(&self) -> &str {
        match self.description {
            None            => "unknown library error",
            Some(ref desc)  => desc.as_ref(), //frame.description(),
        }
    }

    pub fn detail(&self) -> Option<String> {
        self.top().and_then(|frame| frame.detail())
    }
}

pub enum Error {
    LibraryError(ErrorStack),
    InternalError(String),
}

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

impl Error {
    pub fn query() -> Option<Error> {
        match ErrorStack::query() {
            Err(err)        => Some(From::from(err)),
            Ok(Some(stack)) => Some(Error::LibraryError(stack)),
            Ok(None)        => None,
        }
    }

    pub fn description(&self) -> &str {
        match *self {
            Error::InternalError(ref desc) => desc.as_ref(),
            Error::LibraryError(ref stack) => stack.description(),
        }
    }
}

impl<S> From<S> for Error where S: Into<String> {
    fn from(desc: S) -> Error {
        Error::InternalError(desc.into())
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            Error::InternalError(ref desc) => desc.fmt(formatter),
            Error::LibraryError(ref stack) => stack.description().fmt(formatter),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        self.description().fmt(formatter)
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        self.description()
    }
}

pub fn h5check<T>(value: T) -> Result<T> where T: Integer + Zero + Bounded,
{
    let maybe_error = if T::min_value() < T::zero() {
        value < T::zero()
    } else {
        value == T::zero()
    };

    match maybe_error {
        false => Ok(value),
        true  => match Error::query() {
            None       => Ok(value),
            Some(err)  => Err(From::from(err)),
        },
    }
}

#[cfg(test)]
mod tests {
    use ffi::h5::herr_t;
    use ffi::h5p::{H5Pcreate, H5Pclose};
    use globals::H5P_ROOT;
    use super::{ErrorStack, Result, silence_errors};

    #[test]
    pub fn test_error_stack() {
        silence_errors();

        let result_no_error = h5lock!({
            let plist_id = H5Pcreate(*H5P_ROOT);
            H5Pclose(plist_id);
            ErrorStack::query()
        });
        assert!(result_no_error.ok().unwrap().is_none());

        let result_error = h5lock!({
            let plist_id = H5Pcreate(*H5P_ROOT);
            H5Pclose(plist_id);
            H5Pclose(plist_id);
            ErrorStack::query()
        });
        let stack = result_error.ok().unwrap().unwrap();
        assert_eq!(stack.description(), "H5Pclose(): can't close: can't locate ID");
        assert_eq!(&stack.detail().unwrap(),
                   "Error in H5Pclose(): can't close [Property lists: Unable to free object]");

        assert!(stack.len() >= 2 && stack.len() <= 3); // depending on HDF5 version
        assert!(!stack.is_empty());

        assert_eq!(stack[0].description(), "H5Pclose(): can't close");
        assert_eq!(&stack[0].detail().unwrap(),
                   "Error in H5Pclose(): can't close \
                    [Property lists: Unable to free object]");

        assert_eq!(stack[stack.len() - 1].description(), "H5I_dec_ref(): can't locate ID");
        assert_eq!(&stack[stack.len() - 1].detail().unwrap(),
                   "Error in H5I_dec_ref(): can't locate ID \
                    [Object atom: Unable to find atom information (already closed?)]");

        let empty_stack = ErrorStack::new();
        assert!(empty_stack.is_empty());
        assert_eq!(empty_stack.len(), 0);
    }

    #[test]
    pub fn test_h5call() {
        silence_errors();

        let result_no_error = h5call!({
            let plist_id = H5Pcreate(*H5P_ROOT);
            H5Pclose(plist_id)
        });
        assert!(result_no_error.is_ok());

        let result_error = h5call!({
            let plist_id = H5Pcreate(*H5P_ROOT);
            H5Pclose(plist_id);
            H5Pclose(plist_id)
        });
        assert!(result_error.is_err());
    }

    #[test]
    pub fn test_h5try() {
        silence_errors();

        fn f1() -> Result<herr_t> {
            let plist_id = h5try!(H5Pcreate(*H5P_ROOT));
            h5try!(H5Pclose(plist_id));
            Ok(100)
        }

        let result1 = f1();
        assert!(result1.is_ok());
        assert_eq!(result1.ok().unwrap(), 100);

        fn f2() -> Result<herr_t> {
            let plist_id = h5try!(H5Pcreate(*H5P_ROOT));
            h5try!(H5Pclose(plist_id));
            h5try!(H5Pclose(plist_id));
            Ok(100)
        }

        let result2 = f2();
        assert!(result2.is_err());
    }
}
<|endoftext|>


<commit_before>extern crate schannel;

use std::io;
use std::fmt;
use std::error;
use std::sync::Arc;
use self::schannel::cert_store::{PfxImportOptions, Memory, CertStore, CertAdd};
use self::schannel::cert_context::CertContext;
use self::schannel::schannel_cred::{Direction, SchannelCred, Protocol};
use self::schannel::tls_stream;

fn convert_protocols(protocols: &[::Protocol]) -> Vec<Protocol> {
    protocols
        .iter()
        .map(|p| match *p {
            ::Protocol::Sslv3 => Protocol::Ssl3,
            ::Protocol::Tlsv10 => Protocol::Tls10,
            ::Protocol::Tlsv11 => Protocol::Tls11,
            ::Protocol::Tlsv12 => Protocol::Tls12,
            ::Protocol::__NonExhaustive => unreachable!(),
        })
        .collect()
}

pub struct Error(io::Error);

impl error::Error for Error {
    fn description(&self) -> &str {
        error::Error::description(&self.0)
    }

    fn cause(&self) -> Option<&error::Error> {
        error::Error::cause(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Error {
        Error(error)
    }
}

pub struct Pkcs12 {
    cert: CertContext,
}

impl Pkcs12 {
    pub fn from_der(buf: &[u8], pass: &str) -> Result<Pkcs12, Error> {
        let mut store = try!(PfxImportOptions::new().password(pass).import(buf));
        let mut identity = None;

        for cert in store.certs() {
            if cert.private_key()
                .silent(true)
                .compare_key(true)
                .acquire()
                .is_ok()
            {
                identity = Some(cert);
            }
        }

        let identity = match identity {
            Some(identity) => identity,
            None => {
                return Err(
                    io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "No identity found in PKCS #12 archive",
                    ).into(),
                );
            }
        };

        Ok(Pkcs12 { cert: identity })
    }
}

pub struct Certificate(CertContext);

impl Certificate {
    pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = try!(CertContext::new(buf));
        Ok(Certificate(cert))
    }
    pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> {
        match ::std::str::from_utf8(buf) {
            Ok(s) => {
                let cert = try!(CertContext::from_pem(s));
                Ok(Certificate(cert))
            }
            Err(e) => {
                Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "PEM representation contains non-UTF8 bytes {}",
                        e.description()
                    ),
                ))
            }
        }
    }
}

pub struct MidHandshakeTlsStream<S>(tls_stream::MidHandshakeTlsStream<S>);

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S> MidHandshakeTlsStream<S>
where
    S: io::Read + io::Write,
{
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }

    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self.0.handshake() {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub enum HandshakeError<S> {
    Failure(Error),
    Interrupted(MidHandshakeTlsStream<S>),
}

impl<S> From<tls_stream::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: tls_stream::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            tls_stream::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            tls_stream::HandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream(s))
            }
        }
    }
}

impl<S> From<io::Error> for HandshakeError<S> {
    fn from(e: io::Error) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

pub struct TlsConnectorBuilder(TlsConnector);

impl TlsConnectorBuilder {
    pub fn identity(&mut self, pkcs12: Pkcs12) -> Result<(), Error> {
        self.0.cert = Some(pkcs12.cert);
        Ok(())
    }

    pub fn add_root_certificate(&mut self, cert: Certificate) -> Result<(), Error> {
        try!(self.0.roots.add_cert(&cert.0, CertAdd::ReplaceExisting));
        Ok(())
    }

    pub fn supported_protocols(&mut self, protocols: &[::Protocol]) -> Result<(), Error> {
        self.0.protocols = convert_protocols(protocols);
        Ok(())
    }

    pub fn build(self) -> Result<TlsConnector, Error> {
        Ok(self.0)
    }
}

#[derive(Clone)]
pub struct TlsConnector {
    cert: Option<CertContext>,
    roots: CertStore,
    protocols: Vec<Protocol>,
    callback: Option<Arc<Fn(tls_stream::CertValidationResult) -> io::Result<()> + Sync + Send>>,
}

impl TlsConnector {
    pub fn builder() -> Result<TlsConnectorBuilder, Error> {
        Ok(TlsConnectorBuilder(TlsConnector {
            cert: None,
            roots: try!(Memory::new()).into_store(),
            protocols: vec![Protocol::Tls10, Protocol::Tls11, Protocol::Tls12],
            callback: None,
        }))
    }

    pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        self._connect(Some(domain), stream)
    }

    pub fn connect_no_domain<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        self._connect(None, stream)
    }

    fn _connect<S>(
        &self,
        domain: Option<&str>,
        stream: S,
    ) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = SchannelCred::builder();
        builder.enabled_protocols(&self.protocols);
        if let Some(cert) = self.cert.as_ref() {
            builder.cert(cert.clone());
        }
        let cred = try!(builder.acquire(Direction::Outbound));
        let mut builder = tls_stream::Builder::new();
        if let Some(domain) = domain {
            builder.domain(domain);
        }
        if let Some(ref callback) = self.callback {
            let callback = callback.clone();
            builder.verify_callback(move |r| callback(r));
        }
        builder.cert_store(self.roots.clone());
        match builder.connect(cred, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsAcceptorBuilder(TlsAcceptor);

impl TlsAcceptorBuilder {
    pub fn supported_protocols(&mut self, protocols: &[::Protocol]) -> Result<(), Error> {
        self.0.protocols = convert_protocols(protocols);
        Ok(())
    }

    pub fn build(self) -> Result<TlsAcceptor, Error> {
        Ok(self.0)
    }
}

#[derive(Clone)]
pub struct TlsAcceptor {
    cert: CertContext,
    protocols: Vec<Protocol>,
}

impl TlsAcceptor {
    pub fn builder(pkcs12: Pkcs12) -> Result<TlsAcceptorBuilder, Error> {
        Ok(TlsAcceptorBuilder(TlsAcceptor {
            cert: pkcs12.cert,
            protocols: vec![Protocol::Tls10, Protocol::Tls11, Protocol::Tls12],
        }))
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = SchannelCred::builder();
        builder.enabled_protocols(&self.protocols);
        builder.cert(self.cert.clone());
        // FIXME we're probably missing the certificate chain?
        let cred = try!(builder.acquire(Direction::Inbound));
        match tls_stream::Builder::new().accept(cred, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsStream<S>(tls_stream::TlsStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(self.0.get_buf().len())
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        try!(self.0.shutdown());
        Ok(())
    }

    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

/// SChannel-specific extensions to `TlsConnectorBuilder`.
pub trait TlsConnectorBuilderExt {
    /// Sets a callback function which decides if the server's certificate chain
    /// is to be trusted.
    fn verify_callback<F>(&mut self, callback: F)
    where
        F: Fn(tls_stream::CertValidationResult) -> io::Result<()> + 'static + Send + Sync;
}

impl TlsConnectorBuilderExt for ::TlsConnectorBuilder {
    fn verify_callback<F>(&mut self, callback: F)
    where
        F: Fn(tls_stream::CertValidationResult) -> io::Result<()> + 'static + Send + Sync,
    {
        (self.0).0.callback = Some(Arc::new(callback));
    }
}

/// SChannel-specific extensions to `TlsStream`.
pub trait TlsStreamExt<S> {
    /// Returns a shared reference to the SChannel `TlsStream`.
    fn raw_stream(&self) -> &tls_stream::TlsStream<S>;

    /// Returns a mutable reference to the SChannel `TlsStream`.
    fn raw_stream_mut(&mut self) -> &mut tls_stream::TlsStream<S>;
}

impl<S> TlsStreamExt<S> for ::TlsStream<S> {
    fn raw_stream(&self) -> &tls_stream::TlsStream<S> {
        &(self.0).0
    }

    fn raw_stream_mut(&mut self) -> &mut tls_stream::TlsStream<S> {
        &mut (self.0).0
    }
}

/// SChannel-specific extensions to `Error`
pub trait ErrorExt {
    /// Extract the underlying SChannel error for inspection.
    fn schannel_error(&self) -> &io::Error;
}

impl ErrorExt for ::Error {
    fn schannel_error(&self) -> &io::Error {
        &(self.0).0
    }
}

/// SChannel-specific extensions to `Certificate`.
pub trait CertificateExt {
    /// builds a native_Tls `Certificate` from an schannel `CertContext`
    fn from_cert_context(CertContext) -> ::Certificate;
}

impl CertificateExt for ::Certificate {
    fn from_cert_context(cert: CertContext) -> ::Certificate {
        ::Certificate(Certificate(cert))
    }
}
<commit_msg>Fix yet another bug.<commit_after>extern crate schannel;

use std::io;
use std::fmt;
use std::error;
use std::error::Error;
use std::sync::Arc;
use self::schannel::cert_store::{PfxImportOptions, Memory, CertStore, CertAdd};
use self::schannel::cert_context::CertContext;
use self::schannel::schannel_cred::{Direction, SchannelCred, Protocol};
use self::schannel::tls_stream;

fn convert_protocols(protocols: &[::Protocol]) -> Vec<Protocol> {
    protocols
        .iter()
        .map(|p| match *p {
            ::Protocol::Sslv3 => Protocol::Ssl3,
            ::Protocol::Tlsv10 => Protocol::Tls10,
            ::Protocol::Tlsv11 => Protocol::Tls11,
            ::Protocol::Tlsv12 => Protocol::Tls12,
            ::Protocol::__NonExhaustive => unreachable!(),
        })
        .collect()
}

pub struct Error(io::Error);

impl error::Error for Error {
    fn description(&self) -> &str {
        error::Error::description(&self.0)
    }

    fn cause(&self) -> Option<&error::Error> {
        error::Error::cause(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Error {
        Error(error)
    }
}

pub struct Pkcs12 {
    cert: CertContext,
}

impl Pkcs12 {
    pub fn from_der(buf: &[u8], pass: &str) -> Result<Pkcs12, Error> {
        let mut store = try!(PfxImportOptions::new().password(pass).import(buf));
        let mut identity = None;

        for cert in store.certs() {
            if cert.private_key()
                .silent(true)
                .compare_key(true)
                .acquire()
                .is_ok()
            {
                identity = Some(cert);
            }
        }

        let identity = match identity {
            Some(identity) => identity,
            None => {
                return Err(
                    io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "No identity found in PKCS #12 archive",
                    ).into(),
                );
            }
        };

        Ok(Pkcs12 { cert: identity })
    }
}

pub struct Certificate(CertContext);

impl Certificate {
    pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = try!(CertContext::new(buf));
        Ok(Certificate(cert))
    }
    pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> {
        match ::std::str::from_utf8(buf) {
            Ok(s) => {
                let cert = try!(CertContext::from_pem(s));
                Ok(Certificate(cert))
            }
            Err(e) => {
                Err(
                    io::Error::new(
                        io::ErrorKind::InvalidInput,
                        format!(
                            "PEM representation contains non-UTF8 bytes {}",
                            e.description()
                        ),
                    ).into(),
                )
            }
        }
    }
}

pub struct MidHandshakeTlsStream<S>(tls_stream::MidHandshakeTlsStream<S>);

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S> MidHandshakeTlsStream<S>
where
    S: io::Read + io::Write,
{
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }

    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self.0.handshake() {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub enum HandshakeError<S> {
    Failure(Error),
    Interrupted(MidHandshakeTlsStream<S>),
}

impl<S> From<tls_stream::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: tls_stream::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            tls_stream::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            tls_stream::HandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream(s))
            }
        }
    }
}

impl<S> From<io::Error> for HandshakeError<S> {
    fn from(e: io::Error) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

pub struct TlsConnectorBuilder(TlsConnector);

impl TlsConnectorBuilder {
    pub fn identity(&mut self, pkcs12: Pkcs12) -> Result<(), Error> {
        self.0.cert = Some(pkcs12.cert);
        Ok(())
    }

    pub fn add_root_certificate(&mut self, cert: Certificate) -> Result<(), Error> {
        try!(self.0.roots.add_cert(&cert.0, CertAdd::ReplaceExisting));
        Ok(())
    }

    pub fn supported_protocols(&mut self, protocols: &[::Protocol]) -> Result<(), Error> {
        self.0.protocols = convert_protocols(protocols);
        Ok(())
    }

    pub fn build(self) -> Result<TlsConnector, Error> {
        Ok(self.0)
    }
}

#[derive(Clone)]
pub struct TlsConnector {
    cert: Option<CertContext>,
    roots: CertStore,
    protocols: Vec<Protocol>,
    callback: Option<Arc<Fn(tls_stream::CertValidationResult) -> io::Result<()> + Sync + Send>>,
}

impl TlsConnector {
    pub fn builder() -> Result<TlsConnectorBuilder, Error> {
        Ok(TlsConnectorBuilder(TlsConnector {
            cert: None,
            roots: try!(Memory::new()).into_store(),
            protocols: vec![Protocol::Tls10, Protocol::Tls11, Protocol::Tls12],
            callback: None,
        }))
    }

    pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        self._connect(Some(domain), stream)
    }

    pub fn connect_no_domain<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        self._connect(None, stream)
    }

    fn _connect<S>(
        &self,
        domain: Option<&str>,
        stream: S,
    ) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = SchannelCred::builder();
        builder.enabled_protocols(&self.protocols);
        if let Some(cert) = self.cert.as_ref() {
            builder.cert(cert.clone());
        }
        let cred = try!(builder.acquire(Direction::Outbound));
        let mut builder = tls_stream::Builder::new();
        if let Some(domain) = domain {
            builder.domain(domain);
        }
        if let Some(ref callback) = self.callback {
            let callback = callback.clone();
            builder.verify_callback(move |r| callback(r));
        }
        builder.cert_store(self.roots.clone());
        match builder.connect(cred, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsAcceptorBuilder(TlsAcceptor);

impl TlsAcceptorBuilder {
    pub fn supported_protocols(&mut self, protocols: &[::Protocol]) -> Result<(), Error> {
        self.0.protocols = convert_protocols(protocols);
        Ok(())
    }

    pub fn build(self) -> Result<TlsAcceptor, Error> {
        Ok(self.0)
    }
}

#[derive(Clone)]
pub struct TlsAcceptor {
    cert: CertContext,
    protocols: Vec<Protocol>,
}

impl TlsAcceptor {
    pub fn builder(pkcs12: Pkcs12) -> Result<TlsAcceptorBuilder, Error> {
        Ok(TlsAcceptorBuilder(TlsAcceptor {
            cert: pkcs12.cert,
            protocols: vec![Protocol::Tls10, Protocol::Tls11, Protocol::Tls12],
        }))
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = SchannelCred::builder();
        builder.enabled_protocols(&self.protocols);
        builder.cert(self.cert.clone());
        // FIXME we're probably missing the certificate chain?
        let cred = try!(builder.acquire(Direction::Inbound));
        match tls_stream::Builder::new().accept(cred, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsStream<S>(tls_stream::TlsStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(self.0.get_buf().len())
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        try!(self.0.shutdown());
        Ok(())
    }

    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

/// SChannel-specific extensions to `TlsConnectorBuilder`.
pub trait TlsConnectorBuilderExt {
    /// Sets a callback function which decides if the server's certificate chain
    /// is to be trusted.
    fn verify_callback<F>(&mut self, callback: F)
    where
        F: Fn(tls_stream::CertValidationResult) -> io::Result<()> + 'static + Send + Sync;
}

impl TlsConnectorBuilderExt for ::TlsConnectorBuilder {
    fn verify_callback<F>(&mut self, callback: F)
    where
        F: Fn(tls_stream::CertValidationResult) -> io::Result<()> + 'static + Send + Sync,
    {
        (self.0).0.callback = Some(Arc::new(callback));
    }
}

/// SChannel-specific extensions to `TlsStream`.
pub trait TlsStreamExt<S> {
    /// Returns a shared reference to the SChannel `TlsStream`.
    fn raw_stream(&self) -> &tls_stream::TlsStream<S>;

    /// Returns a mutable reference to the SChannel `TlsStream`.
    fn raw_stream_mut(&mut self) -> &mut tls_stream::TlsStream<S>;
}

impl<S> TlsStreamExt<S> for ::TlsStream<S> {
    fn raw_stream(&self) -> &tls_stream::TlsStream<S> {
        &(self.0).0
    }

    fn raw_stream_mut(&mut self) -> &mut tls_stream::TlsStream<S> {
        &mut (self.0).0
    }
}

/// SChannel-specific extensions to `Error`
pub trait ErrorExt {
    /// Extract the underlying SChannel error for inspection.
    fn schannel_error(&self) -> &io::Error;
}

impl ErrorExt for ::Error {
    fn schannel_error(&self) -> &io::Error {
        &(self.0).0
    }
}

/// SChannel-specific extensions to `Certificate`.
pub trait CertificateExt {
    /// builds a native_Tls `Certificate` from an schannel `CertContext`
    fn from_cert_context(CertContext) -> ::Certificate;
}

impl CertificateExt for ::Certificate {
    fn from_cert_context(cert: CertContext) -> ::Certificate {
        ::Certificate(Certificate(cert))
    }
}
<|endoftext|>


<commit_before>use state::editor::Editor;
use io::parse::{Inst, Parameter};
use state::mode::{Mode, CommandMode, PrimitiveMode};
use edit::insert::{InsertOptions, InsertMode};
use io::redraw::RedrawTask;
use edit::buffer::TextBuffer;
use core::prompt::PromptCommand;

// TODO: Move the command definitions outta here
impl Editor {
    /// Execute an instruction
    pub fn exec(&mut self, Inst(para, cmd): Inst) {
        use io::key::Key::*;
        use state::mode::Mode::*;
        use state::mode::PrimitiveMode::*;
        use state::mode::CommandMode::*;

        let n = para.d();
        let bef = self.pos();
        let mut mov = false;

        match (self.cursor().mode, cmd.key) {
            (Primitive(Prompt), Char(' ')) if self.key_state.shift => {
                self.prompt = String::new();
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            },
            (Primitive(Insert(_)), Char(' ')) if self.key_state.shift => {
                let left = self.left(1);
                self.goto(left);
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            },
            (_, Char(' ')) if self.key_state.shift =>
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal),
            (_, Char(' ')) if self.key_state.alt => self.next_cursor(),
            _ if self.key_state.alt => if let Some(m) = self.to_motion(Inst(para, cmd)) {
                self.goto(m);
            },
            (Command(Normal), Char('i')) => {
                self.cursor_mut().mode =
                    Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                        mode: InsertMode::Insert,
                    }));

            }
            (Command(Normal), Char('a')) => {
                let pos = self.right(1, false);
                self.goto( pos );
                self.cursor_mut().mode =
                    Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                        mode: InsertMode::Insert,
                    }));

            }
            (Command(Normal), Char('o')) => {
                let y = self.y();
                let ind = if self.options.autoindent {
                    self.buffers.current_buffer().get_indent(y).to_owned()
                } else {
                    String::new()
                };
                let last = ind.len();
                self.buffers.current_buffer_mut().insert_line(y, ind.into());
                self.goto((last, y + 1));
                self.cursor_mut().mode =
                    Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                        mode: InsertMode::Insert,
                    }));
            }
            (Command(Normal), Char('h')) => {
                let left = self.left(n);
                self.goto(left);
                mov = true;
            }
            (Command(Normal), Char('j')) => {
                let down = self.down(n);
                self.goto(down);
                mov = true;
            }
            (Command(Normal), Char('k')) => {
                let up = self.up(n);
                self.goto(up);
                mov = true;
            }
            (Command(Normal), Char('l')) => {
                let right = self.right(n, true);
                self.goto(right);
                mov = true;
            }
            (Command(Normal), Char('J')) => {
                let down = self.down(15 * n);
                self.goto(down);
                mov = true;
            }
            (Command(Normal), Char('K')) => {
                let up = self.up(15 * n);
                self.goto(up);
                mov = true;
            }
            (Command(Normal), Char('x')) => {
                self.delete();
                let bounded = self.bound(self.pos(), true);
                self.goto(bounded);
            }
            (Command(Normal), Char('X')) => {
                self.backspace();
                let bounded = self.bound(self.pos(), true);
                self.goto(bounded);
            }
            (Command(Normal), Char('L')) => {
                let ln_end = (self.buffers.current_buffer()[self.y()].len(), self.y());
                self.goto(ln_end);
                mov = true;
            }
            (Command(Normal), Char('H')) => {
                self.cursor_mut().x = 0;
                mov = true;
            }
            (Command(Normal), Char('r')) => {
                let (x, y) = self.pos();
                let c = self.get_char();
                let current_buffer = self.buffers.current_buffer_info_mut();
                // If there is nothing in the current buffer
                // ignore the command
                if current_buffer.raw_buffer[y].len() > 0 {
                    current_buffer.raw_buffer[y].remove(x);
                }
                current_buffer.raw_buffer[y].insert(x, c);
            }
            (Command(Normal), Char('R')) => {
                self.cursor_mut().mode =
                    Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                        mode: InsertMode::Replace,
                    }));
            }
            (Command(Normal), Char('d')) => {
                let ins = self.get_inst();
                if let Some(m) = self.to_motion_unbounded(ins) {
                    self.remove_rb(m);
                }
            }
            (Command(Normal), Char('G')) => {
                let last = self.buffers.current_buffer().len() - 1;
                self.goto((0, last));
                mov = true;
            }
            (Command(Normal), Char('g')) => {
                if let Parameter::Int(n) = para {
                    self.goto((0, n - 1));
                    mov = true;
                } else {
                    let inst = self.get_inst();
                    if let Some(m) = self.to_motion(inst) {
                        self.goto(m); // fix
                        mov = true;
                    }
                }

            }
            (Command(Normal), Char('b')) => {
                // Branch cursor
                if self.buffers.current_buffer_info().cursors.len() < 255 {
                    let cursor = self.cursor().clone();
                    let current_cursor_index = self.buffers.current_buffer_info().current_cursor as usize;
                    self.buffers.current_buffer_info_mut().cursors.insert(current_cursor_index, cursor);
                    self.next_cursor();
                }
                else {
                    self.status_bar.msg = format!("At max 255 cursors");
                }
            }
            (Command(Normal), Char('B')) => {
                // Delete cursor
                if self.buffers.current_buffer_info().cursors.len() > 1 {
                    let current_cursor_index = self.buffers.current_buffer_info().current_cursor;
                    self.buffers.current_buffer_info_mut().cursors.remove(current_cursor_index as usize);
                    self.prev_cursor();
                }
                else {
                    self.status_bar.msg = format!("No other cursors!");
                }
            }
            (Command(Normal), Char('t')) => {
                let ch = self.get_char();

                let pos = self.next_ocur(ch, n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char('f')) => {
                let ch = self.get_char();

                let pos = self.previous_ocur(ch, n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char('W')) => {
                let pos = self._next_word_forward(n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char(';')) =>
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Prompt),
            (Command(Normal), Char(' ')) => self.next_cursor(),
            (Command(Normal), Char('z')) => {
                let Inst(param, cmd) = self.get_inst();
                match param {
                    Parameter::Null => {
                        if let Some(m) = self.to_motion(Inst(param, cmd)) {
                            self.buffers.current_buffer_info_mut().scroll_y = m.1;
                            self.goto(m);
                        }
                    }
                    Parameter::Int(n) => {
                        self.buffers.current_buffer_info_mut().scroll_y = n;
                    }
                }
                self.redraw_task = RedrawTask::Full;
            }
            (Command(Normal), Char('Z')) => {
                self.buffers.current_buffer_info_mut().scroll_y = self.y() - 3;
                self.redraw_task = RedrawTask::Full;
            }
            (Command(Normal), Char('~')) => {
                self.invert_chars(n);
            }
            (Command(Normal), Char('.')) => {
                if let Some(inst) = self.previous_instruction {
                    self.exec(inst);
                } else {
                    self.status_bar.msg = "No previous command".into();
                    self.redraw_task = RedrawTask::StatusBar;
                }
            }
            (Command(Normal), Char(c)) => {
                self.status_bar.msg = format!("Unknown command: {}", c);
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Insert(opt)), k) => self.insert(k, opt),
            (Primitive(Prompt), Char('\n')) => {
                self.cursor_mut().mode = Command(Normal);
                if let Some(cmd) = PromptCommand::parse(&self.prompt.clone()) {;
                    self.invoke(cmd);
                    self.prompt = String::new();
                    self.redraw_task = RedrawTask::StatusBar;
                } else {
                    self.status_bar.msg = format!("Unknown command: {}", self.prompt);
                }
            },
            (Primitive(Prompt), Backspace) => {
                self.prompt.pop();
                self.redraw_task = RedrawTask::StatusBar;
            },
            (Primitive(Prompt), Char(c)) => {
                self.prompt.push(c);
                self.redraw_task = RedrawTask::StatusBar;
            },
            _ => {
                self.status_bar.msg = format!("Unknown command");
                self.redraw_task = RedrawTask::StatusBar;
            },
        }
        if mov {
            self.redraw_task = RedrawTask::Cursor(bef, self.pos());
        }

        if !(self.cursor().mode == Command(Normal) && cmd.key == Char('.')) {
            self.previous_instruction = Some(Inst(para, cmd));
        }
    }
}
<commit_msg>Fix a panic when doing 0g<commit_after>use state::editor::Editor;
use io::parse::{Inst, Parameter};
use state::mode::{Mode, CommandMode, PrimitiveMode};
use edit::insert::{InsertOptions, InsertMode};
use io::redraw::RedrawTask;
use edit::buffer::TextBuffer;
use core::prompt::PromptCommand;

// TODO: Move the command definitions outta here
impl Editor {
    /// Execute an instruction
    pub fn exec(&mut self, Inst(para, cmd): Inst) {
        use io::key::Key::*;
        use state::mode::Mode::*;
        use state::mode::PrimitiveMode::*;
        use state::mode::CommandMode::*;

        let n = para.d();
        let bef = self.pos();
        let mut mov = false;

        match (self.cursor().mode, cmd.key) {
            (Primitive(Prompt), Char(' ')) if self.key_state.shift => {
                self.prompt = String::new();
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            }
            (Primitive(Insert(_)), Char(' ')) if self.key_state.shift => {
                let left = self.left(1);
                self.goto(left);
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal);
            }
            (_, Char(' ')) if self.key_state.shift => {
                self.cursor_mut().mode = Mode::Command(CommandMode::Normal)
            }
            (_, Char(' ')) if self.key_state.alt => self.next_cursor(),
            _ if self.key_state.alt => {
                if let Some(m) = self.to_motion(Inst(para, cmd)) {
                    self.goto(m);
                }
            }
            (Command(Normal), Char('i')) => {
                self.cursor_mut().mode =
                    Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                                                              mode: InsertMode::Insert,
                                                          }));

            }
            (Command(Normal), Char('a')) => {
                let pos = self.right(1, false);
                self.goto(pos);
                self.cursor_mut().mode =
                    Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                                                              mode: InsertMode::Insert,
                                                          }));

            }
            (Command(Normal), Char('o')) => {
                let y = self.y();
                let ind = if self.options.autoindent {
                    self.buffers
                        .current_buffer()
                        .get_indent(y)
                        .to_owned()
                } else {
                    String::new()
                };
                let last = ind.len();
                self.buffers.current_buffer_mut().insert_line(y, ind.into());
                self.goto((last, y + 1));
                self.cursor_mut().mode =
                    Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                                                              mode: InsertMode::Insert,
                                                          }));
            }
            (Command(Normal), Char('h')) => {
                let left = self.left(n);
                self.goto(left);
                mov = true;
            }
            (Command(Normal), Char('j')) => {
                let down = self.down(n);
                self.goto(down);
                mov = true;
            }
            (Command(Normal), Char('k')) => {
                let up = self.up(n);
                self.goto(up);
                mov = true;
            }
            (Command(Normal), Char('l')) => {
                let right = self.right(n, true);
                self.goto(right);
                mov = true;
            }
            (Command(Normal), Char('J')) => {
                let down = self.down(15 * n);
                self.goto(down);
                mov = true;
            }
            (Command(Normal), Char('K')) => {
                let up = self.up(15 * n);
                self.goto(up);
                mov = true;
            }
            (Command(Normal), Char('x')) => {
                self.delete();
                let bounded = self.bound(self.pos(), true);
                self.goto(bounded);
            }
            (Command(Normal), Char('X')) => {
                self.backspace();
                let bounded = self.bound(self.pos(), true);
                self.goto(bounded);
            }
            (Command(Normal), Char('L')) => {
                let ln_end = (self.buffers.current_buffer()[self.y()].len(), self.y());
                self.goto(ln_end);
                mov = true;
            }
            (Command(Normal), Char('H')) => {
                self.cursor_mut().x = 0;
                mov = true;
            }
            (Command(Normal), Char('r')) => {
                let (x, y) = self.pos();
                let c = self.get_char();
                let current_buffer = self.buffers.current_buffer_info_mut();
                // If there is nothing in the current buffer
                // ignore the command
                if current_buffer.raw_buffer[y].len() > 0 {
                    current_buffer.raw_buffer[y].remove(x);
                }
                current_buffer.raw_buffer[y].insert(x, c);
            }
            (Command(Normal), Char('R')) => {
                self.cursor_mut().mode =
                    Mode::Primitive(PrimitiveMode::Insert(InsertOptions {
                                                              mode: InsertMode::Replace,
                                                          }));
            }
            (Command(Normal), Char('d')) => {
                let ins = self.get_inst();
                if let Some(m) = self.to_motion_unbounded(ins) {
                    self.remove_rb(m);
                }
            }
            (Command(Normal), Char('G')) => {
                let last = self.buffers.current_buffer().len() - 1;
                self.goto((0, last));
                mov = true;
            }
            (Command(Normal), Char('g')) => {
                if let Parameter::Int(n) = para {
                    self.goto((0, n.wrapping_sub(1)));
                    mov = true;
                } else {
                    let inst = self.get_inst();
                    if let Some(m) = self.to_motion(inst) {
                        self.goto(m); // fix
                        mov = true;
                    }
                }

            }
            (Command(Normal), Char('b')) => {
                // Branch cursor
                if self.buffers
                       .current_buffer_info()
                       .cursors
                       .len() < 255 {
                    let cursor = self.cursor().clone();
                    let current_cursor_index = self.buffers.current_buffer_info().current_cursor as
                                               usize;
                    self.buffers
                        .current_buffer_info_mut()
                        .cursors
                        .insert(current_cursor_index, cursor);
                    self.next_cursor();
                } else {
                    self.status_bar.msg = format!("At max 255 cursors");
                }
            }
            (Command(Normal), Char('B')) => {
                // Delete cursor
                if self.buffers
                       .current_buffer_info()
                       .cursors
                       .len() > 1 {
                    let current_cursor_index = self.buffers.current_buffer_info().current_cursor;
                    self.buffers
                        .current_buffer_info_mut()
                        .cursors
                        .remove(current_cursor_index as usize);
                    self.prev_cursor();
                } else {
                    self.status_bar.msg = format!("No other cursors!");
                }
            }
            (Command(Normal), Char('t')) => {
                let ch = self.get_char();

                let pos = self.next_ocur(ch, n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char('f')) => {
                let ch = self.get_char();

                let pos = self.previous_ocur(ch, n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char('W')) => {
                let pos = self._next_word_forward(n);
                if let Some(p) = pos {
                    let y = self.y();
                    self.goto((p, y));
                    mov = true;
                }
            }
            (Command(Normal), Char(';')) => {
                self.cursor_mut().mode = Mode::Primitive(PrimitiveMode::Prompt)
            }
            (Command(Normal), Char(' ')) => self.next_cursor(),
            (Command(Normal), Char('z')) => {
                let Inst(param, cmd) = self.get_inst();
                match param {
                    Parameter::Null => {
                        if let Some(m) = self.to_motion(Inst(param, cmd)) {
                            self.buffers.current_buffer_info_mut().scroll_y = m.1;
                            self.goto(m);
                        }
                    }
                    Parameter::Int(n) => {
                        self.buffers.current_buffer_info_mut().scroll_y = n;
                    }
                }
                self.redraw_task = RedrawTask::Full;
            }
            (Command(Normal), Char('Z')) => {
                self.buffers.current_buffer_info_mut().scroll_y = self.y() - 3;
                self.redraw_task = RedrawTask::Full;
            }
            (Command(Normal), Char('~')) => {
                self.invert_chars(n);
            }
            (Command(Normal), Char('.')) => {
                if let Some(inst) = self.previous_instruction {
                    self.exec(inst);
                } else {
                    self.status_bar.msg = "No previous command".into();
                    self.redraw_task = RedrawTask::StatusBar;
                }
            }
            (Command(Normal), Char(c)) => {
                self.status_bar.msg = format!("Unknown command: {}", c);
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Insert(opt)), k) => self.insert(k, opt),
            (Primitive(Prompt), Char('\n')) => {
                self.cursor_mut().mode = Command(Normal);
                if let Some(cmd) = PromptCommand::parse(&self.prompt.clone()) {;
                    self.invoke(cmd);
                    self.prompt = String::new();
                    self.redraw_task = RedrawTask::StatusBar;
                } else {
                    self.status_bar.msg = format!("Unknown command: {}", self.prompt);
                }
            }
            (Primitive(Prompt), Backspace) => {
                self.prompt.pop();
                self.redraw_task = RedrawTask::StatusBar;
            }
            (Primitive(Prompt), Char(c)) => {
                self.prompt.push(c);
                self.redraw_task = RedrawTask::StatusBar;
            }
            _ => {
                self.status_bar.msg = format!("Unknown command");
                self.redraw_task = RedrawTask::StatusBar;
            }
        }
        if mov {
            self.redraw_task = RedrawTask::Cursor(bef, self.pos());
        }

        if !(self.cursor().mode == Command(Normal) && cmd.key == Char('.')) {
            self.previous_instruction = Some(Inst(para, cmd));
        }
    }
}
<|endoftext|>


<commit_before>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use std::ptr;
use iup_sys::*;
use libc::{c_char, c_int};
use smallvec::SmallVec;
use super::{
    Control,
    UnwrapHandle,
};
use super::attributes::{
    ActiveAttribute,
    MinMaxSizeAttribute,
    TipAttribute,
    TitleAttribute,
    VisibleAttribute,
    str_to_c_vec,
    set_str_attribute,
    get_str_attribute_slice,
};
use super::callbacks::{
    MenuCommonCallbacks,
    EnterLeaveWindowCallbacks,
};
use super::handle_rc::HandleRc;

#[derive(Clone)]
pub struct Label(HandleRc);

impl Label {
    pub fn new() -> Self {
        unsafe {
            ::iup_open();
            let ih = IupLabel(ptr::null_mut());
            Label(HandleRc::new(ih))
        }
    }

    pub fn with_title(title: &str) -> Self {
        unsafe {
            ::iup_open();
            let mut buf = SmallVec::<[u8; 64]>::new();
            let c_title = str_to_c_vec(title, &mut buf);
            let ih = IupLabel(c_title);
            Label(HandleRc::new(ih))
        }
    }

    pub fn halignment(&self) -> ::HAlignment {
        unsafe {
            let slice = get_str_attribute_slice(self.handle(), "ALIGNMENT");
            ::HAlignment::from_str(slice.as_bytes().split(|c| *c == b':').next().unwrap())
        }
    }

    pub fn set_halignment(&self, alignment: ::HAlignment) -> &Self {
        set_str_attribute(self.handle(), "ALIGNMENT", alignment.to_str());
        self
    }
}

impl_control_traits!(Label);

impl ActiveAttribute for Label {}
impl MinMaxSizeAttribute for Label {}
impl TipAttribute for Label {}
impl TitleAttribute for Label {}
impl VisibleAttribute for Label {}

impl MenuCommonCallbacks for Label {}

impl EnterLeaveWindowCallbacks for Label {}
<commit_msg>Add docs for Label<commit_after>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use std::ptr;
use iup_sys::*;
use libc::{c_char, c_int};
use smallvec::SmallVec;
use super::{
    Control,
    UnwrapHandle,
};
use super::attributes::{
    ActiveAttribute,
    MinMaxSizeAttribute,
    TipAttribute,
    TitleAttribute,
    VisibleAttribute,
    str_to_c_vec,
    set_str_attribute,
    get_str_attribute_slice,
};
use super::callbacks::{
    MenuCommonCallbacks,
    EnterLeaveWindowCallbacks,
};
use super::handle_rc::HandleRc;

/// A label interface element, which displays a separator, a text, or an image.
#[derive(Clone)]
pub struct Label(HandleRc);

impl Label {
    pub fn new() -> Self {
        unsafe {
            ::iup_open();
            let ih = IupLabel(ptr::null_mut());
            Label(HandleRc::new(ih))
        }
    }

    pub fn with_title(title: &str) -> Self {
        unsafe {
            ::iup_open();
            let mut buf = SmallVec::<[u8; 64]>::new();
            let c_title = str_to_c_vec(title, &mut buf);
            let ih = IupLabel(c_title);
            Label(HandleRc::new(ih))
        }
    }

    pub fn halignment(&self) -> ::HAlignment {
        unsafe {
            let slice = get_str_attribute_slice(self.handle(), "ALIGNMENT");
            ::HAlignment::from_str(slice.as_bytes().split(|c| *c == b':').next().unwrap())
        }
    }

    pub fn set_halignment(&self, alignment: ::HAlignment) -> &Self {
        set_str_attribute(self.handle(), "ALIGNMENT", alignment.to_str());
        self
    }
}

impl_control_traits!(Label);

impl ActiveAttribute for Label {}
impl MinMaxSizeAttribute for Label {}
impl TipAttribute for Label {}
impl TitleAttribute for Label {}
impl VisibleAttribute for Label {}

impl MenuCommonCallbacks for Label {}

impl EnterLeaveWindowCallbacks for Label {}
<|endoftext|>