<commit_before>//! Fractal Global Credits amount
//!
//! This module holds the `Amount` type and the `AmountParseError`. It will eventually hold `MAX`
//! and `MIN` values for `Amount`s when constant expressions are implemented in the compiler in the
//! stable chanel.
//!
//! The maximum and minimum amount values can in any case be known by using `max_value()` and
//! `min_value()` functions in the `Amount` type:
//!
//! ```
//! use std::u64;
//! use fractal_utils::Amount;
//!
//! let max_value = Amount::max_value();
//! let min_value = Amount::min_value();
//!
//! assert_eq!(max_value, Amount::from_repr(u64::MAX));
//! assert_eq!(min_value, Amount::from_repr(u64::MIN));
//! ```

#![allow(trivial_numeric_casts)]

use std::convert::From;
use std::{fmt, str, u64};
use std::str::FromStr;
use std::result::Result;
use std::error::Error;
use std::ops::{Add, AddAssign, Sub, SubAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign};
use std::num::ParseIntError;

use rustc_serialize::{Encodable, Decodable, Encoder, Decoder};
#[cfg(feature = "json-types")]
use rustc_serialize::json;

use super::CURRENCY_SYMBOL;

// Largest amount value
// pub const MAX: Amount = Amount::max_value();
// Smallest amount value
// pub const MIN: Amount = Amount::min_value();

/// Fractal Global Credits amount
///
/// This struct can be used the same way as any other number. An `Amount` can be added or
/// substracted to another `Amount`, and it can be divided and multiplied by an integer. All
/// operations that are defined in the `Amount` scope and that are exact can be used directly as
/// usual integer / float point operations.
///
/// No negative amounts can exist, since an `Amount` is unsigned, sothe negation operator '-',
/// then, has no use with an `Amount`.
///
/// Its internal representation is a 64 bit unsigned number, that is displayed as a fixed point,
/// number of factor 1/1,000. This means that an internal representation of `1,000` would be an
/// external amount of `1`. The internal representation shouldn't be used except when serializing
/// and deserializing the data, since this type is sent in *JSON* as its internal `u64`.
///
/// The use is the following:
///
/// ```
/// use fractal_utils::Amount;
///
/// let amount = Amount::from_repr(1_654); // 1.654
/// let ten = Amount::from_repr(10_000); // 10
/// let add_ten = amount + ten;
/// assert_eq!(add_ten, Amount::from_repr(11_654)); // 11.654
/// ```
///
/// They can be divided and multiplied by any other unsigned integer:
///
/// ```
/// # use fractal_utils::Amount;
/// #
/// let mut amount = Amount::from_repr(7_000); // 7
/// amount *= 10u32;
/// assert_eq!(amount, Amount::from_repr(70_000)); // 70
///
/// amount = amount / 30u16;
/// assert_eq!(amount, Amount::from_repr(2_333)); // 2.333
///
/// amount %= 1u8;
/// assert_eq!(amount, Amount::from_repr(333)); // 0.333
/// ```
///
/// Amounts can easily be displayed using the `Display` trait as any other number:
///
/// ```
/// # use fractal_utils::Amount;
/// #
/// let amount = Amount::from_repr(56_000);
/// assert_eq!(format!("{}", amount), "56");
/// assert_eq!(format!("{:.2}", amount), "56.00");
/// assert_eq!(format!("{:.5}", amount), "56.00000");
/// assert_eq!(format!("{:05.1}", amount), "056.0");
///
/// // And with rounding:
/// let amount = Amount::from_repr(56); // 0.056
/// assert_eq!(format!("{:.2}", amount), "0.06");
/// ```
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Amount {
    value: u64,
}

impl Amount {
    /// Creates a new amount from its internal representation.
    pub fn from_repr(value: u64) -> Amount {
        Amount { value: value }
    }

    /// Gets the internal representation of the amount.
    pub fn get_repr(&self) -> u64 {
        self.value
    }

    /// Returns the smallest value that can be represented as a currency amount.
    pub fn min_value() -> Amount {
        Amount { value: u64::MIN }
    }

    /// Returns the largest value that can be represented as a currency amount.
    pub fn max_value() -> Amount {
        Amount { value: u64::MAX }
    }
}

#[cfg(feature = "json-types")]
/// The Amount type can easily be converted to json, using its `to_json()` method. Note that this
/// will print the amount as a float, with up to three decimals. In high amounts this can lead to
/// unnacuracies when printed. This can be avoided by using the `Display` trait.
impl json::ToJson for Amount {
    fn to_json(&self) -> json::Json {
        json::Json::F64(self.value as f64 / 1000.0)
    }
}

impl fmt::Display for Amount {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let units = self.value / 1_000;
        let decimal_repr = self.value % 1_000;
        let result = match f.precision() {
            None => {
                if decimal_repr == 0 {
                    format!("{}", units)
                } else if decimal_repr % 100 == 0 {
                    format!("{}.{:01}", units, decimal_repr / 100)
                } else if decimal_repr % 10 == 0 {
                    format!("{}.{:02}", units, decimal_repr / 10)
                } else {
                    format!("{}.{:03}", units, decimal_repr)
                }
            }
            Some(0) => {
                format!("{}",
                        if decimal_repr >= 500 {
                            units + 1
                        } else {
                            units
                        })
            }
            Some(1) => {
                format!("{}.{:01}",
                        units,
                        if decimal_repr % 100 >= 50 {
                            decimal_repr / 100 + 1
                        } else {
                            decimal_repr / 100
                        })
            }
            Some(2) => {
                format!("{}.{:02}",
                        units,
                        if decimal_repr % 10 >= 5 {
                            decimal_repr / 10 + 1
                        } else {
                            decimal_repr / 10
                        })
            }
            Some(p) => {
                let mut string = format!("{}.{:03}", units, decimal_repr);
                for _ in 3..p {
                    string.push('0');
                }
                string
            }
        };

        match f.width() {
            None => write!(f, "{}", result),
            Some(w) => {
                if w < result.len() {
                    write!(f, "{}", result)
                } else {
                    let mut pad = String::new();
                    for _ in result.len()..w {
                        pad.push('0');
                    }
                    write!(f, "{}{}", pad, result)
                }
            }
        }
    }
}

/// Amount parsing error.
///
/// This struct represents an amount parsing error. It explains the exact error that lead to the
/// parsing error, and implements common `Error` and `Display` traits.
#[derive(Debug)]
pub struct AmountParseError {
    description: String,
    cause: Option<ParseIntError>,
}

impl AmountParseError {
    fn new<S: AsRef<str>>(amount: S, error: S, cause: Option<ParseIntError>) -> AmountParseError {
        AmountParseError {
            description: format!("the amount {:?} is not a valid Fractal Global amount, {}",
                                 amount.as_ref(),
                                 error.as_ref()),
            cause: cause,
        }
    }
}

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

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

    fn cause(&self) -> Option<&Error> {
        match self.cause.as_ref() {
            Some(c) => Some(c),
            None => None,
        }
    }
}

impl FromStr for Amount {
    type Err = AmountParseError;
    fn from_str(s: &str) -> Result<Amount, AmountParseError> {
        if s.contains('.') {
            let parts = s.split('.').count();
            let mut split = s.split('.');
            match parts {
                2 => {
                    let units_str = split.next().unwrap();
                    let units: u64 = if units_str != "" {
                        match units_str.parse::<u64>() {
                            Ok(u) => {
                                if u <= u64::MAX / 1_000 {
                                    u * 1_000
                                } else {
                                    return Err(AmountParseError::new(s,
                                                &format!("it is too big, the maximum amount is {}",
                                                Amount::max_value()), None));
                                }
                            }
                            Err(e) => {
                                return Err(AmountParseError::new(s,
                                                                 "the units part it is not a \
                                                                  valid u64 amount",
                                                                 Some(e)))
                            }
                        }
                    } else {
                        0
                    };
                    let mut decimals_str = String::from(split.next().unwrap());
                    if decimals_str.len() == 0 {
                        return Err(AmountParseError::new(s,
                                                         "no decimals were found after the \
                                                          decimal separator",
                                                         None));
                    }
                    while decimals_str.len() < 3 {
                        decimals_str.push('0');
                    }
                    let decimals: u64 = match decimals_str.parse() {
                        Ok(d) => {
                            if decimals_str.len() == 3 {
                                d
                            } else {
                                let divisor = 10u64.pow(decimals_str.len() as u32 - 3);
                                let rem = d % divisor;
                                if rem >= divisor / 2 {
                                    d / divisor + 1
                                } else {
                                    d / divisor
                                }
                            }
                        }
                        Err(_) => {
                            return Err(AmountParseError::new(s,
                                                             "the decimal part is not a valid \
                                                              u64 number",
                                                             None))
                        }
                    };

                    if (u64::MAX - decimals) >= units {
                        Ok(Amount::from_repr(units + decimals))
                    } else {
                        Err(AmountParseError::new(s,
                                                  &format!("it is too big, the maximum amount \
                                                            is {}",
                                                           Amount::max_value()),
                                                  None))
                    }
                }
                _ => {
                    Err(AmountParseError::new(s,
                                              "an amount can only have one period to separate \
                                               units and decimals",
                                              None))
                }
            }
        } else {
            match s.parse::<u64>() {
                Ok(v) => {
                    if v <= u64::MAX / 1_000 {
                        Ok(Amount::from_repr(v * 1_000))
                    } else {
                        Err(AmountParseError::new(s,
                                                  &format!("it is too big, the maximum amount \
                                                            is {}",
                                                           Amount::max_value()),
                                                  None))
                    }
                }
                Err(_) => Err(AmountParseError::new(s, "it is not a valid u64 number", None)),
            }
        }
    }
}

impl fmt::Debug for Amount {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Amount {{ {:?} }} ({} {})",
               self.value,
               CURRENCY_SYMBOL,
               self)
    }
}

impl Encodable for Amount {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_u64(self.value)
    }
}

impl Decodable for Amount {
    fn decode<D: Decoder>(d: &mut D) -> Result<Amount, D::Error> {
        match d.read_u64() {
            Ok(repr) => Ok(Amount::from_repr(repr)),
            Err(e) => Err(e),
        }
    }
}

macro_rules! impl_ops_int {
    ($($t:ty)*) => ($(
        impl Div<$t> for Amount {
            type Output = Amount;

            fn div(self, rhs: $t) -> Amount {
                Amount { value: self.value / rhs as u64 }
            }
        }

        impl DivAssign<$t> for Amount {
            fn div_assign(&mut self, rhs: $t) {
                self.value /= rhs as u64
            }
        }

        impl Rem<$t> for Amount {
            type Output = Amount;

            fn rem(self, rhs: $t) -> Amount {
                Amount { value: self.value % (rhs as u64 * 1_000)}
            }
        }

        impl RemAssign<$t> for Amount {
            fn rem_assign(&mut self, rhs: $t) {
                self.value %= rhs as u64 * 1_000
            }
        }

        impl Mul<$t> for Amount {
            type Output = Amount;

            fn mul(self, rhs: $t) -> Amount {
                Amount { value: self.value * rhs as u64 }
            }
        }

        impl Mul<Amount> for $t {
            type Output = Amount;

            fn mul(self, rhs: Amount) -> Amount {
                Amount { value: self as u64 * rhs.value }
            }
        }

        impl MulAssign<$t> for Amount {
            fn mul_assign(&mut self, rhs: $t) {
                self.value *= rhs as u64
            }
        }
    )*)
}

impl_ops_int! { u8 u16 u32 u64 usize }

