<commit_before>//! Error types.

use std::error;
use std::fmt;

use cssparser::{BasicParseError, BasicParseErrorKind, ParseErrorKind, ToCss};
use markup5ever::QualName;

use crate::node::Node;
use crate::url_resolver::Fragment;

/// A short-lived error.
///
/// The lifetime of the error is the same as the `cssparser::ParserInput` that
/// was used to create a `cssparser::Parser`.  That is, it is the lifetime of
/// the string data that is being parsed.
///
/// The code flow will sometimes require preserving this error as a long-lived struct;
/// see the `impl<'i, O> AttributeResultExt<O> for Result<O, ParseError<'i>>` for that
/// purpose.
pub type ParseError<'i> = cssparser::ParseError<'i, ValueErrorKind>;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// A property with the specified name was not found
    UnknownProperty,

    /// The value could not be parsed
    Parse(String),

    // The value could be parsed, but is invalid
    Value(String),
}

impl ValueErrorKind {
    pub fn parse_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Parse(s.to_string())
    }

    pub fn value_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Value(s.to_string())
    }
}

impl fmt::Display for ValueErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            ValueErrorKind::UnknownProperty => write!(f, "unknown property name"),

            ValueErrorKind::Parse(ref s) => write!(f, "parse error: {}", s),

            ValueErrorKind::Value(ref s) => write!(f, "invalid value: {}", s),
        }
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        let BasicParseError { kind, .. } = e;

        let msg = match kind {
            BasicParseErrorKind::UnexpectedToken(_) => "unexpected token",
            BasicParseErrorKind::EndOfInput => "unexpected end of input",
            BasicParseErrorKind::AtRuleInvalid(_) => "invalid @-rule",
            BasicParseErrorKind::AtRuleBodyInvalid => "invalid @-rule body",
            BasicParseErrorKind::QualifiedRuleInvalid => "invalid qualified rule",
        };

        ValueErrorKind::parse_error(msg)
    }
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct ElementError {
    pub attr: QualName,
    pub err: ValueErrorKind,
}

impl error::Error for ElementError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::UnknownProperty => "unknown property",
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for ElementError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}: {}", self.attr.expanded(), self.err)
    }
}

/// Errors returned when looking up a resource by URL reference.
#[derive(Debug, Clone, PartialEq)]
pub enum DefsLookupErrorKind {
    /// Error when parsing an [`Href`].
    ///
    /// [`Href`]: allowed_url/enum.Href.html
    HrefError(HrefError),

    /// Used when the public API tries to look up an external URL, which is not allowed.
    ///
    /// This catches the case where a public API wants to be misused to access an external
    /// resource.  For example, `SvgHandle.has_sub("https://evil.com/phone_home#element_id") will
    /// fail with this error.
    CannotLookupExternalReferences,

    /// For internal use only.
    ///
    // FIXME: this is returned internally from Handle.lookup_node(), and gets translated
    // to Ok(false).  Don't expose this internal code in the public API.
    NotFound,
}

/// Errors that can happen while rendering or measuring an SVG document.
#[derive(Debug, Clone, PartialEq)]
pub enum RenderingError {
    /// A Cairo error happened during rendering.
    Cairo(cairo::Status),

    /// The maximum number of rendered objects was reached.
    ///
    /// Librsvg has a limit on the number of rendered objects, so that malicious
    /// files cannot consume CPU time arbitrarily.
    InstancingLimit,

    /// Tried to reference an SVG element from a fragment identifier that is incorrect.
    InvalidId(DefsLookupErrorKind),

    /// Not enough memory was available for rendering.
    // FIXME: right now this is only returned from pixbuf_utils.rs
    OutOfMemory,

    /// Cannot occur from librsvg_crate; this is just for the C API.
    HandleIsNotLoaded,
}

impl error::Error for RenderingError {}

impl fmt::Display for RenderingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            RenderingError::InstancingLimit => write!(f, "instancing limit"),
            RenderingError::OutOfMemory => write!(f, "out of memory"),
            RenderingError::HandleIsNotLoaded => write!(f, "SVG data is not loaded into handle"),
            RenderingError::Cairo(ref status) => write!(f, "cairo error: {:?}", status),
            RenderingError::InvalidId(ref id) => write!(f, "invalid id: {:?}", id),
        }
    }
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

pub enum AcquireError {
    LinkNotFound(Fragment),
    InvalidLinkType(Fragment),
    CircularReference(Node),
    MaxReferencesExceeded,
}

impl fmt::Display for AcquireError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            AcquireError::LinkNotFound(ref frag) => write!(f, "link not found: {}", frag),

            AcquireError::InvalidLinkType(ref frag) => {
                write!(f, "link {} is to object of invalid type", frag)
            }

            AcquireError::CircularReference(ref node) => {
                write!(f, "circular reference in node {}", node)
            }

            AcquireError::MaxReferencesExceeded => {
                write!(f, "maximum number of references exceeded")
            }
        }
    }
}

/// Helper for converting `Result<O, E>` into `Result<O, ElementError>`
///
/// A `ElementError` requires a `QualName` that corresponds to the attribute to which the
/// error refers, plus the actual `ValueErrorKind` that describes the error.  However,
/// parsing functions for attribute value types will want to return their own kind of
/// error, instead of `ValueErrorKind`.  If that particular error type has an `impl
/// From<FooError> for ValueErrorKind`, then this trait helps assign attribute values in
/// `set_atts()` methods as follows:
///
/// ```
/// # use librsvg::doctest_only::AttributeResultExt;
/// # use librsvg::doctest_only::ValueErrorKind;
/// # use librsvg::doctest_only::ElementError;
/// # use markup5ever::{QualName, Prefix, Namespace, LocalName};
/// # type FooError = ValueErrorKind;
/// fn parse_foo(value: &str) -> Result<(), FooError>
/// # { Err(ValueErrorKind::value_error("test")) }
///
/// // It is assumed that there is an impl From<FooError> for ValueErrorKind
/// # let attr = QualName::new(
/// #     Some(Prefix::from("")),
/// #     Namespace::from(""),
/// #     LocalName::from(""),
/// # );
/// let result = parse_foo("value").attribute(attr);
/// assert!(result.is_err());
/// # Ok::<(), ElementError>(())
/// ```
///
/// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
/// `ElementError` with the provided `attr`.
pub trait AttributeResultExt<O> {
    fn attribute(self, attr: QualName) -> Result<O, ElementError>;
}

impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O> for Result<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, ElementError> {
        self.map_err(|e| e.into())
            .map_err(|err| ElementError { attr, err })
    }
}

/// Turns a short-lived `ParseError` into a long-lived `ElementError`
impl<'i, O> AttributeResultExt<O> for Result<O, ParseError<'i>> {
    fn attribute(self, attr: QualName) -> Result<O, ElementError> {
        self.map_err(|e| {
            // FIXME: eventually, here we'll want to preserve the location information

            let ParseError {
                kind,
                location: _location,
            } = e;

            match kind {
                ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(tok)) => {
                    let mut s = String::from("unexpected token '");
                    tok.to_css(&mut s).unwrap(); // FIXME: what do we do with a fmt::Error?
                    s.push_str("'");

                    ElementError {
                        attr,
                        err: ValueErrorKind::Parse(s),
                    }
                }

                ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => ElementError {
                    attr,
                    err: ValueErrorKind::parse_error("unexpected end of input"),
                },

                ParseErrorKind::Basic(_) => {
                    unreachable!("attribute parsers should not return errors for CSS rules")
                }

                ParseErrorKind::Custom(err) => ElementError { attr, err },
            }
        })
    }
}

/// Errors returned when creating an `Href` out of a string
#[derive(Debug, Clone, PartialEq)]
pub enum HrefError {
    /// The href is an invalid URI or has empty components.
    ParseError,

    /// A fragment identifier ("`#foo`") is not allowed here
    ///
    /// For example, the SVG `<image>` element only allows referencing
    /// resources without fragment identifiers like
    /// `xlink:href="foo.png"`.
    FragmentForbidden,

    /// A fragment identifier ("`#foo`") was required but not found.  For example,
    /// the SVG `<use>` element requires one, as in `<use xlink:href="foo.svg#bar">`.
    FragmentRequired,
}

impl From<HrefError> for ValueErrorKind {
    fn from(e: HrefError) -> ValueErrorKind {
        match e {
            HrefError::ParseError => ValueErrorKind::parse_error("url parse error"),
            HrefError::FragmentForbidden => {
                ValueErrorKind::value_error("fragment identifier not allowed")
            }
            HrefError::FragmentRequired => {
                ValueErrorKind::value_error("fragment identifier required")
            }
        }
    }
}

/// Errors that can happen while loading an SVG document.
///
/// All of these codes are for unrecoverable errors that keep an SVG document from being
/// fully loaded and parsed.  Note that SVG is very lenient with respect to document
/// structure and the syntax of CSS property values; most errors there will not lead to a
/// `LoadingError`.  To see those errors, you may want to set the `RSVG_LOG=1` environment
/// variable.
///
/// I/O errors get reported in the `Glib` variant, since librsvg uses GIO internally for
/// all input/output.
#[derive(Debug, Clone)]
pub enum LoadingError {
    // FIXME: C API only
    NoDataPassedToParser,

    /// XML syntax error.
    XmlParseError(String),

    // FIXME: this is OOM in libxml2; we shouldn't expose it.
    CouldNotCreateXmlParser,

    /// A malformed or disallowed URL was used.
    BadUrl,

    /// A `data:` URL could not be decoded.
    BadDataUrl,

    // FIXME: used only if XML processing instruction cannot find the stylesheet.
    BadStylesheet,

    /// An invalid stylesheet was used.
    BadCss,

    /// A Cairo error happened during loading.
    Cairo(cairo::Status),

