<commit_before>extern crate indy_crypto;

use std::error;
use std::error::Error;
use std::io;
use std::fmt;
use std::string::FromUtf8Error;
use libsqlite3_sys;
use std::ffi::NulError;
use std::str::Utf8Error;

use rusqlite;
use serde_json;
use base64;

use api::ErrorCode;
use errors::common::CommonError;
use errors::ToErrorCode;

#[derive(Debug)]
pub enum WalletError {
    InvalidHandle(String),
    UnknownType(String),
    TypeAlreadyRegistered(String),
    AlreadyExists(String),
    NotFound(String),
    IncorrectPool(String),
    PluggedWalletError(ErrorCode),
    AlreadyOpened(String),
    AccessFailed(String),
    CommonError(CommonError),
    InputError(String),
    EncodingError(String),
    StorageError(String),
    EncryptionError(String),
    ItemNotFound,
    ItemAlreadyExists,
    QueryError(String),
    IOError(io::Error),
    StructureError(String),
    ExportPathExists,
    ImportPathDoesNotExist,
    NotEmpty,
}

impl fmt::Display for WalletError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WalletError::InvalidHandle(ref description) => write!(f, "Invalid wallet handle was passed: {}", description),
            WalletError::UnknownType(ref description) => write!(f, "Unknown wallet type: {}", description),
            WalletError::TypeAlreadyRegistered(ref description) => write!(f, "Wallet type already registered: {}", description),
            WalletError::AlreadyExists(ref description) => write!(f, "Wallet with this name already exists: {}", description),
            WalletError::NotFound(ref description) => write!(f, "Wallet not found: {}", description),
            WalletError::IncorrectPool(ref description) => write!(f, "Wallet used with different pool: {}", description),
            WalletError::PluggedWalletError(err_code) => write!(f, "Plugged wallet error: {}", err_code as i32),
            WalletError::AlreadyOpened(ref description) => write!(f, "Wallet already opened: {}", description),
            WalletError::AccessFailed(ref description) => write!(f, "Wallet security error: {}", description),
            WalletError::CommonError(ref err) => err.fmt(f),
            WalletError::InputError(ref description) => write!(f, "Wallet input error: {}", description),
            WalletError::EncodingError(ref description) => write!(f, "Wallet encoding error: {}", description),
            WalletError::StorageError(ref description) => write!(f, "Wallet storage error occurred. Description: {}", description),
            WalletError::EncryptionError(ref description) => write!(f, "Wallet encryption error occurred. Description: {}", description),
            WalletError::ItemNotFound => write!(f, "Item not found"),
            WalletError::ItemAlreadyExists => write!(f, "Item already exists"),
            WalletError::QueryError(ref description) => write!(f, "{}", description),
            WalletError::IOError(ref err) => write!(f, "IO error occurred during wallet operation: {}", err.description()),
            WalletError::StructureError(ref description) => write!(f, "Invalid structure of wallet input: {}", description),
            WalletError::NotEmpty => write!(f, "Wallet is not empty"),
            WalletError::ExportPathExists => write!(f, "Export file already exists"),
            WalletError::ImportPathDoesNotExist => write!(f, "Import file does not exist"),
        }
    }
}

impl error::Error for WalletError {
    fn description(&self) -> &str {
        match *self {
            WalletError::InvalidHandle(ref description) => description,
            WalletError::UnknownType(ref description) => description,
            WalletError::TypeAlreadyRegistered(ref description) => description,
            WalletError::AlreadyExists(ref description) => description,
            WalletError::NotFound(ref description) => description,
            WalletError::IncorrectPool(ref description) => description,
            WalletError::PluggedWalletError(_) => "Plugged wallet error",
            WalletError::AlreadyOpened(ref description) => description,
            WalletError::AccessFailed(ref description) => description,
            WalletError::CommonError(ref err) => err.description(),
            WalletError::InputError(ref description) => description,
            WalletError::EncodingError(ref description) => description,
            WalletError::StorageError(ref description) => description,
            WalletError::EncryptionError(ref description) => description,
            WalletError::ItemNotFound => "Item not found",
            WalletError::ItemAlreadyExists => "Item already exists",
            WalletError::QueryError(ref description) => description,
            WalletError::IOError(ref err) => err.description(),
            WalletError::StructureError(ref description) => description,
            WalletError::NotEmpty => "Wallet is not empty",
            WalletError::ExportPathExists => "Export file already exists",
            WalletError::ImportPathDoesNotExist => "Imort file does not exist",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            WalletError::InvalidHandle(_) => None,
            WalletError::UnknownType(_) => None,
            WalletError::TypeAlreadyRegistered(_) => None,
            WalletError::AlreadyExists(_) => None,
            WalletError::NotFound(_) => None,
            WalletError::IncorrectPool(_) => None,
            WalletError::PluggedWalletError(_) => None,
            WalletError::AlreadyOpened(_) => None,
            WalletError::AccessFailed(_) => None,
            WalletError::CommonError(ref err) => Some(err),
            WalletError::InputError(_) => None,
            WalletError::EncodingError(_) => None,
            WalletError::StorageError(_) => None,
            WalletError::EncryptionError(_) => None,
            WalletError::ItemNotFound => None,
            WalletError::ItemAlreadyExists => None,
            WalletError::QueryError(_) => None,
            WalletError::IOError(ref err) => Some(err),
            WalletError::StructureError(_) => None,
            WalletError::NotEmpty => None,
            WalletError::ExportPathExists => None,
            WalletError::ImportPathDoesNotExist => None,
        }
    }
}

impl ToErrorCode for WalletError {
    fn to_error_code(&self) -> ErrorCode {
        match *self {
            WalletError::InvalidHandle(_) => ErrorCode::WalletInvalidHandle,
            WalletError::UnknownType(_) => ErrorCode::WalletUnknownTypeError,
            WalletError::TypeAlreadyRegistered(_) => ErrorCode::WalletTypeAlreadyRegisteredError,
            WalletError::AlreadyExists(_) => ErrorCode::WalletAlreadyExistsError,
            WalletError::NotFound(_) => ErrorCode::WalletNotFoundError,
            WalletError::IncorrectPool(_) => ErrorCode::WalletIncompatiblePoolError,
            WalletError::PluggedWalletError(err_code) => err_code,
            WalletError::AlreadyOpened(_) => ErrorCode::WalletAlreadyOpenedError,
            WalletError::AccessFailed(_) => ErrorCode::WalletAccessFailed,
            WalletError::CommonError(ref err) => err.to_error_code(),
            WalletError::InputError(_) => ErrorCode::WalletInputError,
            WalletError::EncodingError(_) => ErrorCode::WalletDecodingError,
            WalletError::StorageError(_) => ErrorCode::WalletStorageError,
            WalletError::EncryptionError(_) => ErrorCode::WalletEncryptonError,
            WalletError::ItemNotFound => ErrorCode::WalletItemNotFound,
            WalletError::ItemAlreadyExists => ErrorCode::WalletItemAlreadyExists,
            WalletError::QueryError(_) => ErrorCode::WalletQueryError,
            WalletError::IOError(_) => ErrorCode::WalletIOError,
            WalletError::StructureError(_) => ErrorCode::WalletStructureError,
            WalletError::NotEmpty => ErrorCode::WalletNotEmpty,
            WalletError::ExportPathExists => ErrorCode::WalletExportPathExists,
            WalletError::ImportPathDoesNotExist => ErrorCode::WalletImportPathDoesNotExist,
        }
    }
}

impl From<CommonError> for WalletError {
    fn from(err: CommonError) -> WalletError {
        WalletError::CommonError(err)
    }
}

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

impl From<indy_crypto::errors::IndyCryptoError> for WalletError {
    fn from(err: indy_crypto::errors::IndyCryptoError) -> Self {
        WalletError::CommonError(CommonError::from(err))
    }
}

impl From<WalletStorageError> for WalletError {
    fn from(err: WalletStorageError) -> Self {
        match err {
            WalletStorageError::AlreadyExists => WalletError::AlreadyExists(String::from("Storage already exists")),
            WalletStorageError::NotFound => WalletError::NotFound(String::from("Storage not found")),
            WalletStorageError::ItemNotFound => WalletError::ItemNotFound,
            WalletStorageError::ItemAlreadyExists => WalletError::ItemAlreadyExists,
            WalletStorageError::PluggedStorageError(code) => WalletError::PluggedWalletError(code),
            _ => WalletError::StorageError(err.description().to_string())
        }
    }
}

impl From<WalletQueryError> for WalletError {
    fn from(err: WalletQueryError) -> Self {
        WalletError::QueryError(format!("Invalid wallet query: {}", err.description()))
    }
}

impl From<FromUtf8Error> for WalletError {
    fn from(err: FromUtf8Error) -> Self {
        WalletError::EncodingError(format!("Failed to decode input into utf8: {}", err.description()))
    }
}

impl From<Utf8Error> for WalletError {
    fn from(err: Utf8Error) -> Self {
        WalletError::EncodingError(format!("Failed to decode input into utf8: {}", err.description()))
    }
}

impl From<base64::DecodeError> for WalletError {
    fn from(err: base64::DecodeError) -> Self {
        WalletError::EncodingError(format!("Failed to decode input into base64: {}", err.description()))
    }
}

impl From<serde_json::Error> for WalletError {
    fn from(err: serde_json::Error) -> Self {
        WalletError::EncodingError(format!("Failed to decode input into json: {}", err.description()))
    }
}

#[derive(Debug)]
pub enum WalletStorageError {
    AlreadyExists,
    NotFound,
    ConfigError,
    ItemNotFound,
    ItemAlreadyExists,
    IOError(String),
    PluggedStorageError(ErrorCode),
    CommonError(CommonError),
    QueryError(WalletQueryError),
}

impl From<rusqlite::Error> for WalletStorageError {
    fn from(err: rusqlite::Error) -> WalletStorageError {
        match &err {
            &rusqlite::Error::SqliteFailure(libsqlite3_sys::Error{code: libsqlite3_sys::ErrorCode::ConstraintViolation, extended_code: _}, _) => WalletStorageError::ItemAlreadyExists,
            _ => WalletStorageError::IOError(format!("IO error during storage operation: {}", err.description()))
        }
    }
}

impl From<io::Error> for WalletStorageError {
    fn from(err: io::Error) -> WalletStorageError {
        WalletStorageError::IOError(err.description().to_string())
    }
}

impl From<serde_json::Error> for WalletStorageError {
    fn from(_err: serde_json::Error) -> WalletStorageError {
        WalletStorageError::ConfigError
    }
}

impl From<NulError> for WalletStorageError {
    fn from(err: NulError) -> WalletStorageError { WalletStorageError::IOError(err.description().to_owned()) }
}

impl From<Utf8Error> for WalletStorageError {
    fn from(err: Utf8Error) -> WalletStorageError { WalletStorageError::IOError(err.description().to_owned()) }
}

impl From<base64::DecodeError> for WalletStorageError {
    fn from(err: base64::DecodeError) -> WalletStorageError { WalletStorageError::IOError(err.description().to_owned()) }
}

