<commit_before>//! Database migrations

use error::Result as CratesfyiResult;
use postgres::{Connection, transaction::Transaction, Error as PostgresError};
use schemamama::{Migration, Migrator, Version};
use schemamama_postgres::{PostgresAdapter, PostgresMigration};
use std::borrow::Cow;

#[derive(Copy, Clone)]
enum ApplyMode {
    Permanent,
    #[cfg(test)]
    Temporary,
}

#[derive(Copy, Clone)]
struct MigrationContext {
    apply_mode: ApplyMode,
}

impl MigrationContext {
    fn format_query<'a>(&self, query: &'a str) -> Cow<'a, str> {
        match self.apply_mode {
            ApplyMode::Permanent => Cow::Borrowed(query),
            #[cfg(test)]
            ApplyMode::Temporary => {
                Cow::Owned(query.replace("CREATE TABLE", "CREATE TEMPORARY TABLE"))
            }
        }
    }
}

/// Creates a new PostgresMigration from upgrade and downgrade queries.
/// Downgrade query should return database to previous state.
///
/// Example:
///
/// ```
/// let my_migration = migration!(100,
///                               "Create test table",
///                               "CREATE TABLE test ( id SERIAL);",
///                               "DROP TABLE test;");
/// ```
macro_rules! migration {
    ($context:expr, $version:expr, $description:expr, $up:expr, $down:expr $(,)?) => {{
        struct Amigration {
            ctx: MigrationContext,
        };
        impl Migration for Amigration {
            fn version(&self) -> Version {
                $version
            }
            fn description(&self) -> String {
                $description.to_owned()
            }
        }
        impl PostgresMigration for Amigration {
            fn up(&self, transaction: &Transaction) -> Result<(), PostgresError> {
                info!("Applying migration {}: {}", self.version(), self.description());
                transaction.batch_execute(&self.ctx.format_query($up)).map(|_| ())
            }
            fn down(&self, transaction: &Transaction) -> Result<(), PostgresError> {
                info!("Removing migration {}: {}", self.version(), self.description());
                transaction.batch_execute(&self.ctx.format_query($down)).map(|_| ())
            }
        }
        Box::new(Amigration { ctx: $context })
    }};
}

pub fn migrate(version: Option<Version>, conn: &Connection) -> CratesfyiResult<()> {
    migrate_inner(version, conn, ApplyMode::Permanent)
}

#[cfg(test)]
pub fn migrate_temporary(version: Option<Version>, conn: &Connection) -> CratesfyiResult<()> {
    migrate_inner(version, conn, ApplyMode::Temporary)
}

fn migrate_inner(version: Option<Version>, conn: &Connection, apply_mode: ApplyMode) -> CratesfyiResult<()> {
    let context = MigrationContext { apply_mode };

    conn.execute(
        &context.format_query(
            "CREATE TABLE IF NOT EXISTS database_versions (version BIGINT PRIMARY KEY);"
        ),
        &[],
    )?;
    let adapter = PostgresAdapter::with_metadata_table(conn, "database_versions");

    let mut migrator = Migrator::new(adapter);

    let migrations: Vec<Box<dyn PostgresMigration>> = vec![
        migration!(
            context,
            // version
            1,
            // description
            "Initial database schema",
            // upgrade query
            "CREATE TABLE crates (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255) UNIQUE NOT NULL,
                 latest_version_id INT DEFAULT 0,
                 versions JSON DEFAULT '[]',
                 downloads_total INT DEFAULT 0,
                 github_description VARCHAR(1024),
                 github_stars INT DEFAULT 0,
                 github_forks INT DEFAULT 0,
                 github_issues INT DEFAULT 0,
                 github_last_commit TIMESTAMP,
                 github_last_update TIMESTAMP,
                 content tsvector
             );
             CREATE TABLE releases (
                 id SERIAL PRIMARY KEY,
                 crate_id INT NOT NULL REFERENCES crates(id),
                 version VARCHAR(100),
                 release_time TIMESTAMP,
                 dependencies JSON,
                 target_name VARCHAR(255),
                 yanked BOOL DEFAULT FALSE,
                 is_library BOOL DEFAULT TRUE,
                 build_status BOOL DEFAULT FALSE,
                 rustdoc_status BOOL DEFAULT FALSE,
                 test_status BOOL DEFAULT FALSE,
                 license VARCHAR(100),
                 repository_url VARCHAR(255),
                 homepage_url VARCHAR(255),
                 documentation_url VARCHAR(255),
                 description VARCHAR(1024),
                 description_long VARCHAR(51200),
                 readme VARCHAR(51200),
                 authors JSON,
                 keywords JSON,
                 have_examples BOOL DEFAULT FALSE,
                 downloads INT DEFAULT 0,
                 files JSON,
                 doc_targets JSON DEFAULT '[]',
                 doc_rustc_version VARCHAR(100) NOT NULL,
                 default_target VARCHAR(100),
                 UNIQUE (crate_id, version)
             );
             CREATE TABLE authors (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255),
                 email VARCHAR(255),
                 slug VARCHAR(255) UNIQUE NOT NULL
             );
             CREATE TABLE author_rels (
                 rid INT REFERENCES releases(id),
                 aid INT REFERENCES authors(id),
                 UNIQUE(rid, aid)
             );
             CREATE TABLE keywords (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255),
                 slug VARCHAR(255) NOT NULL UNIQUE
             );
             CREATE TABLE keyword_rels (
                 rid INT REFERENCES releases(id),
                 kid INT REFERENCES keywords(id),
                 UNIQUE(rid, kid)
             );
             CREATE TABLE owners (
                 id SERIAL PRIMARY KEY,
                 login VARCHAR(255) NOT NULL UNIQUE,
                 avatar VARCHAR(255),
                 name VARCHAR(255),
                 email VARCHAR(255)
             );
             CREATE TABLE owner_rels (
                 cid INT REFERENCES releases(id),
                 oid INT REFERENCES owners(id),
                 UNIQUE(cid, oid)
             );
             CREATE TABLE builds (
                 id SERIAL,
                 rid INT NOT NULL REFERENCES releases(id),
                 rustc_version VARCHAR(100) NOT NULL,
                 cratesfyi_version VARCHAR(100) NOT NULL,
                 build_status BOOL NOT NULL,
                 build_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 output TEXT
             );
             CREATE TABLE queue (
                 id SERIAL,
                 name VARCHAR(255),
                 version VARCHAR(100),
                 attempt INT DEFAULT 0,
                 date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 UNIQUE(name, version)
             );
             CREATE TABLE files (
                 path VARCHAR(4096) NOT NULL PRIMARY KEY,
                 mime VARCHAR(100) NOT NULL,
                 date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 date_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 content BYTEA
             );
             CREATE TABLE config (
                 name VARCHAR(100) NOT NULL PRIMARY KEY,
                 value JSON NOT NULL
             );
             CREATE INDEX ON releases (release_time DESC);
             CREATE INDEX content_idx ON crates USING gin(content);",
            // downgrade query
            "DROP TABLE authors, author_rels, keyword_rels, keywords, owner_rels,
                        owners, releases, crates, builds, queue, files, config;"
        ),
        migration!(
            context,
            // version
            2,
            // description
            "Added priority column to build queue",
            // upgrade query
            "ALTER TABLE queue ADD COLUMN priority INT DEFAULT 0;",
            // downgrade query
            "ALTER TABLE queue DROP COLUMN priority;"
        ),
        migration!(
            context,
            // version
            3,
            // description
            "Added sandbox_overrides table",
            // upgrade query
            "CREATE TABLE sandbox_overrides (
                 crate_name VARCHAR NOT NULL PRIMARY KEY,
                 max_memory_bytes INTEGER,
                 timeout_seconds INTEGER
             );",
            // downgrade query
            "DROP TABLE sandbox_overrides;"
        ),
        migration!(
            context,
            4,
            "Make more fields not null",
            "ALTER TABLE releases ALTER COLUMN release_time SET NOT NULL,
                                  ALTER COLUMN yanked SET NOT NULL,
                                  ALTER COLUMN downloads SET NOT NULL",
            "ALTER TABLE releases ALTER COLUMN release_time DROP NOT NULL,
                                  ALTER COLUMN yanked DROP NOT NULL,
                                  ALTER COLUMN downloads DROP NOT NULL"
        ),
        migration!(
            context,
            // version
            5,
            // description
            "Make target_name non-nullable",
            // upgrade query
            "ALTER TABLE releases ALTER COLUMN target_name SET NOT NULL",
            // downgrade query
            "ALTER TABLE releases ALTER COLUMN target_name DROP NOT NULL",
        ),
        migration!(
            context,
            // version
            6,
            // description
            "Added blacklisted_crates table",
            // upgrade query
            "CREATE TABLE blacklisted_crates (
                 crate_name VARCHAR NOT NULL PRIMARY KEY
             );",
            // downgrade query
            "DROP TABLE blacklisted_crates;"
        ),
        migration!(
            context,
            // version
            7,
            // description
            "Allow memory limits of more than 4 GB",
            // upgrade query
            "ALTER TABLE sandbox_overrides ALTER COLUMN max_memory_bytes TYPE BIGINT;",
            // downgrade query
            "ALTER TABLE sandbox_overrides ALTER COLUMN max_memory_bytes TYPE INTEGER;"
        ),
        migration!(
            context,
            // version
            8,
            // description
            "Make default_target non-nullable",
            // upgrade query
            "UPDATE releases SET default_target = 'x86_64-unknown-linux-gnu' WHERE default_target IS NULL;
             ALTER TABLE releases ALTER COLUMN default_target SET NOT NULL",
            // downgrade query
            "ALTER TABLE releases ALTER COLUMN default_target DROP NOT NULL;
             ALTER TABLE releases ALTER COLUMN default_target DROP DEFAULT",
        ),
    ];

    for migration in migrations {
        migrator.register(migration);
    }

    if let Some(version) = version {
        if version > migrator.current_version()?.unwrap_or(0) {
            migrator.up(Some(version))?;
        } else {
            migrator.down(Some(version))?;
        }
    } else {
        migrator.up(version)?;
    }

    Ok(())
}
<commit_msg>Add migration for limits<commit_after>//! Database migrations

use error::Result as CratesfyiResult;
use postgres::{Connection, transaction::Transaction, Error as PostgresError};
use schemamama::{Migration, Migrator, Version};
use schemamama_postgres::{PostgresAdapter, PostgresMigration};
use std::borrow::Cow;

#[derive(Copy, Clone)]
enum ApplyMode {
    Permanent,
    #[cfg(test)]
    Temporary,
}