    // FIXME: only used in load_image()
    EmptyData,

    /// There are no SVG elements in the document.
    SvgHasNoElements,

    /// The outermost element in the document is not `<svg>`.
    RootElementIsNotSvg,

    /// Generally an I/O error, or another error from GIO.
    Glib(glib::Error),

    // FIXME: only used internally when loading pixbufs, and temporarily in c_api.
    Unknown,
}

impl error::Error for LoadingError {}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::NoDataPassedToParser => write!(f, "no data passed to parser"),
            LoadingError::XmlParseError(ref s) => write!(f, "XML parse error: {}", s),
            LoadingError::CouldNotCreateXmlParser => write!(f, "could not create XML parser"),
            LoadingError::BadUrl => write!(f, "invalid URL"),
            LoadingError::BadDataUrl => write!(f, "invalid data: URL"),
            LoadingError::BadStylesheet => write!(f, "invalid stylesheet"),
            LoadingError::BadCss => write!(f, "invalid CSS"),
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::EmptyData => write!(f, "empty data"),
            LoadingError::SvgHasNoElements => write!(f, "SVG has no elements"),
            LoadingError::RootElementIsNotSvg => write!(f, "root element is not <svg>"),
            LoadingError::Glib(ref e) => e.fmt(f),
            LoadingError::Unknown => write!(f, "unknown error"),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}
<commit_msg>Add LoadingError::LimitExceeded<commit_after>//! Error types.

use std::error;
use std::fmt;

use cssparser::{BasicParseError, BasicParseErrorKind, ParseErrorKind, ToCss};
use markup5ever::QualName;

use crate::node::Node;
use crate::url_resolver::Fragment;

/// A short-lived error.
///
/// The lifetime of the error is the same as the `cssparser::ParserInput` that
/// was used to create a `cssparser::Parser`.  That is, it is the lifetime of
/// the string data that is being parsed.
///
/// The code flow will sometimes require preserving this error as a long-lived struct;
/// see the `impl<'i, O> AttributeResultExt<O> for Result<O, ParseError<'i>>` for that
/// purpose.
pub type ParseError<'i> = cssparser::ParseError<'i, ValueErrorKind>;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// A property with the specified name was not found
    UnknownProperty,

    /// The value could not be parsed
    Parse(String),

    // The value could be parsed, but is invalid
    Value(String),
}

impl ValueErrorKind {
    pub fn parse_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Parse(s.to_string())
    }

    pub fn value_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Value(s.to_string())
    }
}

impl fmt::Display for ValueErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            ValueErrorKind::UnknownProperty => write!(f, "unknown property name"),

            ValueErrorKind::Parse(ref s) => write!(f, "parse error: {}", s),

            ValueErrorKind::Value(ref s) => write!(f, "invalid value: {}", s),
        }
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        let BasicParseError { kind, .. } = e;

        let msg = match kind {
            BasicParseErrorKind::UnexpectedToken(_) => "unexpected token",
            BasicParseErrorKind::EndOfInput => "unexpected end of input",
            BasicParseErrorKind::AtRuleInvalid(_) => "invalid @-rule",
            BasicParseErrorKind::AtRuleBodyInvalid => "invalid @-rule body",
            BasicParseErrorKind::QualifiedRuleInvalid => "invalid qualified rule",
        };

        ValueErrorKind::parse_error(msg)
    }
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct ElementError {
    pub attr: QualName,
    pub err: ValueErrorKind,
}

impl error::Error for ElementError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::UnknownProperty => "unknown property",
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for ElementError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}: {}", self.attr.expanded(), self.err)
    }
}

/// Errors returned when looking up a resource by URL reference.
#[derive(Debug, Clone, PartialEq)]
pub enum DefsLookupErrorKind {
    /// Error when parsing an [`Href`].
    ///
    /// [`Href`]: allowed_url/enum.Href.html
    HrefError(HrefError),

    /// Used when the public API tries to look up an external URL, which is not allowed.
    ///
    /// This catches the case where a public API wants to be misused to access an external
    /// resource.  For example, `SvgHandle.has_sub("https://evil.com/phone_home#element_id") will
    /// fail with this error.
    CannotLookupExternalReferences,

    /// For internal use only.
    ///
    // FIXME: this is returned internally from Handle.lookup_node(), and gets translated
    // to Ok(false).  Don't expose this internal code in the public API.
    NotFound,
}

/// Errors that can happen while rendering or measuring an SVG document.
#[derive(Debug, Clone, PartialEq)]
pub enum RenderingError {
    /// A Cairo error happened during rendering.
    Cairo(cairo::Status),

    /// The maximum number of rendered objects was reached.
    ///
    /// Librsvg has a limit on the number of rendered objects, so that malicious
    /// files cannot consume CPU time arbitrarily.
    InstancingLimit,

    /// Tried to reference an SVG element from a fragment identifier that is incorrect.
    InvalidId(DefsLookupErrorKind),

    /// Not enough memory was available for rendering.
    // FIXME: right now this is only returned from pixbuf_utils.rs
    OutOfMemory,

    /// Cannot occur from librsvg_crate; this is just for the C API.
    HandleIsNotLoaded,
}

impl error::Error for RenderingError {}

impl fmt::Display for RenderingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            RenderingError::InstancingLimit => write!(f, "instancing limit"),
            RenderingError::OutOfMemory => write!(f, "out of memory"),
            RenderingError::HandleIsNotLoaded => write!(f, "SVG data is not loaded into handle"),
            RenderingError::Cairo(ref status) => write!(f, "cairo error: {:?}", status),
            RenderingError::InvalidId(ref id) => write!(f, "invalid id: {:?}", id),
        }
    }
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

pub enum AcquireError {
    LinkNotFound(Fragment),
    InvalidLinkType(Fragment),
    CircularReference(Node),
    MaxReferencesExceeded,
}

impl fmt::Display for AcquireError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            AcquireError::LinkNotFound(ref frag) => write!(f, "link not found: {}", frag),

            AcquireError::InvalidLinkType(ref frag) => {
                write!(f, "link {} is to object of invalid type", frag)
            }

            AcquireError::CircularReference(ref node) => {
                write!(f, "circular reference in node {}", node)
            }

            AcquireError::MaxReferencesExceeded => {
                write!(f, "maximum number of references exceeded")
            }
        }
    }
}

/// Helper for converting `Result<O, E>` into `Result<O, ElementError>`
///
/// A `ElementError` requires a `QualName` that corresponds to the attribute to which the
/// error refers, plus the actual `ValueErrorKind` that describes the error.  However,
/// parsing functions for attribute value types will want to return their own kind of
/// error, instead of `ValueErrorKind`.  If that particular error type has an `impl
/// From<FooError> for ValueErrorKind`, then this trait helps assign attribute values in
/// `set_atts()` methods as follows:
///
/// ```
/// # use librsvg::doctest_only::AttributeResultExt;
/// # use librsvg::doctest_only::ValueErrorKind;
/// # use librsvg::doctest_only::ElementError;
/// # use markup5ever::{QualName, Prefix, Namespace, LocalName};
/// # type FooError = ValueErrorKind;
/// fn parse_foo(value: &str) -> Result<(), FooError>
/// # { Err(ValueErrorKind::value_error("test")) }
///
/// // It is assumed that there is an impl From<FooError> for ValueErrorKind
/// # let attr = QualName::new(
/// #     Some(Prefix::from("")),
/// #     Namespace::from(""),
/// #     LocalName::from(""),
/// # );
/// let result = parse_foo("value").attribute(attr);
/// assert!(result.is_err());
/// # Ok::<(), ElementError>(())
/// ```
///
/// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
/// `ElementError` with the provided `attr`.
pub trait AttributeResultExt<O> {
    fn attribute(self, attr: QualName) -> Result<O, ElementError>;
}

impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O> for Result<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, ElementError> {
        self.map_err(|e| e.into())
            .map_err(|err| ElementError { attr, err })
    }
}

/// Turns a short-lived `ParseError` into a long-lived `ElementError`
impl<'i, O> AttributeResultExt<O> for Result<O, ParseError<'i>> {
    fn attribute(self, attr: QualName) -> Result<O, ElementError> {
        self.map_err(|e| {
            // FIXME: eventually, here we'll want to preserve the location information

            let ParseError {
                kind,
                location: _location,
            } = e;

            match kind {
                ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(tok)) => {
                    let mut s = String::from("unexpected token '");
                    tok.to_css(&mut s).unwrap(); // FIXME: what do we do with a fmt::Error?
                    s.push_str("'");

                    ElementError {
                        attr,
                        err: ValueErrorKind::Parse(s),
                    }
                }

                ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => ElementError {
                    attr,
                    err: ValueErrorKind::parse_error("unexpected end of input"),
                },

                ParseErrorKind::Basic(_) => {
                    unreachable!("attribute parsers should not return errors for CSS rules")
                }

                ParseErrorKind::Custom(err) => ElementError { attr, err },
            }
        })
    }
}

/// Errors returned when creating an `Href` out of a string
#[derive(Debug, Clone, PartialEq)]
pub enum HrefError {
    /// The href is an invalid URI or has empty components.
    ParseError,

    /// A fragment identifier ("`#foo`") is not allowed here
    ///
    /// For example, the SVG `<image>` element only allows referencing
    /// resources without fragment identifiers like
    /// `xlink:href="foo.png"`.
    FragmentForbidden,

    /// A fragment identifier ("`#foo`") was required but not found.  For example,
    /// the SVG `<use>` element requires one, as in `<use xlink:href="foo.svg#bar">`.
    FragmentRequired,
}

impl From<HrefError> for ValueErrorKind {
    fn from(e: HrefError) -> ValueErrorKind {
        match e {
            HrefError::ParseError => ValueErrorKind::parse_error("url parse error"),
            HrefError::FragmentForbidden => {
                ValueErrorKind::value_error("fragment identifier not allowed")
            }
            HrefError::FragmentRequired => {
                ValueErrorKind::value_error("fragment identifier required")
            }
        }
    }
}