impl From<CommonError> for WalletStorageError {
    fn from(err: CommonError) -> WalletStorageError {WalletStorageError::CommonError(err)}
}

impl From<WalletQueryError> for WalletStorageError {
    fn from(err: WalletQueryError) -> Self {
        WalletStorageError::QueryError(err)
    }
}

impl error::Error for WalletStorageError {
    fn description(&self) -> &str {
        match *self {
            WalletStorageError::AlreadyExists => "Storage already created",
            WalletStorageError::NotFound => "Storage not found",
            WalletStorageError::ConfigError => "Storage configuration is invalid",
            WalletStorageError::ItemNotFound => "Item not found",
            WalletStorageError::ItemAlreadyExists => "Item already exists",
            WalletStorageError::PluggedStorageError(_err_code) => "Plugged storage error",
            WalletStorageError::IOError(ref s) => s,
            WalletStorageError::CommonError(ref e) => e.description(),
            WalletStorageError::QueryError(ref e) => e.description(),
        }
    }
}

impl fmt::Display for WalletStorageError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WalletStorageError::AlreadyExists => write!(f, "Storage already created"),
            WalletStorageError::NotFound => write!(f, "Storage not found"),
            WalletStorageError::ConfigError => write!(f, "Storage configuration is invalid"),
            WalletStorageError::ItemNotFound => write!(f, "Item not found"),
            WalletStorageError::ItemAlreadyExists => write!(f, "Item already exists"),
            WalletStorageError::IOError(ref s) => write!(f, "IO error occurred during storage operation: {}", s),
            WalletStorageError::PluggedStorageError(err_code) => write!(f, "Plugged storage error: {}", err_code as i32),
            WalletStorageError::CommonError(ref e) => write!(f, "Common error: {}", e.description()),
            WalletStorageError::QueryError(ref e) => write!(f, "Query error: {}", e.description())
        }
    }
}

#[derive(Debug)]
pub enum WalletQueryError {
    ParsingErr(String),
    StructureErr(String),
    ValueErr(String),
}

impl fmt::Display for WalletQueryError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WalletQueryError::ParsingErr(ref s) | WalletQueryError::StructureErr(ref s) | WalletQueryError::ValueErr(ref s) => f.write_str(s),
        }
    }
}
impl error::Error for WalletQueryError {
    fn description(&self) -> &str {
        match *self {
            WalletQueryError::ParsingErr(ref s) | WalletQueryError::StructureErr(ref s) | WalletQueryError::ValueErr(ref s) => s,
        }
    }
}

impl From<serde_json::Error> for WalletQueryError {
    fn from(err: serde_json::Error) -> WalletQueryError {
        WalletQueryError::ParsingErr(err.to_string())
    }
}
<commit_msg>removed WalletError::IOError (CommonError instead)<commit_after>extern crate indy_crypto;

use std::error;
use std::error::Error;
use std::io;
use std::fmt;
use std::string::FromUtf8Error;
use libsqlite3_sys;
use std::ffi::NulError;
use std::str::Utf8Error;

use rusqlite;
use serde_json;
use base64;

use api::ErrorCode;
use errors::common::CommonError;
use errors::ToErrorCode;

#[derive(Debug)]
pub enum WalletError {
    InvalidHandle(String),
    UnknownType(String),
    TypeAlreadyRegistered(String),
    AlreadyExists(String),
    NotFound(String),
    IncorrectPool(String),
    PluggedWalletError(ErrorCode),
    AlreadyOpened(String),
    AccessFailed(String),
    CommonError(CommonError),
    InputError(String),
    EncodingError(String),
    StorageError(String),
    EncryptionError(String),
    ItemNotFound,
    ItemAlreadyExists,
    QueryError(String),
    StructureError(String),
    ExportPathExists,
    ImportPathDoesNotExist,
    NotEmpty,
}

impl fmt::Display for WalletError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WalletError::InvalidHandle(ref description) => write!(f, "Invalid wallet handle was passed: {}", description),
            WalletError::UnknownType(ref description) => write!(f, "Unknown wallet type: {}", description),
            WalletError::TypeAlreadyRegistered(ref description) => write!(f, "Wallet type already registered: {}", description),
            WalletError::AlreadyExists(ref description) => write!(f, "Wallet with this name already exists: {}", description),
            WalletError::NotFound(ref description) => write!(f, "Wallet not found: {}", description),
            WalletError::IncorrectPool(ref description) => write!(f, "Wallet used with different pool: {}", description),
            WalletError::PluggedWalletError(err_code) => write!(f, "Plugged wallet error: {}", err_code as i32),
            WalletError::AlreadyOpened(ref description) => write!(f, "Wallet already opened: {}", description),
            WalletError::AccessFailed(ref description) => write!(f, "Wallet security error: {}", description),
            WalletError::CommonError(ref err) => err.fmt(f),
            WalletError::InputError(ref description) => write!(f, "Wallet input error: {}", description),
            WalletError::EncodingError(ref description) => write!(f, "Wallet encoding error: {}", description),
            WalletError::StorageError(ref description) => write!(f, "Wallet storage error occurred. Description: {}", description),
            WalletError::EncryptionError(ref description) => write!(f, "Wallet encryption error occurred. Description: {}", description),
            WalletError::ItemNotFound => write!(f, "Item not found"),
            WalletError::ItemAlreadyExists => write!(f, "Item already exists"),
            WalletError::QueryError(ref description) => write!(f, "{}", description),
            WalletError::StructureError(ref description) => write!(f, "Invalid structure of wallet input: {}", description),
            WalletError::NotEmpty => write!(f, "Wallet is not empty"),
            WalletError::ExportPathExists => write!(f, "Export file already exists"),
            WalletError::ImportPathDoesNotExist => write!(f, "Import file does not exist"),
        }
    }
}

impl error::Error for WalletError {
    fn description(&self) -> &str {
        match *self {
            WalletError::InvalidHandle(ref description) => description,
            WalletError::UnknownType(ref description) => description,
            WalletError::TypeAlreadyRegistered(ref description) => description,
            WalletError::AlreadyExists(ref description) => description,
            WalletError::NotFound(ref description) => description,
            WalletError::IncorrectPool(ref description) => description,
            WalletError::PluggedWalletError(_) => "Plugged wallet error",
            WalletError::AlreadyOpened(ref description) => description,
            WalletError::AccessFailed(ref description) => description,
            WalletError::CommonError(ref err) => err.description(),
            WalletError::InputError(ref description) => description,
            WalletError::EncodingError(ref description) => description,
            WalletError::StorageError(ref description) => description,
            WalletError::EncryptionError(ref description) => description,
            WalletError::ItemNotFound => "Item not found",
            WalletError::ItemAlreadyExists => "Item already exists",
            WalletError::QueryError(ref description) => description,
            WalletError::StructureError(ref description) => description,
            WalletError::NotEmpty => "Wallet is not empty",
            WalletError::ExportPathExists => "Export file already exists",
            WalletError::ImportPathDoesNotExist => "Imort file does not exist",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            WalletError::InvalidHandle(_) => None,
            WalletError::UnknownType(_) => None,
            WalletError::TypeAlreadyRegistered(_) => None,
            WalletError::AlreadyExists(_) => None,
            WalletError::NotFound(_) => None,
            WalletError::IncorrectPool(_) => None,
            WalletError::PluggedWalletError(_) => None,
            WalletError::AlreadyOpened(_) => None,
            WalletError::AccessFailed(_) => None,
            WalletError::CommonError(ref err) => Some(err),
            WalletError::InputError(_) => None,
            WalletError::EncodingError(_) => None,
            WalletError::StorageError(_) => None,
            WalletError::EncryptionError(_) => None,
            WalletError::ItemNotFound => None,
            WalletError::ItemAlreadyExists => None,
            WalletError::QueryError(_) => None,
            WalletError::StructureError(_) => None,
            WalletError::NotEmpty => None,
            WalletError::ExportPathExists => None,
            WalletError::ImportPathDoesNotExist => None,
        }
    }
}

impl ToErrorCode for WalletError {
    fn to_error_code(&self) -> ErrorCode {
        match *self {
            WalletError::InvalidHandle(_) => ErrorCode::WalletInvalidHandle,
            WalletError::UnknownType(_) => ErrorCode::WalletUnknownTypeError,
            WalletError::TypeAlreadyRegistered(_) => ErrorCode::WalletTypeAlreadyRegisteredError,
            WalletError::AlreadyExists(_) => ErrorCode::WalletAlreadyExistsError,
            WalletError::NotFound(_) => ErrorCode::WalletNotFoundError,
            WalletError::IncorrectPool(_) => ErrorCode::WalletIncompatiblePoolError,
            WalletError::PluggedWalletError(err_code) => err_code,
            WalletError::AlreadyOpened(_) => ErrorCode::WalletAlreadyOpenedError,
            WalletError::AccessFailed(_) => ErrorCode::WalletAccessFailed,
            WalletError::CommonError(ref err) => err.to_error_code(),
            WalletError::InputError(_) => ErrorCode::WalletInputError,
            WalletError::EncodingError(_) => ErrorCode::WalletDecodingError,
            WalletError::StorageError(_) => ErrorCode::WalletStorageError,
            WalletError::EncryptionError(_) => ErrorCode::WalletEncryptonError,
            WalletError::ItemNotFound => ErrorCode::WalletItemNotFound,
            WalletError::ItemAlreadyExists => ErrorCode::WalletItemAlreadyExists,
            WalletError::QueryError(_) => ErrorCode::WalletQueryError,
            WalletError::StructureError(_) => ErrorCode::WalletStructureError,
            WalletError::NotEmpty => ErrorCode::WalletNotEmpty,
            WalletError::ExportPathExists => ErrorCode::WalletExportPathExists,
            WalletError::ImportPathDoesNotExist => ErrorCode::WalletImportPathDoesNotExist,
        }
    }
}

impl From<CommonError> for WalletError {
    fn from(err: CommonError) -> WalletError {
        WalletError::CommonError(err)
    }
}

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

impl From<indy_crypto::errors::IndyCryptoError> for WalletError {
    fn from(err: indy_crypto::errors::IndyCryptoError) -> Self {
        WalletError::CommonError(CommonError::from(err))
    }
}

impl From<WalletStorageError> for WalletError {
    fn from(err: WalletStorageError) -> Self {
        match err {
            WalletStorageError::AlreadyExists => WalletError::AlreadyExists(String::from("Storage already exists")),
            WalletStorageError::NotFound => WalletError::NotFound(String::from("Storage not found")),
            WalletStorageError::ItemNotFound => WalletError::ItemNotFound,
            WalletStorageError::ItemAlreadyExists => WalletError::ItemAlreadyExists,
            WalletStorageError::PluggedStorageError(code) => WalletError::PluggedWalletError(code),
            _ => WalletError::StorageError(err.description().to_string())
        }
    }
}

impl From<WalletQueryError> for WalletError {
    fn from(err: WalletQueryError) -> Self {
        WalletError::QueryError(format!("Invalid wallet query: {}", err.description()))
    }
}

impl From<FromUtf8Error> for WalletError {
    fn from(err: FromUtf8Error) -> Self {
        WalletError::EncodingError(format!("Failed to decode input into utf8: {}", err.description()))
    }
}