#[derive(Copy, Clone)]
struct MigrationContext {
    apply_mode: ApplyMode,
}

impl MigrationContext {
    fn format_query<'a>(&self, query: &'a str) -> Cow<'a, str> {
        match self.apply_mode {
            ApplyMode::Permanent => Cow::Borrowed(query),
            #[cfg(test)]
            ApplyMode::Temporary => {
                Cow::Owned(query.replace("CREATE TABLE", "CREATE TEMPORARY TABLE"))
            }
        }
    }
}

/// Creates a new PostgresMigration from upgrade and downgrade queries.
/// Downgrade query should return database to previous state.
///
/// Example:
///
/// ```
/// let my_migration = migration!(100,
///                               "Create test table",
///                               "CREATE TABLE test ( id SERIAL);",
///                               "DROP TABLE test;");
/// ```
macro_rules! migration {
    ($context:expr, $version:expr, $description:expr, $up:expr, $down:expr $(,)?) => {{
        struct Amigration {
            ctx: MigrationContext,
        };
        impl Migration for Amigration {
            fn version(&self) -> Version {
                $version
            }
            fn description(&self) -> String {
                $description.to_owned()
            }
        }
        impl PostgresMigration for Amigration {
            fn up(&self, transaction: &Transaction) -> Result<(), PostgresError> {
                info!("Applying migration {}: {}", self.version(), self.description());
                transaction.batch_execute(&self.ctx.format_query($up)).map(|_| ())
            }
            fn down(&self, transaction: &Transaction) -> Result<(), PostgresError> {
                info!("Removing migration {}: {}", self.version(), self.description());
                transaction.batch_execute(&self.ctx.format_query($down)).map(|_| ())
            }
        }
        Box::new(Amigration { ctx: $context })
    }};
}

pub fn migrate(version: Option<Version>, conn: &Connection) -> CratesfyiResult<()> {
    migrate_inner(version, conn, ApplyMode::Permanent)
}

#[cfg(test)]
pub fn migrate_temporary(version: Option<Version>, conn: &Connection) -> CratesfyiResult<()> {
    migrate_inner(version, conn, ApplyMode::Temporary)
}

fn migrate_inner(version: Option<Version>, conn: &Connection, apply_mode: ApplyMode) -> CratesfyiResult<()> {
    let context = MigrationContext { apply_mode };

    conn.execute(
        &context.format_query(
            "CREATE TABLE IF NOT EXISTS database_versions (version BIGINT PRIMARY KEY);"
        ),
        &[],
    )?;
    let adapter = PostgresAdapter::with_metadata_table(conn, "database_versions");

    let mut migrator = Migrator::new(adapter);

    let migrations: Vec<Box<dyn PostgresMigration>> = vec![
        migration!(
            context,
            // version
            1,
            // description
            "Initial database schema",
            // upgrade query
            "CREATE TABLE crates (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255) UNIQUE NOT NULL,
                 latest_version_id INT DEFAULT 0,
                 versions JSON DEFAULT '[]',
                 downloads_total INT DEFAULT 0,
                 github_description VARCHAR(1024),
                 github_stars INT DEFAULT 0,
                 github_forks INT DEFAULT 0,
                 github_issues INT DEFAULT 0,
                 github_last_commit TIMESTAMP,
                 github_last_update TIMESTAMP,
                 content tsvector
             );
             CREATE TABLE releases (
                 id SERIAL PRIMARY KEY,
                 crate_id INT NOT NULL REFERENCES crates(id),
                 version VARCHAR(100),
                 release_time TIMESTAMP,
                 dependencies JSON,
                 target_name VARCHAR(255),
                 yanked BOOL DEFAULT FALSE,
                 is_library BOOL DEFAULT TRUE,
                 build_status BOOL DEFAULT FALSE,
                 rustdoc_status BOOL DEFAULT FALSE,
                 test_status BOOL DEFAULT FALSE,
                 license VARCHAR(100),
                 repository_url VARCHAR(255),
                 homepage_url VARCHAR(255),
                 documentation_url VARCHAR(255),
                 description VARCHAR(1024),
                 description_long VARCHAR(51200),
                 readme VARCHAR(51200),
                 authors JSON,
                 keywords JSON,
                 have_examples BOOL DEFAULT FALSE,
                 downloads INT DEFAULT 0,
                 files JSON,
                 doc_targets JSON DEFAULT '[]',
                 doc_rustc_version VARCHAR(100) NOT NULL,
                 default_target VARCHAR(100),
                 UNIQUE (crate_id, version)
             );
             CREATE TABLE authors (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255),
                 email VARCHAR(255),
                 slug VARCHAR(255) UNIQUE NOT NULL
             );
             CREATE TABLE author_rels (
                 rid INT REFERENCES releases(id),
                 aid INT REFERENCES authors(id),
                 UNIQUE(rid, aid)
             );
             CREATE TABLE keywords (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255),
                 slug VARCHAR(255) NOT NULL UNIQUE
             );
             CREATE TABLE keyword_rels (
                 rid INT REFERENCES releases(id),
                 kid INT REFERENCES keywords(id),
                 UNIQUE(rid, kid)
             );
             CREATE TABLE owners (
                 id SERIAL PRIMARY KEY,
                 login VARCHAR(255) NOT NULL UNIQUE,
                 avatar VARCHAR(255),
                 name VARCHAR(255),
                 email VARCHAR(255)
             );
             CREATE TABLE owner_rels (
                 cid INT REFERENCES releases(id),
                 oid INT REFERENCES owners(id),
                 UNIQUE(cid, oid)
             );
             CREATE TABLE builds (
                 id SERIAL,
                 rid INT NOT NULL REFERENCES releases(id),
                 rustc_version VARCHAR(100) NOT NULL,
                 cratesfyi_version VARCHAR(100) NOT NULL,
                 build_status BOOL NOT NULL,
                 build_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 output TEXT
             );
             CREATE TABLE queue (
                 id SERIAL,
                 name VARCHAR(255),
                 version VARCHAR(100),
                 attempt INT DEFAULT 0,
                 date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 UNIQUE(name, version)
             );
             CREATE TABLE files (
                 path VARCHAR(4096) NOT NULL PRIMARY KEY,
                 mime VARCHAR(100) NOT NULL,
                 date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 date_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 content BYTEA
             );
             CREATE TABLE config (
                 name VARCHAR(100) NOT NULL PRIMARY KEY,
                 value JSON NOT NULL
             );
             CREATE INDEX ON releases (release_time DESC);
             CREATE INDEX content_idx ON crates USING gin(content);",
            // downgrade query
            "DROP TABLE authors, author_rels, keyword_rels, keywords, owner_rels,
                        owners, releases, crates, builds, queue, files, config;"
        ),
        migration!(
            context,
            // version
            2,
            // description
            "Added priority column to build queue",
            // upgrade query
            "ALTER TABLE queue ADD COLUMN priority INT DEFAULT 0;",
            // downgrade query
            "ALTER TABLE queue DROP COLUMN priority;"
        ),
        migration!(
            context,
            // version
            3,
            // description
            "Added sandbox_overrides table",
            // upgrade query
            "CREATE TABLE sandbox_overrides (
                 crate_name VARCHAR NOT NULL PRIMARY KEY,
                 max_memory_bytes INTEGER,
                 timeout_seconds INTEGER
             );",
            // downgrade query
            "DROP TABLE sandbox_overrides;"
        ),
        migration!(
            context,
            4,
            "Make more fields not null",
            "ALTER TABLE releases ALTER COLUMN release_time SET NOT NULL,
                                  ALTER COLUMN yanked SET NOT NULL,
                                  ALTER COLUMN downloads SET NOT NULL",
            "ALTER TABLE releases ALTER COLUMN release_time DROP NOT NULL,
                                  ALTER COLUMN yanked DROP NOT NULL,
                                  ALTER COLUMN downloads DROP NOT NULL"
        ),
        migration!(
            context,
            // version
            5,
            // description
            "Make target_name non-nullable",
            // upgrade query
            "ALTER TABLE releases ALTER COLUMN target_name SET NOT NULL",
            // downgrade query
            "ALTER TABLE releases ALTER COLUMN target_name DROP NOT NULL",
        ),
        migration!(
            context,
            // version
            6,
            // description
            "Added blacklisted_crates table",
            // upgrade query
            "CREATE TABLE blacklisted_crates (
                 crate_name VARCHAR NOT NULL PRIMARY KEY
             );",
            // downgrade query
            "DROP TABLE blacklisted_crates;"
        ),
        migration!(
            context,
            // version
            7,
            // description
            "Allow memory limits of more than 4 GB",
            // upgrade query
            "ALTER TABLE sandbox_overrides ALTER COLUMN max_memory_bytes TYPE BIGINT;",
            // downgrade query
            "ALTER TABLE sandbox_overrides ALTER COLUMN max_memory_bytes TYPE INTEGER;"
        ),
        migration!(
            context,
            // version
            8,
            // description
            "Make default_target non-nullable",
            // upgrade query
            "UPDATE releases SET default_target = 'x86_64-unknown-linux-gnu' WHERE default_target IS NULL;
             ALTER TABLE releases ALTER COLUMN default_target SET NOT NULL",
            // downgrade query
            "ALTER TABLE releases ALTER COLUMN default_target DROP NOT NULL;
             ALTER TABLE releases ALTER COLUMN default_target DROP DEFAULT",
        ),
        migration!(
            context,
            // version
            9,
            // description
            "Allow max number of targets to be overriden",
            // upgrade query
            "ALTER TABLE sandbox_overrides ADD COLUMN targets INT;",
            // downgrade query
            "ALTER TABLE sandbox_overrides DROP COLUMN targets;"
        ),
    ];

    for migration in migrations {
        migrator.register(migration);
    }

    if let Some(version) = version {
        if version > migrator.current_version()?.unwrap_or(0) {
            migrator.up(Some(version))?;
        } else {
            migrator.down(Some(version))?;
        }
    } else {
        migrator.up(version)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>use hyper::status::StatusCode;
use rustc_serialize::base64::FromBase64Error;
use rustc_serialize::json::{Json, ToJson, ParserError, DecoderError};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::convert::From;
use std::error::Error;
use std::fmt;
use std::io::Error as IoError;

#[derive(PartialEq, Debug)]
pub enum ErrorStatus {
    ElementNotSelectable,
    ElementNotVisible,
    InsecureCertificate,
    InvalidArgument,
    InvalidCookieDomain,
    InvalidElementCoordinates,
    InvalidElementState,
    InvalidSelector,
    InvalidSessionId,
    JavascriptError,
    MoveTargetOutOfBounds,
    NoSuchAlert,
    NoSuchElement,
    NoSuchFrame,
    NoSuchWindow,
    ScriptTimeout,
    SessionNotCreated,
    StaleElementReference,
    Timeout,
    UnableToSetCookie,
    UnexpectedAlertOpen,
    UnknownError,
    UnknownMethod,
    UnknownPath,
    UnsupportedOperation,
}

impl ErrorStatus {
    pub fn status_code(&self) -> &'static str {
        match self {
            &ErrorStatus::ElementNotSelectable => "element not selectable",
            &ErrorStatus::ElementNotVisible => "element not visible",
            &ErrorStatus::InsecureCertificate => "insecure certificate",
            &ErrorStatus::InvalidArgument => "invalid argument",
            &ErrorStatus::InvalidCookieDomain => "invalid cookie domain",
            &ErrorStatus::InvalidElementCoordinates => "invalid element coordinates",
            &ErrorStatus::InvalidElementState => "invalid element state",
            &ErrorStatus::InvalidSelector => "invalid selector",
            &ErrorStatus::InvalidSessionId => "invalid session id",
            &ErrorStatus::JavascriptError => "javascript error",
            &ErrorStatus::MoveTargetOutOfBounds => "move target out of bounds",
            &ErrorStatus::NoSuchAlert => "no such alert",
            &ErrorStatus::NoSuchElement => "no such element",
            &ErrorStatus::NoSuchFrame => "no such frame",
            &ErrorStatus::NoSuchWindow => "no such window",
            &ErrorStatus::ScriptTimeout => "script timeout",
            &ErrorStatus::SessionNotCreated => "session not created",
            &ErrorStatus::StaleElementReference => "stale element reference",
            &ErrorStatus::Timeout => "timeout",
            &ErrorStatus::UnableToSetCookie => "unable to set cookie",
            &ErrorStatus::UnexpectedAlertOpen => "unexpected alert open",
            &ErrorStatus::UnknownError => "unknown error",
            &ErrorStatus::UnknownMethod => "unknown command",
            &ErrorStatus::UnknownPath => "unknown command",
            &ErrorStatus::UnsupportedOperation => "unsupported operation",
        }
    }

    pub fn http_status(&self) -> StatusCode {
        match self {
            &ErrorStatus::ElementNotSelectable => StatusCode::BadRequest,
            &ErrorStatus::ElementNotVisible => StatusCode::BadRequest,
            &ErrorStatus::InsecureCertificate => StatusCode::BadRequest,
            &ErrorStatus::InvalidArgument => StatusCode::BadRequest,
            &ErrorStatus::InvalidCookieDomain => StatusCode::BadRequest,
            &ErrorStatus::InvalidElementCoordinates => StatusCode::BadRequest,
            &ErrorStatus::InvalidElementState => StatusCode::BadRequest,
            &ErrorStatus::InvalidSelector => StatusCode::BadRequest,
            &ErrorStatus::InvalidSessionId => StatusCode::NotFound,
            &ErrorStatus::JavascriptError => StatusCode::InternalServerError,
            &ErrorStatus::MoveTargetOutOfBounds => StatusCode::InternalServerError,
            &ErrorStatus::NoSuchAlert => StatusCode::BadRequest,
            &ErrorStatus::NoSuchElement => StatusCode::NotFound,
            &ErrorStatus::NoSuchFrame => StatusCode::BadRequest,
            &ErrorStatus::NoSuchWindow => StatusCode::BadRequest,
            &ErrorStatus::ScriptTimeout => StatusCode::RequestTimeout,
            &ErrorStatus::SessionNotCreated => StatusCode::InternalServerError,
            &ErrorStatus::StaleElementReference => StatusCode::BadRequest,
            &ErrorStatus::Timeout => StatusCode::RequestTimeout,
            &ErrorStatus::UnableToSetCookie => StatusCode::InternalServerError,
            &ErrorStatus::UnexpectedAlertOpen => StatusCode::InternalServerError,
            &ErrorStatus::UnknownError => StatusCode::InternalServerError,
            &ErrorStatus::UnknownMethod => StatusCode::MethodNotAllowed,
            &ErrorStatus::UnknownPath => StatusCode::NotFound,
            &ErrorStatus::UnsupportedOperation => StatusCode::InternalServerError,
        }
    }
}