/// Errors that can happen while loading an SVG document.
///
/// All of these codes are for unrecoverable errors that keep an SVG document from being
/// fully loaded and parsed.  Note that SVG is very lenient with respect to document
/// structure and the syntax of CSS property values; most errors there will not lead to a
/// `LoadingError`.  To see those errors, you may want to set the `RSVG_LOG=1` environment
/// variable.
///
/// I/O errors get reported in the `Glib` variant, since librsvg uses GIO internally for
/// all input/output.
#[derive(Debug, Clone)]
pub enum LoadingError {
    // FIXME: C API only
    NoDataPassedToParser,

    /// XML syntax error.
    XmlParseError(String),

    // FIXME: this is OOM in libxml2; we shouldn't expose it.
    CouldNotCreateXmlParser,

    /// A malformed or disallowed URL was used.
    BadUrl,

    /// A `data:` URL could not be decoded.
    BadDataUrl,

    // FIXME: used only if XML processing instruction cannot find the stylesheet.
    BadStylesheet,

    /// An invalid stylesheet was used.
    BadCss,

    /// A Cairo error happened during loading.
    Cairo(cairo::Status),

    // FIXME: only used in load_image()
    EmptyData,

    /// There are no SVG elements in the document.
    SvgHasNoElements,

    /// The outermost element in the document is not `<svg>`.
    RootElementIsNotSvg,

    /// Generally an I/O error, or another error from GIO.
    Glib(glib::Error),

    /// A particular implementation-defined limit was exceeded.
    LimitExceeded(String),

    // FIXME: only used internally when loading pixbufs, and temporarily in c_api.
    Unknown,
}

impl error::Error for LoadingError {}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::NoDataPassedToParser => write!(f, "no data passed to parser"),
            LoadingError::XmlParseError(ref s) => write!(f, "XML parse error: {}", s),
            LoadingError::CouldNotCreateXmlParser => write!(f, "could not create XML parser"),
            LoadingError::BadUrl => write!(f, "invalid URL"),
            LoadingError::BadDataUrl => write!(f, "invalid data: URL"),
            LoadingError::BadStylesheet => write!(f, "invalid stylesheet"),
            LoadingError::BadCss => write!(f, "invalid CSS"),
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::EmptyData => write!(f, "empty data"),
            LoadingError::SvgHasNoElements => write!(f, "SVG has no elements"),
            LoadingError::RootElementIsNotSvg => write!(f, "root element is not <svg>"),
            LoadingError::Glib(ref e) => e.fmt(f),
            LoadingError::LimitExceeded(ref s) => write!(f, "limit exceeded: {}", s),
            LoadingError::Unknown => write!(f, "unknown error"),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}
<|endoftext|>


<commit_before>//! Error types for the screeps api.
use std::{io, fmt};
use std::error::Error as StdError;

use {url, hyper, serde_json};

use data::room_name::RoomNameParseError;

use self::ErrorKind::*;

#[derive(Debug)]
/// Possible error types for library errors.
pub enum ErrorKind {
    /// Unauthorized access. This is caused by either attempting to access a login-only endpoint without a token,
    /// attempting to access a login-only endpoint with an expired token, or providing incorrect login details to the
    /// login endpoint.
    Unauthorized,
    /// Error parsing a server response. This is most likely caused by the server providing unparsable JSON, but it
    /// could also be the server's API response structure has changed and no longer matches the expected data structure.
    SerdeJson(serde_json::error::Error),
    /// URL parsing error.
    Url(url::ParseError),
    /// Error connecting to the server, or error parsing a URL provided.
    Hyper(hyper::error::Error),
    /// IO error.
    Io(io::Error),
    /// Error for when the server responds with a non-success status code.
    StatusCode(hyper::status::StatusCode),
    /// API Error. Either missing fields in the response that were expected, or the API did not include the regular
    /// `"ok": 1` which indicates success.
    Api(ApiError),
    /// Error parsing a room name.
    RoomNameParse(RoomNameParseError<'static>),
    /// A marker variant that tells the compiler that users of this enum cannot match it exhaustively.
    #[doc(hidden)]
    __Nonexhaustive,
}

/// Error deriving from some API call.
#[derive(Debug)]
pub struct Error {
    /// The type specifying what kind of error, and a detailed description if available.
    err: ErrorKind,
    /// The whole URL which was being accessed when this error occurred (not included for URL parsing errors).
    url: Option<url::Url>,
    /// The json data from the request which resulted in this error (not included for URL or JSON parsing errors).
    json: Option<serde_json::Value>,
}

impl Error {
    /// Creates a new error from the given error and the given possible url.
    pub fn with_url<T: Into<Error>>(err: T, url: Option<url::Url>) -> Error {
        Error::with_json(err, url, None)
    }
    /// Creates a new error from the given error, the given possible url, and the given possible JSON data.
    pub fn with_json<T: Into<Error>>(err: T, url: Option<url::Url>, json: Option<serde_json::Value>) -> Error {
        let err = err.into();
        Error {
            err: err.err,
            url: url.or(err.url),
            json: json.or(err.json),
        }
    }

    /// Retrieves the type specifying what kind of error, and a detailed description if available.
    pub fn kind(&self) -> &ErrorKind {
        &self.err
    }

    /// Retrieves the URL associated with this error, if any.
    pub fn url(&self) -> Option<&url::Url> {
        self.url.as_ref()
    }