impl From<Utf8Error> for WalletError {
    fn from(err: Utf8Error) -> Self {
        WalletError::EncodingError(format!("Failed to decode input into utf8: {}", err.description()))
    }
}

impl From<base64::DecodeError> for WalletError {
    fn from(err: base64::DecodeError) -> Self {
        WalletError::EncodingError(format!("Failed to decode input into base64: {}", err.description()))
    }
}

impl From<serde_json::Error> for WalletError {
    fn from(err: serde_json::Error) -> Self {
        WalletError::EncodingError(format!("Failed to decode input into json: {}", err.description()))
    }
}

#[derive(Debug)]
pub enum WalletStorageError {
    AlreadyExists,
    NotFound,
    ConfigError,
    ItemNotFound,
    ItemAlreadyExists,
    IOError(String),
    PluggedStorageError(ErrorCode),
    CommonError(CommonError),
    QueryError(WalletQueryError),
}

impl From<rusqlite::Error> for WalletStorageError {
    fn from(err: rusqlite::Error) -> WalletStorageError {
        match &err {
            &rusqlite::Error::SqliteFailure(libsqlite3_sys::Error{code: libsqlite3_sys::ErrorCode::ConstraintViolation, extended_code: _}, _) => WalletStorageError::ItemAlreadyExists,
            _ => WalletStorageError::IOError(format!("IO error during storage operation: {}", err.description()))
        }
    }
}

impl From<io::Error> for WalletStorageError {
    fn from(err: io::Error) -> WalletStorageError {
        WalletStorageError::IOError(err.description().to_string())
    }
}

impl From<serde_json::Error> for WalletStorageError {
    fn from(_err: serde_json::Error) -> WalletStorageError {
        WalletStorageError::ConfigError
    }
}

impl From<NulError> for WalletStorageError {
    fn from(err: NulError) -> WalletStorageError { WalletStorageError::IOError(err.description().to_owned()) }
}

impl From<Utf8Error> for WalletStorageError {
    fn from(err: Utf8Error) -> WalletStorageError { WalletStorageError::IOError(err.description().to_owned()) }
}

impl From<base64::DecodeError> for WalletStorageError {
    fn from(err: base64::DecodeError) -> WalletStorageError { WalletStorageError::IOError(err.description().to_owned()) }
}

impl From<CommonError> for WalletStorageError {
    fn from(err: CommonError) -> WalletStorageError {WalletStorageError::CommonError(err)}
}

impl From<WalletQueryError> for WalletStorageError {
    fn from(err: WalletQueryError) -> Self {
        WalletStorageError::QueryError(err)
    }
}

impl error::Error for WalletStorageError {
    fn description(&self) -> &str {
        match *self {
            WalletStorageError::AlreadyExists => "Storage already created",
            WalletStorageError::NotFound => "Storage not found",
            WalletStorageError::ConfigError => "Storage configuration is invalid",
            WalletStorageError::ItemNotFound => "Item not found",
            WalletStorageError::ItemAlreadyExists => "Item already exists",
            WalletStorageError::PluggedStorageError(_err_code) => "Plugged storage error",
            WalletStorageError::IOError(ref s) => s,
            WalletStorageError::CommonError(ref e) => e.description(),
            WalletStorageError::QueryError(ref e) => e.description(),
        }
    }
}

impl fmt::Display for WalletStorageError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WalletStorageError::AlreadyExists => write!(f, "Storage already created"),
            WalletStorageError::NotFound => write!(f, "Storage not found"),
            WalletStorageError::ConfigError => write!(f, "Storage configuration is invalid"),
            WalletStorageError::ItemNotFound => write!(f, "Item not found"),
            WalletStorageError::ItemAlreadyExists => write!(f, "Item already exists"),
            WalletStorageError::IOError(ref s) => write!(f, "IO error occurred during storage operation: {}", s),
            WalletStorageError::PluggedStorageError(err_code) => write!(f, "Plugged storage error: {}", err_code as i32),
            WalletStorageError::CommonError(ref e) => write!(f, "Common error: {}", e.description()),
            WalletStorageError::QueryError(ref e) => write!(f, "Query error: {}", e.description())
        }
    }
}

#[derive(Debug)]
pub enum WalletQueryError {
    ParsingErr(String),
    StructureErr(String),
    ValueErr(String),
}

impl fmt::Display for WalletQueryError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WalletQueryError::ParsingErr(ref s) | WalletQueryError::StructureErr(ref s) | WalletQueryError::ValueErr(ref s) => f.write_str(s),
        }
    }
}
impl error::Error for WalletQueryError {
    fn description(&self) -> &str {
        match *self {
            WalletQueryError::ParsingErr(ref s) | WalletQueryError::StructureErr(ref s) | WalletQueryError::ValueErr(ref s) => s,
        }
    }
}

impl From<serde_json::Error> for WalletQueryError {
    fn from(err: serde_json::Error) -> WalletQueryError {
        WalletQueryError::ParsingErr(err.to_string())
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (path) eacces

#[macro_use]
extern crate uucore;

use clap::{crate_version, Arg, Command};
use remove_dir_all::remove_dir_all;
use std::collections::VecDeque;
use std::fs;
use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr;
use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::format_usage;
use walkdir::{DirEntry, WalkDir};

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    Never,
    Once,
    Always,
}

struct Options {
    force: bool,
    interactive: InteractiveMode,
    #[allow(dead_code)]
    one_fs: bool,
    preserve_root: bool,
    recursive: bool,
    dir: bool,
    verbose: bool,
}

static ABOUT: &str = "Remove (unlink) the FILE(s)";
const USAGE: &str = "{} [OPTION]... FILE...";
static OPT_DIR: &str = "dir";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_FORCE: &str = "force";
static OPT_NO_PRESERVE_ROOT: &str = "no-preserve-root";
static OPT_ONE_FILE_SYSTEM: &str = "one-file-system";
static OPT_PRESERVE_ROOT: &str = "preserve-root";
static OPT_PROMPT: &str = "prompt";
static OPT_PROMPT_MORE: &str = "prompt-more";
static OPT_RECURSIVE: &str = "recursive";
static OPT_RECURSIVE_R: &str = "recursive_R";
static OPT_VERBOSE: &str = "verbose";
static PRESUME_INPUT_TTY: &str = "-presume-input-tty";

static ARG_FILES: &str = "files";

fn get_long_usage() -> String {
    String::from(
        "By default, rm does not remove directories.  Use the --recursive (-r or -R)
        option to remove each listed directory, too, along with all of its contents

        To remove a file whose name starts with a '-', for example '-foo',
        use one of these commands:
        rm -- -foo

        rm ./-foo

        Note that if you use rm to remove a file, it might be possible to recover
        some of its contents, given sufficient expertise and/or time.  For greater
        assurance that the contents are truly unrecoverable, consider using shred.",
    )
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let long_usage = get_long_usage();

    let matches = uu_app().after_help(&long_usage[..]).get_matches_from(args);

    let files: Vec<String> = matches
        .get_many::<String>(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let force = matches.contains_id(OPT_FORCE);

    if files.is_empty() && !force {
        // Still check by hand and not use clap
        // Because "rm -f" is a thing
        return Err(UUsageError::new(1, "missing operand"));
    } else {
        let options = Options {
            force,
            interactive: {
                if matches.contains_id(OPT_PROMPT) {
                    InteractiveMode::Always
                } else if matches.contains_id(OPT_PROMPT_MORE) {
                    InteractiveMode::Once
                } else if matches.contains_id(OPT_INTERACTIVE) {
                    match matches.value_of(OPT_INTERACTIVE).unwrap() {
                        "never" => InteractiveMode::Never,
                        "once" => InteractiveMode::Once,
                        "always" => InteractiveMode::Always,
                        val => {
                            return Err(USimpleError::new(
                                1,
                                format!("Invalid argument to interactive ({})", val),
                            ))
                        }
                    }
                } else {
                    InteractiveMode::Never
                }
            },
            one_fs: matches.contains_id(OPT_ONE_FILE_SYSTEM),
            preserve_root: !matches.contains_id(OPT_NO_PRESERVE_ROOT),
            recursive: matches.contains_id(OPT_RECURSIVE) || matches.contains_id(OPT_RECURSIVE_R),
            dir: matches.contains_id(OPT_DIR),
            verbose: matches.contains_id(OPT_VERBOSE),
        };
        if options.interactive == InteractiveMode::Once && (options.recursive || files.len() > 3) {
            let msg = if options.recursive {
                "Remove all arguments recursively? "
            } else {
                "Remove all arguments? "
            };
            if !prompt(msg) {
                return Ok(());
            }
        }

        if remove(&files, &options) {
            return Err(1.into());
        }
    }
    Ok(())
}

pub fn uu_app<'a>() -> Command<'a> {
    Command::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(format_usage(USAGE))
        .infer_long_args(true)
        .arg(
            Arg::new(OPT_FORCE)
            .short('f')
            .long(OPT_FORCE)
            .multiple_occurrences(true)
            .help("ignore nonexistent files and arguments, never prompt")
        )
        .arg(
            Arg::new(OPT_PROMPT)
            .short('i')
            .help("prompt before every removal")
        )
        .arg(
            Arg::new(OPT_PROMPT_MORE)
            .short('I')
            .help("prompt once before removing more than three files, or when removing recursively. Less intrusive than -i, while still giving some protection against most mistakes")
        )
        .arg(
            Arg::new(OPT_INTERACTIVE)
            .long(OPT_INTERACTIVE)
            .help("prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, prompts always")
            .value_name("WHEN")
            .takes_value(true)
        )
        .arg(
            Arg::new(OPT_ONE_FILE_SYSTEM)
            .long(OPT_ONE_FILE_SYSTEM)
            .help("when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::new(OPT_NO_PRESERVE_ROOT)
            .long(OPT_NO_PRESERVE_ROOT)
            .help("do not treat '/' specially")
        )
        .arg(
            Arg::new(OPT_PRESERVE_ROOT)
            .long(OPT_PRESERVE_ROOT)
            .help("do not remove '/' (default)")
        )
        .arg(
            Arg::new(OPT_RECURSIVE).short('r')
            .multiple_occurrences(true)
            .long(OPT_RECURSIVE)
            .help("remove directories and their contents recursively")
        )
        .arg(
            // To mimic GNU's behavior we also want the '-R' flag. However, using clap's
            // alias method 'visible_alias("R")' would result in a long '--R' flag.
            Arg::new(OPT_RECURSIVE_R).short('R')
            .help("Equivalent to -r")
        )
        .arg(
            Arg::new(OPT_DIR)
            .short('d')
            .long(OPT_DIR)
            .help("remove empty directories")
        )
        .arg(
            Arg::new(OPT_VERBOSE)
            .short('v')
            .long(OPT_VERBOSE)
            .help("explain what is being done")
        )
        // From the GNU source code:
        // This is solely for testing.
        // Do not document.
        // It is relatively difficult to ensure that there is a tty on stdin.
        // Since rm acts differently depending on that, without this option,
        // it'd be harder to test the parts of rm that depend on that setting.
        // In contrast with Arg::long, Arg::alias does not strip leading
        // hyphens. Therefore it supports 3 leading hyphens.
        .arg(
            Arg::new(PRESUME_INPUT_TTY)
            .long(PRESUME_INPUT_TTY)
            .alias(PRESUME_INPUT_TTY)
            .hide(true)
        )
        .arg(
            Arg::new(ARG_FILES)
            .multiple_occurrences(true)
            .takes_value(true)
            .min_values(1)
            .value_hint(clap::ValueHint::AnyPath)
        )
}

// TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: &[String], options: &Options) -> bool {
    let mut had_err = false;

    for filename in files {
        let file = Path::new(filename);
        had_err = match file.symlink_metadata() {
            Ok(metadata) => {
                if metadata.is_dir() {
                    handle_dir(file, options)
                } else if is_symlink_dir(&metadata) {
                    remove_dir(file, options)
                } else {
                    remove_file(file, options)
                }
            }
            Err(_e) => {
                // TODO: actually print out the specific error
                // TODO: When the error is not about missing files
                // (e.g., permission), even rm -f should fail with
                // outputting the error, but there's no easy eay.
                if !options.force {
                    show_error!(
                        "cannot remove {}: No such file or directory",
                        filename.quote()
                    );
                    true
                } else {
                    false
                }
            }
        }
        .bitor(had_err);
    }

    had_err
}

fn handle_dir(path: &Path, options: &Options) -> bool {
    let mut had_err = false;

    let is_root = path.has_root() && path.parent().is_none();
    if options.recursive && (!is_root || !options.preserve_root) {
        if options.interactive != InteractiveMode::Always && !options.verbose {
            // we need the extra crate because apparently fs::remove_dir_all() does not function
            // correctly on Windows
            if let Err(e) = remove_dir_all(path) {
                had_err = true;
                if e.kind() == std::io::ErrorKind::PermissionDenied {
                    // GNU compatibility (rm/fail-eacces.sh)
                    // here, GNU doesn't use some kind of remove_dir_all
                    // It will show directory+file
                    show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
                } else {
                    show_error!("cannot remove {}: {}", path.quote(), e);
                }
            }
        } else {
            let mut dirs: VecDeque<DirEntry> = VecDeque::new();

            for entry in WalkDir::new(path) {
                match entry {
                    Ok(entry) => {
                        let file_type = entry.file_type();
                        if file_type.is_dir() {
                            dirs.push_back(entry);
                        } else {
                            had_err = remove_file(entry.path(), options).bitor(had_err);
                        }
                    }
                    Err(e) => {
                        had_err = true;
                        show_error!("recursing in {}: {}", path.quote(), e);
                    }
                }
            }

            for dir in dirs.iter().rev() {
                had_err = remove_dir(dir.path(), options).bitor(had_err);
            }
        }
    } else if options.dir && (!is_root || !options.preserve_root) {
        had_err = remove_dir(path, options).bitor(had_err);
    } else if options.recursive {
        show_error!("could not remove directory {}", path.quote());
        had_err = true;
    } else {
        show_error!(
            "cannot remove {}: Is a directory", // GNU's rm error message does not include help
            path.quote()
        );
        had_err = true;
    }

    had_err
}

fn remove_dir(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, true)
    } else {
        true
    };
    if response {
        if let Ok(mut read_dir) = fs::read_dir(path) {
            if options.dir || options.recursive {
                if read_dir.next().is_none() {
                    match fs::remove_dir(path) {
                        Ok(_) => {
                            if options.verbose {
                                println!("removed directory {}", normalize(path).quote());
                            }
                        }
                        Err(e) => {
                            if e.kind() == std::io::ErrorKind::PermissionDenied {
                                // GNU compatibility (rm/fail-eacces.sh)
                                show_error!(
                                    "cannot remove {}: {}",
                                    path.quote(),
                                    "Permission denied"
                                );
                            } else {
                                show_error!("cannot remove {}: {}", path.quote(), e);
                            }
                            return true;
                        }
                    }
                } else {
                    // directory can be read but is not empty
                    show_error!("cannot remove {}: Directory not empty", path.quote());
                    return true;
                }
            } else {
                // called to remove a symlink_dir (windows) without "-r"/"-R" or "-d"
                show_error!("cannot remove {}: Is a directory", path.quote());
                return true;
            }
        } else {
            // GNU's rm shows this message if directory is empty but not readable
            show_error!("cannot remove {}: Directory not empty", path.quote());
            return true;
        }
    }