pub type WebDriverResult<T> = Result<T, WebDriverError>;

#[derive(Debug)]
pub struct WebDriverError {
    pub error: ErrorStatus,
    pub message: Cow<'static, str>,
    delete_session: bool
}

impl fmt::Display for WebDriverError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.message.fmt(f)
    }
}

impl WebDriverError {
    pub fn new<S>(error: ErrorStatus, message: S) -> WebDriverError
        where S: Into<Cow<'static, str>>
    {
        WebDriverError {
            error: error,
            message: message.into(),
            delete_session: false
        }
    }

    pub fn status_code(&self) -> &'static str {
        self.error.status_code()
    }

    pub fn http_status(&self) -> StatusCode {
        self.error.http_status()
    }

    pub fn to_json_string(&self) -> String {
        self.to_json().to_string()
    }

    pub fn set_delete_session(&mut self) {
        self.delete_session = true
    }

    pub fn delete_session(&self) -> bool {
        self.delete_session
    }
}

impl ToJson for WebDriverError {
    fn to_json(&self) -> Json {
        let mut data = BTreeMap::new();
        data.insert("error".to_string(), self.status_code().to_json());
        data.insert("message".to_string(), self.message.to_json());
        Json::Object(data)
    }
}

impl Error for WebDriverError {
    fn description(&self) -> &str {
        self.status_code()
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl From<ParserError> for WebDriverError {
    fn from(err: ParserError) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError,
            err.description().to_string())
    }
}

impl From<IoError> for WebDriverError {
    fn from(err: IoError) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError,
            err.description().to_string())
    }
}

impl From<DecoderError> for WebDriverError {
    fn from(err: DecoderError) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError,
                            err.description().to_string())
    }
}

impl From<FromBase64Error> for WebDriverError {
    fn from(err: FromBase64Error) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError,
                            err.description().to_string())
    }
}

impl From<Box<Error>> for WebDriverError {
    fn from(err: Box<Error>) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError,
                            err.description().to_string())
    }
}
<commit_msg>error: lint From<T> implementations of WebDriverError<commit_after>use hyper::status::StatusCode;
use rustc_serialize::base64::FromBase64Error;
use rustc_serialize::json::{Json, ToJson, ParserError, DecoderError};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::convert::From;
use std::error::Error;
use std::fmt;
use std::io::Error as IoError;

#[derive(PartialEq, Debug)]
pub enum ErrorStatus {
    ElementNotSelectable,
    ElementNotVisible,
    InsecureCertificate,
    InvalidArgument,
    InvalidCookieDomain,
    InvalidElementCoordinates,
    InvalidElementState,
    InvalidSelector,
    InvalidSessionId,
    JavascriptError,
    MoveTargetOutOfBounds,
    NoSuchAlert,
    NoSuchElement,
    NoSuchFrame,
    NoSuchWindow,
    ScriptTimeout,
    SessionNotCreated,
    StaleElementReference,
    Timeout,
    UnableToSetCookie,
    UnexpectedAlertOpen,
    UnknownError,
    UnknownMethod,
    UnknownPath,
    UnsupportedOperation,
}

impl ErrorStatus {
    pub fn status_code(&self) -> &'static str {
        match self {
            &ErrorStatus::ElementNotSelectable => "element not selectable",
            &ErrorStatus::ElementNotVisible => "element not visible",
            &ErrorStatus::InsecureCertificate => "insecure certificate",
            &ErrorStatus::InvalidArgument => "invalid argument",
            &ErrorStatus::InvalidCookieDomain => "invalid cookie domain",
            &ErrorStatus::InvalidElementCoordinates => "invalid element coordinates",
            &ErrorStatus::InvalidElementState => "invalid element state",
            &ErrorStatus::InvalidSelector => "invalid selector",
            &ErrorStatus::InvalidSessionId => "invalid session id",
            &ErrorStatus::JavascriptError => "javascript error",
            &ErrorStatus::MoveTargetOutOfBounds => "move target out of bounds",
            &ErrorStatus::NoSuchAlert => "no such alert",
            &ErrorStatus::NoSuchElement => "no such element",
            &ErrorStatus::NoSuchFrame => "no such frame",
            &ErrorStatus::NoSuchWindow => "no such window",
            &ErrorStatus::ScriptTimeout => "script timeout",
            &ErrorStatus::SessionNotCreated => "session not created",
            &ErrorStatus::StaleElementReference => "stale element reference",
            &ErrorStatus::Timeout => "timeout",
            &ErrorStatus::UnableToSetCookie => "unable to set cookie",
            &ErrorStatus::UnexpectedAlertOpen => "unexpected alert open",
            &ErrorStatus::UnknownError => "unknown error",
            &ErrorStatus::UnknownMethod => "unknown command",
            &ErrorStatus::UnknownPath => "unknown command",
            &ErrorStatus::UnsupportedOperation => "unsupported operation",
        }
    }

    pub fn http_status(&self) -> StatusCode {
        match self {
            &ErrorStatus::ElementNotSelectable => StatusCode::BadRequest,
            &ErrorStatus::ElementNotVisible => StatusCode::BadRequest,
            &ErrorStatus::InsecureCertificate => StatusCode::BadRequest,
            &ErrorStatus::InvalidArgument => StatusCode::BadRequest,
            &ErrorStatus::InvalidCookieDomain => StatusCode::BadRequest,
            &ErrorStatus::InvalidElementCoordinates => StatusCode::BadRequest,
            &ErrorStatus::InvalidElementState => StatusCode::BadRequest,
            &ErrorStatus::InvalidSelector => StatusCode::BadRequest,
            &ErrorStatus::InvalidSessionId => StatusCode::NotFound,
            &ErrorStatus::JavascriptError => StatusCode::InternalServerError,
            &ErrorStatus::MoveTargetOutOfBounds => StatusCode::InternalServerError,
            &ErrorStatus::NoSuchAlert => StatusCode::BadRequest,
            &ErrorStatus::NoSuchElement => StatusCode::NotFound,
            &ErrorStatus::NoSuchFrame => StatusCode::BadRequest,
            &ErrorStatus::NoSuchWindow => StatusCode::BadRequest,
            &ErrorStatus::ScriptTimeout => StatusCode::RequestTimeout,
            &ErrorStatus::SessionNotCreated => StatusCode::InternalServerError,
            &ErrorStatus::StaleElementReference => StatusCode::BadRequest,
            &ErrorStatus::Timeout => StatusCode::RequestTimeout,
            &ErrorStatus::UnableToSetCookie => StatusCode::InternalServerError,
            &ErrorStatus::UnexpectedAlertOpen => StatusCode::InternalServerError,
            &ErrorStatus::UnknownError => StatusCode::InternalServerError,
            &ErrorStatus::UnknownMethod => StatusCode::MethodNotAllowed,
            &ErrorStatus::UnknownPath => StatusCode::NotFound,
            &ErrorStatus::UnsupportedOperation => StatusCode::InternalServerError,
        }
    }
}