    /// Retrieves the JSON data associated with this error, if any.
    pub fn json(&self) -> Option<&serde_json::Value> {
        self.json.as_ref()
    }
}

/// Result type for screeps API operations.
pub type Result<T> = ::std::result::Result<T, Error>;

impl From<ErrorKind> for Error {
    fn from(err: ErrorKind) -> Error {
        Error {
            err: err,
            url: None,
            json: None,
        }
    }
}

impl From<serde_json::error::Error> for Error {
    fn from(err: serde_json::error::Error) -> Error {
        ErrorKind::SerdeJson(err).into()
    }
}

impl From<hyper::error::Error> for Error {
    fn from(err: hyper::error::Error) -> Error {
        ErrorKind::Hyper(err).into()
    }
}

impl From<url::ParseError> for Error {
    fn from(err: url::ParseError) -> Error {
        ErrorKind::Url(err).into()
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        ErrorKind::Io(err).into()
    }
}

impl From<hyper::status::StatusCode> for Error {
    fn from(code: hyper::status::StatusCode) -> Error {
        if code == hyper::status::StatusCode::Unauthorized {
            ErrorKind::Unauthorized.into()
        } else {
            ErrorKind::StatusCode(code).into()
        }
    }
}

impl From<ApiError> for Error {
    fn from(err: ApiError) -> Error {
        ErrorKind::Api(err).into()
    }
}

impl<'a> From<RoomNameParseError<'a>> for Error {
    fn from(err: RoomNameParseError<'a>) -> Error {
        ErrorKind::RoomNameParse(err.into_owned()).into()
    }
}

impl From<NoToken> for Error {
    /// Creates an `Error` with `ErrorKind::Unauthorized`.
    // NoToken is a no-value struct.
    fn from(_: NoToken) -> Error {
        ErrorKind::Unauthorized.into()
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.err {
            SerdeJson(ref err) => fmt::Display::fmt(err, f)?,
            Hyper(ref err) => fmt::Display::fmt(err, f)?,
            Url(ref err) => fmt::Display::fmt(err, f)?,
            Io(ref err) => fmt::Display::fmt(err, f)?,
            StatusCode(ref status) => fmt::Display::fmt(status, f)?,
            Api(ref err) => fmt::Display::fmt(err, f)?,
            RoomNameParse(ref err) => fmt::Display::fmt(err, f)?,
            Unauthorized => {
                write!(f,
                       "access not authorized: token expired, username/password
                       incorrect or no login provided")?;
            }
            ErrorKind::__Nonexhaustive => unreachable!(),
        }
        if let Some(ref url) = self.url {
            write!(f, " | at url '{}'", url)?;
        }
        if let Some(ref json) = self.json {
            write!(f, " | return json: '{}'", json)?;
        }
        Ok(())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match self.err {
            SerdeJson(ref err) => err.description(),
            Hyper(ref err) => err.description(),
            Url(ref err) => err.description(),
            Io(ref err) => err.description(),
            StatusCode(ref status) => {
                match status.canonical_reason() {
                    Some(reason) => reason,
                    None => {
                        if status.is_informational() {
                            "status code error: informational"
                        } else if status.is_success() {
                            "status code error: success"
                        } else if status.is_redirection() {
                            "status code error: redirection"
                        } else if status.is_client_error() {
                            "status code error: client error"
                        } else if status.is_server_error() {
                            "status code error: server error"
                        } else if status.is_strange_status() {
                            "status code error: strange status"
                        } else {
                            "status code error: wrong status"
                        }
                    }
                }
            }
            Api(ref err) => err.description(),
            RoomNameParse(ref err) => err.description(),
            Unauthorized => "access not authorized: token expired, username/password incorrect or no login provided",
            __Nonexhaustive => unreachable!(),
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match self.err {
            SerdeJson(ref err) => Some(err),
            Hyper(ref err) => Some(err),
            Url(ref err) => Some(err),
            Io(ref err) => Some(err),
            Api(ref err) => Some(err),
            RoomNameParse(ref err) => Some(err),
            StatusCode(_) | Unauthorized => None,
            __Nonexhaustive => unreachable!(),
        }
    }
}

/// Error representing when an authenticated call is made, but there is no token currently available.
#[derive(Debug, Clone, Copy)]
pub struct NoToken;

const NO_TOKEN: &'static str = "token storage empty when attempting to make authenticated call.";

impl fmt::Display for NoToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(NO_TOKEN, f)
    }
}

impl StdError for NoToken {
    fn description(&self) -> &str {
        NO_TOKEN
    }
}

/// Error representing some abnormal response from the API.
#[derive(Debug, Clone)]
pub enum ApiError {
    /// The server responded with an "ok" code which was not `1`.
    NotOk(i32),
    /// The server is offline.
    ServerDown,
    /// A known response to a query about an invalid room.
    InvalidRoom,
    /// The data being requested was not found.
    ResultNotFound,
    /// The user whose data was being requested was not found.
    UserNotFound,
    /// The API returned that invalid parameters were passed.
    InvalidParameters,
    /// An error found from the API. Data is the raw error string reported by the server.
    GenericError(String),
    /// The server response was missing a top-level JSON field that was expected.
    MissingField(&'static str),
    /// A malformed response, including a formatted String description of the error.
    MalformedResponse(String),
    /// A marker variant that tells the compiler that users of this enum cannot match it exhaustively.
    #[doc(hidden)]
    __Nonexhaustive,
}

impl fmt::Display for ApiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ApiError::NotOk(code) => write!(f, "non-ok result from api result: {}", code),
            ApiError::MissingField(field) => write!(f, "missing field from api result: {}", field),
            ApiError::MalformedResponse(ref desc) => write!(f, "malformed field from api result: {}", desc),
            ApiError::GenericError(ref err) => write!(f, "api call resulted in error: {}", err),
            ApiError::InvalidRoom |
            ApiError::ResultNotFound |
            ApiError::UserNotFound |
            ApiError::InvalidParameters |
            ApiError::ServerDown => write!(f, "{}", self.description()),
            ApiError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for ApiError {
    fn description(&self) -> &str {
        match *self {
            ApiError::NotOk(_) => "non-ok result from api call",
            ApiError::MissingField(_) => "missing field in api result",
            ApiError::MalformedResponse(_) => "malformed field in api result",
            ApiError::GenericError(_) => "api call resulted in error",
            ApiError::InvalidRoom => "malformed api call: invalid room",
            ApiError::ResultNotFound => "specific data requested was not found",
            ApiError::UserNotFound => "the user requested was not found",
            ApiError::InvalidParameters => "one or more parameters to the function were invalid",
            ApiError::ServerDown => "the server requested is offline",
            ApiError::__Nonexhaustive => unreachable!(),
        }
    }

    fn cause(&self) -> Option<&StdError> {
        None
    }
}
<commit_msg>Improve error documentation.<commit_after>//! Error types for the screeps api.
use std::{io, fmt};
use std::error::Error as StdError;

use {url, hyper, serde_json};

use data::room_name::RoomNameParseError;

use self::ErrorKind::*;

#[derive(Debug)]
/// Possible error types for library errors.
pub enum ErrorKind {
    /// Unauthorized access. This is caused by either attempting to access a login-only endpoint without a token,
    /// attempting to access a login-only endpoint with an expired token, or providing incorrect login details to the
    /// login endpoint.
    Unauthorized,
    /// Error parsing a server response. This is most likely caused by the server providing unparsable JSON, but it
    /// could also be the server's API response structure has changed and no longer matches the expected data structure.
    SerdeJson(serde_json::error::Error),
    /// URL parsing error.
    Url(url::ParseError),
    /// Error connecting to the server, or error parsing a URL provided.
    Hyper(hyper::error::Error),
    /// IO error.
    Io(io::Error),
    /// Error for when the server responds with a non-success HTTP status code.
    StatusCode(hyper::status::StatusCode),
    /// API Error: when the server responds with a successful HTTP response, but the returned format is not what we
    /// expected.
    Api(ApiError),
    /// Error parsing a room name.
    RoomNameParse(RoomNameParseError<'static>),
    /// A marker variant that tells the compiler that users of this enum cannot match it exhaustively.
    #[doc(hidden)]
    __Nonexhaustive,
}

/// Error deriving from some API call.
#[derive(Debug)]
pub struct Error {
    /// The type specifying what kind of error, and a detailed description if available.
    err: ErrorKind,
    /// The whole URL which was being accessed when this error occurred (not included for URL parsing errors).
    url: Option<url::Url>,
    /// The json data from the request which resulted in this error (not included for URL or JSON parsing errors).
    json: Option<serde_json::Value>,
}

impl Error {
    /// Creates a new error from the given error and the given possible url.
    pub fn with_url<T: Into<Error>>(err: T, url: Option<url::Url>) -> Error {
        Error::with_json(err, url, None)
    }
    /// Creates a new error from the given error, the given possible url, and the given possible JSON data.
    pub fn with_json<T: Into<Error>>(err: T, url: Option<url::Url>, json: Option<serde_json::Value>) -> Error {
        let err = err.into();
        Error {
            err: err.err,
            url: url.or(err.url),
            json: json.or(err.json),
        }
    }

    /// Retrieves the type specifying what kind of error, and a detailed description if available.
    pub fn kind(&self) -> &ErrorKind {
        &self.err
    }

    /// Retrieves the URL associated with this error, if any.
    pub fn url(&self) -> Option<&url::Url> {
        self.url.as_ref()
    }