    false
}

fn remove_file(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, false)
    } else {
        true
    };
    if response {
        match fs::remove_file(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed {}", normalize(path).quote());
                }
            }
            Err(e) => {
                if e.kind() == std::io::ErrorKind::PermissionDenied {
                    // GNU compatibility (rm/fail-eacces.sh)
                    show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
                } else {
                    show_error!("cannot remove {}: {}", path.quote(), e);
                }
                return true;
            }
        }
    }

    false
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory {}? ", path.quote())))
    } else {
        prompt(&(format!("rm: remove file {}? ", path.quote())))
    }
}

fn normalize(path: &Path) -> PathBuf {
    // copied from https://github.com/rust-lang/cargo/blob/2e4cfc2b7d43328b207879228a2ca7d427d188bb/src/cargo/util/paths.rs#L65-L90
    // both projects are MIT https://github.com/rust-lang/cargo/blob/master/LICENSE-MIT
    // for std impl progress see rfc https://github.com/rust-lang/rfcs/issues/2208
    // TODO: replace this once that lands
    uucore::fs::normalize_path(path)
}

fn prompt(msg: &str) -> bool {
    let _ = stderr().write_all(msg.as_bytes());
    let _ = stderr().flush();

    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();
    let read = stdin.read_until(b'\n', &mut buf);
    match read {
        Ok(x) if x > 0 => matches!(buf[0], b'y' | b'Y'),
        _ => false,
    }
}

#[cfg(not(windows))]
fn is_symlink_dir(_metadata: &fs::Metadata) -> bool {
    false
}

#[cfg(windows)]
use std::os::windows::prelude::MetadataExt;

#[cfg(windows)]
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
    use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;

    metadata.file_type().is_symlink()
        && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
}
<commit_msg>rm: added write-protected check for files<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (path) eacces

#[macro_use]
extern crate uucore;

use clap::{crate_version, Arg, Command};
use remove_dir_all::remove_dir_all;
use std::collections::VecDeque;
use std::fs;
use std::fs::File;
use std::io::ErrorKind;
use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr;
use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::format_usage;
use walkdir::{DirEntry, WalkDir};

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    Never,
    Once,
    Always,
}

struct Options {
    force: bool,
    interactive: InteractiveMode,
    #[allow(dead_code)]
    one_fs: bool,
    preserve_root: bool,
    recursive: bool,
    dir: bool,
    verbose: bool,
}

static ABOUT: &str = "Remove (unlink) the FILE(s)";
const USAGE: &str = "{} [OPTION]... FILE...";
static OPT_DIR: &str = "dir";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_FORCE: &str = "force";
static OPT_NO_PRESERVE_ROOT: &str = "no-preserve-root";
static OPT_ONE_FILE_SYSTEM: &str = "one-file-system";
static OPT_PRESERVE_ROOT: &str = "preserve-root";
static OPT_PROMPT: &str = "prompt";
static OPT_PROMPT_MORE: &str = "prompt-more";
static OPT_RECURSIVE: &str = "recursive";
static OPT_RECURSIVE_R: &str = "recursive_R";
static OPT_VERBOSE: &str = "verbose";
static PRESUME_INPUT_TTY: &str = "-presume-input-tty";

static ARG_FILES: &str = "files";

