<commit_before>//! Error types and conversion functions.

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

use gfx;
use glutin;
use winit;

use app_dirs2::AppDirsError;
use gilrs;
use image;
use rodio::decoder::DecoderError;
use toml;
use zip;

/// An enum containing all kinds of game framework errors.
#[derive(Debug)]
pub enum GameError {
    /// An error in the filesystem layout
    FilesystemError(String),
    /// An error in the config file
    ConfigError(String),
    /// Happens when an `EventsLoopProxy` attempts to
    /// wake up an `EventsLoop` that no longer exists.
    EventLoopError(String),
    /// An error trying to parse a resource
    ResourceLoadError(String),
    /// Unable to find a resource; the Vec is the paths it searched for and associated errors
    ResourceNotFound(String, Vec<(std::path::PathBuf, GameError)>),
    /// Something went wrong in the renderer
    RenderError(String),
    /// Something went wrong in the audio playback
    AudioError(String),
    /// Something went wrong trying to create a window
    WindowError(glutin::CreationError),
    /// Something went wrong trying to read from a file
    IOError(std::io::Error),
    /// Something went wrong trying to load/render a font
    FontError(String),
    /// Something went wrong applying video settings.
    VideoError(String),
    /// Something went wrong compiling shaders
    ShaderProgramError(gfx::shade::ProgramError),
    /// Something went wrong with Gilrs
    GilrsError(String),
    /// Something else happened; this is generally a bug.
    UnknownError(String),
}

impl fmt::Display for GameError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            GameError::ConfigError(ref s) => write!(f, "Config error: {}", s),
            GameError::ResourceLoadError(ref s) => write!(f, "Error loading resource: {}", s),
            GameError::ResourceNotFound(ref s, ref paths) => write!(
                f,
                "Resource not found: {}, searched in paths {:?}",
                s, paths
            ),
            GameError::WindowError(ref e) => write!(f, "Window creation error: {}", e),
            _ => write!(f, "GameError {:?}", self),
        }
    }
}

impl Error for GameError {
    fn description(&self) -> &str {
        match *self {
            GameError::FilesystemError(_) => "Filesystem error",
            GameError::ConfigError(_) => "Config file error",
            GameError::EventLoopError(_) => "Event loop error",
            GameError::ResourceLoadError(_) => "Resource load error",
            GameError::ResourceNotFound(_, _) => "Resource not found",
            GameError::RenderError(_) => "Render error",
            GameError::AudioError(_) => "Audio error",
            GameError::WindowError(_) => "Window error",
            GameError::IOError(_) => "IO error",
            GameError::FontError(_) => "Font error",
            GameError::VideoError(_) => "Video error",
            GameError::ShaderProgramError(_) => "Shader program error",
            GameError::GilrsError(_) => "Gamepad error",
            GameError::UnknownError(_) => "Unknown error",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            GameError::WindowError(ref e) => Some(e),
            GameError::IOError(ref e) => Some(e),
            GameError::ShaderProgramError(ref e) => Some(e),
            _ => None,
        }
    }
}

/// A convenient result type consisting of a return type and a `GameError`
pub type GameResult<T = ()> = Result<T, GameError>;

impl From<String> for GameError {
    fn from(s: String) -> GameError {
        GameError::UnknownError(s)
    }
}

impl From<AppDirsError> for GameError {
    fn from(e: AppDirsError) -> GameError {
        let errmessage = format!("{}", e);
        GameError::FilesystemError(errmessage)
    }
}
impl From<std::io::Error> for GameError {
    fn from(e: std::io::Error) -> GameError {
        GameError::IOError(e)
    }
}

impl From<toml::de::Error> for GameError {
    fn from(e: toml::de::Error) -> GameError {
        let errstr = format!("TOML decode error: {}", e.description());

        GameError::ConfigError(errstr)
    }
}

impl From<toml::ser::Error> for GameError {
    fn from(e: toml::ser::Error) -> GameError {
        let errstr = format!("TOML error (possibly encoding?): {}", e.description());
        GameError::ConfigError(errstr)
    }
}

impl From<zip::result::ZipError> for GameError {
    fn from(e: zip::result::ZipError) -> GameError {
        let errstr = format!("Zip error: {}", e.description());
        GameError::ResourceLoadError(errstr)
    }
}

impl From<DecoderError> for GameError {
    fn from(e: DecoderError) -> GameError {
        let errstr = format!("Audio decoder error: {:?}", e);
        GameError::AudioError(errstr)
    }
}

impl From<image::ImageError> for GameError {
    fn from(e: image::ImageError) -> GameError {
        let errstr = format!("Image load error: {}", e.description());
        GameError::ResourceLoadError(errstr)
    }
}

impl From<gfx::PipelineStateError<std::string::String>> for GameError {
    fn from(e: gfx::PipelineStateError<std::string::String>) -> GameError {
        let errstr = format!(
            "Error constructing pipeline!\nThis should probably not be \
             happening; it probably means an error in a shader or \
             something.\nError was: {:?}",
            e
        );
        GameError::VideoError(errstr)
    }
}

impl From<gfx::mapping::Error> for GameError {
    fn from(e: gfx::mapping::Error) -> GameError {
        let errstr = format!("Buffer mapping error: {:?}", e);
        GameError::VideoError(errstr)
    }
}

impl<S, D> From<gfx::CopyError<S, D>> for GameError
where
    S: fmt::Debug,
    D: fmt::Debug,
{
    fn from(e: gfx::CopyError<S, D>) -> GameError {
        let errstr = format!("Memory copy error: {:?}", e);
        GameError::VideoError(errstr)
    }
}

impl From<gfx::CombinedError> for GameError {
    fn from(e: gfx::CombinedError) -> GameError {
        let errstr = format!("Texture+view load error: {}", e.description());
        GameError::VideoError(errstr)
    }
}

impl From<gfx::texture::CreationError> for GameError {
    fn from(e: gfx::texture::CreationError) -> GameError {
        gfx::CombinedError::from(e).into()
    }
}

impl From<gfx::ResourceViewError> for GameError {
    fn from(e: gfx::ResourceViewError) -> GameError {
        gfx::CombinedError::from(e).into()
    }
}

impl From<gfx::TargetViewError> for GameError {
    fn from(e: gfx::TargetViewError) -> GameError {
        gfx::CombinedError::from(e).into()
    }
}

impl<T> From<gfx::UpdateError<T>> for GameError
where
    T: fmt::Debug + fmt::Display + 'static,
{
    fn from(e: gfx::UpdateError<T>) -> GameError {
        let errstr = format!("Buffer update error: {}", e);
        GameError::VideoError(errstr)
    }
}

impl From<gfx::shade::ProgramError> for GameError {
    fn from(e: gfx::shade::ProgramError) -> GameError {
        GameError::ShaderProgramError(e)
    }
}

// TODO: improve winit/glutin error handling.

impl From<winit::EventsLoopClosed> for GameError {
    fn from(_: glutin::EventsLoopClosed) -> GameError {
        let e = "An event loop proxy attempted to wake up an event loop that no longer exists."
            .to_owned();
        GameError::EventLoopError(e)
    }
}

impl From<glutin::CreationError> for GameError {
    fn from(s: glutin::CreationError) -> GameError {
        GameError::WindowError(s)
    }
}

impl From<glutin::ContextError> for GameError {
    fn from(s: glutin::ContextError) -> GameError {
        GameError::RenderError(format!("OpenGL context error: {}", s))
    }
}

impl From<gilrs::Error> for GameError {
    // TODO: Better error type?
    fn from(s: gilrs::Error) -> GameError {
        let errstr = format!("Gamepad error: {}", s);
        GameError::GilrsError(errstr)
    }
}
<commit_msg>Change `GilrsError` to be more inline with the others<commit_after>//! Error types and conversion functions.

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

use gfx;
use glutin;
use winit;

use app_dirs2::AppDirsError;
use gilrs;
use image;
use rodio::decoder::DecoderError;
use toml;
use zip;

/// An enum containing all kinds of game framework errors.
#[derive(Debug)]
pub enum GameError {
    /// An error in the filesystem layout
    FilesystemError(String),
    /// An error in the config file
    ConfigError(String),
    /// Happens when an `EventsLoopProxy` attempts to
    /// wake up an `EventsLoop` that no longer exists.
    EventLoopError(String),
    /// An error trying to parse a resource
    ResourceLoadError(String),
    /// Unable to find a resource; the Vec is the paths it searched for and associated errors
    ResourceNotFound(String, Vec<(std::path::PathBuf, GameError)>),
    /// Something went wrong in the renderer
    RenderError(String),
    /// Something went wrong in the audio playback
    AudioError(String),
    /// Something went wrong trying to create a window
    WindowError(glutin::CreationError),
    /// Something went wrong trying to read from a file
    IOError(std::io::Error),
    /// Something went wrong trying to load/render a font
    FontError(String),
    /// Something went wrong applying video settings.
    VideoError(String),
    /// Something went wrong compiling shaders
    ShaderProgramError(gfx::shade::ProgramError),
    /// Something went wrong with Gilrs
    GamepadError(String),
    /// Something else happened; this is generally a bug.
    UnknownError(String),
}

impl fmt::Display for GameError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            GameError::ConfigError(ref s) => write!(f, "Config error: {}", s),
            GameError::ResourceLoadError(ref s) => write!(f, "Error loading resource: {}", s),
            GameError::ResourceNotFound(ref s, ref paths) => write!(
                f,
                "Resource not found: {}, searched in paths {:?}",
                s, paths
            ),
            GameError::WindowError(ref e) => write!(f, "Window creation error: {}", e),
            _ => write!(f, "GameError {:?}", self),
        }
    }
}

impl Error for GameError {
    fn description(&self) -> &str {
        match *self {
            GameError::FilesystemError(_) => "Filesystem error",
            GameError::ConfigError(_) => "Config file error",
            GameError::EventLoopError(_) => "Event loop error",
            GameError::ResourceLoadError(_) => "Resource load error",
            GameError::ResourceNotFound(_, _) => "Resource not found",
            GameError::RenderError(_) => "Render error",
            GameError::AudioError(_) => "Audio error",
            GameError::WindowError(_) => "Window error",
            GameError::IOError(_) => "IO error",
            GameError::FontError(_) => "Font error",
            GameError::VideoError(_) => "Video error",
            GameError::ShaderProgramError(_) => "Shader program error",
            GameError::GamepadError(_) => "Gamepad error",
            GameError::UnknownError(_) => "Unknown error",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            GameError::WindowError(ref e) => Some(e),
            GameError::IOError(ref e) => Some(e),
            GameError::ShaderProgramError(ref e) => Some(e),
            _ => None,
        }
    }
}