impl Add for Amount {
    type Output = Amount;

    fn add(self, rhs: Amount) -> Amount {
        Amount { value: self.value + rhs.value }
    }
}

impl AddAssign for Amount {
    fn add_assign(&mut self, rhs: Amount) {
        self.value += rhs.value
    }
}

impl Sub for Amount {
    type Output = Amount;

    fn sub(self, rhs: Amount) -> Amount {
        Amount { value: self.value - rhs.value }
    }
}

impl SubAssign for Amount {
    fn sub_assign(&mut self, rhs: Amount) {
        self.value -= rhs.value
    }
}
<commit_msg>Fixed clippy warning<commit_after>//! Fractal Global Credits amount
//!
//! This module holds the `Amount` type and the `AmountParseError`. It will eventually hold `MAX`
//! and `MIN` values for `Amount`s when constant expressions are implemented in the compiler in the
//! stable chanel.
//!
//! The maximum and minimum amount values can in any case be known by using `max_value()` and
//! `min_value()` functions in the `Amount` type:
//!
//! ```
//! use std::u64;
//! use fractal_utils::Amount;
//!
//! let max_value = Amount::max_value();
//! let min_value = Amount::min_value();
//!
//! assert_eq!(max_value, Amount::from_repr(u64::MAX));
//! assert_eq!(min_value, Amount::from_repr(u64::MIN));
//! ```

#![allow(trivial_numeric_casts)]

use std::convert::From;
use std::{fmt, str, u64};
use std::str::FromStr;
use std::result::Result;
use std::error::Error;
use std::ops::{Add, AddAssign, Sub, SubAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign};
use std::num::ParseIntError;

use rustc_serialize::{Encodable, Decodable, Encoder, Decoder};
#[cfg(feature = "json-types")]
use rustc_serialize::json;

use super::CURRENCY_SYMBOL;

// Largest amount value
// pub const MAX: Amount = Amount::max_value();
// Smallest amount value
// pub const MIN: Amount = Amount::min_value();

/// Fractal Global Credits amount
///
/// This struct can be used the same way as any other number. An `Amount` can be added or
/// substracted to another `Amount`, and it can be divided and multiplied by an integer. All
/// operations that are defined in the `Amount` scope and that are exact can be used directly as
/// usual integer / float point operations.
///
/// No negative amounts can exist, since an `Amount` is unsigned, sothe negation operator '-',
/// then, has no use with an `Amount`.
///
/// Its internal representation is a 64 bit unsigned number, that is displayed as a fixed point,
/// number of factor 1/1,000. This means that an internal representation of `1,000` would be an
/// external amount of `1`. The internal representation shouldn't be used except when serializing
/// and deserializing the data, since this type is sent in *JSON* as its internal `u64`.
///
/// The use is the following:
///
/// ```
/// use fractal_utils::Amount;
///
/// let amount = Amount::from_repr(1_654); // 1.654
/// let ten = Amount::from_repr(10_000); // 10
/// let add_ten = amount + ten;
/// assert_eq!(add_ten, Amount::from_repr(11_654)); // 11.654
/// ```
///
/// They can be divided and multiplied by any other unsigned integer:
///
/// ```
/// # use fractal_utils::Amount;
/// #
/// let mut amount = Amount::from_repr(7_000); // 7
/// amount *= 10u32;
/// assert_eq!(amount, Amount::from_repr(70_000)); // 70
///
/// amount = amount / 30u16;
/// assert_eq!(amount, Amount::from_repr(2_333)); // 2.333
///
/// amount %= 1u8;
/// assert_eq!(amount, Amount::from_repr(333)); // 0.333
/// ```
///
/// Amounts can easily be displayed using the `Display` trait as any other number:
///
/// ```
/// # use fractal_utils::Amount;
/// #
/// let amount = Amount::from_repr(56_000);
/// assert_eq!(format!("{}", amount), "56");
/// assert_eq!(format!("{:.2}", amount), "56.00");
/// assert_eq!(format!("{:.5}", amount), "56.00000");
/// assert_eq!(format!("{:05.1}", amount), "056.0");
///
/// // And with rounding:
/// let amount = Amount::from_repr(56); // 0.056
/// assert_eq!(format!("{:.2}", amount), "0.06");
/// ```
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Amount {
    value: u64,
}

impl Amount {
    /// Creates a new amount from its internal representation.
    pub fn from_repr(value: u64) -> Amount {
        Amount { value: value }
    }

    /// Gets the internal representation of the amount.
    pub fn get_repr(&self) -> u64 {
        self.value
    }

    /// Returns the smallest value that can be represented as a currency amount.
    pub fn min_value() -> Amount {
        Amount { value: u64::MIN }
    }

    /// Returns the largest value that can be represented as a currency amount.
    pub fn max_value() -> Amount {
        Amount { value: u64::MAX }
    }
}

#[cfg(feature = "json-types")]
/// The Amount type can easily be converted to json, using its `to_json()` method. Note that this
/// will print the amount as a float, with up to three decimals. In high amounts this can lead to
/// unnacuracies when printed. This can be avoided by using the `Display` trait.
impl json::ToJson for Amount {
    fn to_json(&self) -> json::Json {
        json::Json::F64(self.value as f64 / 1000.0)
    }
}

impl fmt::Display for Amount {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let units = self.value / 1_000;
        let decimal_repr = self.value % 1_000;
        let result = match f.precision() {
            None => {
                if decimal_repr == 0 {
                    format!("{}", units)
                } else if decimal_repr % 100 == 0 {
                    format!("{}.{:01}", units, decimal_repr / 100)
                } else if decimal_repr % 10 == 0 {
                    format!("{}.{:02}", units, decimal_repr / 10)
                } else {
                    format!("{}.{:03}", units, decimal_repr)
                }
            }
            Some(0) => {
                format!("{}",
                        if decimal_repr >= 500 {
                            units + 1
                        } else {
                            units
                        })
            }
            Some(1) => {
                format!("{}.{:01}",
                        units,
                        if decimal_repr % 100 >= 50 {
                            decimal_repr / 100 + 1
                        } else {
                            decimal_repr / 100
                        })
            }
            Some(2) => {
                format!("{}.{:02}",
                        units,
                        if decimal_repr % 10 >= 5 {
                            decimal_repr / 10 + 1
                        } else {
                            decimal_repr / 10
                        })
            }
            Some(p) => {
                let mut string = format!("{}.{:03}", units, decimal_repr);
                for _ in 3..p {
                    string.push('0');
                }
                string
            }
        };

        match f.width() {
            None => write!(f, "{}", result),
            Some(w) => {
                if w < result.len() {
                    write!(f, "{}", result)
                } else {
                    let mut pad = String::new();
                    for _ in result.len()..w {
                        pad.push('0');
                    }
                    write!(f, "{}{}", pad, result)
                }
            }
        }
    }
}

/// Amount parsing error.
///
/// This struct represents an amount parsing error. It explains the exact error that lead to the
/// parsing error, and implements common `Error` and `Display` traits.
#[derive(Debug)]
pub struct AmountParseError {
    description: String,
    cause: Option<ParseIntError>,
}

impl AmountParseError {
    fn new<S: AsRef<str>>(amount: S, error: S, cause: Option<ParseIntError>) -> AmountParseError {
        AmountParseError {
            description: format!("the amount {:?} is not a valid Fractal Global amount, {}",
                                 amount.as_ref(),
                                 error.as_ref()),
            cause: cause,
        }
    }
}

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

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

    fn cause(&self) -> Option<&Error> {
        match self.cause.as_ref() {
            Some(c) => Some(c),
            None => None,
        }
    }
}

impl FromStr for Amount {
    type Err = AmountParseError;
    fn from_str(s: &str) -> Result<Amount, AmountParseError> {
        if s.contains('.') {
            let parts = s.split('.').count();
            let mut split = s.split('.');
            match parts {
                2 => {
                    let units_str = split.next().unwrap();
                    let units: u64 = if units_str != "" {
                        match units_str.parse::<u64>() {
                            Ok(u) => {
                                if u <= u64::MAX / 1_000 {
                                    u * 1_000
                                } else {
                                    return Err(AmountParseError::new(s,
                                                &format!("it is too big, the maximum amount is {}",
                                                Amount::max_value()), None));
                                }
                            }
                            Err(e) => {
                                return Err(AmountParseError::new(s,
                                                                 "the units part it is not a \
                                                                  valid u64 amount",
                                                                 Some(e)))
                            }
                        }
                    } else {
                        0
                    };
                    let mut decimals_str = String::from(split.next().unwrap());
                    if decimals_str.is_empty() {
                        return Err(AmountParseError::new(s,
                                                         "no decimals were found after the \
                                                          decimal separator",
                                                         None));
                    }
                    while decimals_str.len() < 3 {
                        decimals_str.push('0');
                    }
                    let decimals: u64 = match decimals_str.parse() {
                        Ok(d) => {
                            if decimals_str.len() == 3 {
                                d
                            } else {
                                let divisor = 10u64.pow(decimals_str.len() as u32 - 3);
                                let rem = d % divisor;
                                if rem >= divisor / 2 {
                                    d / divisor + 1
                                } else {
                                    d / divisor
                                }
                            }
                        }
                        Err(_) => {
                            return Err(AmountParseError::new(s,
                                                             "the decimal part is not a valid \
                                                              u64 number",
                                                             None))
                        }
                    };

                    if (u64::MAX - decimals) >= units {
                        Ok(Amount::from_repr(units + decimals))
                    } else {
                        Err(AmountParseError::new(s,
                                                  &format!("it is too big, the maximum amount \
                                                            is {}",
                                                           Amount::max_value()),
                                                  None))
                    }
                }
                _ => {
                    Err(AmountParseError::new(s,
                                              "an amount can only have one period to separate \
                                               units and decimals",
                                              None))
                }
            }
        } else {
            match s.parse::<u64>() {
                Ok(v) => {
                    if v <= u64::MAX / 1_000 {
                        Ok(Amount::from_repr(v * 1_000))
                    } else {
                        Err(AmountParseError::new(s,
                                                  &format!("it is too big, the maximum amount \
                                                            is {}",
                                                           Amount::max_value()),
                                                  None))
                    }
                }
                Err(_) => Err(AmountParseError::new(s, "it is not a valid u64 number", None)),
            }
        }
    }
}

impl fmt::Debug for Amount {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Amount {{ {:?} }} ({} {})",
               self.value,
               CURRENCY_SYMBOL,
               self)
    }
}

impl Encodable for Amount {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_u64(self.value)
    }
}

impl Decodable for Amount {
    fn decode<D: Decoder>(d: &mut D) -> Result<Amount, D::Error> {
        match d.read_u64() {
            Ok(repr) => Ok(Amount::from_repr(repr)),
            Err(e) => Err(e),
        }
    }
}

macro_rules! impl_ops_int {
    ($($t:ty)*) => ($(
        impl Div<$t> for Amount {
            type Output = Amount;

            fn div(self, rhs: $t) -> Amount {
                Amount { value: self.value / rhs as u64 }
            }
        }

        impl DivAssign<$t> for Amount {
            fn div_assign(&mut self, rhs: $t) {
                self.value /= rhs as u64
            }
        }

        impl Rem<$t> for Amount {
            type Output = Amount;

            fn rem(self, rhs: $t) -> Amount {
                Amount { value: self.value % (rhs as u64 * 1_000)}
            }
        }

        impl RemAssign<$t> for Amount {
            fn rem_assign(&mut self, rhs: $t) {
                self.value %= rhs as u64 * 1_000
            }
        }

        impl Mul<$t> for Amount {
            type Output = Amount;

            fn mul(self, rhs: $t) -> Amount {
                Amount { value: self.value * rhs as u64 }
            }
        }

        impl Mul<Amount> for $t {
            type Output = Amount;

            fn mul(self, rhs: Amount) -> Amount {
                Amount { value: self as u64 * rhs.value }
            }
        }

        impl MulAssign<$t> for Amount {
            fn mul_assign(&mut self, rhs: $t) {
                self.value *= rhs as u64
            }
        }
    )*)
}