fn get_long_usage() -> String {
    String::from(
        "By default, rm does not remove directories.  Use the --recursive (-r or -R)
        option to remove each listed directory, too, along with all of its contents

        To remove a file whose name starts with a '-', for example '-foo',
        use one of these commands:
        rm -- -foo

        rm ./-foo

        Note that if you use rm to remove a file, it might be possible to recover
        some of its contents, given sufficient expertise and/or time.  For greater
        assurance that the contents are truly unrecoverable, consider using shred.",
    )
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let long_usage = get_long_usage();

    let matches = uu_app().after_help(&long_usage[..]).get_matches_from(args);

    let files: Vec<String> = matches
        .get_many::<String>(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let force = matches.contains_id(OPT_FORCE);

    if files.is_empty() && !force {
        // Still check by hand and not use clap
        // Because "rm -f" is a thing
        return Err(UUsageError::new(1, "missing operand"));
    } else {
        let options = Options {
            force,
            interactive: {
                if matches.contains_id(OPT_PROMPT) {
                    InteractiveMode::Always
                } else if matches.contains_id(OPT_PROMPT_MORE) {
                    InteractiveMode::Once
                } else if matches.contains_id(OPT_INTERACTIVE) {
                    match matches.value_of(OPT_INTERACTIVE).unwrap() {
                        "never" => InteractiveMode::Never,
                        "once" => InteractiveMode::Once,
                        "always" => InteractiveMode::Always,
                        val => {
                            return Err(USimpleError::new(
                                1,
                                format!("Invalid argument to interactive ({})", val),
                            ))
                        }
                    }
                } else {
                    InteractiveMode::Never
                }
            },
            one_fs: matches.contains_id(OPT_ONE_FILE_SYSTEM),
            preserve_root: !matches.contains_id(OPT_NO_PRESERVE_ROOT),
            recursive: matches.contains_id(OPT_RECURSIVE) || matches.contains_id(OPT_RECURSIVE_R),
            dir: matches.contains_id(OPT_DIR),
            verbose: matches.contains_id(OPT_VERBOSE),
        };
        if options.interactive == InteractiveMode::Once && (options.recursive || files.len() > 3) {
            let msg = if options.recursive {
                "Remove all arguments recursively? "
            } else {
                "Remove all arguments? "
            };
            if !prompt(msg) {
                return Ok(());
            }
        }

        if remove(&files, &options) {
            return Err(1.into());
        }
    }
    Ok(())
}

pub fn uu_app<'a>() -> Command<'a> {
    Command::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(format_usage(USAGE))
        .infer_long_args(true)
        .arg(
            Arg::new(OPT_FORCE)
            .short('f')
            .long(OPT_FORCE)
            .multiple_occurrences(true)
            .help("ignore nonexistent files and arguments, never prompt")
        )
        .arg(
            Arg::new(OPT_PROMPT)
            .short('i')
            .help("prompt before every removal")
        )
        .arg(
            Arg::new(OPT_PROMPT_MORE)
            .short('I')
            .help("prompt once before removing more than three files, or when removing recursively. Less intrusive than -i, while still giving some protection against most mistakes")
        )
        .arg(
            Arg::new(OPT_INTERACTIVE)
            .long(OPT_INTERACTIVE)
            .help("prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, prompts always")
            .value_name("WHEN")
            .takes_value(true)
        )
        .arg(
            Arg::new(OPT_ONE_FILE_SYSTEM)
            .long(OPT_ONE_FILE_SYSTEM)
            .help("when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::new(OPT_NO_PRESERVE_ROOT)
            .long(OPT_NO_PRESERVE_ROOT)
            .help("do not treat '/' specially")
        )
        .arg(
            Arg::new(OPT_PRESERVE_ROOT)
            .long(OPT_PRESERVE_ROOT)
            .help("do not remove '/' (default)")
        )
        .arg(
            Arg::new(OPT_RECURSIVE).short('r')
            .multiple_occurrences(true)
            .long(OPT_RECURSIVE)
            .help("remove directories and their contents recursively")
        )
        .arg(
            // To mimic GNU's behavior we also want the '-R' flag. However, using clap's
            // alias method 'visible_alias("R")' would result in a long '--R' flag.
            Arg::new(OPT_RECURSIVE_R).short('R')
            .help("Equivalent to -r")
        )
        .arg(
            Arg::new(OPT_DIR)
            .short('d')
            .long(OPT_DIR)
            .help("remove empty directories")
        )
        .arg(
            Arg::new(OPT_VERBOSE)
            .short('v')
            .long(OPT_VERBOSE)
            .help("explain what is being done")
        )
        // From the GNU source code:
        // This is solely for testing.
        // Do not document.
        // It is relatively difficult to ensure that there is a tty on stdin.
        // Since rm acts differently depending on that, without this option,
        // it'd be harder to test the parts of rm that depend on that setting.
        // In contrast with Arg::long, Arg::alias does not strip leading
        // hyphens. Therefore it supports 3 leading hyphens.
        .arg(
            Arg::new(PRESUME_INPUT_TTY)
            .long(PRESUME_INPUT_TTY)
            .alias(PRESUME_INPUT_TTY)
            .hide(true)
        )
        .arg(
            Arg::new(ARG_FILES)
            .multiple_occurrences(true)
            .takes_value(true)
            .min_values(1)
            .value_hint(clap::ValueHint::AnyPath)
        )
}

// TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: &[String], options: &Options) -> bool {
    let mut had_err = false;

    for filename in files {
        let file = Path::new(filename);
        had_err = match file.symlink_metadata() {
            Ok(metadata) => {
                if metadata.is_dir() {
                    handle_dir(file, options)
                } else if is_symlink_dir(&metadata) {
                    remove_dir(file, options)
                } else {
                    remove_file(file, options)
                }
            }
            Err(_e) => {
                // TODO: actually print out the specific error
                // TODO: When the error is not about missing files
                // (e.g., permission), even rm -f should fail with
                // outputting the error, but there's no easy eay.
                if !options.force {
                    show_error!(
                        "cannot remove {}: No such file or directory",
                        filename.quote()
                    );
                    true
                } else {
                    false
                }
            }
        }
        .bitor(had_err);
    }

    had_err
}

fn handle_dir(path: &Path, options: &Options) -> bool {
    let mut had_err = false;

    let is_root = path.has_root() && path.parent().is_none();
    if options.recursive && (!is_root || !options.preserve_root) {
        if options.interactive != InteractiveMode::Always && !options.verbose {
            // we need the extra crate because apparently fs::remove_dir_all() does not function
            // correctly on Windows
            if let Err(e) = remove_dir_all(path) {
                had_err = true;
                if e.kind() == std::io::ErrorKind::PermissionDenied {
                    // GNU compatibility (rm/fail-eacces.sh)
                    // here, GNU doesn't use some kind of remove_dir_all
                    // It will show directory+file
                    show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
                } else {
                    show_error!("cannot remove {}: {}", path.quote(), e);
                }
            }
        } else {
            let mut dirs: VecDeque<DirEntry> = VecDeque::new();

            for entry in WalkDir::new(path) {
                match entry {
                    Ok(entry) => {
                        let file_type = entry.file_type();
                        if file_type.is_dir() {
                            dirs.push_back(entry);
                        } else {
                            had_err = remove_file(entry.path(), options).bitor(had_err);
                        }
                    }
                    Err(e) => {
                        had_err = true;
                        show_error!("recursing in {}: {}", path.quote(), e);
                    }
                }
            }

            for dir in dirs.iter().rev() {
                had_err = remove_dir(dir.path(), options).bitor(had_err);
            }
        }
    } else if options.dir && (!is_root || !options.preserve_root) {
        had_err = remove_dir(path, options).bitor(had_err);
    } else if options.recursive {
        show_error!("could not remove directory {}", path.quote());
        had_err = true;
    } else {
        show_error!(
            "cannot remove {}: Is a directory", // GNU's rm error message does not include help
            path.quote()
        );
        had_err = true;
    }

    had_err
}

fn remove_dir(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, true)
    } else {
        true
    };
    if response {
        if let Ok(mut read_dir) = fs::read_dir(path) {
            if options.dir || options.recursive {
                if read_dir.next().is_none() {
                    match fs::remove_dir(path) {
                        Ok(_) => {
                            if options.verbose {
                                println!("removed directory {}", normalize(path).quote());
                            }
                        }
                        Err(e) => {
                            if e.kind() == std::io::ErrorKind::PermissionDenied {
                                // GNU compatibility (rm/fail-eacces.sh)
                                show_error!(
                                    "cannot remove {}: {}",
                                    path.quote(),
                                    "Permission denied"
                                );
                            } else {
                                show_error!("cannot remove {}: {}", path.quote(), e);
                            }
                            return true;
                        }
                    }
                } else {
                    // directory can be read but is not empty
                    show_error!("cannot remove {}: Directory not empty", path.quote());
                    return true;
                }
            } else {
                // called to remove a symlink_dir (windows) without "-r"/"-R" or "-d"
                show_error!("cannot remove {}: Is a directory", path.quote());
                return true;
            }
        } else {
            // GNU's rm shows this message if directory is empty but not readable
            show_error!("cannot remove {}: Directory not empty", path.quote());
            return true;
        }
    }

    false
}

fn remove_file(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, false)
    } else {
        true
    };
    if response && prompt_write_protected(path, false) {
        match fs::remove_file(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed {}", normalize(path).quote());
                }
            }
            Err(e) => {
                if e.kind() == std::io::ErrorKind::PermissionDenied {
                    // GNU compatibility (rm/fail-eacces.sh)
                    show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
                } else {
                    show_error!("cannot remove {}: {}", path.quote(), e);
                }
                return true;
            }
        }
    }

    false
}

fn prompt_write_protected(path: &Path, is_dir: bool) -> bool {
    match File::open(path) {
        Ok(_) => true,
        Err(err) => {
            if err.kind() == ErrorKind::PermissionDenied {
                if is_dir {
                    prompt(&(format!("rm: remove write-protected directory {}? ", path.quote())))
                } else {
                    prompt(&(format!("rm: remove write-protected file {}? ", path.quote())))
                }
            } else {
                true
            }
        }
    }
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory {}? ", path.quote())))
    } else {
        prompt(&(format!("rm: remove file {}? ", path.quote())))
    }
}

fn normalize(path: &Path) -> PathBuf {
    // copied from https://github.com/rust-lang/cargo/blob/2e4cfc2b7d43328b207879228a2ca7d427d188bb/src/cargo/util/paths.rs#L65-L90
    // both projects are MIT https://github.com/rust-lang/cargo/blob/master/LICENSE-MIT
    // for std impl progress see rfc https://github.com/rust-lang/rfcs/issues/2208
    // TODO: replace this once that lands
    uucore::fs::normalize_path(path)
}

fn prompt(msg: &str) -> bool {
    let _ = stderr().write_all(msg.as_bytes());
    let _ = stderr().flush();

    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();
    let read = stdin.read_until(b'\n', &mut buf);
    match read {
        Ok(x) if x > 0 => matches!(buf[0], b'y' | b'Y'),
        _ => false,
    }
}

#[cfg(not(windows))]
fn is_symlink_dir(_metadata: &fs::Metadata) -> bool {
    false
}

#[cfg(windows)]
use std::os::windows::prelude::MetadataExt;

#[cfg(windows)]
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
    use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;

    metadata.file_type().is_symlink()
        && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
}
<|endoftext|>


<commit_before>use crate::display_delegate::display;
use proc_macro2::{Group, Span, TokenTree};
use quote::{ToTokens};
use std::fmt::{Error, Formatter};
use syn::{self, ArgCaptured, Block, Expr, ExprVerbatim, FnArg, FnDecl, GenericParam, Ident, Pat, PatIdent, PathSegment,
          Stmt};
use syn::punctuated::Punctuated;
use syn::token::{Colon2, Semi};

const MOCKTOPUS_CRATE_NAME:     &str = "__mocktopus_crate__";
const STD_CRATE_NAME:           &str = "__mocktopus_std__";
const ARGS_TO_CONTINUE_NAME:    &str = "__mocktopus_args_to_continue__";
const UNWIND_DATA_NAME:         &str = "__mocktopus_unwind_data__";

macro_rules! error_msg {
    ($msg:expr) => { concat!("Mocktopus internal error: ", $msg) }
}

pub enum FnHeaderBuilder<'a> {
    StaticFn,
    StructImpl,
    TraitDefault,
    TraitImpl(&'a Punctuated<PathSegment, Colon2>),
}

impl<'a> FnHeaderBuilder<'a> {
    pub fn build(&self, fn_ident: &Ident, fn_decl: &FnDecl, fn_block_span: Span) -> Stmt {
        let fn_args = &fn_decl.inputs;
        let header_str = format!(
r#"{{
    extern crate mocktopus as {mocktopus};
    extern crate std as {std_crate};
    match {std_crate}::panic::catch_unwind({std_crate}::panic::AssertUnwindSafe (
            || {mocktopus}::mocking::Mockable::call_mock(&{full_fn_name}, {extract_args}))) {{
        Ok({mocktopus}::mocking::MockResult::Continue({args_to_continue})) => {restore_args},
        Ok({mocktopus}::mocking::MockResult::Return(result)) => {{
            {forget_args}
            let returned = unsafe {{ {std_crate}::mem::transmute_copy(&result) }};
            {std_crate}::mem::forget(result);
            return returned;
        }},
        Err({unwind}) => {{
            {forget_args}
            {std_crate}::panic::resume_unwind({unwind});
        }},
    }}
}}"#,
        mocktopus           = MOCKTOPUS_CRATE_NAME,
        std_crate           = STD_CRATE_NAME,
        full_fn_name        = display(|f| write_full_fn_name(f, self, fn_ident, fn_decl)),
        extract_args        = display(|f| write_extract_args(f, fn_args)),
        args_to_continue    = ARGS_TO_CONTINUE_NAME,
        restore_args        = display(|f| write_restore_args(f, fn_args)),
        forget_args         = display(|f| write_forget_args(f, fn_args)),
        unwind              = UNWIND_DATA_NAME);
        let header_block = syn::parse_str::<Block>(&header_str)
            .expect(error_msg!("generated header unparsable"));
        create_call_site_spanned_stmt(header_block, fn_block_span)
    }
}

fn create_call_site_spanned_stmt(block: Block, span: Span) -> Stmt {
    let token_stream = block.into_token_stream()
        .into_iter()
        .map(|tt| make_token_tree_span_call_site(tt, span))
        .collect();
    Stmt::Semi(
        Expr::Verbatim(
            ExprVerbatim {
                tts: token_stream,
            }
        ),
        Semi([span])
    )
}