/// A convenient result type consisting of a return type and a `GameError`
pub type GameResult<T = ()> = Result<T, GameError>;

impl From<String> for GameError {
    fn from(s: String) -> GameError {
        GameError::UnknownError(s)
    }
}

impl From<AppDirsError> for GameError {
    fn from(e: AppDirsError) -> GameError {
        let errmessage = format!("{}", e);
        GameError::FilesystemError(errmessage)
    }
}
impl From<std::io::Error> for GameError {
    fn from(e: std::io::Error) -> GameError {
        GameError::IOError(e)
    }
}

impl From<toml::de::Error> for GameError {
    fn from(e: toml::de::Error) -> GameError {
        let errstr = format!("TOML decode error: {}", e.description());

        GameError::ConfigError(errstr)
    }
}

impl From<toml::ser::Error> for GameError {
    fn from(e: toml::ser::Error) -> GameError {
        let errstr = format!("TOML error (possibly encoding?): {}", e.description());
        GameError::ConfigError(errstr)
    }
}

impl From<zip::result::ZipError> for GameError {
    fn from(e: zip::result::ZipError) -> GameError {
        let errstr = format!("Zip error: {}", e.description());
        GameError::ResourceLoadError(errstr)
    }
}

impl From<DecoderError> for GameError {
    fn from(e: DecoderError) -> GameError {
        let errstr = format!("Audio decoder error: {:?}", e);
        GameError::AudioError(errstr)
    }
}

impl From<image::ImageError> for GameError {
    fn from(e: image::ImageError) -> GameError {
        let errstr = format!("Image load error: {}", e.description());
        GameError::ResourceLoadError(errstr)
    }
}

impl From<gfx::PipelineStateError<std::string::String>> for GameError {
    fn from(e: gfx::PipelineStateError<std::string::String>) -> GameError {
        let errstr = format!(
            "Error constructing pipeline!\nThis should probably not be \
             happening; it probably means an error in a shader or \
             something.\nError was: {:?}",
            e
        );
        GameError::VideoError(errstr)
    }
}

impl From<gfx::mapping::Error> for GameError {
    fn from(e: gfx::mapping::Error) -> GameError {
        let errstr = format!("Buffer mapping error: {:?}", e);
        GameError::VideoError(errstr)
    }
}

impl<S, D> From<gfx::CopyError<S, D>> for GameError
where
    S: fmt::Debug,
    D: fmt::Debug,
{
    fn from(e: gfx::CopyError<S, D>) -> GameError {
        let errstr = format!("Memory copy error: {:?}", e);
        GameError::VideoError(errstr)
    }
}

impl From<gfx::CombinedError> for GameError {
    fn from(e: gfx::CombinedError) -> GameError {
        let errstr = format!("Texture+view load error: {}", e.description());
        GameError::VideoError(errstr)
    }
}

impl From<gfx::texture::CreationError> for GameError {
    fn from(e: gfx::texture::CreationError) -> GameError {
        gfx::CombinedError::from(e).into()
    }
}

impl From<gfx::ResourceViewError> for GameError {
    fn from(e: gfx::ResourceViewError) -> GameError {
        gfx::CombinedError::from(e).into()
    }
}

impl From<gfx::TargetViewError> for GameError {
    fn from(e: gfx::TargetViewError) -> GameError {
        gfx::CombinedError::from(e).into()
    }
}

impl<T> From<gfx::UpdateError<T>> for GameError
where
    T: fmt::Debug + fmt::Display + 'static,
{
    fn from(e: gfx::UpdateError<T>) -> GameError {
        let errstr = format!("Buffer update error: {}", e);
        GameError::VideoError(errstr)
    }
}

impl From<gfx::shade::ProgramError> for GameError {
    fn from(e: gfx::shade::ProgramError) -> GameError {
        GameError::ShaderProgramError(e)
    }
}

// TODO: improve winit/glutin error handling.

impl From<winit::EventsLoopClosed> for GameError {
    fn from(_: glutin::EventsLoopClosed) -> GameError {
        let e = "An event loop proxy attempted to wake up an event loop that no longer exists."
            .to_owned();
        GameError::EventLoopError(e)
    }
}

impl From<glutin::CreationError> for GameError {
    fn from(s: glutin::CreationError) -> GameError {
        GameError::WindowError(s)
    }
}

impl From<glutin::ContextError> for GameError {
    fn from(s: glutin::ContextError) -> GameError {
        GameError::RenderError(format!("OpenGL context error: {}", s))
    }
}

impl From<gilrs::Error> for GameError {
    // TODO: Better error type?
    fn from(s: gilrs::Error) -> GameError {
        let errstr = format!("Gamepad error: {}", s);
        GameError::GamepadError(errstr)
    }
}
<|endoftext|>


<commit_before>//! Defines error types used by this library.

use Value;

use xml::reader::Error as XmlError;
use xml::common::TextPosition;

use std::{error, io};
use std::fmt::{self, Formatter, Display};
use std::collections::BTreeMap;

/// Errors that can occur when trying to perform an XML-RPC request.
///
/// This can be a lower-level error (for example, the HTTP request failed), a problem with the
/// server (maybe it's not implementing XML-RPC correctly), or just a failure to execute the
/// operation.
#[derive(Debug)]
pub struct Error(RequestErrorKind);

impl Error {
    /// If this `Error` was caused by the server responding with a `<fault>` response,
    /// returns the `Fault` in question.
    pub fn fault(&self) -> Option<&Fault> {
        match self.0 {
            RequestErrorKind::Fault(ref fault) => Some(fault),
            _ => None,
        }
    }
}

#[doc(hidden)]  // hide internal impl
impl From<RequestErrorKind> for Error {
    fn from(kind: RequestErrorKind) -> Self {
        Error(kind)
    }
}

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

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

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

#[derive(Debug)]
pub enum RequestErrorKind {
    /// The response could not be parsed. This can happen when the server doesn't correctly
    /// implement the XML-RPC spec.
    ParseError(ParseError),

    /// A communication error originating from the transport used to perform the request.
    TransportError(Box<error::Error + 'static>),

    /// The server returned a `<fault>` response, indicating that the execution of the call
    /// encountered a problem (for example, an invalid (number of) arguments was passed).
    Fault(Fault),
}

impl From<ParseError> for RequestErrorKind {
    fn from(e: ParseError) -> Self {
        RequestErrorKind::ParseError(e)
    }
}

impl From<Fault> for RequestErrorKind {
    fn from(f: Fault) -> Self {
        RequestErrorKind::Fault(f)
    }
}

impl Display for RequestErrorKind {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            RequestErrorKind::ParseError(ref err) => write!(fmt, "parse error: {}", err),
            RequestErrorKind::TransportError(ref err) => write!(fmt, "transport error: {}", err),
            RequestErrorKind::Fault(ref err) => write!(fmt, "{}", err),
        }
    }
}

impl error::Error for RequestErrorKind {
    fn description(&self) -> &str {
        match *self {
            RequestErrorKind::ParseError(_) => "parse error",
            RequestErrorKind::TransportError(_) => "transport error",
            RequestErrorKind::Fault(_) => "server returned a fault",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            RequestErrorKind::ParseError(ref err) => Some(err),
            RequestErrorKind::TransportError(ref err) => Some(err.as_ref()),
            RequestErrorKind::Fault(ref err) => Some(err),
        }
    }
}

/// Describes possible error that can occur when parsing a `Response`.
#[derive(Debug, PartialEq)]
pub enum ParseError {
    /// Error while parsing (malformed?) XML.
    XmlError(XmlError),

    /// Could not parse the given CDATA as XML-RPC value.
    ///
    /// For example, `<value><int>AAA</int></value>` describes an invalid value.
    InvalidValue {
        /// The type for which an invalid value was supplied (eg. `int` or `dateTime.iso8601`).
        for_type: &'static str,
        /// The value we encountered, as a string.
        found: String,
        /// The position of the invalid value inside the XML document.
        position: TextPosition,
    },

    /// Found an unexpected tag, attribute, etc.
    UnexpectedXml {
        /// A short description of the kind of data that was expected.
        expected: String,
        found: Option<String>,
        /// The position of the unexpected data inside the XML document.
        position: TextPosition,
    }
}

impl From<XmlError> for ParseError {
    fn from(e: XmlError) -> Self {
        ParseError::XmlError(e)
    }
}

impl From<io::Error> for ParseError {
    fn from(e: io::Error) -> Self {
        ParseError::XmlError(XmlError::from(e))
    }
}

impl Display for ParseError {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            ParseError::XmlError(ref err) => write!(fmt, "malformed XML: {}", err),
            ParseError::InvalidValue {
                for_type,
                ref found,
                ref position,
            } => write!(fmt, "invalid value for type '{}' at {}: {}", for_type, position, found),
            ParseError::UnexpectedXml {
                ref expected,
                ref position,
                found: None,
            } => {
                write!(fmt, "unexpected XML at {} (expected {})", position, expected)
            }
            ParseError::UnexpectedXml {
                ref expected,
                ref position,
                found: Some(ref found),
            } => {
                write!(fmt, "unexpected XML at {} (expected {}, found {})", position, expected, found)
            }
        }
    }
}

impl error::Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::XmlError(ref err) => err.description(),
            ParseError::InvalidValue { .. } => "invalid value for type",
            ParseError::UnexpectedXml { .. } => "unexpected XML content",
        }
    }
}

/// A `<fault>` response, indicating that a request failed.
///
/// The XML-RPC specification requires that a `<faultCode>` and `<faultString>` is returned in the
/// `<fault>` case, further describing the error.
#[derive(Debug, PartialEq, Eq)]
pub struct Fault {
    pub fault_code: i32,
    pub fault_string: String,
}