impl_ops_int! { u8 u16 u32 u64 usize }

impl Add for Amount {
    type Output = Amount;

    fn add(self, rhs: Amount) -> Amount {
        Amount { value: self.value + rhs.value }
    }
}

impl AddAssign for Amount {
    fn add_assign(&mut self, rhs: Amount) {
        self.value += rhs.value
    }
}

impl Sub for Amount {
    type Output = Amount;

    fn sub(self, rhs: Amount) -> Amount {
        Amount { value: self.value - rhs.value }
    }
}

impl SubAssign for Amount {
    fn sub_assign(&mut self, rhs: Amount) {
        self.value -= rhs.value
    }
}
<|endoftext|>


<commit_before>use std::cell;
use std::fmt;
use std::io;
use std::sync::Arc;

use failure::{Backtrace, Context, Fail};
use indy_crypto::errors::IndyCryptoError;
use log;

use api::ErrorCode;

pub mod prelude {
    pub use super::{err_msg, IndyError, IndyErrorExt, IndyErrorKind, IndyResult, IndyResultExt};
}

#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum IndyErrorKind {
    // Common errors
    #[fail(display = "Invalid library state")]
    InvalidState,
    #[fail(display = "Invalid structure")]
    InvalidStructure,
    #[fail(display = "IO error")]
    IOError,
    // Anoncreds errors
    #[fail(display = "Duplicated master secret")]
    MasterSecretDuplicateName,
    #[fail(display = "Proof rejected")]
    ProofRejected,
    #[fail(display = "Revocation registry is full")]
    RevocationRegistryFull,
    #[fail(display = "Invalid revocation id")]
    InvalidUserRevocId,
    #[fail(display = "Credential revoked")]
    CredentialRevoked,
    #[fail(display = "Credential definition already exists")]
    CredDefAlreadyExists,
    // Ledger errors
    #[fail(display = "No consensus")]
    NoConsensus,
    #[fail(display = "Invalid transaction")]
    InvalidTransaction,
    #[fail(display = "Item not found on ledger")]
    LedgerItemNotFound,
    // Pool errors
    #[fail(display = "Pool not created")]
    PoolNotCreated,
    #[fail(display = "Invalid pool handle")]
    InvalidPoolHandle,
    #[fail(display = "Pool work terminated")]
    PoolTerminated,
    #[fail(display = "Pool timeout")]
    PoolTimeout,
    #[fail(display = "Pool ledger config already exists")]
    PoolConfigAlreadyExists,
    #[fail(display = "Pool Genesis Transactions are not compatible with Protocol version")]
    PoolIncompatibleProtocolVersion,
    // Crypto errors
    #[fail(display = "Unknown crypto")]
    UnknownCrypto,
    // Wallet errors
    #[fail(display = "Invalid wallet handle was passed")]
    InvalidWalletHandle,
    #[fail(display = "Unknown wallet storage type")]
    UnknownWalletStorageType,
    #[fail(display = "Wallet storage type already registered")]
    WalletStorageTypeAlreadyRegistered,
    #[fail(display = "Wallet with this name already exists")]
    WalletAlreadyExists,
    #[fail(display = "Wallet not found")]
    WalletNotFound,
    #[fail(display = "Wallet already opened")]
    WalletAlreadyOpened,
    #[fail(display = "Wallet security error")]
    WalletAccessFailed,
    #[fail(display = "Wallet encoding error")]
    WalletEncodingError,
    #[fail(display = "Wallet storage error occurred")]
    WalletStorageError,
    #[fail(display = "Wallet encryption error")]
    WalletEncryptionError,
    #[fail(display = "Wallet item not found")]
    WalletItemNotFound,
    #[fail(display = "Wallet item already exists")]
    WalletItemAlreadyExists,
    #[fail(display = "Wallet query error")]
    WalletQueryError,
    // DID errors
    #[fail(display = "DID already exists")]
    DIDAlreadyExists,
    // Payments errors
    #[fail(display = "Unknown payment method type")]
    UnknownPaymentMethodType,
    #[fail(display = "Plugged payment method error")]
    IncompatiblePaymentMethods,
}

#[derive(Debug, Clone)]
pub struct IndyError {
    inner: Arc<Context<IndyErrorKind>>
}

impl Fail for IndyError {
    fn cause(&self) -> Option<&Fail> {
        self.inner.cause()
    }

    fn backtrace(&self) -> Option<&Backtrace> {
        self.inner.backtrace()
    }
}

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

impl IndyError {
    pub fn from_msg<D>(kind: IndyErrorKind, msg: D) -> IndyError
        where D: fmt::Display + fmt::Debug + Send + Sync + 'static {
        IndyError { inner: Arc::new(Context::new(msg).context(kind)) }
    }

    pub fn kind(&self) -> IndyErrorKind {
        *self.inner.get_context()
    }

    pub fn extend<D>(self, msg: D) -> IndyError
        where D: fmt::Display + fmt::Debug + Send + Sync + 'static {
        let kind = self.kind();
        self.to_indy(kind, msg)
    }
}

pub fn err_msg<D>(kind: IndyErrorKind, msg: D) -> IndyError
    where D: fmt::Display + fmt::Debug + Send + Sync + 'static {
    IndyError::from_msg(kind, msg)
}

impl From<IndyErrorKind> for IndyError {
    fn from(kind: IndyErrorKind) -> IndyError {
        IndyError { inner: Arc::new(Context::new(kind)) }
    }
}

impl From<Context<IndyErrorKind>> for IndyError {
    fn from(inner: Context<IndyErrorKind>) -> IndyError {
        IndyError { inner: Arc::new(inner) }
    }
}

impl From<io::Error> for IndyError {
    fn from(err: io::Error) -> Self {
        err.context(IndyErrorKind::IOError).into()
    }
}

impl From<zmq::Error> for IndyError {
    fn from(err: zmq::Error) -> Self {
        err.context(IndyErrorKind::IOError).into()
    }
}

impl From<cell::BorrowError> for IndyError {
    fn from(err: cell::BorrowError) -> Self {
        err.context(IndyErrorKind::InvalidState).into()
    }
}

impl From<cell::BorrowMutError> for IndyError {
    fn from(err: cell::BorrowMutError) -> Self {
        err.context(IndyErrorKind::InvalidState).into()
    }
}

impl From<log::SetLoggerError> for IndyError {
    fn from(err: log::SetLoggerError) -> IndyError {
        err.context(IndyErrorKind::InvalidState).into()
    }
}

impl From<IndyCryptoError> for IndyError {
    fn from(err: IndyCryptoError) -> Self {
        if let IndyCryptoError::InvalidState(err) = err {
            IndyError::from_msg(IndyErrorKind::InvalidState, err)
        } else if let IndyCryptoError::IOError(err) = err {
            IndyError::from_msg(IndyErrorKind::IOError, err)
        } else {
            IndyError::from_msg(IndyErrorKind::InvalidStructure, err)
        }
    }
}

impl<T> From<IndyResult<T>> for ErrorCode {
    fn from(r: Result<T, IndyError>) -> ErrorCode {
        match r {
            Ok(_) => ErrorCode::Success,
            Err(err) => err.into(),
        }
    }
}

impl From<IndyError> for ErrorCode {
    fn from(code: IndyError) -> ErrorCode {
        code.kind().into()
    }
}

impl From<IndyErrorKind> for ErrorCode {
    fn from(code: IndyErrorKind) -> ErrorCode {
        match code {
            IndyErrorKind::InvalidState => ErrorCode::CommonInvalidState,
            IndyErrorKind::InvalidStructure => ErrorCode::CommonInvalidStructure,
            IndyErrorKind::IOError => ErrorCode::CommonIOError,
            IndyErrorKind::MasterSecretDuplicateName => ErrorCode::AnoncredsMasterSecretDuplicateNameError,
            IndyErrorKind::ProofRejected => ErrorCode::AnoncredsProofRejected,
            IndyErrorKind::RevocationRegistryFull => ErrorCode::AnoncredsRevocationRegistryFullError,
            IndyErrorKind::InvalidUserRevocId => ErrorCode::AnoncredsInvalidUserRevocId,
            IndyErrorKind::CredentialRevoked => ErrorCode::AnoncredsCredentialRevoked,
            IndyErrorKind::CredDefAlreadyExists => ErrorCode::AnoncredsCredDefAlreadyExistsError,
            IndyErrorKind::NoConsensus => ErrorCode::LedgerNoConsensusError,
            IndyErrorKind::InvalidTransaction => ErrorCode::LedgerInvalidTransaction,
            IndyErrorKind::LedgerItemNotFound => ErrorCode::LedgerNotFound,
            IndyErrorKind::PoolNotCreated => ErrorCode::PoolLedgerNotCreatedError,
            IndyErrorKind::InvalidPoolHandle => ErrorCode::PoolLedgerInvalidPoolHandle,
            IndyErrorKind::PoolTerminated => ErrorCode::PoolLedgerTerminated,
            IndyErrorKind::PoolTimeout => ErrorCode::PoolLedgerTimeout,
            IndyErrorKind::PoolConfigAlreadyExists => ErrorCode::PoolLedgerConfigAlreadyExistsError,
            IndyErrorKind::PoolIncompatibleProtocolVersion => ErrorCode::PoolIncompatibleProtocolVersion,
            IndyErrorKind::UnknownCrypto => ErrorCode::UnknownCryptoTypeError,
            IndyErrorKind::InvalidWalletHandle => ErrorCode::WalletInvalidHandle,
            IndyErrorKind::UnknownWalletStorageType => ErrorCode::WalletUnknownTypeError,
            IndyErrorKind::WalletStorageTypeAlreadyRegistered => ErrorCode::WalletTypeAlreadyRegisteredError,
            IndyErrorKind::WalletAlreadyExists => ErrorCode::WalletAlreadyExistsError,
            IndyErrorKind::WalletNotFound => ErrorCode::WalletNotFoundError,
            IndyErrorKind::WalletAlreadyOpened => ErrorCode::WalletAlreadyOpenedError,
            IndyErrorKind::WalletAccessFailed => ErrorCode::WalletAccessFailed,
            IndyErrorKind::WalletEncodingError => ErrorCode::WalletDecodingError,
            IndyErrorKind::WalletStorageError => ErrorCode::WalletStorageError,
            IndyErrorKind::WalletEncryptionError => ErrorCode::WalletEncryptionError,
            IndyErrorKind::WalletItemNotFound => ErrorCode::WalletItemNotFound,
            IndyErrorKind::WalletItemAlreadyExists => ErrorCode::WalletAlreadyExistsError,
            IndyErrorKind::WalletQueryError => ErrorCode::WalletQueryError,
            IndyErrorKind::DIDAlreadyExists => ErrorCode::DidAlreadyExistsError,
            IndyErrorKind::UnknownPaymentMethodType => ErrorCode::PaymentUnknownMethodError,
            IndyErrorKind::IncompatiblePaymentMethods => ErrorCode::PaymentIncompatibleMethodsError,
        }
    }
}