    /// Retrieves the JSON data associated with this error, if any.
    pub fn json(&self) -> Option<&serde_json::Value> {
        self.json.as_ref()
    }
}

/// Result type for screeps API operations.
pub type Result<T> = ::std::result::Result<T, Error>;

impl From<ErrorKind> for Error {
    fn from(err: ErrorKind) -> Error {
        Error {
            err: err,
            url: None,
            json: None,
        }
    }
}

impl From<serde_json::error::Error> for Error {
    fn from(err: serde_json::error::Error) -> Error {
        ErrorKind::SerdeJson(err).into()
    }
}

impl From<hyper::error::Error> for Error {
    fn from(err: hyper::error::Error) -> Error {
        ErrorKind::Hyper(err).into()
    }
}

impl From<url::ParseError> for Error {
    fn from(err: url::ParseError) -> Error {
        ErrorKind::Url(err).into()
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        ErrorKind::Io(err).into()
    }
}

impl From<hyper::status::StatusCode> for Error {
    fn from(code: hyper::status::StatusCode) -> Error {
        if code == hyper::status::StatusCode::Unauthorized {
            ErrorKind::Unauthorized.into()
        } else {
            ErrorKind::StatusCode(code).into()
        }
    }
}

impl From<ApiError> for Error {
    fn from(err: ApiError) -> Error {
        ErrorKind::Api(err).into()
    }
}

impl<'a> From<RoomNameParseError<'a>> for Error {
    fn from(err: RoomNameParseError<'a>) -> Error {
        ErrorKind::RoomNameParse(err.into_owned()).into()
    }
}

impl From<NoToken> for Error {
    /// Creates an `Error` with `ErrorKind::Unauthorized`.
    // NoToken is a no-value struct.
    fn from(_: NoToken) -> Error {
        ErrorKind::Unauthorized.into()
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.err {
            SerdeJson(ref err) => fmt::Display::fmt(err, f)?,
            Hyper(ref err) => fmt::Display::fmt(err, f)?,
            Url(ref err) => fmt::Display::fmt(err, f)?,
            Io(ref err) => fmt::Display::fmt(err, f)?,
            StatusCode(ref status) => fmt::Display::fmt(status, f)?,
            Api(ref err) => fmt::Display::fmt(err, f)?,
            RoomNameParse(ref err) => fmt::Display::fmt(err, f)?,
            Unauthorized => {
                write!(f,
                       "access not authorized: token expired, username/password
                       incorrect or no login provided")?;
            }
            ErrorKind::__Nonexhaustive => unreachable!(),
        }
        if let Some(ref url) = self.url {
            write!(f, " | at url '{}'", url)?;
        }
        if let Some(ref json) = self.json {
            write!(f, " | return json: '{}'", json)?;
        }
        Ok(())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match self.err {
            SerdeJson(ref err) => err.description(),
            Hyper(ref err) => err.description(),
            Url(ref err) => err.description(),
            Io(ref err) => err.description(),
            StatusCode(ref status) => {
                match status.canonical_reason() {
                    Some(reason) => reason,
                    None => {
                        if status.is_informational() {
                            "status code error: informational"
                        } else if status.is_success() {
                            "status code error: success"
                        } else if status.is_redirection() {
                            "status code error: redirection"
                        } else if status.is_client_error() {
                            "status code error: client error"
                        } else if status.is_server_error() {
                            "status code error: server error"
                        } else if status.is_strange_status() {
                            "status code error: strange status"
                        } else {
                            "status code error: wrong status"
                        }
                    }
                }
            }
            Api(ref err) => err.description(),
            RoomNameParse(ref err) => err.description(),
            Unauthorized => "access not authorized: token expired, username/password incorrect or no login provided",
            __Nonexhaustive => unreachable!(),
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match self.err {
            SerdeJson(ref err) => Some(err),
            Hyper(ref err) => Some(err),
            Url(ref err) => Some(err),
            Io(ref err) => Some(err),
            Api(ref err) => Some(err),
            RoomNameParse(ref err) => Some(err),
            StatusCode(_) | Unauthorized => None,
            __Nonexhaustive => unreachable!(),
        }
    }
}

/// Error representing when an authenticated call is made, but there is no token currently available.
#[derive(Debug, Clone, Copy)]
pub struct NoToken;

const NO_TOKEN: &'static str = "token storage empty when attempting to make authenticated call.";

impl fmt::Display for NoToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(NO_TOKEN, f)
    }
}

impl StdError for NoToken {
    fn description(&self) -> &str {
        NO_TOKEN
    }
}

/// Error representing some abnormal response from the API.
#[derive(Debug, Clone)]
pub enum ApiError {
    /// The server responded with an "ok" code which was not `1`.
    NotOk(i32),
    /// The server is offline.
    ServerDown,
    /// A known response to a query about an invalid room.
    InvalidRoom,
    /// The data being requested was not found.
    ResultNotFound,
    /// The user whose data was being requested was not found.
    UserNotFound,
    /// The API returned that invalid parameters were passed.
    InvalidParameters,
    /// An error found from the API. Data is the raw error string reported by the server.
    GenericError(String),
    /// The server response was missing a top-level JSON field that was expected.
    MissingField(&'static str),
    /// A malformed response, including a formatted String description of the error.
    MalformedResponse(String),
    /// A marker variant that tells the compiler that users of this enum cannot match it exhaustively.
    #[doc(hidden)]
    __Nonexhaustive,
}

impl fmt::Display for ApiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ApiError::NotOk(code) => write!(f, "non-ok result from api result: {}", code),
            ApiError::MissingField(field) => write!(f, "missing field from api result: {}", field),
            ApiError::MalformedResponse(ref desc) => write!(f, "malformed field from api result: {}", desc),
            ApiError::GenericError(ref err) => write!(f, "api call resulted in error: {}", err),
            ApiError::InvalidRoom |
            ApiError::ResultNotFound |
            ApiError::UserNotFound |
            ApiError::InvalidParameters |
            ApiError::ServerDown => write!(f, "{}", self.description()),
            ApiError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for ApiError {
    fn description(&self) -> &str {
        match *self {
            ApiError::NotOk(_) => "non-ok result from api call",
            ApiError::MissingField(_) => "missing field in api result",
            ApiError::MalformedResponse(_) => "malformed field in api result",
            ApiError::GenericError(_) => "api call resulted in error",
            ApiError::InvalidRoom => "malformed api call: invalid room",
            ApiError::ResultNotFound => "specific data requested was not found",
            ApiError::UserNotFound => "the user requested was not found",
            ApiError::InvalidParameters => "one or more parameters to the function were invalid",
            ApiError::ServerDown => "the server requested is offline",
            ApiError::__Nonexhaustive => unreachable!(),
        }
    }

    fn cause(&self) -> Option<&StdError> {
        None
    }
}
<|endoftext|>


<commit_before>//! Define Errors

use pyo3::*;
use std::error;
use std::fmt;

pub trait IntoPyErr {
    fn into_pyerr(self, py: Python, msg: &str) -> PyErr;
}

pub trait IntoPyResult {
    type ValueType;
    fn into_pyresult(self, py: Python, message: &str) -> PyResult<Self::ValueType>;
}

impl<T, E: IntoPyErr> IntoPyResult for Result<T, E> {
    type ValueType = T;
    fn into_pyresult(self, py: Python, msg: &str) -> PyResult<T> {
        self.map_err(|e| e.into_pyerr(py, msg))
    }
}

/// Error for casting `PyArray` into `ArrayView` or `ArrayViewMut`
#[derive(Debug)]
pub struct ArrayCastError {
    test: i32,
    truth: i32,
}

impl ArrayCastError {
    pub fn new(test: i32, truth: i32) -> Self {
        Self {
            test: test,
            truth: truth,
        }
    }
}

impl fmt::Display for ArrayCastError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cast failed: from={}, to={}", self.test, self.truth)
    }
}

impl error::Error for ArrayCastError {
    fn description(&self) -> &str {
        "Array cast failed"
    }
}

impl IntoPyErr for ArrayCastError {
    fn into_pyerr(self, py: Python, msg: &str) -> PyErr {
        let msg = format!("rust_numpy::ArrayCastError: {}", msg);
        PyErr::new::<exc::TypeError, _>(py, msg)
    }
}
<commit_msg>error<commit_after>//! Define Errors

use pyo3::*;
use std::error;
use std::fmt;

pub trait IntoPyErr {
    fn into_pyerr(self, msg: &str) -> PyErr;
}

pub trait IntoPyResult {
    type ValueType;
    fn into_pyresult(self, msg: &str) -> PyResult<Self::ValueType>;
}

impl<T, E: IntoPyErr> IntoPyResult for Result<T, E> {
    type ValueType = T;
    fn into_pyresult(self, msg: &str) -> PyResult<T> {
        self.map_err(|e| e.into_pyerr(msg))
    }
}

/// Error for casting `PyArray` into `ArrayView` or `ArrayViewMut`
#[derive(Debug)]
pub struct ArrayCastError {
    test: i32,
    truth: i32,
}

impl ArrayCastError {
    pub fn new(test: i32, truth: i32) -> Self {
        Self {
            test: test,
            truth: truth,
        }
    }
}

impl fmt::Display for ArrayCastError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cast failed: from={}, to={}", self.test, self.truth)
    }
}

impl error::Error for ArrayCastError {
    fn description(&self) -> &str {
        "Array cast failed"
    }
}

impl IntoPyErr for ArrayCastError {
    fn into_pyerr(self, msg: &str) -> PyErr {
        let msg = format!("rust_numpy::ArrayCastError: {}", msg);
        PyErr::new::<exc::TypeError, _>(msg)
    }
}
<|endoftext|>


<commit_before>//! Error manipulations.

use std::{error, ffi, fmt};

use rdkafka_sys as rdsys;
use rdkafka_sys::types::*;

// Re-export rdkafka error
pub use rdsys::types::RDKafkaError;

/// Kafka result.
pub type KafkaResult<T> = Result<T, KafkaError>;

/// Verify if the value represents an error condition.
///
/// Some librdkafka codes are informational, rather than true errors.
pub trait IsError {
    /// Reports whether the value represents an error.
    fn is_error(self) -> bool;
}

impl IsError for RDKafkaRespErr {
    fn is_error(self) -> bool {
        self as i32 != RDKafkaRespErr::RD_KAFKA_RESP_ERR_NO_ERROR as i32
    }
}

impl IsError for RDKafkaConfRes {
    fn is_error(self) -> bool {
        self as i32 != RDKafkaConfRes::RD_KAFKA_CONF_OK as i32
    }
}

// TODO: consider using macro

/// Represents all possible Kafka errors.
///
/// If applicable, check the underlying [`RDKafkaError`] to get details.
#[derive(Clone, PartialEq, Eq)]
pub enum KafkaError {
    /// Creation of admin operation failed.
    AdminOpCreation(String),
    /// The admin operation itself failed.
    AdminOp(RDKafkaError),
    /// The client was dropped before the operation completed.
    Canceled,
    /// Invalid client configuration.
    ClientConfig(RDKafkaConfRes, String, String, String),
    /// Client creation failed.
    ClientCreation(String),
    /// Consumer commit failed.
    ConsumerCommit(RDKafkaError),
    /// Global error.
    Global(RDKafkaError),
    /// Group list fetch failed.
    GroupListFetch(RDKafkaError),
    /// Message consumption failed.
    MessageConsumption(RDKafkaError),
    /// Message production error.
    MessageProduction(RDKafkaError),
    /// Metadata fetch error.
    MetadataFetch(RDKafkaError),
    /// No message was received.
    NoMessageReceived,
    /// Unexpected null pointer
    Nul(ffi::NulError),
    /// Offset fetch failed.
    OffsetFetch(RDKafkaError),
    /// End of partition reached.
    PartitionEOF(i32),
    /// Pause/Resume failed.
    PauseResume(String),
    /// Seeking a partition failed.
    Seek(String),
    /// Setting partition offset failed.
    SetPartitionOffset(RDKafkaError),
    /// Offset store failed.
    StoreOffset(RDKafkaError),
    /// Subscription creation failed.
    Subscription(String),
}

impl fmt::Debug for KafkaError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            KafkaError::AdminOp(err) => write!(f, "KafkaError (Admin operation error: {})", err),
            KafkaError::AdminOpCreation(ref err) => {
                write!(f, "KafkaError (Admin operation creation error: {})", err)
            }
            KafkaError::Canceled => write!(f, "KafkaError (Client dropped)"),
            KafkaError::ClientConfig(_, ref desc, ref key, ref value) => write!(
                f,
                "KafkaError (Client config error: {} {} {})",
                desc, key, value
            ),
            KafkaError::ClientCreation(ref err) => {
                write!(f, "KafkaError (Client creation error: {})", err)
            }
            KafkaError::ConsumerCommit(err) => {
                write!(f, "KafkaError (Consumer commit error: {})", err)
            }
            KafkaError::Global(err) => write!(f, "KafkaError (Global error: {})", err),
            KafkaError::GroupListFetch(err) => {
                write!(f, "KafkaError (Group list fetch error: {})", err)
            }
            KafkaError::MessageConsumption(err) => {
                write!(f, "KafkaError (Message consumption error: {})", err)
            }
            KafkaError::MessageProduction(err) => {
                write!(f, "KafkaError (Message production error: {})", err)
            }
            KafkaError::MetadataFetch(err) => {
                write!(f, "KafkaError (Metadata fetch error: {})", err)
            }
            KafkaError::NoMessageReceived => {
                write!(f, "No message received within the given poll interval")
            }
            KafkaError::Nul(_) => write!(f, "FFI null error"),
            KafkaError::OffsetFetch(err) => write!(f, "KafkaError (Offset fetch error: {})", err),
            KafkaError::PartitionEOF(part_n) => write!(f, "KafkaError (Partition EOF: {})", part_n),
            KafkaError::PauseResume(ref err) => {
                write!(f, "KafkaError (Pause/resume error: {})", err)
            }
            KafkaError::Seek(ref err) => write!(f, "KafkaError (Seek error: {})", err),
            KafkaError::SetPartitionOffset(err) => {
                write!(f, "KafkaError (Set partition offset error: {})", err)
            }
            KafkaError::StoreOffset(err) => write!(f, "KafkaError (Store offset error: {})", err),
            KafkaError::Subscription(ref err) => {
                write!(f, "KafkaError (Subscription error: {})", err)
            }
        }
    }
}