impl Fault {
    /// Creates a `Fault` from a `Value`.
    ///
    /// The `Value` must be a `Value::Struct` with a `faultCode` and `faultString` field (and no
    /// other fields).
    ///
    /// Returns `None` if the value isn't a valid `Fault`.
    pub fn from_value(value: &Value) -> Option<Self> {
        match *value {
            Value::Struct(ref map) => {
                if map.len() != 2 {
                    // incorrect field count
                    return None;
                }

                match (map.get("faultCode"), map.get("faultString")) {
                    (Some(&Value::Int(fault_code)), Some(&Value::String(ref fault_string))) => {
                        Some(Fault {
                            fault_code,
                            fault_string: fault_string.to_string(),
                        })
                    }
                    _ => None
                }
            }
            _ => None
        }
    }

    /// Turns this `Fault` into an equivalent `Value`.
    ///
    /// The returned value can be parsed back into a `Fault` using `Fault::from_value` or returned
    /// as a `<fault>` error response by serializing it into a `<fault></fault>` tag.
    pub fn to_value(&self) -> Value {
        let mut map = BTreeMap::new();
        map.insert("faultCode".to_string(), Value::from(self.fault_code));
        map.insert("faultString".to_string(), Value::from(self.fault_string.as_ref()));

        Value::Struct(map)
    }
}

impl Display for Fault {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{} ({})", self.fault_string, self.fault_code)
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;

    use std::error;

    #[test]
    fn fault_roundtrip() {
        let input = Fault {
            fault_code: -123456,
            fault_string: "The Bald Lazy House Jumps Over The Hyperactive Kitten".to_string()
        };

        assert_eq!(Fault::from_value(&input.to_value()), Some(input));
    }

    #[test]
    fn error_impls_error() {
        fn assert_error<T: error::Error>() {}

        assert_error::<Error>();
    }
}
<commit_msg>Add fixme about error sendability<commit_after>//! Defines error types used by this library.

use Value;

use xml::reader::Error as XmlError;
use xml::common::TextPosition;

use std::{error, io};
use std::fmt::{self, Formatter, Display};
use std::collections::BTreeMap;

/// Errors that can occur when trying to perform an XML-RPC request.
///
/// This can be a lower-level error (for example, the HTTP request failed), a problem with the
/// server (maybe it's not implementing XML-RPC correctly), or just a failure to execute the
/// operation.
#[derive(Debug)]
pub struct Error(RequestErrorKind);

// FIXME: Error isn't currently Send/Sync because transport errors might not be. This will solve
// itself when we move to `failure`.

impl Error {
    /// If this `Error` was caused by the server responding with a `<fault>` response,
    /// returns the `Fault` in question.
    pub fn fault(&self) -> Option<&Fault> {
        match self.0 {
            RequestErrorKind::Fault(ref fault) => Some(fault),
            _ => None,
        }
    }
}

#[doc(hidden)]  // hide internal impl
impl From<RequestErrorKind> for Error {
    fn from(kind: RequestErrorKind) -> Self {
        Error(kind)
    }
}

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

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

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

#[derive(Debug)]
pub enum RequestErrorKind {
    /// The response could not be parsed. This can happen when the server doesn't correctly
    /// implement the XML-RPC spec.
    ParseError(ParseError),

    /// A communication error originating from the transport used to perform the request.
    TransportError(Box<error::Error + 'static>),

    /// The server returned a `<fault>` response, indicating that the execution of the call
    /// encountered a problem (for example, an invalid (number of) arguments was passed).
    Fault(Fault),
}

impl From<ParseError> for RequestErrorKind {
    fn from(e: ParseError) -> Self {
        RequestErrorKind::ParseError(e)
    }
}

impl From<Fault> for RequestErrorKind {
    fn from(f: Fault) -> Self {
        RequestErrorKind::Fault(f)
    }
}

impl Display for RequestErrorKind {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            RequestErrorKind::ParseError(ref err) => write!(fmt, "parse error: {}", err),
            RequestErrorKind::TransportError(ref err) => write!(fmt, "transport error: {}", err),
            RequestErrorKind::Fault(ref err) => write!(fmt, "{}", err),
        }
    }
}

impl error::Error for RequestErrorKind {
    fn description(&self) -> &str {
        match *self {
            RequestErrorKind::ParseError(_) => "parse error",
            RequestErrorKind::TransportError(_) => "transport error",
            RequestErrorKind::Fault(_) => "server returned a fault",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            RequestErrorKind::ParseError(ref err) => Some(err),
            RequestErrorKind::TransportError(ref err) => Some(err.as_ref()),
            RequestErrorKind::Fault(ref err) => Some(err),
        }
    }
}

/// Describes possible error that can occur when parsing a `Response`.
#[derive(Debug, PartialEq)]
pub enum ParseError {
    /// Error while parsing (malformed?) XML.
    XmlError(XmlError),

    /// Could not parse the given CDATA as XML-RPC value.
    ///
    /// For example, `<value><int>AAA</int></value>` describes an invalid value.
    InvalidValue {
        /// The type for which an invalid value was supplied (eg. `int` or `dateTime.iso8601`).
        for_type: &'static str,
        /// The value we encountered, as a string.
        found: String,
        /// The position of the invalid value inside the XML document.
        position: TextPosition,
    },

    /// Found an unexpected tag, attribute, etc.
    UnexpectedXml {
        /// A short description of the kind of data that was expected.
        expected: String,
        found: Option<String>,
        /// The position of the unexpected data inside the XML document.
        position: TextPosition,
    }
}

impl From<XmlError> for ParseError {
    fn from(e: XmlError) -> Self {
        ParseError::XmlError(e)
    }
}

impl From<io::Error> for ParseError {
    fn from(e: io::Error) -> Self {
        ParseError::XmlError(XmlError::from(e))
    }
}

impl Display for ParseError {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            ParseError::XmlError(ref err) => write!(fmt, "malformed XML: {}", err),
            ParseError::InvalidValue {
                for_type,
                ref found,
                ref position,
            } => write!(fmt, "invalid value for type '{}' at {}: {}", for_type, position, found),
            ParseError::UnexpectedXml {
                ref expected,
                ref position,
                found: None,
            } => {
                write!(fmt, "unexpected XML at {} (expected {})", position, expected)
            }
            ParseError::UnexpectedXml {
                ref expected,
                ref position,
                found: Some(ref found),
            } => {
                write!(fmt, "unexpected XML at {} (expected {}, found {})", position, expected, found)
            }
        }
    }
}

impl error::Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::XmlError(ref err) => err.description(),
            ParseError::InvalidValue { .. } => "invalid value for type",
            ParseError::UnexpectedXml { .. } => "unexpected XML content",
        }
    }
}

/// A `<fault>` response, indicating that a request failed.
///
/// The XML-RPC specification requires that a `<faultCode>` and `<faultString>` is returned in the
/// `<fault>` case, further describing the error.
#[derive(Debug, PartialEq, Eq)]
pub struct Fault {
    pub fault_code: i32,
    pub fault_string: String,
}

impl Fault {
    /// Creates a `Fault` from a `Value`.
    ///
    /// The `Value` must be a `Value::Struct` with a `faultCode` and `faultString` field (and no
    /// other fields).
    ///
    /// Returns `None` if the value isn't a valid `Fault`.
    pub fn from_value(value: &Value) -> Option<Self> {
        match *value {
            Value::Struct(ref map) => {
                if map.len() != 2 {
                    // incorrect field count
                    return None;
                }

                match (map.get("faultCode"), map.get("faultString")) {
                    (Some(&Value::Int(fault_code)), Some(&Value::String(ref fault_string))) => {
                        Some(Fault {
                            fault_code,
                            fault_string: fault_string.to_string(),
                        })
                    }
                    _ => None
                }
            }
            _ => None
        }
    }

    /// Turns this `Fault` into an equivalent `Value`.
    ///
    /// The returned value can be parsed back into a `Fault` using `Fault::from_value` or returned
    /// as a `<fault>` error response by serializing it into a `<fault></fault>` tag.
    pub fn to_value(&self) -> Value {
        let mut map = BTreeMap::new();
        map.insert("faultCode".to_string(), Value::from(self.fault_code));
        map.insert("faultString".to_string(), Value::from(self.fault_string.as_ref()));

        Value::Struct(map)
    }
}

impl Display for Fault {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{} ({})", self.fault_string, self.fault_code)
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;

    use std::error;

    #[test]
    fn fault_roundtrip() {
        let input = Fault {
            fault_code: -123456,
            fault_string: "The Bald Lazy House Jumps Over The Hyperactive Kitten".to_string()
        };

        assert_eq!(Fault::from_value(&input.to_value()), Some(input));
    }

    #[test]
    fn error_impls_error() {
        fn assert_error<T: error::Error>() {}

        assert_error::<Error>();
    }
}
<|endoftext|>


<commit_before>use reqwest;
use std::error::Error as StdError;
use std::io;
use std::string::FromUtf8Error;
use toml::de::Error as TomlDeserializeError;
use toml::ser::Error as TomlSerializeError;

/// The error type for the program's every operations, such as TOML Parsing
/// or IO-related errors. Wherever there's an error, it _should_ be wrapped
/// into this type.
#[derive(Debug)]
pub enum PackageError {
    Download(String),
    Parsing(String),
    Utf8(String),
    IO(String),
}

impl From<io::Error> for PackageError {
    /// Converts an `io::Error` into this type.
    fn from(err: io::Error) -> Self {
        PackageError::IO(String::from("An IO error occured:") + err.description())
    }
}

impl From<TomlDeserializeError> for PackageError {
    /// Converts a `toml::de:Error` into this type.
    fn from(err: TomlDeserializeError) -> Self {
        let err_location =
            err
                // Get the position of the error
                .line_col()
                // If it's not available, return a empty string.
                // If it is, then convert the position into a string.
                .map_or(String::from(""), |(line, col)| {
                    format!("in Line: {}, Col: {}",
                        line,
                        col
                    )
                });

        let err_description = err.description();

        PackageError::Parsing(
            format!(
                "An error occured while parsing a TOML, {} due to: {}.",
                err_location,
                err_description
            )
        )
    }
}

impl From<TomlSerializeError> for PackageError {
    /// Converts a `toml::de:Error` into this type.
    fn from(err: TomlSerializeError) -> Self {
        PackageError::Parsing(
            String::from("TOML serialize error:") +
            err.description()
        )
    }
}