pub type WebDriverResult<T> = Result<T, WebDriverError>;

#[derive(Debug)]
pub struct WebDriverError {
    pub error: ErrorStatus,
    pub message: Cow<'static, str>,
    delete_session: bool
}

impl fmt::Display for WebDriverError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.message.fmt(f)
    }
}

impl WebDriverError {
    pub fn new<S>(error: ErrorStatus, message: S) -> WebDriverError
        where S: Into<Cow<'static, str>>
    {
        WebDriverError {
            error: error,
            message: message.into(),
            delete_session: false
        }
    }

    pub fn status_code(&self) -> &'static str {
        self.error.status_code()
    }

    pub fn http_status(&self) -> StatusCode {
        self.error.http_status()
    }

    pub fn to_json_string(&self) -> String {
        self.to_json().to_string()
    }

    pub fn set_delete_session(&mut self) {
        self.delete_session = true
    }

    pub fn delete_session(&self) -> bool {
        self.delete_session
    }
}

impl ToJson for WebDriverError {
    fn to_json(&self) -> Json {
        let mut data = BTreeMap::new();
        data.insert("error".to_string(), self.status_code().to_json());
        data.insert("message".to_string(), self.message.to_json());
        Json::Object(data)
    }
}

impl Error for WebDriverError {
    fn description(&self) -> &str {
        self.status_code()
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl From<ParserError> for WebDriverError {
    fn from(err: ParserError) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
    }
}

impl From<IoError> for WebDriverError {
    fn from(err: IoError) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
    }
}

impl From<DecoderError> for WebDriverError {
    fn from(err: DecoderError) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
    }
}

impl From<FromBase64Error> for WebDriverError {
    fn from(err: FromBase64Error) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
    }
}

impl From<Box<Error>> for WebDriverError {
    fn from(err: Box<Error>) -> WebDriverError {
        WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
    }
}
<|endoftext|>


<commit_before>#[cfg(not(feature = "std"))]
use core2::io::Error as IoError;
#[cfg(feature = "std")]
use std::io::Error as IoError;

use unsigned_varint::decode::Error as DecodeError;
#[cfg(feature = "std")]
use unsigned_varint::io::ReadError;

/// Multihash error.
#[derive(Debug)]
pub enum Error {
    /// Io error.
    Io(IoError),
    /// Unsupported multihash code.
    UnsupportedCode(u64),
    /// Invalid multihash size.
    InvalidSize(u64),
    /// Invalid varint.
    Varint(DecodeError),
}

impl core::fmt::Display for Error {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        match self {
            //#[cfg(feature = "std")]
            Self::Io(err) => write!(f, "{}", err),
            Self::UnsupportedCode(code) => write!(f, "Unsupported multihash code {}.", code),
            Self::InvalidSize(size) => write!(f, "Invalid multihash size {}.", size),
            Self::Varint(err) => write!(f, "{}", err),
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}

#[cfg(not(feature = "std"))]
impl core2::error::Error for Error {}

#[cfg(not(feature = "std"))]
impl From<core2::io::Error> for Error {
    fn from(err: core2::io::Error) -> Self {
        Self::Io(err)
    }
}

#[cfg(feature = "std")]
impl From<IoError> for Error {
    fn from(err: IoError) -> Self {
        Self::Io(err)
    }
}

#[cfg(feature = "std")]
impl From<ReadError> for Error {
    fn from(err: ReadError) -> Self {
        match err {
            ReadError::Io(err) => Self::Io(err),
            ReadError::Decode(err) => Self::Varint(err),
            _ => unreachable!(),
        }
    }
}

/// Multihash result.
pub type Result<T> = core::result::Result<T, Error>;
<commit_msg>simplify core2 imports<commit_after>#[cfg(not(feature = "std"))]
use core2::{error::Error as StdError, io::Error as IoError};
#[cfg(feature = "std")]
use std::{error::Error as StdError, io::Error as IoError};

use unsigned_varint::decode::Error as DecodeError;
#[cfg(feature = "std")]
use unsigned_varint::io::ReadError;

/// Multihash error.
#[derive(Debug)]
pub enum Error {
    /// Io error.
    Io(IoError),
    /// Unsupported multihash code.
    UnsupportedCode(u64),
    /// Invalid multihash size.
    InvalidSize(u64),
    /// Invalid varint.
    Varint(DecodeError),
}

impl core::fmt::Display for Error {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        match self {
            //#[cfg(feature = "std")]
            Self::Io(err) => write!(f, "{}", err),
            Self::UnsupportedCode(code) => write!(f, "Unsupported multihash code {}.", code),
            Self::InvalidSize(size) => write!(f, "Invalid multihash size {}.", size),
            Self::Varint(err) => write!(f, "{}", err),
        }
    }
}

impl StdError for Error {}

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

#[cfg(feature = "std")]
impl From<ReadError> for Error {
    fn from(err: ReadError) -> Self {
        match err {
            ReadError::Io(err) => Self::Io(err),
            ReadError::Decode(err) => Self::Varint(err),
            _ => unreachable!(),
        }
    }
}

/// Multihash result.
pub type Result<T> = core::result::Result<T, Error>;
<|endoftext|>


<commit_before>use crate::path::Path;
use serde::{de, ser};
use std::error;
use std::fmt::{self, Debug, Display};
use std::io;
use std::result;
use std::str;
use std::string;
use std::sync::Arc;
use yaml_rust::emitter;
use yaml_rust::scanner::{self, Marker, ScanError};

/// An error that happened serializing or deserializing YAML data.
pub struct Error(Box<ErrorImpl>);

/// Alias for a `Result` with the error type `serde_yaml::Error`.
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub enum ErrorImpl {
    Message(String, Option<Pos>),

    Emit(emitter::EmitError),
    Scan(scanner::ScanError),
    Io(io::Error),
    Utf8(str::Utf8Error),
    FromUtf8(string::FromUtf8Error),

    EndOfStream,
    MoreThanOneDocument,
    RecursionLimitExceeded,

    Shared(Arc<ErrorImpl>),
}

#[derive(Debug)]
pub struct Pos {
    marker: Marker,
    path: String,
}

/// The input location that an error occured.
#[derive(Debug)]
pub struct Location {
    index: usize,
    line: usize,
    column: usize,
}

impl Location {
    /// The byte index of the error
    pub fn index(&self) -> usize {
        self.index
    }

    /// The line of the error
    pub fn line(&self) -> usize {
        self.line
    }

    /// The column of the error
    pub fn column(&self) -> usize {
        self.column
    }

    // This is to keep decoupled with the yaml crate
    #[doc(hidden)]
    fn from_marker(marker: &Marker) -> Self {
        Location {
            // `col` returned from the `yaml` crate is 0-indexed but all error messages add + 1 to this value
            column: marker.col() + 1,
            index: marker.index(),
            line: marker.line(),
        }
    }
}

impl Error {
    /// Returns the Location from the error if one exists.
    ///
    /// Not all types of errors have a location so this can return `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use serde_yaml::{Value, Error};
    /// #
    /// // The `@` character as the first character makes this invalid yaml
    /// let invalid_yaml: Result<Value, Error> = serde_yaml::from_str("@invalid_yaml");
    ///
    /// let location = invalid_yaml.unwrap_err().location().unwrap();
    ///
    /// assert_eq!(location.line(), 1);
    /// assert_eq!(location.column(), 1);
    /// ```
    pub fn location(&self) -> Option<Location> {
        match self.0.as_ref() {
            ErrorImpl::Message(_, Some(pos)) => Some(Location::from_marker(&pos.marker)),
            ErrorImpl::Scan(scan) => Some(Location::from_marker(scan.marker())),
            _ => None,
        }
    }
}

pub(crate) fn end_of_stream() -> Error {
    Error(Box::new(ErrorImpl::EndOfStream))
}

pub(crate) fn more_than_one_document() -> Error {
    Error(Box::new(ErrorImpl::MoreThanOneDocument))
}

pub(crate) fn io(err: io::Error) -> Error {
    Error(Box::new(ErrorImpl::Io(err)))
}

pub(crate) fn emitter(err: emitter::EmitError) -> Error {
    Error(Box::new(ErrorImpl::Emit(err)))
}

pub(crate) fn scanner(err: scanner::ScanError) -> Error {
    Error(Box::new(ErrorImpl::Scan(err)))
}

pub(crate) fn str_utf8(err: str::Utf8Error) -> Error {
    Error(Box::new(ErrorImpl::Utf8(err)))
}

pub(crate) fn string_utf8(err: string::FromUtf8Error) -> Error {
    Error(Box::new(ErrorImpl::FromUtf8(err)))
}

pub(crate) fn recursion_limit_exceeded() -> Error {
    Error(Box::new(ErrorImpl::RecursionLimitExceeded))
}

pub(crate) fn shared(shared: Arc<ErrorImpl>) -> Error {
    Error(Box::new(ErrorImpl::Shared(shared)))
}

pub(crate) fn fix_marker(mut error: Error, marker: Marker, path: Path) -> Error {
    if let ErrorImpl::Message(_, none @ None) = error.0.as_mut() {
        *none = Some(Pos {
            marker,
            path: path.to_string(),
        });
    }
    error
}

impl Error {
    pub(crate) fn shared(self) -> Arc<ErrorImpl> {
        if let ErrorImpl::Shared(err) = *self.0 {
            err
        } else {
            Arc::from(self.0)
        }
    }
}

impl error::Error for Error {
    // TODO: deprecated, remove in next major version.
    fn description(&self) -> &str {
        self.0.description()
    }

    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        self.0.source()
    }
}

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

// Remove two layers of verbosity from the debug representation. Humans often
// end up seeing this representation because it is what unwrap() shows.
impl Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.debug(f)
    }
}

impl ser::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
    }
}

impl de::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
    }
}