impl From<ErrorCode> for IndyResult<()> {
    fn from(err: ErrorCode) -> IndyResult<()> {
        if err == ErrorCode::Success {
            Ok(())
        } else {
            err.into()
        }
    }
}

impl From<ErrorCode> for IndyError {
    fn from(err: ErrorCode) -> IndyError {
        let kind = match err {
            ErrorCode::CommonInvalidState => IndyErrorKind::InvalidState,
            ErrorCode::CommonInvalidStructure => IndyErrorKind::InvalidStructure,
            ErrorCode::CommonIOError => IndyErrorKind::IOError,
            ErrorCode::AnoncredsMasterSecretDuplicateNameError => IndyErrorKind::MasterSecretDuplicateName,
            ErrorCode::AnoncredsProofRejected => IndyErrorKind::ProofRejected,
            ErrorCode::AnoncredsRevocationRegistryFullError => IndyErrorKind::RevocationRegistryFull,
            ErrorCode::AnoncredsInvalidUserRevocId => IndyErrorKind::InvalidUserRevocId,
            ErrorCode::AnoncredsCredentialRevoked => IndyErrorKind::CredentialRevoked,
            ErrorCode::AnoncredsCredDefAlreadyExistsError => IndyErrorKind::CredDefAlreadyExists,
            ErrorCode::LedgerNoConsensusError => IndyErrorKind::NoConsensus,
            ErrorCode::LedgerInvalidTransaction => IndyErrorKind::InvalidTransaction,
            ErrorCode::LedgerNotFound => IndyErrorKind::LedgerItemNotFound,
            ErrorCode::PoolLedgerNotCreatedError => IndyErrorKind::PoolNotCreated,
            ErrorCode::PoolLedgerInvalidPoolHandle => IndyErrorKind::InvalidPoolHandle,
            ErrorCode::PoolLedgerTerminated => IndyErrorKind::PoolTerminated,
            ErrorCode::PoolLedgerTimeout => IndyErrorKind::PoolTimeout,
            ErrorCode::PoolLedgerConfigAlreadyExistsError => IndyErrorKind::PoolConfigAlreadyExists,
            ErrorCode::PoolIncompatibleProtocolVersion => IndyErrorKind::PoolIncompatibleProtocolVersion,
            ErrorCode::UnknownCryptoTypeError => IndyErrorKind::UnknownCrypto,
            ErrorCode::WalletInvalidHandle => IndyErrorKind::InvalidWalletHandle,
            ErrorCode::WalletUnknownTypeError => IndyErrorKind::UnknownWalletStorageType,
            ErrorCode::WalletTypeAlreadyRegisteredError => IndyErrorKind::WalletStorageTypeAlreadyRegistered,
            ErrorCode::WalletAlreadyExistsError => IndyErrorKind::WalletAlreadyExists,
            ErrorCode::WalletNotFoundError => IndyErrorKind::WalletNotFound,
            ErrorCode::WalletAlreadyOpenedError => IndyErrorKind::WalletAlreadyOpened,
            ErrorCode::WalletAccessFailed => IndyErrorKind::WalletAccessFailed,
            ErrorCode::WalletDecodingError => IndyErrorKind::WalletEncodingError,
            ErrorCode::WalletStorageError => IndyErrorKind::WalletStorageError,
            ErrorCode::WalletEncryptionError => IndyErrorKind::WalletEncryptionError,
            ErrorCode::WalletItemNotFound => IndyErrorKind::WalletItemNotFound,
            ErrorCode::WalletItemAlreadyExists => IndyErrorKind::WalletItemAlreadyExists,
            ErrorCode::WalletQueryError => IndyErrorKind::WalletQueryError,
            ErrorCode::DidAlreadyExistsError => IndyErrorKind::DIDAlreadyExists,
            ErrorCode::PaymentUnknownMethodError => IndyErrorKind::UnknownPaymentMethodType,
            ErrorCode::PaymentIncompatibleMethodsError => IndyErrorKind::IncompatiblePaymentMethods,
            code => return err_msg(IndyErrorKind::InvalidState, format!("Trying to interpret unsupported error code as error: {:?}", code)),
        };

        err_msg(kind, format!("Plugin returned error"))
    }
}

pub type IndyResult<T> = Result<T, IndyError>;

/// Extension methods for `Result`.
pub trait IndyResultExt<T, E> {
    fn to_indy<D>(self, kind: IndyErrorKind, msg: D) -> IndyResult<T> where D: fmt::Display + Send + Sync + 'static;
}

impl<T, E> IndyResultExt<T, E> for Result<T, E> where E: Fail
{
    fn to_indy<D>(self, kind: IndyErrorKind, msg: D) -> IndyResult<T> where D: fmt::Display + Send + Sync + 'static {
        self.map_err(|err| err.context(msg).context(kind).into())
    }
}

/// Extension methods for `Error`.
pub trait IndyErrorExt {
    fn to_indy<D>(self, kind: IndyErrorKind, msg: D) -> IndyError where D: fmt::Display + Send + Sync + 'static;
}

impl<E> IndyErrorExt for E where E: Fail
{
    fn to_indy<D>(self, kind: IndyErrorKind, msg: D) -> IndyError where D: fmt::Display + Send + Sync + 'static {
        self.context(msg).context(kind).into()
    }
}

<commit_msg>* Fixed regression found by integration tests<commit_after>use std::cell;
use std::fmt;
use std::io;
use std::sync::Arc;

use failure::{Backtrace, Context, Fail};
use indy_crypto::errors::IndyCryptoError;
use log;

use api::ErrorCode;

pub mod prelude {
    pub use super::{err_msg, IndyError, IndyErrorExt, IndyErrorKind, IndyResult, IndyResultExt};
}

#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum IndyErrorKind {
    // Common errors
    #[fail(display = "Invalid library state")]
    InvalidState,
    #[fail(display = "Invalid structure")]
    InvalidStructure,
    #[fail(display = "IO error")]
    IOError,
    // Anoncreds errors
    #[fail(display = "Duplicated master secret")]
    MasterSecretDuplicateName,
    #[fail(display = "Proof rejected")]
    ProofRejected,
    #[fail(display = "Revocation registry is full")]
    RevocationRegistryFull,
    #[fail(display = "Invalid revocation id")]
    InvalidUserRevocId,
    #[fail(display = "Credential revoked")]
    CredentialRevoked,
    #[fail(display = "Credential definition already exists")]
    CredDefAlreadyExists,
    // Ledger errors
    #[fail(display = "No consensus")]
    NoConsensus,
    #[fail(display = "Invalid transaction")]
    InvalidTransaction,
    #[fail(display = "Item not found on ledger")]
    LedgerItemNotFound,
    // Pool errors
    #[fail(display = "Pool not created")]
    PoolNotCreated,
    #[fail(display = "Invalid pool handle")]
    InvalidPoolHandle,
    #[fail(display = "Pool work terminated")]
    PoolTerminated,
    #[fail(display = "Pool timeout")]
    PoolTimeout,
    #[fail(display = "Pool ledger config already exists")]
    PoolConfigAlreadyExists,
    #[fail(display = "Pool Genesis Transactions are not compatible with Protocol version")]
    PoolIncompatibleProtocolVersion,
    // Crypto errors
    #[fail(display = "Unknown crypto")]
    UnknownCrypto,
    // Wallet errors
    #[fail(display = "Invalid wallet handle was passed")]
    InvalidWalletHandle,
    #[fail(display = "Unknown wallet storage type")]
    UnknownWalletStorageType,
    #[fail(display = "Wallet storage type already registered")]
    WalletStorageTypeAlreadyRegistered,
    #[fail(display = "Wallet with this name already exists")]
    WalletAlreadyExists,
    #[fail(display = "Wallet not found")]
    WalletNotFound,
    #[fail(display = "Wallet already opened")]
    WalletAlreadyOpened,
    #[fail(display = "Wallet security error")]
    WalletAccessFailed,
    #[fail(display = "Wallet encoding error")]
    WalletEncodingError,
    #[fail(display = "Wallet storage error occurred")]
    WalletStorageError,
    #[fail(display = "Wallet encryption error")]
    WalletEncryptionError,
    #[fail(display = "Wallet item not found")]
    WalletItemNotFound,
    #[fail(display = "Wallet item already exists")]
    WalletItemAlreadyExists,
    #[fail(display = "Wallet query error")]
    WalletQueryError,
    // DID errors
    #[fail(display = "DID already exists")]
    DIDAlreadyExists,
    // Payments errors
    #[fail(display = "Unknown payment method type")]
    UnknownPaymentMethodType,
    #[fail(display = "Plugged payment method error")]
    IncompatiblePaymentMethods,
}

#[derive(Debug, Clone)]
pub struct IndyError {
    inner: Arc<Context<IndyErrorKind>>
}

impl Fail for IndyError {
    fn cause(&self) -> Option<&Fail> {
        self.inner.cause()
    }

    fn backtrace(&self) -> Option<&Backtrace> {
        self.inner.backtrace()
    }
}

impl fmt::Display for IndyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut first = true;

        // FIXME: It seems like a bug in failure. I can't use iter_chain for context to solve deprecation warning.
        #[allow(deprecated)]
        for cause in self.inner.causes() {
            if first {
                first = false;
                writeln!(f, "Error: {}", cause)?;
            } else {
                writeln!(f, "  caused by: {}", cause)?;
            }
        }

        Ok(())
    }
}

impl IndyError {
    pub fn from_msg<D>(kind: IndyErrorKind, msg: D) -> IndyError
        where D: fmt::Display + fmt::Debug + Send + Sync + 'static {
        IndyError { inner: Arc::new(Context::new(msg).context(kind)) }
    }

    pub fn kind(&self) -> IndyErrorKind {
        *self.inner.get_context()
    }

    pub fn extend<D>(self, msg: D) -> IndyError
        where D: fmt::Display + fmt::Debug + Send + Sync + 'static {
        let kind = self.kind();
        self.to_indy(kind, msg)
    }
}

pub fn err_msg<D>(kind: IndyErrorKind, msg: D) -> IndyError
    where D: fmt::Display + fmt::Debug + Send + Sync + 'static {
    IndyError::from_msg(kind, msg)
}

impl From<IndyErrorKind> for IndyError {
    fn from(kind: IndyErrorKind) -> IndyError {
        IndyError { inner: Arc::new(Context::new(kind)) }
    }
}

impl From<Context<IndyErrorKind>> for IndyError {
    fn from(inner: Context<IndyErrorKind>) -> IndyError {
        IndyError { inner: Arc::new(inner) }
    }
}

impl From<io::Error> for IndyError {
    fn from(err: io::Error) -> Self {
        err.context(IndyErrorKind::IOError).into()
    }
}

impl From<zmq::Error> for IndyError {
    fn from(err: zmq::Error) -> Self {
        err.context(IndyErrorKind::IOError).into()
    }
}

impl From<cell::BorrowError> for IndyError {
    fn from(err: cell::BorrowError) -> Self {
        err.context(IndyErrorKind::InvalidState).into()
    }
}

impl From<cell::BorrowMutError> for IndyError {
    fn from(err: cell::BorrowMutError) -> Self {
        err.context(IndyErrorKind::InvalidState).into()
    }
}

impl From<log::SetLoggerError> for IndyError {
    fn from(err: log::SetLoggerError) -> IndyError {
        err.context(IndyErrorKind::InvalidState).into()
    }
}