fn make_token_tree_span_call_site(mut token_tree: TokenTree, span: Span) -> TokenTree {
    token_tree.set_span(span);
    if let TokenTree::Group(ref mut group) = token_tree {
        let tokens = group.stream().into_iter()
            .map(|tt| make_token_tree_span_call_site(tt, span))
            .collect();
        *group = Group::new(group.delimiter(), tokens);
    }
    token_tree
}

fn write_full_fn_name(f: &mut Formatter, builder: &FnHeaderBuilder, fn_ident: &Ident, fn_decl: &FnDecl)
        -> Result<(), Error> {
    match *builder {
        FnHeaderBuilder::StaticFn               => (),
        FnHeaderBuilder::StructImpl |
        FnHeaderBuilder::TraitDefault           => write!(f, "Self::")?,
        FnHeaderBuilder::TraitImpl(ref path)    => write!(f, "<Self as {}>::", display(|f| write_trait_path(f, path)))?,
    }
    write!(f, "{}::<{}>", fn_ident, display(|f| write_fn_generics(f, fn_decl)))
}

fn write_trait_path<T: ToTokens + Clone>(f: &mut Formatter, path: &Punctuated<PathSegment, T>) -> Result<(), Error> {
    write!(f, "{}", path.into_token_stream())
}

fn write_fn_generics(f: &mut Formatter, fn_decl: &FnDecl) -> Result<(), Error> {
    fn_decl.generics.params.iter()
        .filter_map(get_generic_param_name)
        .map(|param| write!(f, "{},", param))
        .collect()
}

fn get_generic_param_name(param: &GenericParam) -> Option<String> {
    match *param {
        GenericParam::Type(ref type_param) => Some(type_param.ident.to_string()),
        _ => None,
    }
}

fn write_extract_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    if fn_args.is_empty() {
        return write!(f, "()");
    }
    write!(f, "unsafe {{ (")?;
    for fn_arg_name in iter_fn_arg_names(fn_args) {
        write!(f, "{}::mem::replace(&mut *(&{} as *const _ as *mut _), {}::mem::zeroed()), ",
               STD_CRATE_NAME, fn_arg_name, STD_CRATE_NAME)?;
    }
    write!(f, ") }}")
}

fn write_restore_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    if fn_args.is_empty() {
        return writeln!(f, "()");
    }
    writeln!(f, "unsafe {{")?;
    for (fn_arg_index, fn_arg_name) in iter_fn_arg_names(fn_args).enumerate() {
        writeln!(f, "{}::mem::forget({}::mem::replace({}::mocking_utils::as_mut(&{}), {}.{}));",
            STD_CRATE_NAME, STD_CRATE_NAME, MOCKTOPUS_CRATE_NAME, fn_arg_name, ARGS_TO_CONTINUE_NAME, fn_arg_index)?;
    }
    writeln!(f, "}}")
}

fn write_forget_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    for fn_arg_name in iter_fn_arg_names(fn_args) {
        writeln!(f, "{}::mem::forget({});", STD_CRATE_NAME, fn_arg_name)?;
    }
    Ok(())
}

fn iter_fn_arg_names<'a, T>(input_args: &'a Punctuated<FnArg, T>) -> impl Iterator<Item = String> + 'a {
    input_args.iter()
        .map(|fn_arg| match *fn_arg {
            FnArg::SelfRef(_) | FnArg::SelfValue(_) => "self".to_string(),
            FnArg::Captured(
                ArgCaptured {
                    pat: Pat::Ident(
                        PatIdent {
                            ref ident,
                            subpat: None,
                            ..
                        }
                    ),
                    ..
                }
            ) => ident.to_string(),
            _ => panic!("{}: '{}'", error_msg!("invalid fn arg type"), fn_arg.clone().into_token_stream()),
        })
}
<commit_msg>Use std::ptr::read instead of mem::zeroed to avoid UB. Replacing references with zeroed() lead to Illegal instruction panic on newest compilers when opt-level > 0. #49<commit_after>use crate::display_delegate::display;
use proc_macro2::{Group, Span, TokenTree};
use quote::{ToTokens};
use std::fmt::{Error, Formatter};
use syn::{self, ArgCaptured, Block, Expr, ExprVerbatim, FnArg, FnDecl, GenericParam, Ident, Pat, PatIdent, PathSegment,
          Stmt};
use syn::punctuated::Punctuated;
use syn::token::{Colon2, Semi};

const MOCKTOPUS_CRATE_NAME:     &str = "__mocktopus_crate__";
const STD_CRATE_NAME:           &str = "__mocktopus_std__";
const ARGS_TO_CONTINUE_NAME:    &str = "__mocktopus_args_to_continue__";
const UNWIND_DATA_NAME:         &str = "__mocktopus_unwind_data__";

macro_rules! error_msg {
    ($msg:expr) => { concat!("Mocktopus internal error: ", $msg) }
}

pub enum FnHeaderBuilder<'a> {
    StaticFn,
    StructImpl,
    TraitDefault,
    TraitImpl(&'a Punctuated<PathSegment, Colon2>),
}

impl<'a> FnHeaderBuilder<'a> {
    pub fn build(&self, fn_ident: &Ident, fn_decl: &FnDecl, fn_block_span: Span) -> Stmt {
        let fn_args = &fn_decl.inputs;
        let header_str = format!(
r#"{{
    extern crate mocktopus as {mocktopus};
    extern crate std as {std_crate};
    match {std_crate}::panic::catch_unwind({std_crate}::panic::AssertUnwindSafe (
            || {mocktopus}::mocking::Mockable::call_mock(&{full_fn_name}, {extract_args}))) {{
        Ok({mocktopus}::mocking::MockResult::Continue({args_to_continue})) => {restore_args},
        Ok({mocktopus}::mocking::MockResult::Return(result)) => {{
            {forget_args}
            let returned = unsafe {{ {std_crate}::mem::transmute_copy(&result) }};
            {std_crate}::mem::forget(result);
            return returned;
        }},
        Err({unwind}) => {{
            {forget_args}
            {std_crate}::panic::resume_unwind({unwind});
        }},
    }}
}}"#,
        mocktopus           = MOCKTOPUS_CRATE_NAME,
        std_crate           = STD_CRATE_NAME,
        full_fn_name        = display(|f| write_full_fn_name(f, self, fn_ident, fn_decl)),
        extract_args        = display(|f| write_extract_args(f, fn_args)),
        args_to_continue    = ARGS_TO_CONTINUE_NAME,
        restore_args        = display(|f| write_restore_args(f, fn_args)),
        forget_args         = display(|f| write_forget_args(f, fn_args)),
        unwind              = UNWIND_DATA_NAME);
        let header_block = syn::parse_str::<Block>(&header_str)
            .expect(error_msg!("generated header unparsable"));
        create_call_site_spanned_stmt(header_block, fn_block_span)
    }
}

fn create_call_site_spanned_stmt(block: Block, span: Span) -> Stmt {
    let token_stream = block.into_token_stream()
        .into_iter()
        .map(|tt| make_token_tree_span_call_site(tt, span))
        .collect();
    Stmt::Semi(
        Expr::Verbatim(
            ExprVerbatim {
                tts: token_stream,
            }
        ),
        Semi([span])
    )
}

fn make_token_tree_span_call_site(mut token_tree: TokenTree, span: Span) -> TokenTree {
    token_tree.set_span(span);
    if let TokenTree::Group(ref mut group) = token_tree {
        let tokens = group.stream().into_iter()
            .map(|tt| make_token_tree_span_call_site(tt, span))
            .collect();
        *group = Group::new(group.delimiter(), tokens);
    }
    token_tree
}

fn write_full_fn_name(f: &mut Formatter, builder: &FnHeaderBuilder, fn_ident: &Ident, fn_decl: &FnDecl)
        -> Result<(), Error> {
    match *builder {
        FnHeaderBuilder::StaticFn               => (),
        FnHeaderBuilder::StructImpl |
        FnHeaderBuilder::TraitDefault           => write!(f, "Self::")?,
        FnHeaderBuilder::TraitImpl(ref path)    => write!(f, "<Self as {}>::", display(|f| write_trait_path(f, path)))?,
    }
    write!(f, "{}::<{}>", fn_ident, display(|f| write_fn_generics(f, fn_decl)))
}

fn write_trait_path<T: ToTokens + Clone>(f: &mut Formatter, path: &Punctuated<PathSegment, T>) -> Result<(), Error> {
    write!(f, "{}", path.into_token_stream())
}

fn write_fn_generics(f: &mut Formatter, fn_decl: &FnDecl) -> Result<(), Error> {
    fn_decl.generics.params.iter()
        .filter_map(get_generic_param_name)
        .map(|param| write!(f, "{},", param))
        .collect()
}

fn get_generic_param_name(param: &GenericParam) -> Option<String> {
    match *param {
        GenericParam::Type(ref type_param) => Some(type_param.ident.to_string()),
        _ => None,
    }
}

fn write_extract_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    if fn_args.is_empty() {
        return write!(f, "()");
    }
    write!(f, "unsafe {{ (")?;
    for fn_arg_name in iter_fn_arg_names(fn_args) {
        write!(f, "{}::ptr::read(&{} as *const _), ", STD_CRATE_NAME, fn_arg_name)?;
    }
    write!(f, ") }}")
}

fn write_restore_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    if fn_args.is_empty() {
        return writeln!(f, "()");
    }
    writeln!(f, "unsafe {{")?;
    for (fn_arg_index, fn_arg_name) in iter_fn_arg_names(fn_args).enumerate() {
        writeln!(f, "{}::mem::forget({}::mem::replace({}::mocking_utils::as_mut(&{}), {}.{}));",
            STD_CRATE_NAME, STD_CRATE_NAME, MOCKTOPUS_CRATE_NAME, fn_arg_name, ARGS_TO_CONTINUE_NAME, fn_arg_index)?;
    }
    writeln!(f, "}}")
}

fn write_forget_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    for fn_arg_name in iter_fn_arg_names(fn_args) {
        writeln!(f, "{}::mem::forget({});", STD_CRATE_NAME, fn_arg_name)?;
    }
    Ok(())
}

fn iter_fn_arg_names<'a, T>(input_args: &'a Punctuated<FnArg, T>) -> impl Iterator<Item = String> + 'a {
    input_args.iter()
        .map(|fn_arg| match *fn_arg {
            FnArg::SelfRef(_) | FnArg::SelfValue(_) => "self".to_string(),
            FnArg::Captured(
                ArgCaptured {
                    pat: Pat::Ident(
                        PatIdent {
                            ref ident,
                            subpat: None,
                            ..
                        }
                    ),
                    ..
                }
            ) => ident.to_string(),
            _ => panic!("{}: '{}'", error_msg!("invalid fn arg type"), fn_arg.clone().into_token_stream()),
        })
}
<|endoftext|>


<commit_before>use once_cell::sync::OnceCell;
use std::ops::RangeBounds;
use std::sync::RwLock;
use serde_json::json;