impl ErrorImpl {
    #[allow(deprecated)]
    fn description(&self) -> &str {
        match self {
            ErrorImpl::Message(msg, _) => msg,
            ErrorImpl::Emit(_) => "emit error",
            ErrorImpl::Scan(_) => "scan error",
            ErrorImpl::Io(err) => error::Error::description(err),
            ErrorImpl::Utf8(err) => error::Error::description(err),
            ErrorImpl::FromUtf8(err) => error::Error::description(err),
            ErrorImpl::EndOfStream => "EOF while parsing a value",
            ErrorImpl::MoreThanOneDocument => {
                "deserializing from YAML containing more than one document is not supported"
            }
            ErrorImpl::RecursionLimitExceeded => "recursion limit exceeded",
            ErrorImpl::Shared(err) => err.description(),
        }
    }

    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            ErrorImpl::Scan(err) => Some(err),
            ErrorImpl::Io(err) => Some(err),
            ErrorImpl::Utf8(err) => Some(err),
            ErrorImpl::FromUtf8(err) => Some(err),
            ErrorImpl::Shared(err) => err.source(),
            _ => None,
        }
    }

    fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErrorImpl::Message(msg, None) => Display::fmt(msg, f),
            ErrorImpl::Message(msg, Some(Pos { marker, path })) => {
                if path == "." {
                    write!(f, "{}", ScanError::new(*marker, msg))
                } else {
                    write!(f, "{}: {}", path, ScanError::new(*marker, msg))
                }
            }
            ErrorImpl::Emit(emitter::EmitError::FmtError(_)) => f.write_str("yaml-rust fmt error"),
            ErrorImpl::Emit(emitter::EmitError::BadHashmapKey) => f.write_str("bad hash map key"),
            ErrorImpl::Scan(err) => Display::fmt(err, f),
            ErrorImpl::Io(err) => Display::fmt(err, f),
            ErrorImpl::Utf8(err) => Display::fmt(err, f),
            ErrorImpl::FromUtf8(err) => Display::fmt(err, f),
            ErrorImpl::EndOfStream => f.write_str("EOF while parsing a value"),
            ErrorImpl::MoreThanOneDocument => f.write_str(
                "deserializing from YAML containing more than one document is not supported",
            ),
            ErrorImpl::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
            ErrorImpl::Shared(err) => err.display(f),
        }
    }

    fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErrorImpl::Message(msg, pos) => f.debug_tuple("Message").field(msg).field(pos).finish(),
            ErrorImpl::Emit(emit) => f.debug_tuple("Emit").field(emit).finish(),
            ErrorImpl::Scan(scan) => f.debug_tuple("Scan").field(scan).finish(),
            ErrorImpl::Io(io) => f.debug_tuple("Io").field(io).finish(),
            ErrorImpl::Utf8(utf8) => f.debug_tuple("Utf8").field(utf8).finish(),
            ErrorImpl::FromUtf8(from_utf8) => f.debug_tuple("FromUtf8").field(from_utf8).finish(),
            ErrorImpl::EndOfStream => f.debug_tuple("EndOfStream").finish(),
            ErrorImpl::MoreThanOneDocument => f.debug_tuple("MoreThanOneDocument").finish(),
            ErrorImpl::RecursionLimitExceeded => f.debug_tuple("RecursionLimitExceeded").finish(),
            ErrorImpl::Shared(err) => err.debug(f),
        }
    }
}
<commit_msg>Remove major version comment on description fn<commit_after>use crate::path::Path;
use serde::{de, ser};
use std::error;
use std::fmt::{self, Debug, Display};
use std::io;
use std::result;
use std::str;
use std::string;
use std::sync::Arc;
use yaml_rust::emitter;
use yaml_rust::scanner::{self, Marker, ScanError};

/// An error that happened serializing or deserializing YAML data.
pub struct Error(Box<ErrorImpl>);

/// Alias for a `Result` with the error type `serde_yaml::Error`.
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub enum ErrorImpl {
    Message(String, Option<Pos>),

    Emit(emitter::EmitError),
    Scan(scanner::ScanError),
    Io(io::Error),
    Utf8(str::Utf8Error),
    FromUtf8(string::FromUtf8Error),

    EndOfStream,
    MoreThanOneDocument,
    RecursionLimitExceeded,

    Shared(Arc<ErrorImpl>),
}

#[derive(Debug)]
pub struct Pos {
    marker: Marker,
    path: String,
}

/// The input location that an error occured.
#[derive(Debug)]
pub struct Location {
    index: usize,
    line: usize,
    column: usize,
}

impl Location {
    /// The byte index of the error
    pub fn index(&self) -> usize {
        self.index
    }

    /// The line of the error
    pub fn line(&self) -> usize {
        self.line
    }

    /// The column of the error
    pub fn column(&self) -> usize {
        self.column
    }

    // This is to keep decoupled with the yaml crate
    #[doc(hidden)]
    fn from_marker(marker: &Marker) -> Self {
        Location {
            // `col` returned from the `yaml` crate is 0-indexed but all error messages add + 1 to this value
            column: marker.col() + 1,
            index: marker.index(),
            line: marker.line(),
        }
    }
}

impl Error {
    /// Returns the Location from the error if one exists.
    ///
    /// Not all types of errors have a location so this can return `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use serde_yaml::{Value, Error};
    /// #
    /// // The `@` character as the first character makes this invalid yaml
    /// let invalid_yaml: Result<Value, Error> = serde_yaml::from_str("@invalid_yaml");
    ///
    /// let location = invalid_yaml.unwrap_err().location().unwrap();
    ///
    /// assert_eq!(location.line(), 1);
    /// assert_eq!(location.column(), 1);
    /// ```
    pub fn location(&self) -> Option<Location> {
        match self.0.as_ref() {
            ErrorImpl::Message(_, Some(pos)) => Some(Location::from_marker(&pos.marker)),
            ErrorImpl::Scan(scan) => Some(Location::from_marker(scan.marker())),
            _ => None,
        }
    }
}

pub(crate) fn end_of_stream() -> Error {
    Error(Box::new(ErrorImpl::EndOfStream))
}

pub(crate) fn more_than_one_document() -> Error {
    Error(Box::new(ErrorImpl::MoreThanOneDocument))
}

pub(crate) fn io(err: io::Error) -> Error {
    Error(Box::new(ErrorImpl::Io(err)))
}

pub(crate) fn emitter(err: emitter::EmitError) -> Error {
    Error(Box::new(ErrorImpl::Emit(err)))
}

pub(crate) fn scanner(err: scanner::ScanError) -> Error {
    Error(Box::new(ErrorImpl::Scan(err)))
}

pub(crate) fn str_utf8(err: str::Utf8Error) -> Error {
    Error(Box::new(ErrorImpl::Utf8(err)))
}

pub(crate) fn string_utf8(err: string::FromUtf8Error) -> Error {
    Error(Box::new(ErrorImpl::FromUtf8(err)))
}

pub(crate) fn recursion_limit_exceeded() -> Error {
    Error(Box::new(ErrorImpl::RecursionLimitExceeded))
}

pub(crate) fn shared(shared: Arc<ErrorImpl>) -> Error {
    Error(Box::new(ErrorImpl::Shared(shared)))
}

pub(crate) fn fix_marker(mut error: Error, marker: Marker, path: Path) -> Error {
    if let ErrorImpl::Message(_, none @ None) = error.0.as_mut() {
        *none = Some(Pos {
            marker,
            path: path.to_string(),
        });
    }
    error
}

impl Error {
    pub(crate) fn shared(self) -> Arc<ErrorImpl> {
        if let ErrorImpl::Shared(err) = *self.0 {
            err
        } else {
            Arc::from(self.0)
        }
    }
}

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

    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        self.0.source()
    }
}

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

// Remove two layers of verbosity from the debug representation. Humans often
// end up seeing this representation because it is what unwrap() shows.
impl Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.debug(f)
    }
}

impl ser::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
    }
}

impl de::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
    }
}

impl ErrorImpl {
    #[allow(deprecated)]
    fn description(&self) -> &str {
        match self {
            ErrorImpl::Message(msg, _) => msg,
            ErrorImpl::Emit(_) => "emit error",
            ErrorImpl::Scan(_) => "scan error",
            ErrorImpl::Io(err) => error::Error::description(err),
            ErrorImpl::Utf8(err) => error::Error::description(err),
            ErrorImpl::FromUtf8(err) => error::Error::description(err),
            ErrorImpl::EndOfStream => "EOF while parsing a value",
            ErrorImpl::MoreThanOneDocument => {
                "deserializing from YAML containing more than one document is not supported"
            }
            ErrorImpl::RecursionLimitExceeded => "recursion limit exceeded",
            ErrorImpl::Shared(err) => err.description(),
        }
    }

    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            ErrorImpl::Scan(err) => Some(err),
            ErrorImpl::Io(err) => Some(err),
            ErrorImpl::Utf8(err) => Some(err),
            ErrorImpl::FromUtf8(err) => Some(err),
            ErrorImpl::Shared(err) => err.source(),
            _ => None,
        }
    }

    fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErrorImpl::Message(msg, None) => Display::fmt(msg, f),
            ErrorImpl::Message(msg, Some(Pos { marker, path })) => {
                if path == "." {
                    write!(f, "{}", ScanError::new(*marker, msg))
                } else {
                    write!(f, "{}: {}", path, ScanError::new(*marker, msg))
                }
            }
            ErrorImpl::Emit(emitter::EmitError::FmtError(_)) => f.write_str("yaml-rust fmt error"),
            ErrorImpl::Emit(emitter::EmitError::BadHashmapKey) => f.write_str("bad hash map key"),
            ErrorImpl::Scan(err) => Display::fmt(err, f),
            ErrorImpl::Io(err) => Display::fmt(err, f),
            ErrorImpl::Utf8(err) => Display::fmt(err, f),
            ErrorImpl::FromUtf8(err) => Display::fmt(err, f),
            ErrorImpl::EndOfStream => f.write_str("EOF while parsing a value"),
            ErrorImpl::MoreThanOneDocument => f.write_str(
                "deserializing from YAML containing more than one document is not supported",
            ),
            ErrorImpl::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
            ErrorImpl::Shared(err) => err.display(f),
        }
    }

    fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErrorImpl::Message(msg, pos) => f.debug_tuple("Message").field(msg).field(pos).finish(),
            ErrorImpl::Emit(emit) => f.debug_tuple("Emit").field(emit).finish(),
            ErrorImpl::Scan(scan) => f.debug_tuple("Scan").field(scan).finish(),
            ErrorImpl::Io(io) => f.debug_tuple("Io").field(io).finish(),
            ErrorImpl::Utf8(utf8) => f.debug_tuple("Utf8").field(utf8).finish(),
            ErrorImpl::FromUtf8(from_utf8) => f.debug_tuple("FromUtf8").field(from_utf8).finish(),
            ErrorImpl::EndOfStream => f.debug_tuple("EndOfStream").finish(),
            ErrorImpl::MoreThanOneDocument => f.debug_tuple("MoreThanOneDocument").finish(),
            ErrorImpl::RecursionLimitExceeded => f.debug_tuple("RecursionLimitExceeded").finish(),
            ErrorImpl::Shared(err) => err.debug(f),
        }
    }
}
<|endoftext|>