impl From<reqwest::Error> for PackageError {
    fn from(err: reqwest::Error) -> PackageError {
        let related_url =
            err
                // Try to retrieve the final stop if an error occured.
                .url()
                // If there's none, return "none"
                .map_or("none", |url| {
                    url.as_str()
                });

        let status_code =
            &err
                // Try to retrieve the status code if the error was generated
                // from an response.
                .status()
                // If there's none, return "none"
                .map_or(String::from("none"), |code| {
                    code.as_u16().to_string()
                });

        PackageError::Download(
            String::new() +
            "related url:           " + related_url +
            "http related:          " + &err.is_http().to_string() +
            "serialization related: " + &err.is_serialization().to_string() +
            "redirect related:      " + &err.is_redirect().to_string() +
            "client related:        " + &err.is_client_error().to_string() +
            "server related:        " + &err.is_server_error().to_string() +
            "status code:           " + status_code +
            "other messages:        " + err.description()
        )
    }
}

impl From<FromUtf8Error> for PackageError {
    fn from(err: FromUtf8Error) -> Self {
        let utf8_error = err.utf8_error();
        let errored_bytes =
            err
            // Gets the invalid bytes of the string
            .as_bytes()
            // Convert the bytes into an iterator.
            .iter()
            // Reduce the array of bytes into a string.
            // Example output: `152, 84, 156, 78`
            .fold(String::from(""), |acc, cur| {
                acc + ", " + &cur.to_string()
            });

        PackageError::Utf8(
            String::new() +
            "description:         " + err.description() +
            "further description: " + utf8_error.description() +
            "bytes valid up to:   " + &utf8_error.valid_up_to().to_string() +
            "invalid bytes:       " + &errored_bytes
        )
    }
}<commit_msg>Display trait for PackageError<commit_after>use reqwest;
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::string::FromUtf8Error;
use toml::de::Error as TomlDeserializeError;
use toml::ser::Error as TomlSerializeError;

/// The error type for the program's every operations, such as TOML Parsing
/// or IO-related errors. Wherever there's an error, it _should_ be wrapped
/// into this type.
#[derive(Debug)]
pub enum PackageError {
    Download(String),
    Parsing(String),
    Utf8(String),
    IO(String),
}

impl fmt::Display for PackageError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::PackageError::*;
        match self {
            &Download(ref why) => write!(f, "An error occured during download: {}", why),
            &Parsing(ref why) => write!(f, "An error occured during TOML parsing: {}", why),
            &Utf8(ref why) => write!(f, "An error occured during UTF8 parsing: {}", why),
            &IO(ref why) => write!(f, "An error occured during an IO operation: {}", why),
        }
    }
}

impl From<io::Error> for PackageError {
    /// Converts an `io::Error` into this type.
    fn from(err: io::Error) -> Self {
        PackageError::IO(String::from("An IO error occured:") + err.description())
    }
}

impl From<TomlDeserializeError> for PackageError {
    /// Converts a `toml::de:Error` into this type.
    fn from(err: TomlDeserializeError) -> Self {
        let err_location =
            err
                // Get the position of the error
                .line_col()
                // If it's not available, return a empty string.
                // If it is, then convert the position into a string.
                .map_or(String::from(""), |(line, col)| {
                    format!("in Line: {}, Col: {}",
                        line,
                        col
                    )
                });

        let err_description = err.description();

        PackageError::Parsing(
            format!(
                "An error occured while parsing a TOML, {} due to: {}.",
                err_location,
                err_description
            )
        )
    }
}

impl From<TomlSerializeError> for PackageError {
    /// Converts a `toml::de:Error` into this type.
    fn from(err: TomlSerializeError) -> Self {
        PackageError::Parsing(
            String::from("TOML serialize error:") +
            err.description()
        )
    }
}

impl From<reqwest::Error> for PackageError {
    fn from(err: reqwest::Error) -> PackageError {
        let related_url =
            err
                // Try to retrieve the final stop if an error occured.
                .url()
                // If there's none, return "none"
                .map_or("none", |url| {
                    url.as_str()
                });

        let status_code =
            &err
                // Try to retrieve the status code if the error was generated
                // from an response.
                .status()
                // If there's none, return "none"
                .map_or(String::from("none"), |code| {
                    code.as_u16().to_string()
                });

        PackageError::Download(
            String::new() +
            "related url:           " + related_url +
            "http related:          " + &err.is_http().to_string() +
            "serialization related: " + &err.is_serialization().to_string() +
            "redirect related:      " + &err.is_redirect().to_string() +
            "client related:        " + &err.is_client_error().to_string() +
            "server related:        " + &err.is_server_error().to_string() +
            "status code:           " + status_code +
            "other messages:        " + err.description()
        )
    }
}

impl From<FromUtf8Error> for PackageError {
    fn from(err: FromUtf8Error) -> Self {
        let utf8_error = err.utf8_error();
        let errored_bytes =
            err
            // Gets the invalid bytes of the string
            .as_bytes()
            // Convert the bytes into an iterator.
            .iter()
            // Reduce the array of bytes into a string.
            // Example output: `152, 84, 156, 78`
            .fold(String::from(""), |acc, cur| {
                acc + ", " + &cur.to_string()
            });

        PackageError::Utf8(
            String::new() +
            "description:         " + err.description() +
            "further description: " + utf8_error.description() +
            "bytes valid up to:   " + &utf8_error.valid_up_to().to_string() +
            "invalid bytes:       " + &errored_bytes
        )
    }
}<|endoftext|>


<commit_before>use std;

#[derive(Debug)]
pub enum Error {
    BadSample,
    EmptySample,
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "{:?}", self)
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::BadSample => "All sample data must be finite",
            Error::EmptySample => "Sample data set cannot be empty",
        }
    }
}
<commit_msg>Improve error `Display` impl<commit_after>use std;

#[derive(Debug)]
pub enum Error {
    BadSample,
    EmptySample,
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "{}", std::error::Error::description(self))
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::BadSample => "All sample data must be finite",
            Error::EmptySample => "Sample data set cannot be empty",
        }
    }
}
<|endoftext|>


<commit_before>//! Errors for `frippy` crate using `failure`.

use std::io::Error as IoError;
use std::str::Utf8Error;
use irc::error::IrcError;
use reqwest::Error as ReqwestError;
use r2d2::Error as R2d2Error;