impl From<IndyCryptoError> for IndyError {
    fn from(err: IndyCryptoError) -> Self {
        if let IndyCryptoError::InvalidState(err) = err {
            IndyError::from_msg(IndyErrorKind::InvalidState, err)
        } else if let IndyCryptoError::IOError(err) = err {
            IndyError::from_msg(IndyErrorKind::IOError, err)
        } else {
            IndyError::from_msg(IndyErrorKind::InvalidStructure, err)
        }
    }
}

impl<T> From<IndyResult<T>> for ErrorCode {
    fn from(r: Result<T, IndyError>) -> ErrorCode {
        match r {
            Ok(_) => ErrorCode::Success,
            Err(err) => err.into(),
        }
    }
}

impl From<IndyError> for ErrorCode {
    fn from(code: IndyError) -> ErrorCode {
        code.kind().into()
    }
}

impl From<IndyErrorKind> for ErrorCode {
    fn from(code: IndyErrorKind) -> ErrorCode {
        match code {
            IndyErrorKind::InvalidState => ErrorCode::CommonInvalidState,
            IndyErrorKind::InvalidStructure => ErrorCode::CommonInvalidStructure,
            IndyErrorKind::IOError => ErrorCode::CommonIOError,
            IndyErrorKind::MasterSecretDuplicateName => ErrorCode::AnoncredsMasterSecretDuplicateNameError,
            IndyErrorKind::ProofRejected => ErrorCode::AnoncredsProofRejected,
            IndyErrorKind::RevocationRegistryFull => ErrorCode::AnoncredsRevocationRegistryFullError,
            IndyErrorKind::InvalidUserRevocId => ErrorCode::AnoncredsInvalidUserRevocId,
            IndyErrorKind::CredentialRevoked => ErrorCode::AnoncredsCredentialRevoked,
            IndyErrorKind::CredDefAlreadyExists => ErrorCode::AnoncredsCredDefAlreadyExistsError,
            IndyErrorKind::NoConsensus => ErrorCode::LedgerNoConsensusError,
            IndyErrorKind::InvalidTransaction => ErrorCode::LedgerInvalidTransaction,
            IndyErrorKind::LedgerItemNotFound => ErrorCode::LedgerNotFound,
            IndyErrorKind::PoolNotCreated => ErrorCode::PoolLedgerNotCreatedError,
            IndyErrorKind::InvalidPoolHandle => ErrorCode::PoolLedgerInvalidPoolHandle,
            IndyErrorKind::PoolTerminated => ErrorCode::PoolLedgerTerminated,
            IndyErrorKind::PoolTimeout => ErrorCode::PoolLedgerTimeout,
            IndyErrorKind::PoolConfigAlreadyExists => ErrorCode::PoolLedgerConfigAlreadyExistsError,
            IndyErrorKind::PoolIncompatibleProtocolVersion => ErrorCode::PoolIncompatibleProtocolVersion,
            IndyErrorKind::UnknownCrypto => ErrorCode::UnknownCryptoTypeError,
            IndyErrorKind::InvalidWalletHandle => ErrorCode::WalletInvalidHandle,
            IndyErrorKind::UnknownWalletStorageType => ErrorCode::WalletUnknownTypeError,
            IndyErrorKind::WalletStorageTypeAlreadyRegistered => ErrorCode::WalletTypeAlreadyRegisteredError,
            IndyErrorKind::WalletAlreadyExists => ErrorCode::WalletAlreadyExistsError,
            IndyErrorKind::WalletNotFound => ErrorCode::WalletNotFoundError,
            IndyErrorKind::WalletAlreadyOpened => ErrorCode::WalletAlreadyOpenedError,
            IndyErrorKind::WalletAccessFailed => ErrorCode::WalletAccessFailed,
            IndyErrorKind::WalletEncodingError => ErrorCode::WalletDecodingError,
            IndyErrorKind::WalletStorageError => ErrorCode::WalletStorageError,
            IndyErrorKind::WalletEncryptionError => ErrorCode::WalletEncryptionError,
            IndyErrorKind::WalletItemNotFound => ErrorCode::WalletItemNotFound,
            IndyErrorKind::WalletItemAlreadyExists => ErrorCode::WalletItemAlreadyExists,
            IndyErrorKind::WalletQueryError => ErrorCode::WalletQueryError,
            IndyErrorKind::DIDAlreadyExists => ErrorCode::DidAlreadyExistsError,
            IndyErrorKind::UnknownPaymentMethodType => ErrorCode::PaymentUnknownMethodError,
            IndyErrorKind::IncompatiblePaymentMethods => ErrorCode::PaymentIncompatibleMethodsError,
        }
    }
}

impl From<ErrorCode> for IndyResult<()> {
    fn from(err: ErrorCode) -> IndyResult<()> {
        if err == ErrorCode::Success {
            Ok(())
        } else {
            err.into()
        }
    }
}

impl From<ErrorCode> for IndyError {
    fn from(err: ErrorCode) -> IndyError {
        let kind = match err {
            ErrorCode::CommonInvalidState => IndyErrorKind::InvalidState,
            ErrorCode::CommonInvalidStructure => IndyErrorKind::InvalidStructure,
            ErrorCode::CommonIOError => IndyErrorKind::IOError,
            ErrorCode::AnoncredsMasterSecretDuplicateNameError => IndyErrorKind::MasterSecretDuplicateName,
            ErrorCode::AnoncredsProofRejected => IndyErrorKind::ProofRejected,
            ErrorCode::AnoncredsRevocationRegistryFullError => IndyErrorKind::RevocationRegistryFull,
            ErrorCode::AnoncredsInvalidUserRevocId => IndyErrorKind::InvalidUserRevocId,
            ErrorCode::AnoncredsCredentialRevoked => IndyErrorKind::CredentialRevoked,
            ErrorCode::AnoncredsCredDefAlreadyExistsError => IndyErrorKind::CredDefAlreadyExists,
            ErrorCode::LedgerNoConsensusError => IndyErrorKind::NoConsensus,
            ErrorCode::LedgerInvalidTransaction => IndyErrorKind::InvalidTransaction,
            ErrorCode::LedgerNotFound => IndyErrorKind::LedgerItemNotFound,
            ErrorCode::PoolLedgerNotCreatedError => IndyErrorKind::PoolNotCreated,
            ErrorCode::PoolLedgerInvalidPoolHandle => IndyErrorKind::InvalidPoolHandle,
            ErrorCode::PoolLedgerTerminated => IndyErrorKind::PoolTerminated,
            ErrorCode::PoolLedgerTimeout => IndyErrorKind::PoolTimeout,
            ErrorCode::PoolLedgerConfigAlreadyExistsError => IndyErrorKind::PoolConfigAlreadyExists,
            ErrorCode::PoolIncompatibleProtocolVersion => IndyErrorKind::PoolIncompatibleProtocolVersion,
            ErrorCode::UnknownCryptoTypeError => IndyErrorKind::UnknownCrypto,
            ErrorCode::WalletInvalidHandle => IndyErrorKind::InvalidWalletHandle,
            ErrorCode::WalletUnknownTypeError => IndyErrorKind::UnknownWalletStorageType,
            ErrorCode::WalletTypeAlreadyRegisteredError => IndyErrorKind::WalletStorageTypeAlreadyRegistered,
            ErrorCode::WalletAlreadyExistsError => IndyErrorKind::WalletAlreadyExists,
            ErrorCode::WalletNotFoundError => IndyErrorKind::WalletNotFound,
            ErrorCode::WalletAlreadyOpenedError => IndyErrorKind::WalletAlreadyOpened,
            ErrorCode::WalletAccessFailed => IndyErrorKind::WalletAccessFailed,
            ErrorCode::WalletDecodingError => IndyErrorKind::WalletEncodingError,
            ErrorCode::WalletStorageError => IndyErrorKind::WalletStorageError,
            ErrorCode::WalletEncryptionError => IndyErrorKind::WalletEncryptionError,
            ErrorCode::WalletItemNotFound => IndyErrorKind::WalletItemNotFound,
            ErrorCode::WalletItemAlreadyExists => IndyErrorKind::WalletItemAlreadyExists,
            ErrorCode::WalletQueryError => IndyErrorKind::WalletQueryError,
            ErrorCode::DidAlreadyExistsError => IndyErrorKind::DIDAlreadyExists,
            ErrorCode::PaymentUnknownMethodError => IndyErrorKind::UnknownPaymentMethodType,
            ErrorCode::PaymentIncompatibleMethodsError => IndyErrorKind::IncompatiblePaymentMethods,
            code => return err_msg(IndyErrorKind::InvalidState, format!("Trying to interpret unsupported error code as error: {:?}", code)),
        };

        err_msg(kind, format!("Plugin returned error"))
    }
}

pub type IndyResult<T> = Result<T, IndyError>;

/// Extension methods for `Result`.
pub trait IndyResultExt<T, E> {
    fn to_indy<D>(self, kind: IndyErrorKind, msg: D) -> IndyResult<T> where D: fmt::Display + Send + Sync + 'static;
}

impl<T, E> IndyResultExt<T, E> for Result<T, E> where E: Fail
{
    fn to_indy<D>(self, kind: IndyErrorKind, msg: D) -> IndyResult<T> where D: fmt::Display + Send + Sync + 'static {
        self.map_err(|err| err.context(msg).context(kind).into())
    }
}

/// Extension methods for `Error`.
pub trait IndyErrorExt {
    fn to_indy<D>(self, kind: IndyErrorKind, msg: D) -> IndyError where D: fmt::Display + Send + Sync + 'static;
}

impl<E> IndyErrorExt for E where E: Fail
{
    fn to_indy<D>(self, kind: IndyErrorKind, msg: D) -> IndyError where D: fmt::Display + Send + Sync + 'static {
        self.context(msg).context(kind).into()
    }
}

<|endoftext|>


<commit_before>use std::{collections::HashSet, fs, time::Duration};

// use flexi_logger::Logger;
use crossbeam_channel::RecvTimeoutError;
use ra_vfs::{Vfs, VfsChange};
use tempfile::tempdir;

/// Processes exactly `num_tasks` events waiting in the `vfs` message queue.
///
/// Panics if there are not exactly that many tasks enqueued for processing.
fn process_tasks(vfs: &mut Vfs, num_tasks: u32) {
    process_tasks_in_range(vfs, num_tasks, num_tasks);
}

/// Processes up to `max_count` events waiting in the `vfs` message queue.
///
/// Panics if it cannot process at least `min_count` events.
/// Panics if more than `max_count` events are enqueued for processing.
fn process_tasks_in_range(vfs: &mut Vfs, min_count: u32, max_count: u32) {
    for i in 0..max_count {
        let task = match vfs.task_receiver().recv_timeout(Duration::from_secs(3)) {
            Err(RecvTimeoutError::Timeout) if i >= min_count => return,
            otherwise => otherwise.unwrap(),
        };
        log::debug!("{:?}", task);
        vfs.handle_task(task);
    }
    assert!(vfs.task_receiver().is_empty());
}

macro_rules! assert_match {
    ($x:expr, $pat:pat) => {
        assert_match!($x, $pat, ())
    };
    ($x:expr, $pat:pat, $assert:expr) => {
        match $x {
            $pat => $assert,
            x => assert!(false, "Expected {}, got {:?}", stringify!($pat), x),
        };
    };
}