<commit_before>use hyper;
use serde_json;
use std;
use url;

use dbtype;

/// Principal error type.
///
/// The public API guarantees only the non-hidden variants and non-hidden
/// variant fields. Do not match against hidden variants or hidden variant
/// fields.
///
///
#[derive(Debug)]
pub enum Error {

    /// The database already exists.
    DatabaseExists {
        response: ErrorResponse,
    },

    /// JSON-decoding error.
    Decode {
        #[doc(hidden)]
        kind: DecodeKind,
    },

    /// The client request conflicts with an existing document.
    DocumentConflict {
        response: ErrorResponse,
    },

    /// JSON-encoding error.
    Encode {
        #[doc(hidden)]
        cause: serde_json::error::Error,
    },

    /// An internal server error occurred.
    InternalServerError {
        response: ErrorResponse,
    },

    /// The database name is invalid.
    InvalidDatabaseName {
        response: ErrorResponse,
    },

    /// The client request is invalid.
    InvalidRequest {
        response: ErrorResponse,
    },

    /// I/O error with a compile-time description.
    #[doc(hidden)]
    Io {
        description: &'static str,
        cause: std::io::Error,
    },

    /// The CouchDB server responded without a Content-Type header.
    #[doc(hidden)]
    NoContentTypeHeader {
        expected: &'static str,
    },

    /// The resource does not exist.
    NotFound {

        /// CouchDB server response.
        ///
        /// In case of a HEAD request, the response value is None (because the
        /// server doesn't send response content for HEAD requests). Otherwise,
        /// the response value is Some.
        response: Option<ErrorResponse>,
    },

    /// Channel-receiver error with a compile-time description and thread-join
    /// error.
    #[doc(hidden)]
    ReceiveFromThread {
        description: &'static str,
        cause: std::sync::mpsc::RecvError,
    },

    /// HTTP-transport error.
    Transport {
        #[doc(hidden)]
        cause: TransportCause,
    },

    /// The client is unauthorized to carry out the operation.
    Unauthorized {
        response: ErrorResponse,
    },

    /// The CouchDB server responded with a Content-Type header that the client
    /// didn't expect.
    #[doc(hidden)]
    UnexpectedContentTypeHeader {
        expected: &'static str,
        got: String,
    },

    /// The CouchDB server responded with an HTTP status code that the client
    /// didn't expect.
    #[doc(hidden)]
    UnexpectedHttpStatus {
        got: hyper::status::StatusCode,
    },

    /// URI-parse error.
    UriParse {
        #[doc(hidden)]
        cause: url::ParseError,
    },
}

impl std::error::Error for Error {

    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            DatabaseExists { .. } => "The database already exists",
            Decode { .. }  => "The client failed to decode a JSON response from CouchDB server",
            DocumentConflict { .. } => "The client request conflicts with an existing document",
            Encode { .. }  => "The client failed to encode a JSON request to CouchDB server",
            InternalServerError { .. } => "An internal server error occurred",
            InvalidDatabaseName { .. } => "The database name is invalid",
            InvalidRequest { .. } => "The client request is invalid",
            Io { ref description, .. } => description,
            NoContentTypeHeader { .. } =>
                "The CouchDB server responded without a Content-Type header",
            NotFound { .. } => "The resource does not exist",
            ReceiveFromThread { ref description, .. } => description,
            Transport { .. } => "An HTTP transport error occurred",
            Unauthorized { .. } => "The client is unauthorized to carry out the operation",
            UnexpectedContentTypeHeader { .. } =>
                "The CouchDB server responded with a Content-Type header the client did not expect",
            UnexpectedHttpStatus { .. } =>
                "The CouchDB server responded with an HTTP status code the client did not expect",
            UriParse { .. } => "Invalid URI argument",
        }
    }

    fn cause(&self) -> std::option::Option<&std::error::Error> {
        use self::Error::*;
        match *self {
            DatabaseExists { .. } => None,
            Decode { ref kind } => kind.cause(),
            DocumentConflict { .. } => None,
            Encode { ref cause } => Some(cause),
            InternalServerError { .. } => None,
            InvalidDatabaseName { .. } => None,
            InvalidRequest { .. } => None,
            Io { ref cause, .. } => Some(cause),
            NoContentTypeHeader { .. } => None,
            NotFound { .. } => None,
            ReceiveFromThread { ref cause, .. } => Some(cause),
            Transport { ref cause }  => {
                use self::TransportCause::*;
                match *cause {
                    Hyper(ref cause) => Some(cause),
                    Io(ref cause) => Some(cause),
                }
            },
            Unauthorized { .. } => None,
            UnexpectedContentTypeHeader { .. }  => None,
            UnexpectedHttpStatus { .. } => None,
            UriParse { ref cause } => Some(cause),
        }
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use std::error::Error;
        use self::Error::*;
        match *self {
            // FIXME: Implement Display for ErrorResponse.
            DatabaseExists { ref response } =>
                write!(f, "{}: {}: {}", self.description(), response.error, response.reason),
            Decode { ref kind } => write!(f, "{}: {}", self.description(), kind),
            DocumentConflict { ref response } =>
                write!(f, "{}: {}: {}", self.description(), response.error, response.reason),
            Encode { ref cause } => write!(f, "{}: {}", self.description(), cause),
            InternalServerError { ref response } =>
                write!(f, "{}: {}: {}", self.description(), response.error, response.reason),
            InvalidDatabaseName { ref response } =>
                write!(f, "{}: {}: {}", self.description(), response.error, response.reason),
            InvalidRequest { ref response } =>
                write!(f, "{}: {}: {}", self.description(), response.error, response.reason),
            Io { ref cause, .. } => write!(f, "{}: {}", self.description(), cause),
            NoContentTypeHeader { ref expected } =>
                write!(f, "{}: Expected '{}'", self.description(), expected),
            NotFound { ref response } => match *response {
                Some(ref response) =>
                    write!(f, "{}: {}: {}", self.description(), response.error, response.reason),
                None => write!(f, "{}", self.description()),
            },
            ReceiveFromThread { ref cause, .. } =>
                write!(f, "{}: {}", self.description(), cause),
            Transport { ref cause } => write!(f, "{}: {}", self.description(), cause),
            Unauthorized { ref response } =>
                write!(f, "{}: {}: {}", self.description(), response.error, response.reason),
            UnexpectedContentTypeHeader { ref expected, ref got } =>
                write!(f, "{}: Expected '{}', got '{}'", self.description(), expected, got),
            UnexpectedHttpStatus { ref got } => write!(f, "{}: Got {}", self.description(), got),
            UriParse { ref cause } => write!(f, "{}: {}", self.description(), cause),
        }
    }
}

/// Response content from the CouchDB server in case of error.
#[derive(Debug)]
pub struct ErrorResponse {

    /// Error string returned by CouchDB Server.
    ///
    /// This is a high-level description of the error—e.g., “file_exists”.

    pub error: String,

    /// Reason string returned by CouchDB Server.
    ///
    /// This is a low-level description of the error—e.g., “The database could
    /// not be created, the file already exists.”
    ///
    pub reason: String,
}

impl ErrorResponse {
    pub fn from_reader<R>(r: R) -> Result<Self, Error>
        where R: std::io::Read
    {
        serde_json::from_reader::<_, dbtype::ErrorResponse>(r)
            .map_err(|e| {
                Error::Decode { kind: DecodeKind::Serde { cause: e } }
            })
            .map(|x| {
                ErrorResponse {
                    error: x.error,
                    reason: x.reason,
                }
            })
    }
}

#[derive(Debug)]
pub enum DecodeKind {
    InstanceStartTime {
        got: String,
        cause: std::num::ParseIntError,
    },
    InvalidDocument {
        what: &'static str,
    },
    Serde {
        cause: serde_json::Error,
    },
}

impl DecodeKind {
    fn cause(&self) -> Option<&std::error::Error> {
        use self::DecodeKind::*;
        match *self {
            InstanceStartTime { ref cause, .. } => Some(cause),
            InvalidDocument { .. } => None,
            Serde { ref cause } => Some(cause),
        }
    }
}

impl std::fmt::Display for DecodeKind {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use self::DecodeKind::*;
        match *self {
            InstanceStartTime { ref got, ref cause } =>
                write!(f, "Could not convert `instance_start_time` field to numeric type: {}: \
                       got {}", cause, got),
            InvalidDocument { ref what } => write!(f, "Unexpected document content: {}", what),
            Serde { ref cause } => cause.fmt(f),
        }
    }
}

#[derive(Debug)]
pub enum TransportCause {
    Hyper(hyper::error::Error),
    Io(std::io::Error),
}

impl std::fmt::Display for TransportCause {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use self::TransportCause::*;
        match *self {
            Hyper(ref e) => e.fmt(f),
            Io(ref e) => e.fmt(f),
        }
    }
}
<commit_msg>Implement Display trait for ErrorResponse<commit_after>use hyper;
use serde_json;
use std;
use url;

use dbtype;

/// Principal error type.
///
/// The public API guarantees only the non-hidden variants and non-hidden
/// variant fields. Do not match against hidden variants or hidden variant
/// fields.
///
///
#[derive(Debug)]
pub enum Error {

    /// The database already exists.
    DatabaseExists {
        response: ErrorResponse,
    },

    /// JSON-decoding error.
    Decode {
        #[doc(hidden)]
        kind: DecodeKind,
    },

    /// The client request conflicts with an existing document.
    DocumentConflict {
        response: ErrorResponse,
    },

    /// JSON-encoding error.
    Encode {
        #[doc(hidden)]
        cause: serde_json::error::Error,
    },

    /// An internal server error occurred.
    InternalServerError {
        response: ErrorResponse,
    },

    /// The database name is invalid.
    InvalidDatabaseName {
        response: ErrorResponse,
    },

    /// The client request is invalid.
    InvalidRequest {
        response: ErrorResponse,
    },

    /// I/O error with a compile-time description.
    #[doc(hidden)]
    Io {
        description: &'static str,
        cause: std::io::Error,
    },

    /// The CouchDB server responded without a Content-Type header.
    #[doc(hidden)]
    NoContentTypeHeader {
        expected: &'static str,
    },