/// The main crate-wide error type.
#[derive(Debug, Fail)]
pub enum FrippyError {
    /// A plugin error
    #[fail(display = "A plugin error occured")]
    Plugin(#[cause] PluginError),

    /// An IRC error
    #[fail(display = "An IRC error occured")]
    Irc(#[cause] IrcError),

    /// Missing config error
    #[fail(display = "No config file was found")]
    MissingConfig,

    /// A reqwest error
    #[fail(display = "A reqwest error occured")]
    Reqwest(#[cause] ReqwestError),

    /// An I/O error
    #[fail(display = "An I/O error occured")]
    Io(#[cause] IoError),

    /// A UTF8 error
    #[fail(display = "A UTF8 error occured")]
    Utf8(#[cause] Utf8Error),

    /// An r2d2 error
    #[fail(display = "An r2d2 error occured")]
    R2d2,

    /// Reached download limit error
    #[fail(display = "Reached download limit of {} KiB", limit)]
    DownloadLimit { limit: usize },
}

/// Errors related to plugins
#[derive(Debug, Fail)]
pub enum PluginError {
    /// A Url error
    #[fail(display = "A Url error occured")]
    Url(#[cause] UrlError),

    /// A Factoids error
    #[fail(display = "{}", error)]
    Factoids { error: String },
}

/// A URL plugin error
#[derive(Debug, Fail)]
pub enum UrlError {
    /// Missing URL error
    #[fail(display = "No URL was found")]
    MissingUrl,

    /// Missing title error
    #[fail(display = "No title was found")]
    MissingTitle,
}

impl From<UrlError> for FrippyError {
    fn from(e: UrlError) -> FrippyError {
        FrippyError::Plugin(PluginError::Url(e))
    }
}

impl From<PluginError> for FrippyError {
    fn from(e: PluginError) -> FrippyError {
        FrippyError::Plugin(e)
    }
}

impl From<IrcError> for FrippyError {
    fn from(e: IrcError) -> FrippyError {
        FrippyError::Irc(e)
    }
}

impl From<ReqwestError> for FrippyError {
    fn from(e: ReqwestError) -> FrippyError {
        FrippyError::Reqwest(e)
    }
}

impl From<IoError> for FrippyError {
    fn from(e: IoError) -> FrippyError {
        FrippyError::Io(e)
    }
}

impl From<Utf8Error> for FrippyError {
    fn from(e: Utf8Error) -> FrippyError {
        FrippyError::Utf8(e)
    }
}

impl From<R2d2Error> for FrippyError {
    fn from(e: R2d2Error) -> FrippyError {
        FrippyError::R2d2(e)
    }
}
<commit_msg>Add missing cause to r2d2 error<commit_after>//! Errors for `frippy` crate using `failure`.

use std::io::Error as IoError;
use std::str::Utf8Error;
use irc::error::IrcError;
use reqwest::Error as ReqwestError;
use r2d2::Error as R2d2Error;

/// The main crate-wide error type.
#[derive(Debug, Fail)]
pub enum FrippyError {
    /// A plugin error
    #[fail(display = "A plugin error occured")]
    Plugin(#[cause] PluginError),

    /// An IRC error
    #[fail(display = "An IRC error occured")]
    Irc(#[cause] IrcError),

    /// Missing config error
    #[fail(display = "No config file was found")]
    MissingConfig,

    /// A reqwest error
    #[fail(display = "A reqwest error occured")]
    Reqwest(#[cause] ReqwestError),

    /// An I/O error
    #[fail(display = "An I/O error occured")]
    Io(#[cause] IoError),

    /// A UTF8 error
    #[fail(display = "A UTF8 error occured")]
    Utf8(#[cause] Utf8Error),

    /// An r2d2 error
    #[fail(display = "An r2d2 error occured")]
    R2d2(#[cause] R2d2Error),

    /// Reached download limit error
    #[fail(display = "Reached download limit of {} KiB", limit)]
    DownloadLimit { limit: usize },
}

/// Errors related to plugins
#[derive(Debug, Fail)]
pub enum PluginError {
    /// A Url error
    #[fail(display = "A Url error occured")]
    Url(#[cause] UrlError),

    /// A Factoids error
    #[fail(display = "{}", error)]
    Factoids { error: String },
}

/// A URL plugin error
#[derive(Debug, Fail)]
pub enum UrlError {
    /// Missing URL error
    #[fail(display = "No URL was found")]
    MissingUrl,

    /// Missing title error
    #[fail(display = "No title was found")]
    MissingTitle,
}

impl From<UrlError> for FrippyError {
    fn from(e: UrlError) -> FrippyError {
        FrippyError::Plugin(PluginError::Url(e))
    }
}

impl From<PluginError> for FrippyError {
    fn from(e: PluginError) -> FrippyError {
        FrippyError::Plugin(e)
    }
}

impl From<IrcError> for FrippyError {
    fn from(e: IrcError) -> FrippyError {
        FrippyError::Irc(e)
    }
}

impl From<ReqwestError> for FrippyError {
    fn from(e: ReqwestError) -> FrippyError {
        FrippyError::Reqwest(e)
    }
}

impl From<IoError> for FrippyError {
    fn from(e: IoError) -> FrippyError {
        FrippyError::Io(e)
    }
}

impl From<Utf8Error> for FrippyError {
    fn from(e: Utf8Error) -> FrippyError {
        FrippyError::Utf8(e)
    }
}

impl From<R2d2Error> for FrippyError {
    fn from(e: R2d2Error) -> FrippyError {
        FrippyError::R2d2(e)
    }
}
<|endoftext|>


<commit_before>use alloc::arc::Arc;
use alloc::BTreeMap;
use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};

use context;
use scheme::{self, SchemeId};
use sync::WaitQueue;
use syscall::data::Event;
use syscall::error::{Error, Result, EBADF, ESRCH};

int_like!(EventQueueId, AtomicEventQueueId, usize, AtomicUsize);

pub struct EventQueue {
    id: EventQueueId,
    queue: WaitQueue<Event>,
}

impl EventQueue {
    pub fn new(id: EventQueueId) -> EventQueue {
        EventQueue {
            id: id,
            queue: WaitQueue::new()
        }
    }

    pub fn dup(&self, other: &EventQueue) {
        panic!("EventQeuue::dup");
    }

    pub fn read(&self, events: &mut [Event]) -> Result<usize> {
        Ok(self.queue.receive_into(events, true))
    }

    pub fn write(&self, events: &[Event]) -> Result<usize> {
        for event in events {
            let file = {
                let contexts = context::contexts();
                let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
                let context = context_lock.read();
                let mut files = context.files.lock();
                match files.get(event.id).ok_or(Error::new(EBADF))? {
                    Some(file) => file.clone(),
                    None => return Err(Error::new(EBADF))
                }
            };

            let (scheme, number) = {
                let description = file.description.read();
                (description.scheme, description.number)
            };

            register(
                RegKey { scheme, number },
                QueueKey { queue: self.id, id: event.id, data: event.data },
                event.flags
            );

            send_flags(RegKey { scheme, number })?;
        }

        Ok(events.len())
    }
}

pub type EventQueueList = BTreeMap<EventQueueId, Arc<EventQueue>>;

// Next queue id
static NEXT_QUEUE_ID: AtomicUsize = ATOMIC_USIZE_INIT;

/// Get next queue id
pub fn next_queue_id() -> EventQueueId {
    EventQueueId::from(NEXT_QUEUE_ID.fetch_add(1, Ordering::SeqCst))
}

// Current event queues
static QUEUES: Once<RwLock<EventQueueList>> = Once::new();

/// Initialize queues, called if needed
fn init_queues() -> RwLock<EventQueueList> {
    RwLock::new(BTreeMap::new())
}

/// Get the event queues list, const
pub fn queues() -> RwLockReadGuard<'static, EventQueueList> {
    QUEUES.call_once(init_queues).read()
}

/// Get the event queues list, mutable
pub fn queues_mut() -> RwLockWriteGuard<'static, EventQueueList> {
    QUEUES.call_once(init_queues).write()
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct RegKey {
    pub scheme: SchemeId,
    pub number: usize,
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct QueueKey {
    pub queue: EventQueueId,
    pub id: usize,
    pub data: usize
}

type Registry = BTreeMap<RegKey, BTreeMap<QueueKey, usize>>;

static REGISTRY: Once<RwLock<Registry>> = Once::new();

/// Initialize registry, called if needed
fn init_registry() -> RwLock<Registry> {
    RwLock::new(Registry::new())
}

/// Get the global schemes list, const
fn registry() -> RwLockReadGuard<'static, Registry> {
    REGISTRY.call_once(init_registry).read()
}

/// Get the global schemes list, mutable
pub fn registry_mut() -> RwLockWriteGuard<'static, Registry> {
    REGISTRY.call_once(init_registry).write()
}

pub fn register(reg_key: RegKey, queue_key: QueueKey, flags: usize) {
    let mut registry = registry_mut();

    let entry = registry.entry(reg_key).or_insert_with(|| {
        BTreeMap::new()
    });

    if flags == 0 {
        entry.remove(&queue_key);
    } else {
        entry.insert(queue_key, flags);
    }
}

pub fn send_flags(reg_key: RegKey) -> Result<()> {
    let mut flags = 0;

    {
        let registry = registry();

        if let Some(queue_list) = registry.get(&reg_key) {
            for (_queue_key, queue_flags) in queue_list.iter() {
                flags |= queue_flags;
            }
        }
    }

    let event_id = {
        let scheme = {
            let schemes = scheme::schemes();
            let scheme = schemes.get(reg_key.scheme).ok_or(Error::new(EBADF))?;
            Arc::clone(&scheme)
        };

        scheme.fevent(reg_key.number, flags)?
    };

    if event_id != reg_key.number {
        let scheme_ns = {
            let contexts = context::contexts();
            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
            let context = context_lock.read();
            context.ens
        };

        let schemes = scheme::schemes();
        for (name, &scheme_id) in schemes.iter_name(scheme_ns) {
            if scheme_id == reg_key.scheme {
                println!("  {}", unsafe { ::core::str::from_utf8_unchecked(name) });
            }
        }

        panic!("schemes returned event id {} instead of {}", event_id, reg_key.number);
    }

    Ok(())
}

pub fn unregister_file(scheme: SchemeId, number: usize) {
    let mut registry = registry_mut();

    registry.remove(&RegKey { scheme, number });
}

//TODO: Implement unregister_queue
// pub fn unregister_queue(scheme: SchemeId, number: usize) {
//
// }

pub fn trigger(scheme: SchemeId, number: usize, flags: usize) {
    let registry = registry();

    if let Some(queue_list) = registry.get(&RegKey { scheme, number }) {
        for (queue_key, queue_flags) in queue_list.iter() {
            let common_flags = flags & queue_flags;
            if common_flags != 0 {
                let queues = queues();
                if let Some(queue) = queues.get(&queue_key.queue) {
                    queue.queue.send(Event {
                        id: queue_key.id,
                        flags: common_flags,
                        data: queue_key.data
                    });
                }
            }
        }
    }
}
<commit_msg>Reduce messages from fevent warning<commit_after>use alloc::arc::Arc;
use alloc::BTreeMap;
use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};

use context;
use scheme::{self, SchemeId};
use sync::WaitQueue;
use syscall::data::Event;
use syscall::error::{Error, Result, EBADF, ESRCH};

int_like!(EventQueueId, AtomicEventQueueId, usize, AtomicUsize);

pub struct EventQueue {
    id: EventQueueId,
    queue: WaitQueue<Event>,
}

impl EventQueue {
    pub fn new(id: EventQueueId) -> EventQueue {
        EventQueue {
            id: id,
            queue: WaitQueue::new()
        }
    }

    pub fn dup(&self, other: &EventQueue) {
        panic!("EventQeuue::dup");
    }

    pub fn read(&self, events: &mut [Event]) -> Result<usize> {
        Ok(self.queue.receive_into(events, true))
    }

    pub fn write(&self, events: &[Event]) -> Result<usize> {
        for event in events {
            let file = {
                let contexts = context::contexts();
                let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
                let context = context_lock.read();
                let mut files = context.files.lock();
                match files.get(event.id).ok_or(Error::new(EBADF))? {
                    Some(file) => file.clone(),
                    None => return Err(Error::new(EBADF))
                }
            };

            let (scheme, number) = {
                let description = file.description.read();
                (description.scheme, description.number)
            };

            register(
                RegKey { scheme, number },
                QueueKey { queue: self.id, id: event.id, data: event.data },
                event.flags
            );

            send_flags(RegKey { scheme, number })?;
        }

        Ok(events.len())
    }
}

pub type EventQueueList = BTreeMap<EventQueueId, Arc<EventQueue>>;

// Next queue id
static NEXT_QUEUE_ID: AtomicUsize = ATOMIC_USIZE_INIT;

/// Get next queue id
pub fn next_queue_id() -> EventQueueId {
    EventQueueId::from(NEXT_QUEUE_ID.fetch_add(1, Ordering::SeqCst))
}

// Current event queues
static QUEUES: Once<RwLock<EventQueueList>> = Once::new();

/// Initialize queues, called if needed
fn init_queues() -> RwLock<EventQueueList> {
    RwLock::new(BTreeMap::new())
}

/// Get the event queues list, const
pub fn queues() -> RwLockReadGuard<'static, EventQueueList> {
    QUEUES.call_once(init_queues).read()
}

/// Get the event queues list, mutable
pub fn queues_mut() -> RwLockWriteGuard<'static, EventQueueList> {
    QUEUES.call_once(init_queues).write()
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct RegKey {
    pub scheme: SchemeId,
    pub number: usize,
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct QueueKey {
    pub queue: EventQueueId,
    pub id: usize,
    pub data: usize
}

type Registry = BTreeMap<RegKey, BTreeMap<QueueKey, usize>>;

static REGISTRY: Once<RwLock<Registry>> = Once::new();

/// Initialize registry, called if needed
fn init_registry() -> RwLock<Registry> {
    RwLock::new(Registry::new())
}

/// Get the global schemes list, const
fn registry() -> RwLockReadGuard<'static, Registry> {
    REGISTRY.call_once(init_registry).read()
}

/// Get the global schemes list, mutable
pub fn registry_mut() -> RwLockWriteGuard<'static, Registry> {
    REGISTRY.call_once(init_registry).write()
}

pub fn register(reg_key: RegKey, queue_key: QueueKey, flags: usize) {
    let mut registry = registry_mut();

    let entry = registry.entry(reg_key).or_insert_with(|| {
        BTreeMap::new()
    });

    if flags == 0 {
        entry.remove(&queue_key);
    } else {
        entry.insert(queue_key, flags);
    }
}

pub fn send_flags(reg_key: RegKey) -> Result<()> {
    let mut flags = 0;

    {
        let registry = registry();

        if let Some(queue_list) = registry.get(&reg_key) {
            for (_queue_key, queue_flags) in queue_list.iter() {
                flags |= queue_flags;
            }
        }
    }

    let event_id = {
        let scheme = {
            let schemes = scheme::schemes();
            let scheme = schemes.get(reg_key.scheme).ok_or(Error::new(EBADF))?;
            Arc::clone(&scheme)
        };

        scheme.fevent(reg_key.number, flags)?
    };

    if event_id != reg_key.number {
        println!("scheme {} returned event id {} instead of {}", reg_key.scheme.into(), event_id, reg_key.number);
    }

    Ok(())
}

pub fn unregister_file(scheme: SchemeId, number: usize) {
    let mut registry = registry_mut();

    registry.remove(&RegKey { scheme, number });
}

//TODO: Implement unregister_queue
// pub fn unregister_queue(scheme: SchemeId, number: usize) {
//
// }

pub fn trigger(scheme: SchemeId, number: usize, flags: usize) {
    let registry = registry();

    if let Some(queue_list) = registry.get(&RegKey { scheme, number }) {
        for (queue_key, queue_flags) in queue_list.iter() {
            let common_flags = flags & queue_flags;
            if common_flags != 0 {
                let queues = queues();
                if let Some(queue) = queues.get(&queue_key.queue) {
                    queue.queue.send(Event {
                        id: queue_key.id,
                        flags: common_flags,
                        data: queue_key.data
                    });
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{cluster::Cluster, instance::Instance};
use admission_control_proto::proto::admission_control::{
    AdmissionControlClient, SubmitTransactionRequest,
};
use grpcio::{ChannelBuilder, EnvBuilder};
use std::{
    convert::TryFrom,
    slice,
    sync::Arc,
    thread,
    time::{Duration, Instant},
};

use admission_control_proto::{AdmissionControlStatus, SubmitTransactionResponse};
use failure::{
    self,
    prelude::{bail, format_err},
};
use generate_keypair::load_key_from_file;
use itertools::zip;
use libra_crypto::{
    ed25519::{Ed25519PrivateKey, Ed25519PublicKey},
    test_utils::KeyPair,
    traits::Uniform,
};
use libra_types::{
    account_address::AccountAddress,
    account_config::{association_address, get_account_resource_or_default},
    get_with_proof::ResponseItem,
    proto::types::{
        request_item::RequestedItems, GetAccountStateRequest, RequestItem,
        UpdateToLatestLedgerRequest,
    },
    transaction::{helpers::create_user_txn, Script, TransactionPayload},
};
use rand::{
    prelude::ThreadRng,
    rngs::{EntropyRng, StdRng},
    seq::SliceRandom,
    Rng, SeedableRng,
};
use slog_scope::{debug, info, warn};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::JoinHandle;

const MAX_TXN_BATCH_SIZE: usize = 100; // Max transactions per account in mempool

pub struct TxEmitter {
    accounts: Vec<AccountData>,
    faucet_account: AccountData,
}

pub struct EmitJob {
    workers: Vec<Worker>,
    stop: Arc<AtomicBool>,
}

#[derive(Clone)]
pub struct EmitThreadParams {
    pub wait_millis: u64,
    pub wait_committed: bool,
}

impl Default for EmitThreadParams {
    fn default() -> Self {
        Self {
            wait_millis: 50,
            wait_committed: true,
        }
    }
}

pub struct EmitJobRequest {
    pub instances: Vec<Instance>,
    pub accounts_per_client: usize,
    pub thread_params: EmitThreadParams,
}

impl TxEmitter {
    pub fn new(cluster: &Cluster) -> Self {
        let mint_client = Self::pick_mint_client(cluster.instances());
        let faucet_account = load_faucet_account(&mint_client, cluster.mint_file());
        Self {
            accounts: vec![],
            faucet_account,
        }
    }

    pub fn clear(&mut self) {
        self.accounts.clear();
    }

    fn pick_mint_client(instances: &[Instance]) -> AdmissionControlClient {
        let mut rng = ThreadRng::default();
        let mint_instance = instances
            .choose(&mut rng)
            .expect("Instances can not be empty");
        Self::make_client(&mint_instance)
    }

    pub fn start_job(&mut self, req: EmitJobRequest) -> failure::Result<EmitJob> {
        let num_clients = req.instances.len();
        let num_accounts = req.accounts_per_client * num_clients;
        let mut mint_client = Self::pick_mint_client(&req.instances);
        let mut mint_failures = 0;
        info!("Minting accounts");
        while self.accounts.len() < num_accounts {
            let mut accounts = gen_random_accounts(MAX_TXN_BATCH_SIZE);
            let mint_requests = gen_mint_txn_requests(&mut self.faucet_account, &accounts);
            if let Err(e) =
                execute_and_wait_transactions(&mint_client, &mut self.faucet_account, mint_requests)
            {
                mint_failures += 1;
                if mint_failures > 5 {
                    return Err(e);
                }
                warn!("Mint attempt {} failed, retrying", mint_failures);
                mint_client = Self::pick_mint_client(&req.instances);
                continue;
            }
            self.accounts.append(&mut accounts);
        }
        let all_accounts = self.accounts.split_off(self.accounts.len() - num_accounts);
        info!("Mint is done");
        let mut workers = vec![];
        let all_addresses: Vec<_> = all_accounts.iter().map(|d| d.address).collect();
        let all_addresses = Arc::new(all_addresses);
        let mut all_accounts = all_accounts.into_iter();
        let stop = Arc::new(AtomicBool::new(false));
        for instance in req.instances {
            let client = Self::make_client(&instance);
            let accounts = (&mut all_accounts).take(req.accounts_per_client).collect();
            let all_addresses = all_addresses.clone();
            let stop = stop.clone();
            let peer_id = instance.short_hash().clone();
            let params = req.thread_params.clone();
            let thread = SubmissionThread {
                accounts,
                instance,
                client,
                all_addresses,
                stop,
                params,
            };
            let join_handle = thread::Builder::new()
                .name(format!("txem-{}", peer_id))
                .spawn(move || thread.run())
                .unwrap();
            workers.push(Worker { join_handle });
            thread::sleep(Duration::from_millis(10)); // Small stagger between starting threads
        }
        Ok(EmitJob { workers, stop })
    }

    pub fn stop_job(&mut self, job: EmitJob) {
        job.stop.store(true, Ordering::Relaxed);
        for worker in job.workers {
            let mut accounts = worker
                .join_handle
                .join()
                .expect("TxEmitter worker thread failed");
            self.accounts.append(&mut accounts);
        }
    }

    fn make_client(instance: &Instance) -> AdmissionControlClient {
        let address = format!("{}:{}", instance.ip(), instance.ac_port());
        let env_builder = Arc::new(EnvBuilder::new().name_prefix("ac-grpc-").build());
        let ch = ChannelBuilder::new(env_builder).connect(&address);
        AdmissionControlClient::new(ch)
    }
}

struct Worker {
    join_handle: JoinHandle<Vec<AccountData>>,
}

struct SubmissionThread {
    accounts: Vec<AccountData>,
    instance: Instance,
    client: AdmissionControlClient,
    all_addresses: Arc<Vec<AccountAddress>>,
    stop: Arc<AtomicBool>,
    params: EmitThreadParams,
}

impl SubmissionThread {
    #[allow(clippy::collapsible_if)]
    fn run(mut self) -> Vec<AccountData> {
        let wait = Duration::from_millis(self.params.wait_millis);
        let mut rng = ThreadRng::default();
        while !self.stop.load(Ordering::Relaxed) {
            let requests = self.gen_requests(&mut rng);
            for request in requests {
                let wait_util = Instant::now() + wait;
                let resp = self.client.submit_transaction(&request);
                match resp {
                    Err(e) => {
                        info!("Failed to submit request to {}: {:?}", self.instance, e);
                    }
                    Ok(r) => {
                        let r = SubmitTransactionResponse::try_from(r)
                            .expect("Failed to parse SubmitTransactionResponse");
                        if !is_accepted(&r) {
                            info!("Request declined: {:?}", r);
                        }
                    }
                }
                let now = Instant::now();
                if wait_util > now {
                    thread::sleep(wait_util - now);
                } else {
                    debug!("Thread for {} won't sleep", self.instance);
                }
            }
            if self.params.wait_committed {
                if wait_for_accounts_sequence(&self.client, &mut self.accounts).is_err() {
                    info!(
                        "Some transactions was not committed before expiration {}",
                        self.instance
                    );
                }
            }
        }
        self.accounts
    }

    fn gen_requests(&mut self, rng: &mut ThreadRng) -> Vec<SubmitTransactionRequest> {
        let mut requests = Vec::with_capacity(self.accounts.len());
        let all_addresses = &self.all_addresses;
        for sender in &mut self.accounts {
            let receiver = all_addresses
                .choose(rng)
                .expect("all_addresses can't be empty");
            let request = gen_transfer_txn_request(sender, receiver, 1);
            requests.push(request);
        }
        requests
    }
}

fn wait_for_accounts_sequence(
    client: &AdmissionControlClient,
    accounts: &mut [AccountData],
) -> Result<(), ()> {
    let deadline = Instant::now() + TXN_MAX_WAIT;
    let addresses: Vec<_> = accounts.iter().map(|d| d.address).collect();
    loop {
        match query_sequence_numbers(client, &addresses) {
            Err(e) => info!("Failed to query ledger info: {:?}", e),
            Ok(sequence_numbers) => {
                if is_sequence_equal(accounts, &sequence_numbers) {
                    break;
                }
                if Instant::now() > deadline {
                    for (account, sequence_number) in zip(accounts, &sequence_numbers) {
                        account.sequence_number = *sequence_number;
                    }
                    return Err(());
                }
            }
        }
        thread::sleep(Duration::from_millis(100));
    }
    Ok(())
}

fn is_sequence_equal(accounts: &[AccountData], sequence_numbers: &[u64]) -> bool {
    for (account, sequence_number) in zip(accounts, sequence_numbers) {
        if *sequence_number != account.sequence_number {
            return false;
        }
    }
    true
}

fn query_sequence_numbers(
    client: &AdmissionControlClient,
    addresses: &[AccountAddress],
) -> failure::Result<Vec<u64>> {
    let mut update_request = UpdateToLatestLedgerRequest::default();
    for address in addresses {
        let mut request_item = RequestItem::default();
        let mut account_state_request = GetAccountStateRequest::default();
        account_state_request.address = address.to_vec();
        request_item.requested_items = Some(RequestedItems::GetAccountStateRequest(
            account_state_request,
        ));
        update_request.requested_items.push(request_item);
    }
    let resp = client
        .update_to_latest_ledger(&update_request)
        .map_err(|e| format_err!("update_to_latest_ledger failed: {:?} ", e))?;
    let mut result = Vec::with_capacity(resp.response_items.len());
    for item in resp.response_items.into_iter() {
        let item = ResponseItem::try_from(item)
            .map_err(|e| format_err!("ResponseItem::from_proto failed: {:?} ", e))?;
        if let ResponseItem::GetAccountState {
            account_state_with_proof,
        } = item
        {
            let account_resource = get_account_resource_or_default(&account_state_with_proof.blob)
                .map_err(|e| format_err!("get_account_resource_or_default failed: {:?} ", e))?;
            result.push(account_resource.sequence_number());
        } else {
            bail!(
                "Unexpected item in UpdateToLatestLedgerResponse: {:?}",
                item
            );
        }
    }
    Ok(result)
}

const MAX_GAS_AMOUNT: u64 = 1_000_000;
const GAS_UNIT_PRICE: u64 = 0;
const TXN_EXPIRATION_SECONDS: i64 = 50;
const TXN_MAX_WAIT: Duration = Duration::from_secs(TXN_EXPIRATION_SECONDS as u64 + 10);

fn gen_submit_transaction_request(
    script: Script,
    sender_account: &mut AccountData,
) -> SubmitTransactionRequest {
    let transaction = create_user_txn(
        &sender_account.key_pair,
        TransactionPayload::Script(script),
        sender_account.address,
        sender_account.sequence_number,
        MAX_GAS_AMOUNT,
        GAS_UNIT_PRICE,
        TXN_EXPIRATION_SECONDS,
    )
    .expect("Failed to create signed transaction");
    let mut req = SubmitTransactionRequest::default();
    req.transaction = Some(transaction.into());
    sender_account.sequence_number += 1;
    req
}

fn gen_transfer_txn_request(
    sender: &mut AccountData,
    receiver: &AccountAddress,
    num_coins: u64,
) -> SubmitTransactionRequest {
    let script = transaction_builder::encode_transfer_script(&receiver, num_coins);
    gen_submit_transaction_request(script, sender)
}

fn gen_random_account(rng: &mut StdRng) -> AccountData {
    let key_pair = KeyPair::generate_for_testing(rng);
    AccountData {
        address: AccountAddress::from_public_key(&key_pair.public_key),
        key_pair,
        sequence_number: 0,
    }
}

fn gen_random_accounts(num_accounts: usize) -> Vec<AccountData> {
    let seed: [u8; 32] = EntropyRng::new().gen();
    let mut rng = StdRng::from_seed(seed);
    (0..num_accounts)
        .map(|_| gen_random_account(&mut rng))
        .collect()
}

fn gen_mint_txn_request(
    faucet_account: &mut AccountData,
    receiver: &AccountAddress,
) -> SubmitTransactionRequest {
    let program = transaction_builder::encode_mint_script(receiver, 1_000_000);
    gen_submit_transaction_request(program, faucet_account)
}

fn gen_mint_txn_requests(
    faucet_account: &mut AccountData,
    accounts: &[AccountData],
) -> Vec<SubmitTransactionRequest> {
    accounts
        .iter()
        .map(|account| gen_mint_txn_request(faucet_account, &account.address))
        .collect()
}

fn execute_and_wait_transactions(
    client: &AdmissionControlClient,
    account: &mut AccountData,
    txn: Vec<SubmitTransactionRequest>,
) -> failure::Result<()> {
    for request in txn {
        let resp = client.submit_transaction(&request);
        match resp {
            Err(e) => info!("Failed to submit request: {:?}", e),
            Ok(r) => {
                let r = SubmitTransactionResponse::try_from(r)
                    .expect("Failed to parse SubmitTransactionResponse");
                if !is_accepted(&r) {
                    info!("Request declined: {:?}", r);
                }
            }
        }
    }
    wait_for_accounts_sequence(client, slice::from_mut(account))
        .map_err(|_| format_err!("Mint transactions was not committed before expiration"))
}

fn load_faucet_account(client: &AdmissionControlClient, faucet_account_path: &str) -> AccountData {
    let key_pair: KeyPair<Ed25519PrivateKey, Ed25519PublicKey> =
        load_key_from_file(faucet_account_path).expect("invalid faucet keypair file");
    let address = association_address();
    let sequence_number = query_sequence_numbers(client, &[address])
        .expect("query_sequence_numbers for faucet account failed")[0];
    AccountData {
        address,
        key_pair,
        sequence_number,
    }
}

struct AccountData {
    pub address: AccountAddress,
    pub key_pair: KeyPair<Ed25519PrivateKey, Ed25519PublicKey>,
    pub sequence_number: u64,
}

fn is_accepted(resp: &SubmitTransactionResponse) -> bool {
    if let Some(ref status) = resp.ac_status {
        return *status == AdmissionControlStatus::Accepted;
    }
    false
}
<commit_msg>[cluster-test] Debug info for mint failures<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{cluster::Cluster, instance::Instance};
use admission_control_proto::proto::admission_control::{
    AdmissionControlClient, SubmitTransactionRequest,
};
use grpcio::{ChannelBuilder, EnvBuilder};
use std::{
    convert::TryFrom,
    slice,
    sync::Arc,
    thread,
    time::{Duration, Instant},
};

use admission_control_proto::{AdmissionControlStatus, SubmitTransactionResponse};
use failure::{
    self,
    prelude::{bail, format_err},
};
use generate_keypair::load_key_from_file;
use itertools::zip;
use libra_crypto::{
    ed25519::{Ed25519PrivateKey, Ed25519PublicKey},
    test_utils::KeyPair,
    traits::Uniform,
};
use libra_types::{
    account_address::AccountAddress,
    account_config::{association_address, get_account_resource_or_default},
    get_with_proof::ResponseItem,
    proto::types::{
        request_item::RequestedItems, GetAccountStateRequest, RequestItem,
        UpdateToLatestLedgerRequest,
    },
    transaction::{helpers::create_user_txn, Script, TransactionPayload},
};
use rand::{
    prelude::ThreadRng,
    rngs::{EntropyRng, StdRng},
    seq::SliceRandom,
    Rng, SeedableRng,
};
use slog_scope::{debug, info, warn};
use std::env;
use std::fmt;
use std::ops::Deref;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::JoinHandle;

const MAX_TXN_BATCH_SIZE: usize = 100; // Max transactions per account in mempool

pub struct TxEmitter {
    accounts: Vec<AccountData>,
    faucet_account: AccountData,
}

pub struct EmitJob {
    workers: Vec<Worker>,
    stop: Arc<AtomicBool>,
}

#[derive(Clone)]
pub struct EmitThreadParams {
    pub wait_millis: u64,
    pub wait_committed: bool,
}

impl Default for EmitThreadParams {
    fn default() -> Self {
        Self {
            wait_millis: 50,
            wait_committed: true,
        }
    }
}

pub struct EmitJobRequest {
    pub instances: Vec<Instance>,
    pub accounts_per_client: usize,
    pub thread_params: EmitThreadParams,
}

impl TxEmitter {
    pub fn new(cluster: &Cluster) -> Self {
        let mint_client = Self::pick_mint_client(cluster.instances());
        let faucet_account = load_faucet_account(&mint_client, cluster.mint_file());
        Self {
            accounts: vec![],
            faucet_account,
        }
    }

    pub fn clear(&mut self) {
        self.accounts.clear();
    }

    fn pick_mint_client(instances: &[Instance]) -> NamedAdmissionControlClient {
        let mut rng = ThreadRng::default();
        let mint_instance = instances
            .choose(&mut rng)
            .expect("Instances can not be empty");
        NamedAdmissionControlClient(mint_instance.clone(), Self::make_client(mint_instance))
    }

    pub fn start_job(&mut self, req: EmitJobRequest) -> failure::Result<EmitJob> {
        let num_clients = req.instances.len();
        let num_accounts = req.accounts_per_client * num_clients;
        self.mint_accounts(&req, num_accounts)?;
        let all_accounts = self.accounts.split_off(self.accounts.len() - num_accounts);
        let mut workers = vec![];
        let all_addresses: Vec<_> = all_accounts.iter().map(|d| d.address).collect();
        let all_addresses = Arc::new(all_addresses);
        let mut all_accounts = all_accounts.into_iter();
        let stop = Arc::new(AtomicBool::new(false));
        for instance in req.instances {
            let client = Self::make_client(&instance);
            let accounts = (&mut all_accounts).take(req.accounts_per_client).collect();
            let all_addresses = all_addresses.clone();
            let stop = stop.clone();
            let peer_id = instance.short_hash().clone();
            let params = req.thread_params.clone();
            let thread = SubmissionThread {
                accounts,
                instance,
                client,
                all_addresses,
                stop,
                params,
            };
            let join_handle = thread::Builder::new()
                .name(format!("txem-{}", peer_id))
                .spawn(move || thread.run())
                .unwrap();
            workers.push(Worker { join_handle });
            thread::sleep(Duration::from_millis(10)); // Small stagger between starting threads
        }
        Ok(EmitJob { workers, stop })
    }

    fn mint_accounts(&mut self, req: &EmitJobRequest, num_accounts: usize) -> failure::Result<()> {
        if self.accounts.len() >= num_accounts {
            info!("Not minting accounts");
            return Ok(()); // Early return to skip printing 'Minting ...' logs
        }
        let mut mint_client = Self::pick_mint_client(&req.instances);
        let mut mint_failures = 0;
        info!("Minting accounts on {}", mint_client);
        let retry_mint = env::var_os("NO_MINT_RETRY").is_none();
        while self.accounts.len() < num_accounts {
            let mut accounts = gen_random_accounts(MAX_TXN_BATCH_SIZE);
            let mint_requests = gen_mint_txn_requests(&mut self.faucet_account, &accounts);
            if let Err(e) =
                execute_and_wait_transactions(&mint_client, &mut self.faucet_account, mint_requests)
            {
                mint_failures += 1;
                if retry_mint && mint_failures > 5 {
                    return Err(e);
                }
                mint_client = Self::pick_mint_client(&req.instances);
                warn!(
                    "Mint attempt {} failed, retrying on {}",
                    mint_failures, mint_client
                );
                continue;
            }
            self.accounts.append(&mut accounts);
        }
        info!("Mint is done");
        Ok(())
    }

    pub fn stop_job(&mut self, job: EmitJob) {
        job.stop.store(true, Ordering::Relaxed);
        for worker in job.workers {
            let mut accounts = worker
                .join_handle
                .join()
                .expect("TxEmitter worker thread failed");
            self.accounts.append(&mut accounts);
        }
    }

    fn make_client(instance: &Instance) -> AdmissionControlClient {
        let address = format!("{}:{}", instance.ip(), instance.ac_port());
        let env_builder = Arc::new(EnvBuilder::new().name_prefix("ac-grpc-").build());
        let ch = ChannelBuilder::new(env_builder).connect(&address);
        AdmissionControlClient::new(ch)
    }
}

struct Worker {
    join_handle: JoinHandle<Vec<AccountData>>,
}

struct SubmissionThread {
    accounts: Vec<AccountData>,
    instance: Instance,
    client: AdmissionControlClient,
    all_addresses: Arc<Vec<AccountAddress>>,
    stop: Arc<AtomicBool>,
    params: EmitThreadParams,
}

impl SubmissionThread {
    #[allow(clippy::collapsible_if)]
    fn run(mut self) -> Vec<AccountData> {
        let wait = Duration::from_millis(self.params.wait_millis);
        let mut rng = ThreadRng::default();
        while !self.stop.load(Ordering::Relaxed) {
            let requests = self.gen_requests(&mut rng);
            for request in requests {
                let wait_util = Instant::now() + wait;
                let resp = self.client.submit_transaction(&request);
                match resp {
                    Err(e) => {
                        info!("[{}] Failed to submit request: {:?}", self.instance, e);
                    }
                    Ok(r) => {
                        let r = SubmitTransactionResponse::try_from(r)
                            .expect("Failed to parse SubmitTransactionResponse");
                        if !is_accepted(&r) {
                            info!("[{}] Request declined: {:?}", self.instance, r);
                        }
                    }
                }
                let now = Instant::now();
                if wait_util > now {
                    thread::sleep(wait_util - now);
                } else {
                    debug!("[{}] Thread won't sleep", self.instance);
                }
            }
            if self.params.wait_committed {
                if wait_for_accounts_sequence(&self.client, &mut self.accounts).is_err() {
                    info!(
                        "[{}] Some transactions was not committed before expiration",
                        self.instance
                    );
                }
            }
        }
        self.accounts
    }

    fn gen_requests(&mut self, rng: &mut ThreadRng) -> Vec<SubmitTransactionRequest> {
        let mut requests = Vec::with_capacity(self.accounts.len());
        let all_addresses = &self.all_addresses;
        for sender in &mut self.accounts {
            let receiver = all_addresses
                .choose(rng)
                .expect("all_addresses can't be empty");
            let request = gen_transfer_txn_request(sender, receiver, 1);
            requests.push(request);
        }
        requests
    }
}

fn wait_for_accounts_sequence(
    client: &AdmissionControlClient,
    accounts: &mut [AccountData],
) -> Result<(), ()> {
    let deadline = Instant::now() + TXN_MAX_WAIT;
    let addresses: Vec<_> = accounts.iter().map(|d| d.address).collect();
    loop {
        match query_sequence_numbers(client, &addresses) {
            Err(e) => info!("Failed to query ledger info: {:?}", e),
            Ok(sequence_numbers) => {
                if is_sequence_equal(accounts, &sequence_numbers) {
                    break;
                }
                if Instant::now() > deadline {
                    for (account, sequence_number) in zip(accounts, &sequence_numbers) {
                        account.sequence_number = *sequence_number;
                    }
                    return Err(());
                }
            }
        }
        thread::sleep(Duration::from_millis(100));
    }
    Ok(())
}

fn is_sequence_equal(accounts: &[AccountData], sequence_numbers: &[u64]) -> bool {
    for (account, sequence_number) in zip(accounts, sequence_numbers) {
        if *sequence_number != account.sequence_number {
            return false;
        }
    }
    true
}

fn query_sequence_numbers(
    client: &AdmissionControlClient,
    addresses: &[AccountAddress],
) -> failure::Result<Vec<u64>> {
    let mut update_request = UpdateToLatestLedgerRequest::default();
    for address in addresses {
        let mut request_item = RequestItem::default();
        let mut account_state_request = GetAccountStateRequest::default();
        account_state_request.address = address.to_vec();
        request_item.requested_items = Some(RequestedItems::GetAccountStateRequest(
            account_state_request,
        ));
        update_request.requested_items.push(request_item);
    }
    let resp = client
        .update_to_latest_ledger(&update_request)
        .map_err(|e| format_err!("update_to_latest_ledger failed: {:?} ", e))?;
    let mut result = Vec::with_capacity(resp.response_items.len());
    for item in resp.response_items.into_iter() {
        let item = ResponseItem::try_from(item)
            .map_err(|e| format_err!("ResponseItem::from_proto failed: {:?} ", e))?;
        if let ResponseItem::GetAccountState {
            account_state_with_proof,
        } = item
        {
            let account_resource = get_account_resource_or_default(&account_state_with_proof.blob)
                .map_err(|e| format_err!("get_account_resource_or_default failed: {:?} ", e))?;
            result.push(account_resource.sequence_number());
        } else {
            bail!(
                "Unexpected item in UpdateToLatestLedgerResponse: {:?}",
                item
            );
        }
    }
    Ok(result)
}

const MAX_GAS_AMOUNT: u64 = 1_000_000;
const GAS_UNIT_PRICE: u64 = 0;
const TXN_EXPIRATION_SECONDS: i64 = 50;
const TXN_MAX_WAIT: Duration = Duration::from_secs(TXN_EXPIRATION_SECONDS as u64 + 10);

fn gen_submit_transaction_request(
    script: Script,
    sender_account: &mut AccountData,
) -> SubmitTransactionRequest {
    let transaction = create_user_txn(
        &sender_account.key_pair,
        TransactionPayload::Script(script),
        sender_account.address,
        sender_account.sequence_number,
        MAX_GAS_AMOUNT,
        GAS_UNIT_PRICE,
        TXN_EXPIRATION_SECONDS,
    )
    .expect("Failed to create signed transaction");
    let mut req = SubmitTransactionRequest::default();
    req.transaction = Some(transaction.into());
    sender_account.sequence_number += 1;
    req
}

fn gen_transfer_txn_request(
    sender: &mut AccountData,
    receiver: &AccountAddress,
    num_coins: u64,
) -> SubmitTransactionRequest {
    let script = transaction_builder::encode_transfer_script(&receiver, num_coins);
    gen_submit_transaction_request(script, sender)
}

fn gen_random_account(rng: &mut StdRng) -> AccountData {
    let key_pair = KeyPair::generate_for_testing(rng);
    AccountData {
        address: AccountAddress::from_public_key(&key_pair.public_key),
        key_pair,
        sequence_number: 0,
    }
}

fn gen_random_accounts(num_accounts: usize) -> Vec<AccountData> {
    let seed: [u8; 32] = EntropyRng::new().gen();
    let mut rng = StdRng::from_seed(seed);
    (0..num_accounts)
        .map(|_| gen_random_account(&mut rng))
        .collect()
}

fn gen_mint_txn_request(
    faucet_account: &mut AccountData,
    receiver: &AccountAddress,
) -> SubmitTransactionRequest {
    let program = transaction_builder::encode_mint_script(receiver, 1_000_000);
    gen_submit_transaction_request(program, faucet_account)
}

fn gen_mint_txn_requests(
    faucet_account: &mut AccountData,
    accounts: &[AccountData],
) -> Vec<SubmitTransactionRequest> {
    accounts
        .iter()
        .map(|account| gen_mint_txn_request(faucet_account, &account.address))
        .collect()
}

fn execute_and_wait_transactions(
    client: &NamedAdmissionControlClient,
    account: &mut AccountData,
    txn: Vec<SubmitTransactionRequest>,
) -> failure::Result<()> {
    debug!(
        "[{}] Submitting transactions {} - {} for {}",
        client,
        account.sequence_number - txn.len() as u64,
        account.sequence_number,
        account.address
    );
    for request in txn {
        let resp = client.submit_transaction(&request);
        match resp {
            Err(e) => info!("[{}] Failed to submit request: {:?}", client, e),
            Ok(r) => {
                let r = SubmitTransactionResponse::try_from(r)
                    .expect("Failed to parse SubmitTransactionResponse");
                if !is_accepted(&r) {
                    info!("[{}] Request declined: {:?}", client, r);
                }
            }
        }
    }
    let r = wait_for_accounts_sequence(client, slice::from_mut(account))
        .map_err(|_| format_err!("Mint transactions was not committed before expiration"));
    debug!(
        "[{}] Account {} is at sequence number {} now",
        client, account.address, account.sequence_number
    );
    r
}

fn load_faucet_account(client: &AdmissionControlClient, faucet_account_path: &str) -> AccountData {
    let key_pair: KeyPair<Ed25519PrivateKey, Ed25519PublicKey> =
        load_key_from_file(faucet_account_path).expect("invalid faucet keypair file");
    let address = association_address();
    let sequence_number = query_sequence_numbers(client, &[address])
        .expect("query_sequence_numbers for faucet account failed")[0];
    AccountData {
        address,
        key_pair,
        sequence_number,
    }
}

struct AccountData {
    pub address: AccountAddress,
    pub key_pair: KeyPair<Ed25519PrivateKey, Ed25519PublicKey>,
    pub sequence_number: u64,
}

fn is_accepted(resp: &SubmitTransactionResponse) -> bool {
    if let Some(ref status) = resp.ac_status {
        return *status == AdmissionControlStatus::Accepted;
    }
    false
}

struct NamedAdmissionControlClient(Instance, AdmissionControlClient);

impl fmt::Display for NamedAdmissionControlClient {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl Deref for NamedAdmissionControlClient {
    type Target = AdmissionControlClient;

    fn deref(&self) -> &Self::Target {
        &self.1
    }
}
<|endoftext|>