#[test]
fn test_vfs_works() -> std::io::Result<()> {
    // Logger::with_str("vfs=debug,ra_vfs=debug").start().unwrap();

    let files = [("a/foo.rs", "hello"), ("a/bar.rs", "world"), ("a/b/baz.rs", "nested hello")];

    let dir = tempdir().unwrap();
    for (path, text) in files.iter() {
        let file_path = dir.path().join(path);
        fs::create_dir_all(file_path.parent().unwrap()).unwrap();
        fs::write(file_path, text)?
    }

    let a_root = dir.path().join("a");
    let b_root = dir.path().join("a/b");

    let (mut vfs, _) = Vfs::new(vec![a_root, b_root]);
    process_tasks(&mut vfs, 2);
    {
        let files = vfs
            .commit_changes()
            .into_iter()
            .flat_map(|change| {
                let files = match change {
                    VfsChange::AddRoot { files, .. } => files,
                    _ => panic!("unexpected change"),
                };
                files.into_iter().map(|(_id, path, text)| {
                    let text: String = (&*text).clone();
                    (format!("{}", path.display()), text)
                })
            })
            .collect::<HashSet<_>>();

        let expected_files = [("foo.rs", "hello"), ("bar.rs", "world"), ("baz.rs", "nested hello")]
            .iter()
            .map(|(path, text)| (path.to_string(), text.to_string()))
            .collect::<HashSet<_>>();

        assert_eq!(files, expected_files);
    }

    // rust-analyzer#734: fsevents has a bunch of events still sitting around.
    process_tasks_in_range(&mut vfs, 0, 7);
    match vfs.commit_changes().as_slice() {
        [] => {}

        // This arises on fsevents (unless we wait 30 seconds before
        // calling `Vfs::new` above). We need to churn through these
        // events so that we can focus on the event that arises from
        // the `fs::write` below.
        [VfsChange::ChangeFile { .. }, // hello
         VfsChange::ChangeFile { .. }, // world
         VfsChange::AddFile { .. }, // b/baz.rs, nested hello
         VfsChange::ChangeFile { .. }, // hello
         VfsChange::ChangeFile { .. }, // world
         VfsChange::ChangeFile { .. }, // nested hello
         VfsChange::ChangeFile { .. }, // nested hello
        ] => {}

        changes => panic!("Expected events for setting up initial files, got: {GOT:?}",
                          GOT=changes),
    }

    fs::write(&dir.path().join("a/b/baz.rs"), "quux").unwrap();
    process_tasks(&mut vfs, 1);
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::ChangeFile { text, .. }],
        assert_eq!(text.as_str(), "quux")
    );

    vfs.add_file_overlay(&dir.path().join("a/b/baz.rs"), "m".to_string());
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::ChangeFile { text, .. }],
        assert_eq!(text.as_str(), "m")
    );

    // changing file on disk while overlayed doesn't generate a VfsChange
    fs::write(&dir.path().join("a/b/baz.rs"), "corge").unwrap();
    process_tasks(&mut vfs, 1);
    assert_match!(vfs.commit_changes().as_slice(), []);

    // removing overlay restores data on disk
    vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs"));
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::ChangeFile { text, .. }],
        assert_eq!(text.as_str(), "corge")
    );

    vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), "spam".to_string());
    assert_match!(vfs.commit_changes().as_slice(), [VfsChange::AddFile { text, path, .. }], {
        assert_eq!(text.as_str(), "spam");
        assert_eq!(path, "spam.rs");
    });

    vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs"));
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::RemoveFile { path, .. }],
        assert_eq!(path, "spam.rs")
    );

    fs::create_dir_all(dir.path().join("a/sub1/sub2")).unwrap();
    fs::write(dir.path().join("a/sub1/sub2/new.rs"), "new hello").unwrap();
    process_tasks(&mut vfs, 1);
    assert_match!(vfs.commit_changes().as_slice(), [VfsChange::AddFile { text, path, .. }], {
        assert_eq!(text.as_str(), "new hello");
        assert_eq!(path, "sub1/sub2/new.rs");
    });

    fs::rename(&dir.path().join("a/sub1/sub2/new.rs"), &dir.path().join("a/sub1/sub2/new1.rs"))
        .unwrap();

    // rust-analyzer#734: For testing purposes, work-around
    // passcod/notify#181 by processing either 1 or 2 events. (In
    // particular, Mac can hand back either 1 or 2 events in a
    // timing-dependent fashion.)
    //
    // rust-analyzer#827: Windows generates extra `Write` events when
    // renaming? meaning we have extra tasks to process.
    process_tasks_in_range(&mut vfs, 1, if cfg!(windows) { 4 } else { 2 });
    match vfs.commit_changes().as_slice() {
        [VfsChange::RemoveFile { path: removed_path, .. }, VfsChange::AddFile { text, path: added_path, .. }] =>
        {
            assert_eq!(removed_path, "sub1/sub2/new.rs");
            assert_eq!(added_path, "sub1/sub2/new1.rs");
            assert_eq!(text.as_str(), "new hello");
        }

        // Hopefully passcod/notify#181 will be addressed in some
        // manner that will reliably emit an event mentioning
        // `sub1/sub2/new.rs`. But until then, must accept that
        // debouncing loses information unrecoverably.
        [VfsChange::AddFile { text, path: added_path, .. }] => {
            assert_eq!(added_path, "sub1/sub2/new1.rs");
            assert_eq!(text.as_str(), "new hello");
        }

        changes => panic!(
            "Expected events for rename of {OLD} to {NEW}, got: {GOT:?}",
            OLD = "sub1/sub2/new.rs",
            NEW = "sub1/sub2/new1.rs",
            GOT = changes
        ),
    }

    fs::remove_file(&dir.path().join("a/sub1/sub2/new1.rs")).unwrap();
    process_tasks(&mut vfs, 1);
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::RemoveFile { path, .. }],
        assert_eq!(path, "sub1/sub2/new1.rs")
    );

    {
        vfs.add_file_overlay(&dir.path().join("a/memfile.rs"), "memfile".to_string());
        assert_match!(
            vfs.commit_changes().as_slice(),
            [VfsChange::AddFile { text, .. }],
            assert_eq!(text.as_str(), "memfile")
        );
        fs::write(&dir.path().join("a/memfile.rs"), "ignore me").unwrap();
        process_tasks(&mut vfs, 1);
        assert_match!(vfs.commit_changes().as_slice(), []);
    }

    // should be ignored
    fs::create_dir_all(dir.path().join("a/target")).unwrap();
    fs::write(&dir.path().join("a/target/new.rs"), "ignore me").unwrap();

    assert_match!(
        vfs.task_receiver().recv_timeout(Duration::from_millis(300)), // slightly more than watcher debounce delay
        Err(RecvTimeoutError::Timeout)
    );

    Ok(())
}
<commit_msg>Dont slow down everyone else's testing (especially the CI's Linux) just for OS X.<commit_after>use std::{collections::HashSet, fs, time::Duration};

// use flexi_logger::Logger;
use crossbeam_channel::RecvTimeoutError;
use ra_vfs::{Vfs, VfsChange};
use tempfile::tempdir;

/// Processes exactly `num_tasks` events waiting in the `vfs` message queue.
///
/// Panics if there are not exactly that many tasks enqueued for processing.
fn process_tasks(vfs: &mut Vfs, num_tasks: u32) {
    process_tasks_in_range(vfs, num_tasks, num_tasks);
}

/// Processes up to `max_count` events waiting in the `vfs` message queue.
///
/// Panics if it cannot process at least `min_count` events.
/// Panics if more than `max_count` events are enqueued for processing.
fn process_tasks_in_range(vfs: &mut Vfs, min_count: u32, max_count: u32) {
    for i in 0..max_count {
        let task = match vfs.task_receiver().recv_timeout(Duration::from_secs(3)) {
            Err(RecvTimeoutError::Timeout) if i >= min_count => return,
            otherwise => otherwise.unwrap(),
        };
        log::debug!("{:?}", task);
        vfs.handle_task(task);
    }
    assert!(vfs.task_receiver().is_empty());
}

macro_rules! assert_match {
    ($x:expr, $pat:pat) => {
        assert_match!($x, $pat, ())
    };
    ($x:expr, $pat:pat, $assert:expr) => {
        match $x {
            $pat => $assert,
            x => assert!(false, "Expected {}, got {:?}", stringify!($pat), x),
        };
    };
}

#[test]
fn test_vfs_works() -> std::io::Result<()> {
    // Logger::with_str("vfs=debug,ra_vfs=debug").start().unwrap();

    let files = [("a/foo.rs", "hello"), ("a/bar.rs", "world"), ("a/b/baz.rs", "nested hello")];

    let dir = tempdir().unwrap();
    for (path, text) in files.iter() {
        let file_path = dir.path().join(path);
        fs::create_dir_all(file_path.parent().unwrap()).unwrap();
        fs::write(file_path, text)?
    }

    let a_root = dir.path().join("a");
    let b_root = dir.path().join("a/b");

    let (mut vfs, _) = Vfs::new(vec![a_root, b_root]);
    process_tasks(&mut vfs, 2);
    {
        let files = vfs
            .commit_changes()
            .into_iter()
            .flat_map(|change| {
                let files = match change {
                    VfsChange::AddRoot { files, .. } => files,
                    _ => panic!("unexpected change"),
                };
                files.into_iter().map(|(_id, path, text)| {
                    let text: String = (&*text).clone();
                    (format!("{}", path.display()), text)
                })
            })
            .collect::<HashSet<_>>();

        let expected_files = [("foo.rs", "hello"), ("bar.rs", "world"), ("baz.rs", "nested hello")]
            .iter()
            .map(|(path, text)| (path.to_string(), text.to_string()))
            .collect::<HashSet<_>>();

        assert_eq!(files, expected_files);
    }

    // rust-analyzer#734: fsevents has a bunch of events still sitting around.
    process_tasks_in_range(&mut vfs, 0, if cfg!(target_os = "macos") { 7 } else { 0 });
    match vfs.commit_changes().as_slice() {
        [] => {}

        // This arises on fsevents (unless we wait 30 seconds before
        // calling `Vfs::new` above). We need to churn through these
        // events so that we can focus on the event that arises from
        // the `fs::write` below.
        [VfsChange::ChangeFile { .. }, // hello
         VfsChange::ChangeFile { .. }, // world
         VfsChange::AddFile { .. }, // b/baz.rs, nested hello
         VfsChange::ChangeFile { .. }, // hello
         VfsChange::ChangeFile { .. }, // world
         VfsChange::ChangeFile { .. }, // nested hello
         VfsChange::ChangeFile { .. }, // nested hello
        ] => {}

        changes => panic!("Expected events for setting up initial files, got: {GOT:?}",
                          GOT=changes),
    }

    fs::write(&dir.path().join("a/b/baz.rs"), "quux").unwrap();
    process_tasks(&mut vfs, 1);
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::ChangeFile { text, .. }],
        assert_eq!(text.as_str(), "quux")
    );

    vfs.add_file_overlay(&dir.path().join("a/b/baz.rs"), "m".to_string());
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::ChangeFile { text, .. }],
        assert_eq!(text.as_str(), "m")
    );

    // changing file on disk while overlayed doesn't generate a VfsChange
    fs::write(&dir.path().join("a/b/baz.rs"), "corge").unwrap();
    process_tasks(&mut vfs, 1);
    assert_match!(vfs.commit_changes().as_slice(), []);

    // removing overlay restores data on disk
    vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs"));
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::ChangeFile { text, .. }],
        assert_eq!(text.as_str(), "corge")
    );

    vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), "spam".to_string());
    assert_match!(vfs.commit_changes().as_slice(), [VfsChange::AddFile { text, path, .. }], {
        assert_eq!(text.as_str(), "spam");
        assert_eq!(path, "spam.rs");
    });

    vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs"));
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::RemoveFile { path, .. }],
        assert_eq!(path, "spam.rs")
    );

    fs::create_dir_all(dir.path().join("a/sub1/sub2")).unwrap();
    fs::write(dir.path().join("a/sub1/sub2/new.rs"), "new hello").unwrap();
    process_tasks(&mut vfs, 1);
    assert_match!(vfs.commit_changes().as_slice(), [VfsChange::AddFile { text, path, .. }], {
        assert_eq!(text.as_str(), "new hello");
        assert_eq!(path, "sub1/sub2/new.rs");
    });

    fs::rename(&dir.path().join("a/sub1/sub2/new.rs"), &dir.path().join("a/sub1/sub2/new1.rs"))
        .unwrap();

    // rust-analyzer#734: For testing purposes, work-around
    // passcod/notify#181 by processing either 1 or 2 events. (In
    // particular, Mac can hand back either 1 or 2 events in a
    // timing-dependent fashion.)
    //
    // rust-analyzer#827: Windows generates extra `Write` events when
    // renaming? meaning we have extra tasks to process.
    process_tasks_in_range(&mut vfs, 1, if cfg!(windows) { 4 } else { 2 });
    match vfs.commit_changes().as_slice() {
        [VfsChange::RemoveFile { path: removed_path, .. }, VfsChange::AddFile { text, path: added_path, .. }] =>
        {
            assert_eq!(removed_path, "sub1/sub2/new.rs");
            assert_eq!(added_path, "sub1/sub2/new1.rs");
            assert_eq!(text.as_str(), "new hello");
        }

        // Hopefully passcod/notify#181 will be addressed in some
        // manner that will reliably emit an event mentioning
        // `sub1/sub2/new.rs`. But until then, must accept that
        // debouncing loses information unrecoverably.
        [VfsChange::AddFile { text, path: added_path, .. }] => {
            assert_eq!(added_path, "sub1/sub2/new1.rs");
            assert_eq!(text.as_str(), "new hello");
        }

        changes => panic!(
            "Expected events for rename of {OLD} to {NEW}, got: {GOT:?}",
            OLD = "sub1/sub2/new.rs",
            NEW = "sub1/sub2/new1.rs",
            GOT = changes
        ),
    }

    fs::remove_file(&dir.path().join("a/sub1/sub2/new1.rs")).unwrap();
    process_tasks(&mut vfs, 1);
    assert_match!(
        vfs.commit_changes().as_slice(),
        [VfsChange::RemoveFile { path, .. }],
        assert_eq!(path, "sub1/sub2/new1.rs")
    );

    {
        vfs.add_file_overlay(&dir.path().join("a/memfile.rs"), "memfile".to_string());
        assert_match!(
            vfs.commit_changes().as_slice(),
            [VfsChange::AddFile { text, .. }],
            assert_eq!(text.as_str(), "memfile")
        );
        fs::write(&dir.path().join("a/memfile.rs"), "ignore me").unwrap();
        process_tasks(&mut vfs, 1);
        assert_match!(vfs.commit_changes().as_slice(), []);
    }

    // should be ignored
    fs::create_dir_all(dir.path().join("a/target")).unwrap();
    fs::write(&dir.path().join("a/target/new.rs"), "ignore me").unwrap();

    assert_match!(
        vfs.task_receiver().recv_timeout(Duration::from_millis(300)), // slightly more than watcher debounce delay
        Err(RecvTimeoutError::Timeout)
    );

    Ok(())
}
<|endoftext|>