impl fmt::Display for KafkaError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            KafkaError::AdminOp(err) => write!(f, "Admin operation error: {}", err),
            KafkaError::AdminOpCreation(ref err) => {
                write!(f, "Admin operation creation error: {}", err)
            }
            KafkaError::Canceled => write!(f, "KafkaError (Client dropped)"),
            KafkaError::ClientConfig(_, ref desc, ref key, ref value) => {
                write!(f, "Client config error: {} {} {}", desc, key, value)
            }
            KafkaError::ClientCreation(ref err) => write!(f, "Client creation error: {}", err),
            KafkaError::ConsumerCommit(err) => write!(f, "Consumer commit error: {}", err),
            KafkaError::Global(err) => write!(f, "Global error: {}", err),
            KafkaError::GroupListFetch(err) => write!(f, "Group list fetch error: {}", err),
            KafkaError::MessageConsumption(err) => write!(f, "Message consumption error: {}", err),
            KafkaError::MessageProduction(err) => write!(f, "Message production error: {}", err),
            KafkaError::MetadataFetch(err) => write!(f, "Meta data fetch error: {}", err),
            KafkaError::NoMessageReceived => {
                write!(f, "No message received within the given poll interval")
            }
            KafkaError::Nul(_) => write!(f, "FFI nul error"),
            KafkaError::OffsetFetch(err) => write!(f, "Offset fetch error: {}", err),
            KafkaError::PartitionEOF(part_n) => write!(f, "Partition EOF: {}", part_n),
            KafkaError::PauseResume(ref err) => write!(f, "Pause/resume error: {}", err),
            KafkaError::Seek(ref err) => write!(f, "Seek error: {}", err),
            KafkaError::SetPartitionOffset(err) => write!(f, "Set partition offset error: {}", err),
            KafkaError::StoreOffset(err) => write!(f, "Store offset error: {}", err),
            KafkaError::Subscription(ref err) => write!(f, "Subscription error: {}", err),
        }
    }
}

impl error::Error for KafkaError {
    fn description(&self) -> &str {
        match *self {
            KafkaError::AdminOp(_) => "Admin operation error",
            KafkaError::AdminOpCreation(_) => "Admin operation creation error",
            KafkaError::Canceled => "Client dropped",
            KafkaError::ClientConfig(_, _, _, _) => "Client config error",
            KafkaError::ClientCreation(_) => "Client creation error",
            KafkaError::ConsumerCommit(_) => "Consumer commit error",
            KafkaError::Global(_) => "Global error",
            KafkaError::GroupListFetch(_) => "Group list fetch error",
            KafkaError::MessageConsumption(_) => "Message consumption error",
            KafkaError::MessageProduction(_) => "Message production error",
            KafkaError::MetadataFetch(_) => "Meta data fetch error",
            KafkaError::NoMessageReceived => "No message received within the given poll interval",
            KafkaError::Nul(_) => "FFI nul error",
            KafkaError::OffsetFetch(_) => "Offset fetch error",
            KafkaError::PartitionEOF(_) => "Partition EOF error",
            KafkaError::PauseResume(_) => "Pause/resume error",
            KafkaError::Seek(_) => "Seek error",
            KafkaError::SetPartitionOffset(_) => "Set partition offset error",
            KafkaError::StoreOffset(_) => "Store offset error",
            KafkaError::Subscription(_) => "Subscription error",
        }
    }

    #[allow(clippy::match_same_arms)]
    fn cause(&self) -> Option<&dyn error::Error> {
        match *self {
            KafkaError::AdminOp(_) => None,
            KafkaError::AdminOpCreation(_) => None,
            KafkaError::Canceled => None,
            KafkaError::ClientConfig(_, _, _, _) => None,
            KafkaError::ClientCreation(_) => None,
            KafkaError::ConsumerCommit(ref err) => Some(err),
            KafkaError::Global(ref err) => Some(err),
            KafkaError::GroupListFetch(ref err) => Some(err),
            KafkaError::MessageConsumption(ref err) => Some(err),
            KafkaError::MessageProduction(ref err) => Some(err),
            KafkaError::MetadataFetch(ref err) => Some(err),
            KafkaError::NoMessageReceived => None,
            KafkaError::Nul(_) => None,
            KafkaError::OffsetFetch(ref err) => Some(err),
            KafkaError::PartitionEOF(_) => None,
            KafkaError::PauseResume(_) => None,
            KafkaError::Seek(_) => None,
            KafkaError::SetPartitionOffset(ref err) => Some(err),
            KafkaError::StoreOffset(ref err) => Some(err),
            KafkaError::Subscription(_) => None,
        }
    }
}

impl From<ffi::NulError> for KafkaError {
    fn from(err: ffi::NulError) -> KafkaError {
        KafkaError::Nul(err)
    }
}
<commit_msg>Make it easier to retrieve underlying RDKafkaError<commit_after>//! Error manipulations.

use std::{error, ffi, fmt};

use rdkafka_sys as rdsys;
use rdkafka_sys::types::*;

// Re-export rdkafka error
pub use rdsys::types::RDKafkaError;

/// Kafka result.
pub type KafkaResult<T> = Result<T, KafkaError>;

/// Verify if the value represents an error condition.
///
/// Some librdkafka codes are informational, rather than true errors.
pub trait IsError {
    /// Reports whether the value represents an error.
    fn is_error(self) -> bool;
}

impl IsError for RDKafkaRespErr {
    fn is_error(self) -> bool {
        self as i32 != RDKafkaRespErr::RD_KAFKA_RESP_ERR_NO_ERROR as i32
    }
}

impl IsError for RDKafkaConfRes {
    fn is_error(self) -> bool {
        self as i32 != RDKafkaConfRes::RD_KAFKA_CONF_OK as i32
    }
}

// TODO: consider using macro

/// Represents all possible Kafka errors.
///
/// If applicable, check the underlying [`RDKafkaError`] to get details.
#[derive(Clone, PartialEq, Eq)]
pub enum KafkaError {
    /// Creation of admin operation failed.
    AdminOpCreation(String),
    /// The admin operation itself failed.
    AdminOp(RDKafkaError),
    /// The client was dropped before the operation completed.
    Canceled,
    /// Invalid client configuration.
    ClientConfig(RDKafkaConfRes, String, String, String),
    /// Client creation failed.
    ClientCreation(String),
    /// Consumer commit failed.
    ConsumerCommit(RDKafkaError),
    /// Global error.
    Global(RDKafkaError),
    /// Group list fetch failed.
    GroupListFetch(RDKafkaError),
    /// Message consumption failed.
    MessageConsumption(RDKafkaError),
    /// Message production error.
    MessageProduction(RDKafkaError),
    /// Metadata fetch error.
    MetadataFetch(RDKafkaError),
    /// No message was received.
    NoMessageReceived,
    /// Unexpected null pointer
    Nul(ffi::NulError),
    /// Offset fetch failed.
    OffsetFetch(RDKafkaError),
    /// End of partition reached.
    PartitionEOF(i32),
    /// Pause/Resume failed.
    PauseResume(String),
    /// Seeking a partition failed.
    Seek(String),
    /// Setting partition offset failed.
    SetPartitionOffset(RDKafkaError),
    /// Offset store failed.
    StoreOffset(RDKafkaError),
    /// Subscription creation failed.
    Subscription(String),
}

impl fmt::Debug for KafkaError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            KafkaError::AdminOp(err) => write!(f, "KafkaError (Admin operation error: {})", err),
            KafkaError::AdminOpCreation(ref err) => {
                write!(f, "KafkaError (Admin operation creation error: {})", err)
            }
            KafkaError::Canceled => write!(f, "KafkaError (Client dropped)"),
            KafkaError::ClientConfig(_, ref desc, ref key, ref value) => write!(
                f,
                "KafkaError (Client config error: {} {} {})",
                desc, key, value
            ),
            KafkaError::ClientCreation(ref err) => {
                write!(f, "KafkaError (Client creation error: {})", err)
            }
            KafkaError::ConsumerCommit(err) => {
                write!(f, "KafkaError (Consumer commit error: {})", err)
            }
            KafkaError::Global(err) => write!(f, "KafkaError (Global error: {})", err),
            KafkaError::GroupListFetch(err) => {
                write!(f, "KafkaError (Group list fetch error: {})", err)
            }
            KafkaError::MessageConsumption(err) => {
                write!(f, "KafkaError (Message consumption error: {})", err)
            }
            KafkaError::MessageProduction(err) => {
                write!(f, "KafkaError (Message production error: {})", err)
            }
            KafkaError::MetadataFetch(err) => {
                write!(f, "KafkaError (Metadata fetch error: {})", err)
            }
            KafkaError::NoMessageReceived => {
                write!(f, "No message received within the given poll interval")
            }
            KafkaError::Nul(_) => write!(f, "FFI null error"),
            KafkaError::OffsetFetch(err) => write!(f, "KafkaError (Offset fetch error: {})", err),
            KafkaError::PartitionEOF(part_n) => write!(f, "KafkaError (Partition EOF: {})", part_n),
            KafkaError::PauseResume(ref err) => {
                write!(f, "KafkaError (Pause/resume error: {})", err)
            }
            KafkaError::Seek(ref err) => write!(f, "KafkaError (Seek error: {})", err),
            KafkaError::SetPartitionOffset(err) => {
                write!(f, "KafkaError (Set partition offset error: {})", err)
            }
            KafkaError::StoreOffset(err) => write!(f, "KafkaError (Store offset error: {})", err),
            KafkaError::Subscription(ref err) => {
                write!(f, "KafkaError (Subscription error: {})", err)
            }
        }
    }
}