    /// The resource does not exist.
    NotFound {

        /// CouchDB server response.
        ///
        /// In case of a HEAD request, the response value is None (because the
        /// server doesn't send response content for HEAD requests). Otherwise,
        /// the response value is Some.
        response: Option<ErrorResponse>,
    },

    /// Channel-receiver error with a compile-time description and thread-join
    /// error.
    #[doc(hidden)]
    ReceiveFromThread {
        description: &'static str,
        cause: std::sync::mpsc::RecvError,
    },

    /// HTTP-transport error.
    Transport {
        #[doc(hidden)]
        cause: TransportCause,
    },

    /// The client is unauthorized to carry out the operation.
    Unauthorized {
        response: ErrorResponse,
    },

    /// The CouchDB server responded with a Content-Type header that the client
    /// didn't expect.
    #[doc(hidden)]
    UnexpectedContentTypeHeader {
        expected: &'static str,
        got: String,
    },

    /// The CouchDB server responded with an HTTP status code that the client
    /// didn't expect.
    #[doc(hidden)]
    UnexpectedHttpStatus {
        got: hyper::status::StatusCode,
    },

    /// URI-parse error.
    UriParse {
        #[doc(hidden)]
        cause: url::ParseError,
    },
}

impl std::error::Error for Error {

    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            DatabaseExists { .. } => "The database already exists",
            Decode { .. }  => "The client failed to decode a JSON response from CouchDB server",
            DocumentConflict { .. } => "The client request conflicts with an existing document",
            Encode { .. }  => "The client failed to encode a JSON request to CouchDB server",
            InternalServerError { .. } => "An internal server error occurred",
            InvalidDatabaseName { .. } => "The database name is invalid",
            InvalidRequest { .. } => "The client request is invalid",
            Io { ref description, .. } => description,
            NoContentTypeHeader { .. } =>
                "The CouchDB server responded without a Content-Type header",
            NotFound { .. } => "The resource does not exist",
            ReceiveFromThread { ref description, .. } => description,
            Transport { .. } => "An HTTP transport error occurred",
            Unauthorized { .. } => "The client is unauthorized to carry out the operation",
            UnexpectedContentTypeHeader { .. } =>
                "The CouchDB server responded with a Content-Type header the client did not expect",
            UnexpectedHttpStatus { .. } =>
                "The CouchDB server responded with an HTTP status code the client did not expect",
            UriParse { .. } => "Invalid URI argument",
        }
    }

    fn cause(&self) -> std::option::Option<&std::error::Error> {
        use self::Error::*;
        match *self {
            DatabaseExists { .. } => None,
            Decode { ref kind } => kind.cause(),
            DocumentConflict { .. } => None,
            Encode { ref cause } => Some(cause),
            InternalServerError { .. } => None,
            InvalidDatabaseName { .. } => None,
            InvalidRequest { .. } => None,
            Io { ref cause, .. } => Some(cause),
            NoContentTypeHeader { .. } => None,
            NotFound { .. } => None,
            ReceiveFromThread { ref cause, .. } => Some(cause),
            Transport { ref cause }  => {
                use self::TransportCause::*;
                match *cause {
                    Hyper(ref cause) => Some(cause),
                    Io(ref cause) => Some(cause),
                }
            },
            Unauthorized { .. } => None,
            UnexpectedContentTypeHeader { .. }  => None,
            UnexpectedHttpStatus { .. } => None,
            UriParse { ref cause } => Some(cause),
        }
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use std::error::Error;
        use self::Error::*;
        match *self {
            DatabaseExists { ref response } =>
                write!(f, "{}: {}", self.description(), response),
            Decode { ref kind } => write!(f, "{}: {}", self.description(), kind),
            DocumentConflict { ref response } =>
                write!(f, "{}: {}", self.description(), response),
            Encode { ref cause } => write!(f, "{}: {}", self.description(), cause),
            InternalServerError { ref response } =>
                write!(f, "{}: {}", self.description(), response),
            InvalidDatabaseName { ref response } =>
                write!(f, "{}: {}", self.description(), response),
            InvalidRequest { ref response } =>
                write!(f, "{}: {}", self.description(), response),
            Io { ref cause, .. } => write!(f, "{}: {}", self.description(), cause),
            NoContentTypeHeader { ref expected } =>
                write!(f, "{}: Expected '{}'", self.description(), expected),
            NotFound { ref response } => match *response {
                Some(ref response) =>
                    write!(f, "{}: {}", self.description(), response),
                None => write!(f, "{}", self.description()),
            },
            ReceiveFromThread { ref cause, .. } =>
                write!(f, "{}: {}", self.description(), cause),
            Transport { ref cause } => write!(f, "{}: {}", self.description(), cause),
            Unauthorized { ref response } => write!(f, "{}: {}", self.description(), response),
            UnexpectedContentTypeHeader { ref expected, ref got } =>
                write!(f, "{}: Expected '{}', got '{}'", self.description(), expected, got),
            UnexpectedHttpStatus { ref got } => write!(f, "{}: Got {}", self.description(), got),
            UriParse { ref cause } => write!(f, "{}: {}", self.description(), cause),
        }
    }
}

/// Response content from the CouchDB server in case of error.
#[derive(Debug)]
pub struct ErrorResponse {

    /// Error string returned by CouchDB Server.
    ///
    /// This is a high-level description of the error—e.g., “file_exists”.

    pub error: String,

    /// Reason string returned by CouchDB Server.
    ///
    /// This is a low-level description of the error—e.g., “The database could
    /// not be created, the file already exists.”
    ///
    pub reason: String,
}

impl ErrorResponse {
    pub fn from_reader<R>(r: R) -> Result<Self, Error>
        where R: std::io::Read
    {
        serde_json::from_reader::<_, dbtype::ErrorResponse>(r)
            .map_err(|e| {
                Error::Decode { kind: DecodeKind::Serde { cause: e } }
            })
            .map(|x| {
                ErrorResponse {
                    error: x.error,
                    reason: x.reason,
                }
            })
    }
}

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

#[derive(Debug)]
pub enum DecodeKind {
    InstanceStartTime {
        got: String,
        cause: std::num::ParseIntError,
    },
    InvalidDocument {
        what: &'static str,
    },
    Serde {
        cause: serde_json::Error,
    },
}

impl DecodeKind {
    fn cause(&self) -> Option<&std::error::Error> {
        use self::DecodeKind::*;
        match *self {
            InstanceStartTime { ref cause, .. } => Some(cause),
            InvalidDocument { .. } => None,
            Serde { ref cause } => Some(cause),
        }
    }
}

impl std::fmt::Display for DecodeKind {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use self::DecodeKind::*;
        match *self {
            InstanceStartTime { ref got, ref cause } =>
                write!(f, "Could not convert `instance_start_time` field to numeric type: {}: \
                       got {}", cause, got),
            InvalidDocument { ref what } => write!(f, "Unexpected document content: {}", what),
            Serde { ref cause } => cause.fmt(f),
        }
    }
}

#[derive(Debug)]
pub enum TransportCause {
    Hyper(hyper::error::Error),
    Io(std::io::Error),
}

impl std::fmt::Display for TransportCause {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use self::TransportCause::*;
        match *self {
            Hyper(ref e) => e.fmt(f),
            Io(ref e) => e.fmt(f),
        }
    }
}
<|endoftext|>


<commit_before>//! User-input events and their effects.
//!
//! * Every user input the application receives is converted to an
//!   [`Event`](./enum.Event.html).
//! * Each event is then given to the root, and descends the view tree down to
//!   the view currently in focus, through the
//!   [`on_event`](../view/trait.View.html#method.on_event) method.
//!     * If the view consumes the event, it may return a callback to be
//!       executed.
//!     * Otherwise, it ignores the event, and the view parent can in turn
//!       choose to consume it or not.
//! * If no view consumes the event, the
//!   [global callback](../struct.Cursive.html#method.add_global_callback)
//!   table is checked.

use Cursive;
use std::ops::Deref;
use std::rc::Rc;

/// Callback is a function that can be triggered by an event.
/// It has a mutable access to the cursive root.
#[derive(Clone)]
pub struct Callback(Rc<Box<Fn(&mut Cursive)>>);
// TODO: remove the Box when Box<T: Sized> -> Rc<T> is possible

impl Callback {
    /// Wraps the given function into a `Callback` object.
    pub fn from_fn<F: Fn(&mut Cursive) + 'static>(f: F) -> Self {
        Callback(Rc::new(Box::new(f)))
    }
}

impl Deref for Callback {
    type Target = Box<Fn(&mut Cursive)>;
    fn deref<'a>(&'a self) -> &'a Box<Fn(&mut Cursive)> {
        &self.0
    }
}

impl From<Rc<Box<Fn(&mut Cursive)>>> for Callback {
    fn from(f: Rc<Box<Fn(&mut Cursive)>>) -> Self {
        Callback(f)
    }
}

impl From<Box<Fn(&mut Cursive) + Send>> for Callback {
    fn from(f: Box<Fn(&mut Cursive) + Send>) -> Self {
        Callback(Rc::new(f))
    }
}

impl From<Box<Fn(&mut Cursive)>> for Callback {
    fn from(f: Box<Fn(&mut Cursive)>) -> Self {
        Callback(Rc::new(f))
    }
}

/// Answer to an event notification.
/// The event can be consumed or ignored.
pub enum EventResult {
    /// The event was ignored. The parent can keep handling it.
    Ignored,
    /// The event was consumed. An optionnal callback to run is attached.
    Consumed(Option<Callback>), // TODO: make this a FnOnce?
}

impl EventResult {
    /// Convenient method to create `Consumed(Some(f))`
    pub fn with_cb<F: 'static + Fn(&mut Cursive)>(f: F) -> Self {
        EventResult::Consumed(Some(Callback::from_fn(f)))
    }

    /// Returns `true` if `self` is `EventResult::Consumed`.
    pub fn is_consumed(&self) -> bool {
        match *self {
            EventResult::Consumed(_) => true,
            EventResult::Ignored => false,
        }
    }

    /// Process this result if it is a callback.
    ///
    /// Does nothing otherwise.
    pub fn process(self, s: &mut Cursive) {
        if let EventResult::Consumed(Some(cb)) = self {
            cb(s);
        }
    }

    /// Returns `self` if it is not `Event::Ignored`, otherwise returns `f()`.
    pub fn or_else<F>(self, f: F) -> Self
    where
        F: FnOnce() -> EventResult,
    {
        match self {
            EventResult::Ignored => f(),
            other => other,
        }
    }
}

/// A non-character key on the keyboard
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
pub enum Key {
    /// Both Enter (or Return) and numpad Enter
    Enter,
    /// Tabulation key
    Tab,
    /// Backspace key
    Backspace,
    /// Escape key
    Esc,