<commit_before>//! Metamethods for accesssing the registry values!

use super::super::super::registry;

use hlua::any::AnyLuaValue;

use rustc_serialize::json::{Json, ToJson};
use std::ops::Deref;

pub fn index(table: AnyLuaValue, lua_key: AnyLuaValue)
             -> Result<AnyLuaValue, &'static str> {
    if let AnyLuaValue::LuaString(key) = lua_key {
        let maybe_json = registry::get_json(&key);
        if let Some(json) = maybe_json {
            Ok(convert_json(json.deref().to_json()))
        }
        else {
            // Should be nil, nil doesn't seem to be included
            Err("No value found for that key!")
        }
    }
    else {
        Err("The registry can only be indexed with keys!")
    }
}

// Prevent lua from changing the registry?
pub fn new_index(table: AnyLuaValue, key: AnyLuaValue, val: AnyLuaValue)
                 -> Result<(), &'static str> {
    //Err("You can't set the registry!")
}

fn convert_json(json: Json) -> AnyLuaValue {
    match json {
        Json::String(val) => AnyLuaValue::LuaString(val),
        Json::Boolean(val) => AnyLuaValue::LuaBoolean(val),
        Json::F64(val) => AnyLuaValue::LuaNumber(val),
        Json::I64(val) => AnyLuaValue::LuaNumber((val as i32) as f64),
        Json::U64(val) => AnyLuaValue::LuaNumber((val as u32) as f64),
        Json::Null => AnyLuaValue::LuaNil,
        Json::Array(vals) => {
            let mut count = 0f64;
            // Gotta love that 1-based indexing. Start at zero but increment for
            // the first one. It works here at least.
            AnyLuaValue::LuaArray(vals.into_iter().map(|v| {
                count += 1.0;
                (AnyLuaValue::LuaNumber(count), convert_json(v))
            }).collect())
        },
        Json::Object(vals) => {
            AnyLuaValue::LuaArray(vals.into_iter().map(|(key, val)| {
                (AnyLuaValue::LuaString(key), convert_json(val))
            }).collect())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::convert_json;
    use hlua::any::AnyLuaValue::*;
    use rustc_serialize::json::{Json, ToJson, Object};
    use std::collections::BTreeMap;

    #[test]
    fn convert_string() {
        let input = "Hello World".to_string();
        let json_input = Json::String(input.clone());
        assert_eq!(convert_json(json_input), LuaString(input));
    }
    #[test]
    fn convert_numbers() {
        let lua_number = LuaNumber(42f64);
        assert_eq!(convert_json(Json::F64(42f64)), lua_number);
        assert_eq!(convert_json(Json::I64(42i64)), lua_number);
        assert_eq!(convert_json(Json::U64(42u64)), lua_number);
    }

    #[test]
    fn convert_bool() {
        assert_eq!(convert_json(Json::Boolean(true)), LuaBoolean(true));
        assert_eq!(convert_json(Json::Boolean(false)), LuaBoolean(false));
    }

    #[test]
    fn convert_array() {
        let json = Json::Array(
            vec![Json::Boolean(true), Json::F64(11f64),
                 Json::String("Foo".to_string())]
        );
        let lua = LuaArray(vec![
                (LuaNumber(1f64), LuaBoolean(true)),
                (LuaNumber(2f64), LuaNumber(11f64)),
                (LuaNumber(3f64), LuaString("Foo".to_string()))
            ]);
        assert_eq!(convert_json(json), lua);
    }

    #[test]
    fn convert_object() {
        // { name="foo", point: {x=0, y=1}, valid=false }
        // The fields are emitted in alphabetical order thanks to Json's
        // internal BTreeMap representation.

        let mut json_tree: Object = BTreeMap::new();
        let mut point_tree: Object = BTreeMap::new();
        point_tree.insert("x".to_string(), Json::U64(0u64));
        point_tree.insert("y".to_string(), Json::U64(1u64));
        json_tree.insert("point".to_string(), Json::Object(point_tree));
        json_tree.insert("name".to_string(), Json::String("foo".to_string()));
        json_tree.insert("valid".to_string(), Json::Boolean(false));

        let lua = LuaArray(vec![
            (LuaString("name".to_string()), LuaString("foo".to_string())),
            (LuaString("point".to_string()), LuaArray(vec![
                (LuaString("x".to_string()), LuaNumber(0f64)),
                (LuaString("y".to_string()), LuaNumber(1f64))
            ])),
            (LuaString("valid".to_string()), LuaBoolean(false)),
        ]);

        assert_eq!(convert_json(Json::Object(json_tree)), lua);
    }
}
<commit_msg>Fix registry compile errors<commit_after>//! Metamethods for accesssing the registry values!

use super::super::super::registry;

use hlua::any::AnyLuaValue;

use rustc_serialize::json::{Json, ToJson};
use std::ops::Deref;

pub fn index(table: AnyLuaValue, lua_key: AnyLuaValue)
             -> Result<AnyLuaValue, &'static str> {
    if let AnyLuaValue::LuaString(key) = lua_key {
        let maybe_json = registry::get_json(&key);
        if let Some(json) = maybe_json {
            Ok(convert_json(json.deref().to_json()))
        }
        else {
            // Should be nil, nil doesn't seem to be included
            Err("No value found for that key!")
        }
    }
    else {
        Err("The registry can only be indexed with keys!")
    }
}

// Prevent lua from changing the registry?
pub fn new_index(table: AnyLuaValue, key: AnyLuaValue, val: AnyLuaValue)
                 -> Result<(), &'static str> {
    Err("You can't set the registry!")
}