impl fmt::Display for KafkaError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            KafkaError::AdminOp(err) => write!(f, "Admin operation error: {}", err),
            KafkaError::AdminOpCreation(ref err) => {
                write!(f, "Admin operation creation error: {}", err)
            }
            KafkaError::Canceled => write!(f, "KafkaError (Client dropped)"),
            KafkaError::ClientConfig(_, ref desc, ref key, ref value) => {
                write!(f, "Client config error: {} {} {}", desc, key, value)
            }
            KafkaError::ClientCreation(ref err) => write!(f, "Client creation error: {}", err),
            KafkaError::ConsumerCommit(err) => write!(f, "Consumer commit error: {}", err),
            KafkaError::Global(err) => write!(f, "Global error: {}", err),
            KafkaError::GroupListFetch(err) => write!(f, "Group list fetch error: {}", err),
            KafkaError::MessageConsumption(err) => write!(f, "Message consumption error: {}", err),
            KafkaError::MessageProduction(err) => write!(f, "Message production error: {}", err),
            KafkaError::MetadataFetch(err) => write!(f, "Meta data fetch error: {}", err),
            KafkaError::NoMessageReceived => {
                write!(f, "No message received within the given poll interval")
            }
            KafkaError::Nul(_) => write!(f, "FFI nul error"),
            KafkaError::OffsetFetch(err) => write!(f, "Offset fetch error: {}", err),
            KafkaError::PartitionEOF(part_n) => write!(f, "Partition EOF: {}", part_n),
            KafkaError::PauseResume(ref err) => write!(f, "Pause/resume error: {}", err),
            KafkaError::Seek(ref err) => write!(f, "Seek error: {}", err),
            KafkaError::SetPartitionOffset(err) => write!(f, "Set partition offset error: {}", err),
            KafkaError::StoreOffset(err) => write!(f, "Store offset error: {}", err),
            KafkaError::Subscription(ref err) => write!(f, "Subscription error: {}", err),
        }
    }
}

impl error::Error for KafkaError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        self.rdkafka_error().map(|e| e as &(dyn error::Error + 'static))
    }
}

impl From<ffi::NulError> for KafkaError {
    fn from(err: ffi::NulError) -> KafkaError {
        KafkaError::Nul(err)
    }
}

impl KafkaError {
    /// Returns if an error is `Fatal` and requires reinitialisation.
    /// for details see https://docs.confluent.io/5.5.0/clients/librdkafka/rdkafka_8h.html
    pub fn is_fatal(&self) -> bool {
        match self.rdkafka_error() {
            Some(RDKafkaError::Fatal) => true,
            _ => false,
        }
    }

    /// Returns the [`RDKafkaError`] underlying this error, if any.
    #[allow(clippy::match_same_arms)]
    pub fn rdkafka_error(&self) -> Option<&RDKafkaError> {
        match self {
            KafkaError::AdminOp(_) => None,
            KafkaError::AdminOpCreation(_) => None,
            KafkaError::Canceled => None,
            KafkaError::ClientConfig(_, _, _, _) => None,
            KafkaError::ClientCreation(_) => None,
            KafkaError::ConsumerCommit(err) => Some(err),
            KafkaError::Global(err) => Some(err),
            KafkaError::GroupListFetch(err) => Some(err),
            KafkaError::MessageConsumption(err) => Some(err),
            KafkaError::MessageProduction(err) => Some(err),
            KafkaError::MetadataFetch(err) => Some(err),
            KafkaError::NoMessageReceived => None,
            KafkaError::Nul(_) => None,
            KafkaError::OffsetFetch(err) => Some(err),
            KafkaError::PartitionEOF(_) => None,
            KafkaError::PauseResume(_) => None,
            KafkaError::Seek(_) => None,
            KafkaError::SetPartitionOffset(err) => Some(err),
            KafkaError::StoreOffset(err) => Some(err),
            KafkaError::Subscription(_) => None,
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::fmt;

#[derive(Clone)]
pub struct SassRule<'a> {
    pub selectors: Vec<SassSelector<'a>>,
    pub selectors_done: bool,
    pub children: Vec<Event<'a>>,
}

impl<'a> SassRule<'a> {
    pub fn new() -> SassRule<'a> {
        SassRule {
            selectors: Vec::new(),
            selectors_done: false,
            children: Vec::new(),
        }
    }

    pub fn expanded(&self) -> String {
        let mut exp = String::new();
        exp.push_str(&format!("{} {{\n", self.selectors.iter().map(|s| (*s.name).to_string()).collect::<Vec<_>>().connect(", ")));
        exp.push_str(&self.children.iter().map(|c| c.expanded()).collect::<Vec<_>>().connect("\n  "));
        exp.push_str("}");
        exp
    }
}

impl<'a> fmt::Debug for SassRule<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let children = self.children.iter().map(|c| format!("{:?}", c)).collect::<Vec<_>>().connect("\n");
        let indented_children = children.split("\n").collect::<Vec<_>>().connect("\n  ");
        write!(f, "{:?} {{\n  {}\n}}", self.selectors, indented_children)
    }
}

#[derive(Debug,Clone)]
pub struct SassVariable<'a> {
    pub variable: Event<'a>,
}

#[derive(Debug,Clone)]
pub struct SassComment<'a> {
    pub comment: Event<'a>,
}

#[derive(Debug,Clone)]
pub struct SassSelector<'a> {
    pub name: Cow<'a, str>,
}

#[derive(PartialEq, Debug, Clone)]
pub enum State {
    OutsideRules,
    InVariable,
    InComment,
    InRule,
    InSelectors,
    InProperties,
    EndRule,
    Eof,
}

#[derive(Debug, Clone)]
pub enum Event<'a> {
    Property(Cow<'a, str>, Cow<'a, str>),
    Variable(Cow<'a, str>, Cow<'a, str>),
    Comment(Cow<'a, str>),
    ChildRule(SassRule<'a>),
    Selector(SassSelector<'a>),
}

impl<'a> Event<'a> {
    pub fn expanded(&self) -> String {
        match (*self).clone() {
            Event::Property(name, value) => format!("  {}: {};\n", name, value),
            Event::Variable(..) => String::new(),
            Event::Comment(comment) => (*comment).to_string(),
            Event::ChildRule(sass_rule) => sass_rule.expanded(),
            Event::Selector(..) => unreachable!(),
        }
    }
}

#[derive(Debug, Clone)]
pub enum TopLevelEvent<'a> {
    Rule(SassRule<'a>),
    Variable(SassVariable<'a>),
    Comment(SassComment<'a>),
}
<commit_msg>Got 02 expanded output working again<commit_after>use std::borrow::Cow;
use std::fmt;

#[derive(Clone)]
pub struct SassRule<'a> {
    pub selectors: Vec<SassSelector<'a>>,
    pub selectors_done: bool,
    pub children: Vec<Event<'a>>,
}

impl<'a> SassRule<'a> {
    pub fn new() -> SassRule<'a> {
        SassRule {
            selectors: Vec::new(),
            selectors_done: false,
            children: Vec::new(),
        }
    }

    pub fn expanded(&self) -> String {
        let mut exp = String::new();

        let selector_string = self.selectors.iter().map(|s| (*s.name).to_string()).collect::<Vec<_>>().connect(", ");
        exp.push_str(&selector_string);
        exp.push_str(" ");

        let children_string = self.children.iter().map(|c| c.expanded()).collect::<Vec<_>>().connect("\n  ");

        if self.has_properties() {
            exp.push_str(&format!("{{\n{}}}", children_string));
        } else {
            exp.push_str(&children_string);
        }

        exp
    }

    fn has_properties(&self) -> bool {
        self.children.iter().any(|c| c.is_property() )
    }
}

impl<'a> fmt::Debug for SassRule<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let children = self.children.iter().map(|c| format!("{:?}", c)).collect::<Vec<_>>().connect("\n");
        let indented_children = children.split("\n").collect::<Vec<_>>().connect("\n  ");
        write!(f, "{:?} {{\n  {}\n}}", self.selectors, indented_children)
    }
}

#[derive(Debug,Clone)]
pub struct SassVariable<'a> {
    pub variable: Event<'a>,
}

#[derive(Debug,Clone)]
pub struct SassComment<'a> {
    pub comment: Event<'a>,
}

#[derive(Debug,Clone)]
pub struct SassSelector<'a> {
    pub name: Cow<'a, str>,
}

#[derive(PartialEq, Debug, Clone)]
pub enum State {
    OutsideRules,
    InVariable,
    InComment,
    InRule,
    InSelectors,
    InProperties,
    EndRule,
    Eof,
}