    /// Left arrow
    Left,
    /// Right arrow
    Right,
    /// Up arrow
    Up,
    /// Down arrow
    Down,

    /// Insert key
    Ins,
    /// Delete key
    Del,
    /// Home key
    Home,
    /// End key
    End,
    /// Page Up key
    PageUp,
    /// Page Down key
    PageDown,

    /// Pause Break key
    PauseBreak,

    /// The 5 in the center of the keypad, when numlock is disabled.
    NumpadCenter,

    /// F0 key
    F0,
    /// F1 key
    F1,
    /// F2 key
    F2,
    /// F3 key
    F3,
    /// F4 key
    F4,
    /// F5 key
    F5,
    /// F6 key
    F6,
    /// F7 key
    F7,
    /// F8 key
    F8,
    /// F9 key
    F9,
    /// F10 key
    F10,
    /// F11 key
    F11,
    /// F12 key
    F12,
}

impl Key {
    /// Returns the function key corresponding to the given number
    ///
    /// 1 -> F1, etc...
    ///
    /// # Panics
    ///
    /// If `n == 0 || n > 12`
    pub fn from_f(n: u8) -> Key {
        match n {
            0 => Key::F0,
            1 => Key::F1,
            2 => Key::F2,
            3 => Key::F3,
            4 => Key::F4,
            5 => Key::F5,
            6 => Key::F6,
            7 => Key::F7,
            8 => Key::F8,
            9 => Key::F9,
            10 => Key::F10,
            11 => Key::F11,
            12 => Key::F12,
            _ => panic!("unknown function key: F{}", n),
        }
    }
}

/// Represents an event as seen by the application.
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
pub enum Event {
    /// Event fired when the window is resized.
    WindowResize,

    /// Event fired regularly when a auto-refresh is set.
    Refresh,

    /// A character was entered (includes numbers, punctuation, ...).
    Char(char),
    /// A character was entered with the Ctrl key pressed.
    CtrlChar(char),
    /// A character was entered with the Alt key pressed.
    AltChar(char),

    /// A non-character key was pressed.
    Key(Key),
    /// A non-character key was pressed with the Shift key pressed.
    Shift(Key),
    /// A non-character key was pressed with the Alt key pressed.
    Alt(Key),
    /// A non-character key was pressed with the Shift and Alt keys pressed.
    AltShift(Key),
    /// A non-character key was pressed with the Ctrl key pressed.
    Ctrl(Key),
    /// A non-character key was pressed with the Ctrl and Shift keys pressed.
    CtrlShift(Key),
    /// A non-character key was pressed with the Ctrl and Alt keys pressed.
    CtrlAlt(Key),

    /// An unknown event was received.
    Unknown(Vec<u8>),

    #[doc(hidden)]
    /// The application is about to exit.
    Exit,
}

impl From<char> for Event {
    fn from(c: char) -> Event {
        Event::Char(c)
    }
}

impl From<Key> for Event {
    fn from(k: Key) -> Event {
        Event::Key(k)
    }
}
<commit_msg>Add MouseEvent to the Event enum<commit_after>//! User-input events and their effects.
//!
//! * Every user input the application receives is converted to an
//!   [`Event`](./enum.Event.html).
//! * Each event is then given to the root, and descends the view tree down to
//!   the view currently in focus, through the
//!   [`on_event`](../view/trait.View.html#method.on_event) method.
//!     * If the view consumes the event, it may return a callback to be
//!       executed.
//!     * Otherwise, it ignores the event, and the view parent can in turn
//!       choose to consume it or not.
//! * If no view consumes the event, the
//!   [global callback](../struct.Cursive.html#method.add_global_callback)
//!   table is checked.

use Cursive;
use std::ops::Deref;
use std::rc::Rc;

use vec::Vec2;

/// Callback is a function that can be triggered by an event.
/// It has a mutable access to the cursive root.
#[derive(Clone)]
pub struct Callback(Rc<Box<Fn(&mut Cursive)>>);
// TODO: remove the Box when Box<T: Sized> -> Rc<T> is possible

impl Callback {
    /// Wraps the given function into a `Callback` object.
    pub fn from_fn<F: Fn(&mut Cursive) + 'static>(f: F) -> Self {
        Callback(Rc::new(Box::new(f)))
    }
}

impl Deref for Callback {
    type Target = Box<Fn(&mut Cursive)>;
    fn deref<'a>(&'a self) -> &'a Box<Fn(&mut Cursive)> {
        &self.0
    }
}

impl From<Rc<Box<Fn(&mut Cursive)>>> for Callback {
    fn from(f: Rc<Box<Fn(&mut Cursive)>>) -> Self {
        Callback(f)
    }
}

impl From<Box<Fn(&mut Cursive) + Send>> for Callback {
    fn from(f: Box<Fn(&mut Cursive) + Send>) -> Self {
        Callback(Rc::new(f))
    }
}

impl From<Box<Fn(&mut Cursive)>> for Callback {
    fn from(f: Box<Fn(&mut Cursive)>) -> Self {
        Callback(Rc::new(f))
    }
}

/// Answer to an event notification.
/// The event can be consumed or ignored.
pub enum EventResult {
    /// The event was ignored. The parent can keep handling it.
    Ignored,
    /// The event was consumed. An optionnal callback to run is attached.
    Consumed(Option<Callback>), // TODO: make this a FnOnce?
}

impl EventResult {
    /// Convenient method to create `Consumed(Some(f))`
    pub fn with_cb<F: 'static + Fn(&mut Cursive)>(f: F) -> Self {
        EventResult::Consumed(Some(Callback::from_fn(f)))
    }

    /// Returns `true` if `self` is `EventResult::Consumed`.
    pub fn is_consumed(&self) -> bool {
        match *self {
            EventResult::Consumed(_) => true,
            EventResult::Ignored => false,
        }
    }

    /// Process this result if it is a callback.
    ///
    /// Does nothing otherwise.
    pub fn process(self, s: &mut Cursive) {
        if let EventResult::Consumed(Some(cb)) = self {
            cb(s);
        }
    }

    /// Returns `self` if it is not `Event::Ignored`, otherwise returns `f()`.
    pub fn or_else<F>(self, f: F) -> Self
    where
        F: FnOnce() -> EventResult,
    {
        match self {
            EventResult::Ignored => f(),
            other => other,
        }
    }
}

/// A non-character key on the keyboard
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
pub enum Key {
    /// Both Enter (or Return) and numpad Enter
    Enter,
    /// Tabulation key
    Tab,
    /// Backspace key
    Backspace,
    /// Escape key
    Esc,

    /// Left arrow
    Left,
    /// Right arrow
    Right,
    /// Up arrow
    Up,
    /// Down arrow
    Down,

    /// Insert key
    Ins,
    /// Delete key
    Del,
    /// Home key
    Home,
    /// End key
    End,
    /// Page Up key
    PageUp,
    /// Page Down key
    PageDown,

    /// Pause Break key
    PauseBreak,

    /// The 5 in the center of the keypad, when numlock is disabled.
    NumpadCenter,

    /// F0 key
    F0,
    /// F1 key
    F1,
    /// F2 key
    F2,
    /// F3 key
    F3,
    /// F4 key
    F4,
    /// F5 key
    F5,
    /// F6 key
    F6,
    /// F7 key
    F7,
    /// F8 key
    F8,
    /// F9 key
    F9,
    /// F10 key
    F10,
    /// F11 key
    F11,
    /// F12 key
    F12,
}

impl Key {
    /// Returns the function key corresponding to the given number
    ///
    /// 1 -> F1, etc...
    ///
    /// # Panics
    ///
    /// If `n == 0 || n > 12`
    pub fn from_f(n: u8) -> Key {
        match n {
            0 => Key::F0,
            1 => Key::F1,
            2 => Key::F2,
            3 => Key::F3,
            4 => Key::F4,
            5 => Key::F5,
            6 => Key::F6,
            7 => Key::F7,
            8 => Key::F8,
            9 => Key::F9,
            10 => Key::F10,
            11 => Key::F11,
            12 => Key::F12,
            _ => panic!("unknown function key: F{}", n),
        }
    }
}

/// One of the buttons present on the mouse
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
pub enum MouseButton {
    /// The left button, used for main actions.
    Left,
    /// Middle button, probably the wheel. Often pastes text in X11 on linux.
    Middle,
    /// The right button, for special actions.
    Right,

    // TODO: handle more buttons?
    #[doc(hidden)]
    Other,
}

/// Represents a possible event sent by the mouse.
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
pub enum MouseEvent {
    /// A button was pressed.
    Press(MouseButton),
    /// A button was released.
    Release(MouseButton),
    /// The wheel was moved up.
    WheelUp,
    /// The wheel was moved down.
    WheelDown,
}

/// Represents an event as seen by the application.
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
pub enum Event {
    /// Event fired when the window is resized.
    WindowResize,

    /// Event fired regularly when a auto-refresh is set.
    Refresh,

    // TODO: have Char(modifier, char) and Key(modifier, key) enums?

    /// A character was entered (includes numbers, punctuation, ...).
    Char(char),
    /// A character was entered with the Ctrl key pressed.
    CtrlChar(char),
    /// A character was entered with the Alt key pressed.
    AltChar(char),

    /// A non-character key was pressed.
    Key(Key),
    /// A non-character key was pressed with the Shift key pressed.
    Shift(Key),
    /// A non-character key was pressed with the Alt key pressed.
    Alt(Key),
    /// A non-character key was pressed with the Shift and Alt keys pressed.
    AltShift(Key),
    /// A non-character key was pressed with the Ctrl key pressed.
    Ctrl(Key),
    /// A non-character key was pressed with the Ctrl and Shift keys pressed.
    CtrlShift(Key),
    /// A non-character key was pressed with the Ctrl and Alt keys pressed.
    CtrlAlt(Key),

    /// A mouse event was sent.
    Mouse {
        /// Position of the top-left corner of the view receiving this event.
        offset: Vec2,
        /// Position of the mouse when this event was fired.
        position: Vec2,
        /// The mouse event itself.
        event: MouseEvent,
    },

    // TODO: use a backend-dependent type for the unknown values?
    /// An unknown event was received.
    Unknown(Vec<u8>),

    // Having a doc-hidden event prevents people from having exhaustive matches,
    // allowing us to add events in the future.
    #[doc(hidden)]
    /// The application is about to exit.
    Exit,
}

impl From<char> for Event {
    fn from(c: char) -> Event {
        Event::Char(c)
    }
}

impl From<Key> for Event {
    fn from(k: Key) -> Event {
        Event::Key(k)
    }
}
<|endoftext|>