/// グローバルに保持するデータ。
/// ユーザによる設定と、見つからなかった場合に使うデフォルト値からなる。
/// それぞれは Json object によるツリー構造。
struct ConfigData {
    def:  serde_json::Value,
    main: serde_json::Value,
}

// default = empty Object (map)
impl Default for ConfigData {
    fn default() -> Self {
        Self { def: json!({}), main: json!({}) }
    }
}

/// 設定データ(グローバル変数)。
/// 一応スレッドセーフとするが、初期化時に一度だけ書き換え、
/// その後は read only アクセスしかしないので RwLock とする。
static CONFIG: OnceCell<RwLock<ConfigData>> = OnceCell::new();

/// 設定データを json 文字列からロードして設定データシステムを初期化する。
pub fn init_and_load(def_json: &str, main_json: &str) -> Result<(), String>
{
    // lazy inialize + wlock
    let mut config = CONFIG
        .get_or_init(|| RwLock::new(Default::default()))
        .write()
        .unwrap();

    config.def = match serde_json::from_str(def_json) {
        Ok(json) => json,
        Err(e) => return Err(format!("{}", e)),
    };
    config.main = match serde_json::from_str(main_json) {
        Ok(json) => json,
        Err(e) => return Err(format!("{}", e)),
    };

    Ok(())
}

/// Object tree を keys: 文字列の配列 (スライス) で検索する。
/// 見つからなかった場合、および
/// 数値, 真偽値, 文字列, 配列 以外が見つかった場合は json - null を返す。
/// グローバル構造内のデータを返す場合、コピーを返す。
fn search(root: &serde_json::Value, keys: &[&str]) -> serde_json::Value {
    let null_value = serde_json::Value::Null;

    let mut value = root;
    for key in keys.iter() {
        value = match value.as_object() {
            Some(map) => map.get(*key).unwrap_or(&null_value),
            None => &null_value,
        };
    }

    if value.is_number() || value.is_boolean() || value.is_string() || value.is_array() {
        value.clone()
    }
    else {
        serde_json::Value::Null
    }
}

/// main -> def の順番で検索する。
fn search_all(keys: &[&str]) -> serde_json::Value {
    // rlock
    let config = CONFIG
        .get().unwrap()
        .read().unwrap();

    let value = search(&config.main, keys);
    if !value.is_null() {
        value
    }
    else {
        search(&config.def, keys)
    }
}

/// 真偽値設定データを取得する。
/// 見つからない場合や真偽値でない場合は None を返す。
pub fn get_boolean(keys: &[&str]) -> Option<bool> {
    search_all(keys).as_bool()
}

/// 数値データを i64 で取得する。
/// 見つからない場合、数値でない場合、範囲外の場合は None を返す。
pub fn get_i64<R: RangeBounds<i64>>(keys: &[&str], range: R) -> Option<i64> {
    search_all(keys).as_i64().and_then(|num| {
        if range.contains(&num) { Some(num) }
        else { None }
    })
}

/// 数値データを u64 で取得する。
/// 見つからない場合、数値でない場合、範囲外の場合は None を返す。
pub fn get_u64<R: RangeBounds<u64>>(keys: &[&str], range: R) -> Option<u64> {
    search_all(keys).as_u64().and_then(|num| {
        if range.contains(&num) { Some(num) }
        else { None }
    })
}

/// 文字列設定データを取得する。
/// 見つからない場合や文字列でない場合は None を返す。
pub fn get_string(keys: &[&str]) -> Option<String> {
    search_all(keys).as_str().map(|s| s.into())
}

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

    #[test]
    #[serial(config)]
    fn search() {
        let main = r#"{"a": {"b": {"c": "main"}}}"#;
        let def = r#"{"a": {"b": {"d": "default"}}}"#;

        let result = init_and_load(def, main);
        assert!(result.is_ok());

        let v = get_string(&["a", "b", "c"]);
        assert!(v.unwrap() == "main");
        let v = get_string(&["a", "b", "C"]);
        assert!(v.is_none());
        let v = get_string(&["a", "b", "d"]);
        assert!(v.unwrap() == "default");
        let v = get_string(&["a", "b", "D"]);
        assert!(v.is_none());
    }

    #[test]
    #[serial(config)]
    fn i64() {
        let main = r#"{"i64": {"min": -9223372036854775808, "max": 9223372036854775807}}"#;
        let def = "{}";

        let result = init_and_load(def, main);
        assert!(result.is_ok());

        let v = get_i64(&["i64", "min"], ..);
        assert_eq!(v.unwrap(), i64::MIN);
        let v = get_i64(&["i64", "max"], ..);
        assert_eq!(v.unwrap(), i64::MAX);

        let v = get_i64(&["i64", "min"], i64::MIN+1..);
        assert!(v.is_none());
        let v = get_i64(&["i64", "max"], ..=i64::MAX-1);
        assert!(v.is_none());
    }

    #[test]
    #[serial(config)]
    fn u64() {
        let main = r#"{"u64": {"min": 0, "max": 18446744073709551615}}"#;
        let def = "{}";

        let result = init_and_load(def, main);
        assert!(result.is_ok());

        let v = get_u64(&["u64", "min"], ..);
        assert_eq!(v.unwrap(), u64::MIN);
        let v = get_u64(&["u64", "max"], ..);
        assert_eq!(v.unwrap(), u64::MAX);

        let v = get_u64(&["u64", "min"], u64::MIN+1..);
        assert!(v.is_none());
        let v = get_u64(&["u64", "max"], ..=u64::MAX-1);
        assert!(v.is_none());
    }
}
<commit_msg>パースエラーのテスト<commit_after>use once_cell::sync::OnceCell;
use std::ops::RangeBounds;
use std::sync::RwLock;
use serde_json::json;

/// グローバルに保持するデータ。
/// ユーザによる設定と、見つからなかった場合に使うデフォルト値からなる。
/// それぞれは Json object によるツリー構造。
struct ConfigData {
    def:  serde_json::Value,
    main: serde_json::Value,
}

// default = empty Object (map)
impl Default for ConfigData {
    fn default() -> Self {
        Self { def: json!({}), main: json!({}) }
    }
}

/// 設定データ(グローバル変数)。
/// 一応スレッドセーフとするが、初期化時に一度だけ書き換え、
/// その後は read only アクセスしかしないので RwLock とする。
static CONFIG: OnceCell<RwLock<ConfigData>> = OnceCell::new();

/// 設定データを json 文字列からロードして設定データシステムを初期化する。
pub fn init_and_load(def_json: &str, main_json: &str) -> Result<(), String>
{
    // parse
    let vdef = match serde_json::from_str(def_json) {
        Ok(json) => json,
        Err(e) => return Err(format!("{}", e)),
    };
    let vmain = match serde_json::from_str(main_json) {
        Ok(json) => json,
        Err(e) => return Err(format!("{}", e)),
    };

    // lazy inialize + wlock
    let mut config = CONFIG
        .get_or_init(|| RwLock::new(Default::default()))
        .write()
        .unwrap();

    config.def = vdef;
    config.main = vmain;

    Ok(())
}

/// Object tree を keys: 文字列の配列 (スライス) で検索する。
/// 見つからなかった場合、および
/// 数値, 真偽値, 文字列, 配列 以外が見つかった場合は json - null を返す。
/// グローバル構造内のデータを返す場合、コピーを返す。
fn search(root: &serde_json::Value, keys: &[&str]) -> serde_json::Value {
    let null_value = serde_json::Value::Null;

    let mut value = root;
    for key in keys.iter() {
        value = match value.as_object() {
            Some(map) => map.get(*key).unwrap_or(&null_value),
            None => &null_value,
        };
    }

    if value.is_number() || value.is_boolean() || value.is_string() || value.is_array() {
        value.clone()
    }
    else {
        serde_json::Value::Null
    }
}

/// main -> def の順番で検索する。
fn search_all(keys: &[&str]) -> serde_json::Value {
    // rlock
    let config = CONFIG
        .get().unwrap()
        .read().unwrap();

    let value = search(&config.main, keys);
    if !value.is_null() {
        value
    }
    else {
        search(&config.def, keys)
    }
}

/// 真偽値設定データを取得する。
/// 見つからない場合や真偽値でない場合は None を返す。
pub fn get_boolean(keys: &[&str]) -> Option<bool> {
    search_all(keys).as_bool()
}

/// 数値データを i64 で取得する。
/// 見つからない場合、数値でない場合、範囲外の場合は None を返す。
pub fn get_i64<R: RangeBounds<i64>>(keys: &[&str], range: R) -> Option<i64> {
    search_all(keys).as_i64().and_then(|num| {
        if range.contains(&num) { Some(num) }
        else { None }
    })
}

/// 数値データを u64 で取得する。
/// 見つからない場合、数値でない場合、範囲外の場合は None を返す。
pub fn get_u64<R: RangeBounds<u64>>(keys: &[&str], range: R) -> Option<u64> {
    search_all(keys).as_u64().and_then(|num| {
        if range.contains(&num) { Some(num) }
        else { None }
    })
}