#[derive(Debug, Clone)]
pub enum Event<'a> {
    Property(Cow<'a, str>, Cow<'a, str>),
    Variable(Cow<'a, str>, Cow<'a, str>),
    Comment(Cow<'a, str>),
    ChildRule(SassRule<'a>),
    Selector(SassSelector<'a>),
}

impl<'a> Event<'a> {
    pub fn expanded(&self) -> String {
        match (*self).clone() {
            Event::Property(name, value) => format!("  {}: {};\n", name, value),
            Event::Variable(..) => String::new(),
            Event::Comment(comment) => (*comment).to_string(),
            Event::ChildRule(sass_rule) => sass_rule.expanded(),
            Event::Selector(..) => unreachable!(),
        }
    }

    pub fn is_property(&self) -> bool {
        match self {
            &Event::Property(..) => true,
            _ => false,
        }
    }
}

#[derive(Debug, Clone)]
pub enum TopLevelEvent<'a> {
    Rule(SassRule<'a>),
    Variable(SassVariable<'a>),
    Comment(SassComment<'a>),
}
<|endoftext|>


<commit_before>#![feature(dynamic_lib)]
#![feature(path_relative_from)]

extern crate libc;
extern crate errno;

#[macro_use]
extern crate lazy_static;
lazy_static! {
    static ref DELAY_FDS: RwLock<HashSet<c_int>> = RwLock::new(HashSet::new());
    static ref ERR_FDS: RwLock<HashSet<c_int>> = RwLock::new(HashSet::new());
}

macro_rules! get_libc_func(
    ($destination_t:ty, $funcname:expr) =>
        (
            {
                use std::dynamic_lib::DynamicLibrary;
                use std::mem::transmute;
                use std::path::Path;

                let system_c_library: &str = "libc.so.6";

                unsafe {
                    let libc_dl = match DynamicLibrary::open(Some(Path::new(system_c_library))) {
                        Ok(libc) => libc,
                        Err(error) => panic!("Couldn't open libc: '{}'", error),
                    };

                    match libc_dl.symbol::<c_void>($funcname) {
                        Ok(open_func) => transmute::<* mut c_void, $destination_t>(open_func),
                        Err(error) => panic!("Couldn't '{}'", error),
                }
            }
        })
);

use errno::{Errno, set_errno};
use std::sync::Mutex;
use std::sync::RwLock;

use std::collections::hash_set::HashSet;

use libc::{c_char, c_int, c_void};

type OpenFunc = fn(* const c_char, c_int, libc::mode_t) -> c_int;
use libc::types::os::arch::posix88::ssize_t;
type ReadFunc = fn(fd: c_int, buf: * mut c_void, nbytes: c_int) -> ssize_t;
type WriteFunc = ReadFunc;
type CloseFunc = fn(fd: c_int) -> c_int;

use std::env;

fn get_delay_amount_ms() -> u32 {
    let default_delay_amount_ms = 200;

    match env::var("LIBFAULTINJ_DELAY_MS") {
        Ok(p) => match p.parse::<u32>() {
            Ok(i) => i,
            Err(_) => default_delay_amount_ms,
        },
        Err(_) => default_delay_amount_ms,
    }
}

use std::path::Path;

macro_rules! matchesPath(
        ($filename: expr, $env_name: expr) =>
    {
        match env::var($env_name) {
            Ok(p) => {
                let filename_path = Path::new(&$filename);
                let delay_path_match = (filename_path.relative_from(Path::new(&p))) != None;
                let filename_match = filename_path == Path::new(&p);

                delay_path_match || filename_match
            }
            Err(_) => false
        }
    });

fn initialize_sets()
{
    let fd: c_int = 0;
    DELAY_FDS.read().unwrap().contains(&fd);
    ERR_FDS.read().unwrap().contains(&fd);
}

#[no_mangle]
pub extern "C" fn open(filename_: *const c_char, flags: c_int, mode: libc::mode_t) -> c_int {
    let filename: String = unsafe {
        std::ffi::CStr::from_ptr(filename_).to_string_lossy().into_owned()
    };
    let open_func = get_libc_func!(OpenFunc, "open");
    let fd: c_int = open_func(filename_, flags, mode);

    initialize_sets();

    if matchesPath!(filename, "LIBFAULTINJ_DELAY_PATH") {
        DELAY_FDS.write().unwrap().insert(fd);
    }

    fd
}

#[no_mangle]
pub extern "C" fn read(fd: c_int, buf: *mut c_void, nbytes: c_int) -> ssize_t {
    use std::thread;
    let read_func = get_libc_func!(ReadFunc, "read");

    let delay_match = DELAY_FDS.read().unwrap().contains(&fd);
    let err_match = ERR_FDS.read().unwrap().contains(&fd);

    let ret: ssize_t = read_func(fd, buf, nbytes);

    if delay_match {
        thread::sleep_ms(get_delay_amount_ms());
    }
    if err_match {
        use libc::consts::os::posix88::EIO;
        set_errno(Errno(EIO));

        return -1
    }

    ret
}

#[no_mangle]
pub extern "C" fn write(fd: c_int, buf: *mut c_void, nbytes: c_int) -> ssize_t {
    let write_func = get_libc_func!(WriteFunc, "write");

    let matches = DELAY_FDS.read().unwrap().contains(&fd);

    let ret: ssize_t = write_func(fd, buf, nbytes);

    if matches {
        use libc::consts::os::posix88::EIO;
        set_errno(Errno(EIO));

        return -1
    }

    ret
}

#[no_mangle]
pub extern "C" fn close(fd: c_int) -> c_int {
    let close_func = get_libc_func!(CloseFunc, "close");

    let ret: c_int = close_func(fd);

    let mut err_fds = ERR_FDS.write().unwrap();
    if err_fds.contains(&fd) {
        err_fds.remove(&fd);
    }

    let mut delay_fds = DELAY_FDS.write().unwrap();
    if delay_fds.contains(&fd) {
        delay_fds.remove(&fd);
    }

    ret
}
<commit_msg>removed RandomState, should address deadlock<commit_after>#![feature(dynamic_lib,path_relative_from,hashmap_hasher)]

extern crate libc;
extern crate errno;

use std::collections::hash_state::DefaultState;
use std::hash::{Hasher, SipHasher};

#[macro_use]
extern crate lazy_static;
lazy_static! {
    static ref DELAY_FDS: RwLock<HashSet<c_int, DefaultState<SipHasher>>> = RwLock::new(Default::default());
    static ref ERR_FDS: RwLock<HashSet<c_int, DefaultState<SipHasher>>> = RwLock::new(Default::default());
}

macro_rules! get_libc_func(
    ($destination_t:ty, $funcname:expr) =>
        (
            {
                use std::dynamic_lib::DynamicLibrary;
                use std::mem::transmute;
                use std::path::Path;

                let system_c_library: &str = "libc.so.6";

                unsafe {
                    let libc_dl = match DynamicLibrary::open(Some(Path::new(system_c_library))) {
                        Ok(libc) => libc,
                        Err(error) => panic!("Couldn't open libc: '{}'", error),
                    };

                    match libc_dl.symbol::<c_void>($funcname) {
                        Ok(open_func) => transmute::<* mut c_void, $destination_t>(open_func),
                        Err(error) => panic!("Couldn't '{}'", error),
                }
            }
        })
);

use errno::{Errno, set_errno};
use std::sync::RwLock;

use std::collections::hash_set::HashSet;

use libc::{c_char, c_int, c_void};

type OpenFunc = fn(* const c_char, c_int, libc::mode_t) -> c_int;
use libc::types::os::arch::posix88::ssize_t;
type ReadFunc = fn(fd: c_int, buf: * mut c_void, nbytes: c_int) -> ssize_t;
type WriteFunc = ReadFunc;
type CloseFunc = fn(fd: c_int) -> c_int;

use std::env;

fn get_delay_amount_ms() -> u32 {
    let default_delay_amount_ms = 200;

    match env::var("LIBFAULTINJ_DELAY_MS") {
        Ok(p) => match p.parse::<u32>() {
            Ok(i) => i,
            Err(_) => default_delay_amount_ms,
        },
        Err(_) => default_delay_amount_ms,
    }
}

use std::path::Path;

macro_rules! matchesPath(
        ($filename: expr, $env_name: expr) =>
    {
        match env::var($env_name) {
            Ok(p) => {
                let filename_path = Path::new(&$filename);
                let delay_path_match = (filename_path.relative_from(Path::new(&p))) != None;
                let filename_match = filename_path == Path::new(&p);

                delay_path_match || filename_match
            }
            Err(_) => false
        }
    });

fn initialize_sets()
{
    let fd: c_int = 0;
    DELAY_FDS.read().unwrap().contains(&fd);
    ERR_FDS.read().unwrap().contains(&fd);
}

#[no_mangle]
pub extern "C" fn open(filename_: *const c_char, flags: c_int, mode: libc::mode_t) -> c_int {
    let filename: String = unsafe {
        std::ffi::CStr::from_ptr(filename_).to_string_lossy().into_owned()
    };
    let open_func = get_libc_func!(OpenFunc, "open");
    let fd: c_int = open_func(filename_, flags, mode);

    initialize_sets();

    if matchesPath!(filename, "LIBFAULTINJ_DELAY_PATH") {
        DELAY_FDS.write().unwrap().insert(fd);
    }

    fd
}

#[no_mangle]
pub extern "C" fn read(fd: c_int, buf: *mut c_void, nbytes: c_int) -> ssize_t {
    use std::thread;
    let read_func = get_libc_func!(ReadFunc, "read");

    let delay_match = DELAY_FDS.read().unwrap().contains(&fd);
    let err_match = ERR_FDS.read().unwrap().contains(&fd);

    let ret: ssize_t = read_func(fd, buf, nbytes);

    if delay_match {
        thread::sleep_ms(get_delay_amount_ms());
    }
    if err_match {
        use libc::consts::os::posix88::EIO;
        set_errno(Errno(EIO));

        return -1
    }

    ret
}

#[no_mangle]
pub extern "C" fn write(fd: c_int, buf: *mut c_void, nbytes: c_int) -> ssize_t {
    let write_func = get_libc_func!(WriteFunc, "write");

    let matches = DELAY_FDS.read().unwrap().contains(&fd);

    let ret: ssize_t = write_func(fd, buf, nbytes);

    if matches {
        use libc::consts::os::posix88::EIO;
        set_errno(Errno(EIO));

        return -1
    }

    ret
}

#[no_mangle]
pub extern "C" fn close(fd: c_int) -> c_int {
    let close_func = get_libc_func!(CloseFunc, "close");

    let ret: c_int = close_func(fd);

    let mut err_fds = ERR_FDS.write().unwrap();
    if err_fds.contains(&fd) {
        err_fds.remove(&fd);
    }

    let mut delay_fds = DELAY_FDS.write().unwrap();
    if delay_fds.contains(&fd) {
        delay_fds.remove(&fd);
    }

    ret
}
<|endoftext|>