fn convert_json(json: Json) -> AnyLuaValue {
    match json {
        Json::String(val) => AnyLuaValue::LuaString(val),
        Json::Boolean(val) => AnyLuaValue::LuaBoolean(val),
        Json::F64(val) => AnyLuaValue::LuaNumber(val),
        Json::I64(val) => AnyLuaValue::LuaNumber((val as i32) as f64),
        Json::U64(val) => AnyLuaValue::LuaNumber((val as u32) as f64),
        Json::Null => AnyLuaValue::LuaNil,
        Json::Array(vals) => {
            let mut count = 0f64;
            // Gotta love that 1-based indexing. Start at zero but increment for
            // the first one. It works here at least.
            AnyLuaValue::LuaArray(vals.into_iter().map(|v| {
                count += 1.0;
                (AnyLuaValue::LuaNumber(count), convert_json(v))
            }).collect())
        },
        Json::Object(vals) => {
            AnyLuaValue::LuaArray(vals.into_iter().map(|(key, val)| {
                (AnyLuaValue::LuaString(key), convert_json(val))
            }).collect())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::convert_json;
    use hlua::any::AnyLuaValue::*;
    use rustc_serialize::json::{Json, ToJson, Object};
    use std::collections::BTreeMap;

    #[test]
    fn convert_string() {
        let input = "Hello World".to_string();
        let json_input = Json::String(input.clone());
        assert_eq!(convert_json(json_input), LuaString(input));
    }
    #[test]
    fn convert_numbers() {
        let lua_number = LuaNumber(42f64);
        assert_eq!(convert_json(Json::F64(42f64)), lua_number);
        assert_eq!(convert_json(Json::I64(42i64)), lua_number);
        assert_eq!(convert_json(Json::U64(42u64)), lua_number);
    }

    #[test]
    fn convert_bool() {
        assert_eq!(convert_json(Json::Boolean(true)), LuaBoolean(true));
        assert_eq!(convert_json(Json::Boolean(false)), LuaBoolean(false));
    }

    #[test]
    fn convert_array() {
        let json = Json::Array(
            vec![Json::Boolean(true), Json::F64(11f64),
                 Json::String("Foo".to_string())]
        );
        let lua = LuaArray(vec![
                (LuaNumber(1f64), LuaBoolean(true)),
                (LuaNumber(2f64), LuaNumber(11f64)),
                (LuaNumber(3f64), LuaString("Foo".to_string()))
            ]);
        assert_eq!(convert_json(json), lua);
    }

    #[test]
    fn convert_object() {
        // { name="foo", point: {x=0, y=1}, valid=false }
        // The fields are emitted in alphabetical order thanks to Json's
        // internal BTreeMap representation.

        let mut json_tree: Object = BTreeMap::new();
        let mut point_tree: Object = BTreeMap::new();
        point_tree.insert("x".to_string(), Json::U64(0u64));
        point_tree.insert("y".to_string(), Json::U64(1u64));
        json_tree.insert("point".to_string(), Json::Object(point_tree));
        json_tree.insert("name".to_string(), Json::String("foo".to_string()));
        json_tree.insert("valid".to_string(), Json::Boolean(false));

        let lua = LuaArray(vec![
            (LuaString("name".to_string()), LuaString("foo".to_string())),
            (LuaString("point".to_string()), LuaArray(vec![
                (LuaString("x".to_string()), LuaNumber(0f64)),
                (LuaString("y".to_string()), LuaNumber(1f64))
            ])),
            (LuaString("valid".to_string()), LuaBoolean(false)),
        ]);

        assert_eq!(convert_json(Json::Object(json_tree)), lua);
    }
}
<|endoftext|>


<commit_before>//! The Ref object is a helper over the link functionality, so one is able to create references to
//! files outside of the imag store.

use std::path::PathBuf;
use std::ops::Deref;
use std::ops::DerefMut;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::store::Store;
use libimagerror::into::IntoError;

use toml::Value;

use error::RefErrorKind as REK;
use flags::RefFlags;
use result::Result;

pub struct Ref<'a>(FileLockEntry<'a>);

impl<'a> Ref<'a> {

    /// Try to get `si` as Ref object from the store
    pub fn get(store: &'a Store, si: StoreId) -> Result<Ref<'a>> {
        match store.get(si) {
            Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
            Ok(None) => return Err(REK::RefNotInStore.into_error()),
            Ok(Some(fle)) => Ref::read_reference(&fle).map(|_| Ref(fle)),
        }
    }

    fn read_reference(fle: &FileLockEntry<'a>) -> Result<PathBuf> {
        match fle.get_header().read("ref.reference") {
            Ok(Some(Value::String(s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Create a Ref object which refers to `pb`
    pub fn create(store: &Store, pb: PathBuf, flags: RefFlags) -> Result<Ref<'a>> {
        unimplemented!()
    }

    /// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash
    /// algorithm on it
    fn hash_path(pb: &PathBuf) -> String {
        unimplemented!()
    }

    /// check whether the pointer the Ref represents still points to a file which exists
    pub fn fs_link_exists(&self) -> bool {
        unimplemented!()
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_file()`
    pub fn is_ref_to_file(&self) -> bool {
        unimplemented!()
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_dir()`
    pub fn is_ref_to_dir(&self) -> bool {
        unimplemented!()
    }

    /// Alias for `!Ref::fs_link_exists()`
    pub fn is_dangling(&self) -> bool {
        !self.fs_link_exists()
    }

    /// check whether the pointer the Ref represents is valid
    /// This includes:
    ///     - Hashsum of the file is still the same as stored in the Ref
    ///     - file permissions are still valid
    pub fn fs_link_valid(&self) -> bool {
        unimplemented!()
    }

    /// Check whether the file permissions of the referenced file are equal to the stored
    /// permissions
    pub fn fs_link_valid_permissions(&self) -> bool {
        unimplemented!()
    }

    /// Check whether the Hashsum of the referenced file is equal to the stored hashsum
    pub fn fs_link_valid_hash(&self) -> bool {
        unimplemented!()
    }

    /// Update the Ref by re-checking the file from FS
    /// This errors if the file is not present or cannot be read()
    pub fn update_ref(&mut self) -> Result<()> {
        unimplemented!()
    }

    /// Get the path of the file which is reffered to by this Ref
    pub fn fs_file(&self) -> &PathBuf {
        unimplemented!()
    }

    /// Check whether there is a reference to the file at `pb`
    pub fn exists(store: &Store, pb: PathBuf) -> Result<bool> {
        unimplemented!()
    }

    /// Re-find a referenced file
    ///
    /// This function tries to re-find a ref by searching all directories in `search_roots` recursively
    /// for a file which matches the hash of the Ref `ref`.
    ///
    /// If `search_roots` is `None`, it starts at the filesystem root `/`.
    ///
    /// # Warning
    ///
    /// This option causes heavy I/O as it recursively searches the Filesystem.
    pub fn refind(&self, search_roots: Option<Vec<PathBuf>>) -> Option<PathBuf> {
        unimplemented!()
    }

}

impl<'a> Deref for Ref<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Ref<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}
<commit_msg>Impl Ref::create()<commit_after>//! The Ref object is a helper over the link functionality, so one is able to create references to
//! files outside of the imag store.

use std::path::PathBuf;
use std::ops::Deref;
use std::ops::DerefMut;
use std::collections::BTreeMap;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::Store;
use libimagerror::into::IntoError;

use toml::Value;

use error::RefErrorKind as REK;
use flags::RefFlags;
use result::Result;
use module_path::ModuleEntryPath;

pub struct Ref<'a>(FileLockEntry<'a>);

impl<'a> Ref<'a> {

    /// Try to get `si` as Ref object from the store
    pub fn get(store: &'a Store, si: StoreId) -> Result<Ref<'a>> {
        match store.get(si) {
            Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
            Ok(None) => return Err(REK::RefNotInStore.into_error()),
            Ok(Some(fle)) => Ref::read_reference(&fle).map(|_| Ref(fle)),
        }
    }

    fn read_reference(fle: &FileLockEntry<'a>) -> Result<PathBuf> {
        match fle.get_header().read("ref.reference") {
            Ok(Some(Value::String(s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Create a Ref object which refers to `pb`
    pub fn create(store: &'a Store, pb: PathBuf, flags: RefFlags) -> Result<Ref<'a>> {
        use std::fs::File;
        use std::io::Read;
        use crypto::sha1::Sha1;
        use crypto::digest::Digest;

        if !pb.exists() {
            return Err(REK::RefTargetDoesNotExist.into_error());
        }
        if flags.get_content_hashing() && pb.is_dir() {
            return Err(REK::RefTargetCannotBeHashed.into_error());
        }

        let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold
            try!(File::open(pb.clone())
                .map_err(Box::new)
                .map_err(|e| REK::RefTargetFileCannotBeOpened.into_error_with_cause(e))

                // If we were able to open this file,
                // we hash the contents of the file and return (file, hash)
                .and_then(|mut file| {
                    let opt_contenthash = if flags.get_content_hashing() {
                        let mut hasher = Sha1::new();
                        let mut s = String::new();
                        file.read_to_string(&mut s);
                        hasher.input_str(&s[..]);
                        Some(hasher.result_str())
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash))
                })

                // and then we get the permissions if we have to
                // and return (file, content hash, permissions)
                .and_then(|(file, opt_contenthash)| {
                    let opt_permissions = if flags.get_permission_tracking() {
                        Some(try!(file
                                  .metadata()
                                  .map(|md| md.permissions())
                                  .map_err(Box::new)
                                  .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
                        ))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash, opt_permissions))
                })

                // and then we try to canonicalize the PathBuf, because we want to store a
                // canonicalized path
                // and return (file, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_contenthash, opt_permissions)| {
                    pb.canonicalize()
                        .map(|can| (file, opt_contenthash, opt_permissions, can))
                        // if PathBuf::canonicalize() failed, build an error from the return value
                        .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(Box::new(e)))
                })

                // and then we hash the canonicalized path
                // and return (file, content hash, permissions, canonicalized path, path hash)
                .and_then(|(file, opt_contenthash, opt_permissions, can)| {
                    let path_hash = try!(Ref::hash_path(&can)
                        .map_err(Box::new)
                        .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
                    );

                    Ok((file, opt_contenthash, opt_permissions, can, path_hash))
                })

                // and then we convert the PathBuf of the canonicalized path to a String to be able
                // to save it in the Ref FileLockEntry obj
                // and return
                // (file, content hash, permissions, canonicalized path as String, path hash)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    match can.to_str().map(String::from) {
                        // UTF convert error in PathBuf::to_str(),
                        None      => Err(REK::PathUTF8Error.into_error()),
                        Some(can) => Ok((file, opt_conhash, opt_perm, can, path_hash))
                    }
                })

                // and then we create the FileLockEntry in the Store
                // and return (filelockentry, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    let fle = try!(store
                                   .create(ModuleEntryPath::new(path_hash))
                                   .map_err(Box::new)
                                   .map_err(|e| REK::StoreWriteError.into_error_with_cause(e))
                    );

                    Ok((fle, opt_conhash, opt_perm, can))
                })
            )
        };

        for tpl in [
                Some(("ref",             Value::Table(BTreeMap::new()))),
                Some(("ref.permissions", Value::Table(BTreeMap::new()))),

                Some(("ref.path", Value::String(canonical_path))),

                content_hash.map(|h| ("ref.content_hash", Value::String(h))),
                permissions.map(|p| ("ref.permissions.ro", Value::Boolean(p.readonly()))),
            ].into_iter()
        {
            match tpl {
                &Some((ref s, ref v)) => {
                    match fle.get_header_mut().insert(s, v.clone()) {
                        Ok(false) => {
                            let e = REK::HeaderFieldAlreadyExistsError.into_error();
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        Err(e) => {
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        _ => (),
                    }
                }
                &None => {
                    debug!("Not going to insert.");
                }
            }
        }

        Ok(Ref(fle))
    }

    /// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash
    /// algorithm on it
    fn hash_path(pb: &PathBuf) -> String {
        unimplemented!()
    }

    /// check whether the pointer the Ref represents still points to a file which exists
    pub fn fs_link_exists(&self) -> bool {
        unimplemented!()
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_file()`
    pub fn is_ref_to_file(&self) -> bool {
        unimplemented!()
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_dir()`
    pub fn is_ref_to_dir(&self) -> bool {
        unimplemented!()
    }

    /// Alias for `!Ref::fs_link_exists()`
    pub fn is_dangling(&self) -> bool {
        !self.fs_link_exists()
    }

    /// check whether the pointer the Ref represents is valid
    /// This includes:
    ///     - Hashsum of the file is still the same as stored in the Ref
    ///     - file permissions are still valid
    pub fn fs_link_valid(&self) -> bool {
        unimplemented!()
    }

    /// Check whether the file permissions of the referenced file are equal to the stored
    /// permissions
    pub fn fs_link_valid_permissions(&self) -> bool {
        unimplemented!()
    }

    /// Check whether the Hashsum of the referenced file is equal to the stored hashsum
    pub fn fs_link_valid_hash(&self) -> bool {
        unimplemented!()
    }

    /// Update the Ref by re-checking the file from FS
    /// This errors if the file is not present or cannot be read()
    pub fn update_ref(&mut self) -> Result<()> {
        unimplemented!()
    }

    /// Get the path of the file which is reffered to by this Ref
    pub fn fs_file(&self) -> &PathBuf {
        unimplemented!()
    }

    /// Check whether there is a reference to the file at `pb`
    pub fn exists(store: &Store, pb: PathBuf) -> Result<bool> {
        unimplemented!()
    }

    /// Re-find a referenced file
    ///
    /// This function tries to re-find a ref by searching all directories in `search_roots` recursively
    /// for a file which matches the hash of the Ref `ref`.
    ///
    /// If `search_roots` is `None`, it starts at the filesystem root `/`.
    ///
    /// # Warning
    ///
    /// This option causes heavy I/O as it recursively searches the Filesystem.
    pub fn refind(&self, search_roots: Option<Vec<PathBuf>>) -> Option<PathBuf> {
        unimplemented!()
    }

}

impl<'a> Deref for Ref<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Ref<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}
<|endoftext|>