/// 文字列設定データを取得する。
/// 見つからない場合や文字列でない場合は None を返す。
pub fn get_string(keys: &[&str]) -> Option<String> {
    search_all(keys).as_str().map(|s| s.into())
}

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

    #[test]
    #[serial(config)]
    fn search() {
        let main = r#"{"a": {"b": {"c": "main"}}}"#;
        let def = r#"{"a": {"b": {"d": "default"}}}"#;

        init_and_load(def, main).unwrap();

        let v = get_string(&["a", "b", "c"]);
        assert!(v.unwrap() == "main");
        let v = get_string(&["a", "b", "C"]);
        assert!(v.is_none());
        let v = get_string(&["a", "b", "d"]);
        assert!(v.unwrap() == "default");
        let v = get_string(&["a", "b", "D"]);
        assert!(v.is_none());
    }

    #[test]
    #[serial(config)]
    fn i64() {
        let main = r#"{"i64": {"min": -9223372036854775808, "max": 9223372036854775807}}"#;
        let def = "{}";

        init_and_load(def, main).unwrap();

        let v = get_i64(&["i64", "min"], ..);
        assert_eq!(v.unwrap(), i64::MIN);
        let v = get_i64(&["i64", "max"], ..);
        assert_eq!(v.unwrap(), i64::MAX);

        let v = get_i64(&["i64", "min"], i64::MIN+1..);
        assert!(v.is_none());
        let v = get_i64(&["i64", "max"], ..=i64::MAX-1);
        assert!(v.is_none());
    }

    #[test]
    #[serial(config)]
    fn u64() {
        let main = r#"{"u64": {"min": 0, "max": 18446744073709551615}}"#;
        let def = "{}";

        init_and_load(def, main).unwrap();

        let v = get_u64(&["u64", "min"], ..);
        assert_eq!(v.unwrap(), u64::MIN);
        let v = get_u64(&["u64", "max"], ..);
        assert_eq!(v.unwrap(), u64::MAX);

        let v = get_u64(&["u64", "min"], u64::MIN+1..);
        assert!(v.is_none());
        let v = get_u64(&["u64", "max"], ..=u64::MAX-1);
        assert!(v.is_none());
    }

    #[test]
    #[should_panic]
    fn parse_error() {
        let jsonstr = "{1: 2";
        init_and_load(jsonstr, jsonstr).unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate queuecheck;
extern crate npnc;

use std::env;

const WARMUP: usize = 1_000_000;
const MEASUREMENT: usize = 100_000_000;

fn thousands(ops: f64) -> String {
    let mut string = format!("{:.2}", ops);
    let mut index = string.find('.').unwrap();
    while index > 3 {
        index -= 3;
        string.insert(index, '_');
    }
    string
}

macro_rules! bench_throughput {
    ([$($path:tt)*], $producers:expr, $consumers:expr) => ({
        queuecheck_bench_throughput!(
            (WARMUP, MEASUREMENT),
            $producers,
            $consumers,
            |p: &npnc::$($path)*::Producer<i32>, i: i32| p.produce(i).unwrap(),
            |c: &npnc::$($path)*::Consumer<i32>| c.consume().ok()
        )
    });
}

macro_rules! bench_throughput_spsc {
    ([$($path:tt)*]$(, $size:expr)*) => ({
        let (producer, consumer) = npnc::$($path)*::channel($($size)*);
        bench_throughput!([$($path)*], vec![producer], vec![consumer])
    });
}

macro_rules! run_throughput {
    ($filter:expr, $name:expr, $runs:expr, $bench:expr) => ({
        let name = format!("throughput_{}", $name);
        if $filter.as_ref().map_or(true, |f| name.contains(f)) {
            println!("bench {} ...", name);
            let mut runs = (0..$runs).map(|_| $bench).collect::<Vec<_>>();
            runs.sort_by(|a, b| a.partial_cmp(b).unwrap());
            println!("  {} operation/second", thousands(runs[$runs / 2]));
        }
    });
}

macro_rules! bench_latency {
    ([$($path:tt)*], $producers:expr, $consumers:expr) => ({
        queuecheck_bench_latency!(
            (WARMUP, MEASUREMENT),
            $producers,
            $consumers,
            |p: &npnc::$($path)*::Producer<i32>, i: i32| p.produce(i).unwrap(),
            |c: &npnc::$($path)*::Consumer<i32>| c.consume().ok()
        )
    });
}

macro_rules! bench_latency_spsc {
    ([$($path:tt)*]$(, $size:expr)*) => ({
        let (producer, consumer) = npnc::$($path)*::channel($($size)*);
        bench_latency!([$($path)*], vec![producer], vec![consumer])
    });
}

macro_rules! run_latency {
    ($filter:expr, $name:expr, $bench:expr) => ({
        let name = format!("latency_{}", $name);
        if $filter.as_ref().map_or(true, |f| name.contains(f)) {
            println!("bench {} ...", name);
            let latency = $bench;
            println!("  consume");
            println!("    50.0%: {}ns", thousands(latency.consume.percentile(50.0)));
            println!("    70.0%: {}ns", thousands(latency.consume.percentile(70.0)));
            println!("    90.0%: {}ns", thousands(latency.consume.percentile(90.0)));
            println!("    95.0%: {}ns", thousands(latency.consume.percentile(95.0)));
            println!("    99.0%: {}ns", thousands(latency.consume.percentile(99.0)));
            println!("    99.9%: {}ns", thousands(latency.consume.percentile(99.9)));
            println!("  produce");
            println!("    50.0%: {}ns", thousands(latency.produce.percentile(50.0)));
            println!("    70.0%: {}ns", thousands(latency.produce.percentile(70.0)));
            println!("    90.0%: {}ns", thousands(latency.produce.percentile(90.0)));
            println!("    95.0%: {}ns", thousands(latency.produce.percentile(95.0)));
            println!("    99.0%: {}ns", thousands(latency.produce.percentile(99.0)));
            println!("    99.9%: {}ns", thousands(latency.produce.percentile(99.9)));
        }
    });
}

fn main() {
    let filter = env::args().nth(1).and_then(|f| if f == "--bench" { None } else { Some(f) });
    run_throughput!(filter, "bounded_spsc", 25, bench_throughput_spsc!([bounded::spsc], 2 << 24));
    run_throughput!(filter, "unbounded_spsc", 5, bench_throughput_spsc!([unbounded::spsc]));
    run_throughput!(filter, "bounded_mpmc", 5, bench_throughput_spsc!([bounded::mpmc], 2 << 24));
    run_throughput!(filter, "unbounded_mpmc", 3, bench_throughput_spsc!([unbounded::mpmc], 0));
    run_latency!(filter, "bounded_spsc", bench_latency_spsc!([bounded::spsc], 2 << 24));
    run_latency!(filter, "unbounded_spsc", bench_latency_spsc!([unbounded::spsc]));
    run_latency!(filter, "bounded_mpmc", bench_latency_spsc!([bounded::mpmc], 2 << 24));
    run_latency!(filter, "unbounded_mpmc", bench_latency_spsc!([unbounded::mpmc], 0));
}
<commit_msg>Improve latency benchmarks<commit_after>// Copyright 2017 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate queuecheck;
extern crate npnc;

use std::env;

use queuecheck::{Data};

const WARMUP: usize = 1_000_000;
const MEASUREMENT: usize = 100_000_000;

fn thousands(ops: f64) -> String {
    let mut string = format!("{:.2}", ops);
    let mut index = string.find('.').unwrap();
    while index > 3 {
        index -= 3;
        string.insert(index, '_');
    }
    string
}

fn print_latencies(name: &str, data: &Data) {
    println!("  {}", name);
    for p in [50.0, 70.0, 90.0, 95.0, 99.0, 99.9, 99.99, 99.999, 99.9999, 99.99999].iter() {
        let name = format!("{}%:", p);
        println!("    {:<10} {}ns", name, thousands(data.percentile(*p)));
    }
}

macro_rules! bench_throughput {
    ([$($path:tt)*], $producers:expr, $consumers:expr) => ({
        queuecheck_bench_throughput!(
            (WARMUP, MEASUREMENT),
            $producers,
            $consumers,
            |p: &npnc::$($path)*::Producer<i32>, i: i32| p.produce(i).unwrap(),
            |c: &npnc::$($path)*::Consumer<i32>| c.consume().ok()
        )
    });
}

macro_rules! bench_throughput_spsc {
    ([$($path:tt)*]$(, $size:expr)*) => ({
        let (producer, consumer) = npnc::$($path)*::channel($($size)*);
        bench_throughput!([$($path)*], vec![producer], vec![consumer])
    });
}

macro_rules! run_throughput {
    ($filter:expr, $name:expr, $runs:expr, $bench:expr) => ({
        let name = format!("throughput_{}", $name);
        if $filter.as_ref().map_or(true, |f| name.contains(f)) {
            println!("bench {} ...", name);
            let mut runs = (0..$runs).map(|_| $bench).collect::<Vec<_>>();
            runs.sort_by(|a, b| a.partial_cmp(b).unwrap());
            println!("  {} operation/second", thousands(runs[$runs / 2]));
        }
    });
}

macro_rules! bench_latency {
    ([$($path:tt)*], $producers:expr, $consumers:expr) => ({
        queuecheck_bench_latency!(
            (WARMUP, MEASUREMENT),
            $producers,
            $consumers,
            |p: &npnc::$($path)*::Producer<i32>, i: i32| p.produce(i).unwrap(),
            |c: &npnc::$($path)*::Consumer<i32>| c.consume().ok()
        )
    });
}

macro_rules! bench_latency_spsc {
    ([$($path:tt)*]$(, $size:expr)*) => ({
        let (producer, consumer) = npnc::$($path)*::channel($($size)*);
        bench_latency!([$($path)*], vec![producer], vec![consumer])
    });
}

macro_rules! run_latency {
    ($filter:expr, $name:expr, $bench:expr) => ({
        let name = format!("latency_{}", $name);
        if $filter.as_ref().map_or(true, |f| name.contains(f)) {
            println!("bench {} ...", name);
            let latency = $bench;
            print_latencies("consume", &latency.consume);
            print_latencies("produce", &latency.produce);
        }
    });
}

fn main() {
    let filter = env::args().nth(1).and_then(|f| if f == "--bench" { None } else { Some(f) });
    run_throughput!(filter, "bounded_spsc", 25, bench_throughput_spsc!([bounded::spsc], 2 << 24));
    run_throughput!(filter, "unbounded_spsc", 5, bench_throughput_spsc!([unbounded::spsc]));
    run_throughput!(filter, "bounded_mpmc", 5, bench_throughput_spsc!([bounded::mpmc], 2 << 24));
    run_throughput!(filter, "unbounded_mpmc", 3, bench_throughput_spsc!([unbounded::mpmc], 0));
    run_latency!(filter, "bounded_spsc", bench_latency_spsc!([bounded::spsc], 2 << 24));
    run_latency!(filter, "unbounded_spsc", bench_latency_spsc!([unbounded::spsc]));
    run_latency!(filter, "bounded_mpmc", bench_latency_spsc!([bounded::mpmc], 2 << 24));
    run_latency!(filter, "unbounded_mpmc", bench_latency_spsc!([unbounded::mpmc], 0));
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate criterion;
extern crate rs_poker;

use criterion::{Bencher, BenchmarkId, Criterion};
use rs_poker::simulated_icm::simulate_icm_tournament;

use rand::{thread_rng, Rng};

fn simulate_one_tournament(c: &mut Criterion) {
    let payments = vec![10_000, 6_000, 4_000, 1_000, 800];
    let mut rng = thread_rng();
    let mut group = c.benchmark_group("Tournament ICM");

    for num_players in [2, 3, 4, 6, 128, 256, 8000].iter() {
        let id = BenchmarkId::new("num_players", num_players);
        group.bench_with_input(id, num_players, |b: &mut Bencher, num_players: &usize| {
            let chips: Vec<i32> = (0..*num_players).map(|_pn| rng.gen_range(1, 500)).collect();
            b.iter(|| simulate_icm_tournament(&chips, &payments))
        });
    }

    group.finish();
}

criterion_group!(benches, simulate_one_tournament);
criterion_main!(benches);
<commit_msg>Fix benchmarks<commit_after>#[macro_use]
extern crate criterion;
extern crate rs_poker;

use criterion::{Bencher, BenchmarkId, Criterion};
use rs_poker::simulated_icm::simulate_icm_tournament;

use rand::{thread_rng, Rng};

fn simulate_one_tournament(c: &mut Criterion) {
    let payments = vec![10_000, 6_000, 4_000, 1_000, 800];
    let mut rng = thread_rng();
    let mut group = c.benchmark_group("Tournament ICM");

    for num_players in [2, 3, 4, 6, 128, 256, 8000].iter() {
        let id = BenchmarkId::new("num_players", num_players);
        group.bench_with_input(id, num_players, |b: &mut Bencher, num_players: &usize| {
            let chips: Vec<i32> = (0..*num_players).map(|_pn| rng.gen_range(1..500)).collect();
            b.iter(|| simulate_icm_tournament(&chips, &payments))
        });
    }

    group.finish();
}

criterion_group!(benches, simulate_one_tournament);
criterion_main!(benches);
<|endoftext|>