<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use std::mem;

use token::Token;

/// Returns the content of an AST as raw text, without any formatting
/// Useful for tools like grammar checks
pub fn view_as_text(tokens: &[Token]) -> String {
    let mut output = String::new();

    for token in tokens {
        match token {
            &Token::Str(ref s) => output.push_str(s),
            &Token::Rule
                | &Token::SoftBreak
                | &Token::HardBreak
                => output.push('\n'),

            _ => output.push_str(&view_as_text(token.inner().unwrap()))
        }
    }
    output
}

/// Insert a comment at the given pos in the text_view
pub fn insert_at(tokens: &mut Vec<Token>, comment: &str, pos: usize) -> Option<usize> {
    let mut pos = pos;
    let mut found = None;
    for i in 0..tokens.len() {
        let recurse  = match tokens[i] {
            Token::Str(ref s) => {
                let len = s.chars().count();
                if pos < len {
                    found = Some(i);
                    break;
                } else {
                    pos = pos - len;
                    false
                }
            },

            Token::Rule
                | Token::SoftBreak
                | Token::HardBreak
                => {
                    if pos < 1 {
                        found = Some(i);
                        break;
                    } else {
                        pos -= 1;
                        false
                    }
                }
            Token::Comment(_) => {
                false
            },

            _ => true
        };

        // Moved out of the match 'thanks' to borrowcheck
        if recurse {
            if let Some(ref mut inner) = tokens[i].inner_mut() {
                if let Some(new_pos) = insert_at(inner, comment, pos) {
                    pos = new_pos;
                } else {
                    return None;
                }
            }
        }
    }

    let new_token = Token::Comment(comment.to_owned());
    if let Some(i) = found {
        if !tokens[i].is_str() {
            if i >= tokens.len() - 1 {
                tokens.push(new_token);
            } else {
                tokens.insert(i+1, new_token);
            }
        } else {
            let old_token = mem::replace(&mut tokens[i], Token::Str(String::new()));
            if let Token::Str(old_str) = old_token {
                let mut chars_left:Vec<char> = old_str.chars().collect();
                let chars_right = chars_left.split_off(pos);
                let str_left:String = chars_left.into_iter().collect();
                let str_right:String = chars_right.into_iter().collect();
                tokens[i] = Token::Str(str_left);
                if i >= tokens.len() - 1 {
                    tokens.push(new_token);
                    tokens.push(Token::Str(str_right));
                } else {
                    tokens.insert(i+1, new_token);
                    tokens.insert(i+2, Token::Str(str_right));
                }
            }
        }
        return None;
    } else {
        return Some(pos);
    }

}

#[test]
fn test_text_view() {
    let ast = vec!(Token::Str("123".to_owned()),
                   Token::Emphasis(vec!(Token::Str("456".to_owned()))),
                   Token::Str("789".to_owned()));
    assert_eq!(view_as_text(&ast), "123456789");
}

#[test]
fn test_text_insert() {
    let mut ast = vec!(Token::Str("123".to_owned()),
                       Token::Emphasis(vec!(Token::Str("456".to_owned()))),
                       Token::Str("789".to_owned()));
    insert_at(&mut ast, "!!!", 5);
    let expected = vec!(Token::Str("123".to_owned()),
                        Token::Emphasis(vec!(Token::Str("45".to_owned()),
                                             Token::Comment("!!!".to_owned()),
                                             Token::Str("6".to_owned()))),
                       Token::Str("789".to_owned()));
    assert_eq!(expected, ast);
}
<commit_msg>Rewrite view_as_text more functionnaly<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use std::mem;

use token::Token;
use std::default::Default;

pub fn traverse_token<F1, F2, R>(token: &Token, f: &F1, add: &F2) -> R
    where F1: Fn(&str) -> R,
          R: Default,
          F2: Fn(R, R) -> R
{
    match token {
        &Token::Str(ref s) => f(s),

        &Token::Rule
            | &Token::SoftBreak
            | &Token::HardBreak
            => f("\n"),

        & Token::Comment(..)
            | & Token::Image(..)
            | & Token::StandaloneImage(..)
            | & Token::Footnote(..)
            | & Token::Table(..)
            | & Token::TableHead(..)
            | & Token::TableRow(..)
            | & Token::TableCell(..)
            => f(""),

        _ => traverse_vec(token.inner().unwrap(), f, add),
    }
}

/// Traverse a vector of tokens
#[doc(hidden)]
pub fn traverse_vec<F1, F2, R>(tokens: &[Token], f:&F1, add: &F2) -> R
    where F1: Fn(&str) -> R,
          F2: Fn(R, R) -> R,
          R: Default
{
    tokens.iter()
        .map(|t| traverse_token(t, f, add))
        .fold(R::default(), |r1, r2| add(r1, r2))
}

/// Returns the content of an AST as raw text, without any formatting
/// Useful for tools like grammar checks
pub fn view_as_text(tokens: &[Token]) -> String {
    traverse_vec(tokens,
                 &|s| s.to_owned(),
                 &|s1, s2| s1 + &s2)
}

// /// Insert an annotation at begin and end pos begin+len in the text_view
// #[doc(hidden)]
// pub fn insert_annotation(tokens: &mut Vec<Token>, comment: &str, pos: usize, length: usize) -> Option<usize> {
//     let mut pos = pos;
//     let mut found_left = None;
//     let mut found_right = None;
//     for i in 0..tokens.len() {
//         let recurse  = match tokens[i] {
//             Token::Str(ref s) => {
//                 let len = s.chars().count();
//                 if pos < len {
//                     // We found the first element
//                     if found_left.is_some() {
//                         found_right = Some(i);
//                         break;
//                     }
//                     found_left = Some(i);
//                     pos += length;
//                     if pos < len {
//                         found_right = Some(i);
//                         break;
//                     }
//                 } else {
//                     pos = pos - len;
//                     false
//                 }
//             },

//             Token::Rule
//                 | Token::SoftBreak
//                 | Token::HardBreak
//                 => {
//                     if pos < 1 {
//                         found = Some(i);
//                         break;
//                     } else {
//                         pos -= 1;
//                         false
//                     }
//                 }
//             Token::Comment(_) => {
//                 false
//             },

//             _ => true
//         };

//         // Moved out of the match 'thanks' to borrowcheck
//         if recurse {
//             if let Some(ref mut inner) = tokens[i].inner_mut() {
//                 if let Some(new_pos) = insert_at(inner, comment, pos) {
//                     pos = new_pos;
//                 } else {
//                     return None;
//                 }
//             }
//         }
//     }

//     let new_token = Token::Comment(comment.to_owned());
//     if let Some(i) = found {
//         if !tokens[i].is_str() {
//             if i >= tokens.len() - 1 {
//                 tokens.push(new_token);
//             } else {
//                 tokens.insert(i+1, new_token);
//             }
//         } else {
//             let old_token = mem::replace(&mut tokens[i], Token::Str(String::new()));
//             if let Token::Str(old_str) = old_token {
//                 let mut chars_left:Vec<char> = old_str.chars().collect();
//                 let chars_right = chars_left.split_off(pos);
//                 let str_left:String = chars_left.into_iter().collect();
//                 let str_right:String = chars_right.into_iter().collect();
//                 tokens[i] = Token::Str(str_left);
//                 if i >= tokens.len() - 1 {
//                     tokens.push(new_token);
//                     tokens.push(Token::Str(str_right));
//                 } else {
//                     tokens.insert(i+1, new_token);
//                     tokens.insert(i+2, Token::Str(str_right));
//                 }
//             }
//         }
//         return None;
//     } else {
//         return Some(pos);
//     }
// }

/// Insert a comment at the given pos in the text_view
#[doc(hidden)]
pub fn insert_at(tokens: &mut Vec<Token>, comment: &str, pos: usize) -> Option<usize> {
    let mut pos = pos;
    let mut found = None;
    for i in 0..tokens.len() {
        let recurse  = match tokens[i] {
            Token::Str(ref s) => {
                let len = s.chars().count();
                if pos < len {
                    found = Some(i);
                    break;
                } else {
                    pos = pos - len;
                    false
                }
            },

            Token::Rule
                | Token::SoftBreak
                | Token::HardBreak
                => {
                    if pos < 1 {
                        found = Some(i);
                        break;
                    } else {
                        pos -= 1;
                        false
                    }
                }
            Token::Comment(_) => {
                false
            },

            _ => true
        };

        // Moved out of the match 'thanks' to borrowcheck
        if recurse {
            if let Some(ref mut inner) = tokens[i].inner_mut() {
                if let Some(new_pos) = insert_at(inner, comment, pos) {
                    pos = new_pos;
                } else {
                    return None;
                }
            }
        }
    }

    let new_token = Token::Comment(comment.to_owned());
    if let Some(i) = found {
        if !tokens[i].is_str() {
            if i >= tokens.len() - 1 {
                tokens.push(new_token);
            } else {
                tokens.insert(i+1, new_token);
            }
        } else {
            let old_token = mem::replace(&mut tokens[i], Token::Str(String::new()));
            if let Token::Str(old_str) = old_token {
                let mut chars_left:Vec<char> = old_str.chars().collect();
                let chars_right = chars_left.split_off(pos);
                let str_left:String = chars_left.into_iter().collect();
                let str_right:String = chars_right.into_iter().collect();
                tokens[i] = Token::Str(str_left);
                if i >= tokens.len() - 1 {
                    tokens.push(new_token);
                    tokens.push(Token::Str(str_right));
                } else {
                    tokens.insert(i+1, new_token);
                    tokens.insert(i+2, Token::Str(str_right));
                }
            }
        }
        return None;
    } else {
        return Some(pos);
    }
}

#[test]
fn test_text_view() {
    let ast = vec!(Token::Str("123".to_owned()),
                   Token::Emphasis(vec!(Token::Str("456".to_owned()))),
                   Token::Str("789".to_owned()));
    assert_eq!(view_as_text(&ast), "123456789");
}

#[test]
fn test_text_insert() {
    let mut ast = vec!(Token::Str("123".to_owned()),
                       Token::Emphasis(vec!(Token::Str("456".to_owned()))),
                       Token::Str("789".to_owned()));
    insert_at(&mut ast, "!!!", 5);
    let expected = vec!(Token::Str("123".to_owned()),
                        Token::Emphasis(vec!(Token::Str("45".to_owned()),
                                             Token::Comment("!!!".to_owned()),
                                             Token::Str("6".to_owned()))),
                       Token::Str("789".to_owned()));
    assert_eq!(expected, ast);
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! The Clone trait for types that cannot be "implicitly copied"

In Rust, some simple types are "implicitly copyable" and when you
assign them or pass them as arguments, the receiver will get a copy,
leaving the original value in place. These types do not require
allocation to copy and do not have finalizers (i.e. they do not
contain owned boxes or implement `Drop`), so the compiler considers
them cheap and safe to copy and automatically implements the `Copy`
trait for them. For other types copies must be made explicitly,
by convention implementing the `Clone` trait and calling the
`clone` method.

*/

pub trait Clone {
    /// Return a deep copy of the owned object tree. Managed boxes are cloned with a shallow copy.
    fn clone(&self) -> Self;
}

impl Clone for () {
    /// Return a copy of the value.
    #[inline(always)]
    fn clone(&self) -> () { () }
}

impl<T:Clone> Clone for ~T {
    /// Return a deep copy of the owned box.
    #[inline(always)]
    fn clone(&self) -> ~T { ~(**self).clone() }
}

impl<T> Clone for @T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @T { *self }
}

impl<T> Clone for @mut T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @mut T { *self }
}

macro_rules! clone_impl(
    ($t:ty) => {
        impl Clone for $t {
            /// Return a copy of the value.
            #[inline(always)]
            fn clone(&self) -> $t { *self }
        }
    }
)

clone_impl!(int)
clone_impl!(i8)
clone_impl!(i16)
clone_impl!(i32)
clone_impl!(i64)

clone_impl!(uint)
clone_impl!(u8)
clone_impl!(u16)
clone_impl!(u32)
clone_impl!(u64)

clone_impl!(float)
clone_impl!(f32)
clone_impl!(f64)

clone_impl!(bool)
clone_impl!(char)

#[test]
fn test_owned_clone() {
    let a: ~int = ~5i;
    let b: ~int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_clone() {
    let a: @int = @5i;
    let b: @int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_mut_clone() {
    let a: @mut int = @mut 5i;
    let b: @mut int = a.clone();
    assert!(a == b);
    *b = 10;
    assert!(a == b);
}
<commit_msg>add a Clone impl for borrowed pointers<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! The Clone trait for types that cannot be "implicitly copied"

In Rust, some simple types are "implicitly copyable" and when you
assign them or pass them as arguments, the receiver will get a copy,
leaving the original value in place. These types do not require
allocation to copy and do not have finalizers (i.e. they do not
contain owned boxes or implement `Drop`), so the compiler considers
them cheap and safe to copy and automatically implements the `Copy`
trait for them. For other types copies must be made explicitly,
by convention implementing the `Clone` trait and calling the
`clone` method.

*/

pub trait Clone {
    /// Return a deep copy of the owned object tree. Managed boxes are cloned with a shallow copy.
    fn clone(&self) -> Self;
}

impl Clone for () {
    /// Return a copy of the value.
    #[inline(always)]
    fn clone(&self) -> () { () }
}

impl<T:Clone> Clone for ~T {
    /// Return a deep copy of the owned box.
    #[inline(always)]
    fn clone(&self) -> ~T { ~(**self).clone() }
}

impl<T> Clone for @T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @T { *self }
}

impl<T> Clone for @mut T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @mut T { *self }
}

impl<'self, T> Clone for &'self T {
    /// Return a shallow copy of the borrowed pointer.
    #[inline(always)]
    fn clone(&self) -> &'self T { *self }
}

macro_rules! clone_impl(
    ($t:ty) => {
        impl Clone for $t {
            /// Return a copy of the value.
            #[inline(always)]
            fn clone(&self) -> $t { *self }
        }
    }
)

clone_impl!(int)
clone_impl!(i8)
clone_impl!(i16)
clone_impl!(i32)
clone_impl!(i64)

clone_impl!(uint)
clone_impl!(u8)
clone_impl!(u16)
clone_impl!(u32)
clone_impl!(u64)

clone_impl!(float)
clone_impl!(f32)
clone_impl!(f64)

clone_impl!(bool)
clone_impl!(char)

#[test]
fn test_owned_clone() {
    let a: ~int = ~5i;
    let b: ~int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_clone() {
    let a: @int = @5i;
    let b: @int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_mut_clone() {
    let a: @mut int = @mut 5i;
    let b: @mut int = a.clone();
    assert!(a == b);
    *b = 10;
    assert!(a == b);
}

#[test]
fn test_borrowed_clone() {
    let x = 5i;
    let y: &int = &x;
    let z: &int = (&y).clone();
    assert_eq!(*z, 5);
}
<|endoftext|>


<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 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>Added Mult<Amount> for uints<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.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
    }
}
<|endoftext|>


<commit_before>use mio::*;
use mio::net::*;
use rusty_ulid::Ulid;
use {SessionResult,Readiness};
use protocol::ProtocolResult;
use openssl::ssl::{HandshakeError,MidHandshakeSslStream,Ssl,SslStream,NameType,SslVersion};
use std::net::SocketAddr;
use LogDuration;
use SessionMetrics;
use sozu_command::ready::Ready;

pub enum TlsState {
  Initial,
  Handshake,
  Established,
  Error(HandshakeError<TcpStream>),
}

pub struct TlsHandshake {
  pub readiness:       Readiness,
  pub front:           Option<TcpStream>,
  pub request_id:      Ulid,
  pub ssl:             Option<Ssl>,
  pub stream:          Option<SslStream<TcpStream>>,
  mid:                 Option<MidHandshakeSslStream<TcpStream>>,
  state:               TlsState,
  address:             Option<SocketAddr>,
}

impl TlsHandshake {
  pub fn new(ssl:Ssl, sock: TcpStream, request_id: Ulid, address: Option<SocketAddr>) -> TlsHandshake {
    TlsHandshake {
      front:          Some(sock),
      ssl:            Some(ssl),
      mid:            None,
      stream:         None,
      state:          TlsState::Initial,
      readiness:      Readiness {
                        interest: Ready::readable() | Ready::hup() | Ready::error(),
                        event:    Ready::empty(),
      },
      request_id,
      address,
    }
  }

  pub fn readable(&mut self, metrics: &mut SessionMetrics) -> (ProtocolResult,SessionResult) {
    match self.state {
      TlsState::Error(_) => return (ProtocolResult::Continue, SessionResult::CloseSession),
      TlsState::Initial => {
        let ssl     = self.ssl.take().expect("TlsHandshake should have a Ssl backend");
        let sock    = self.front.take().expect("TlsHandshake should have a front socket");
        match ssl.accept(sock) {
          Ok(stream) => {
            self.stream = Some(stream);
            self.state = TlsState::Established;
            return (ProtocolResult::Upgrade, SessionResult::Continue);
          },
          Err(HandshakeError::SetupFailure(e)) => {
            error!("accept: handshake setup failed (client = {:?}): {:?}", self.address, e);
            self.state = TlsState::Error(HandshakeError::SetupFailure(e));
            return (ProtocolResult::Continue, SessionResult::CloseSession);
          },
          Err(HandshakeError::Failure(e)) => {
            {
              if let Some(error_stack) = e.error().ssl_error() {
                let errors = error_stack.errors();
                if errors.len() == 1 {
                  if errors[0].code() == 0x140A1175 {
                    incr!("openssl.inappropriate_fallback.error");
                  } else if errors[0].code() == 0x1408A10B {
                    incr!("openssl.wrong_version_number.error");
                  } else if errors[0].code() == 0x140760FC {
                    incr!("openssl.unknown_protocol.error");
                  } else if errors[0].code() == 0x1407609C {
                    //someone tried to connect in plain HTTP to a TLS server
                    incr!("openssl.http_request.error");
                  } else if errors[0].code() == 0x1422E0EA {
                    // SNI error
                    self.log_request_error(metrics, &e);
                  } else {
                    error!("accept: handshake failed (client = {:?}): {:?}", self.address, e);
                  }
                } else if errors.len() == 2 && errors[0].code() == 0x1412E0E2 && errors[1].code() == 0x1408A0E3 {
                  incr!("openssl.sni.error");
                } else {
                  error!("accept: handshake failed (client = {:?}): {:?}", self.address, e);
                }
              } else {
                error!("accept: handshake failed (client = {:?}): {:?}", self.address, e);
              }
            }
            self.state = TlsState::Error(HandshakeError::Failure(e));
            return (ProtocolResult::Continue, SessionResult::CloseSession);
          },
          Err(HandshakeError::WouldBlock(mid)) => {
            self.state = TlsState::Handshake;
            self.mid = Some(mid);
            self.readiness.event.remove(Ready::readable());
            return (ProtocolResult::Continue, SessionResult::Continue);
          }
        }
      },
      TlsState::Handshake => {
        let mid = self.mid.take().expect("TlsHandshake should have a MidHandshakeSslStream backend");
        match mid.handshake() {
          Ok(stream) => {
            self.stream = Some(stream);
            self.state = TlsState::Established;
            return (ProtocolResult::Upgrade, SessionResult::Continue);
          },
          Err(HandshakeError::SetupFailure(e)) => {
            debug!("mid handshake setup failed (client = {:?}): {:?}", self.address, e);
            self.state = TlsState::Error(HandshakeError::SetupFailure(e));
            return (ProtocolResult::Continue, SessionResult::CloseSession);
          },
          Err(HandshakeError::Failure(e)) => {
            debug!("mid handshake failed (client = {:?}): {:?}", self.address, e);
            self.state = TlsState::Error(HandshakeError::Failure(e));
            return (ProtocolResult::Continue, SessionResult::CloseSession);
          },
          Err(HandshakeError::WouldBlock(new_mid)) => {
            self.state = TlsState::Handshake;
            self.mid = Some(new_mid);
            self.readiness.event.remove(Ready::readable());
            return (ProtocolResult::Continue, SessionResult::Continue);
          }
        }
      },
      TlsState::Established => {
        return (ProtocolResult::Upgrade, SessionResult::Continue);
      }
    }

  }

  pub fn socket(&self) -> Option<&TcpStream> {
    match self.state {
      TlsState::Initial => self.front.as_ref(),
      TlsState::Handshake => self.mid.as_ref().map(|mid| mid.get_ref()),
      TlsState::Established => self.stream.as_ref().map(|stream| stream.get_ref()),
      TlsState::Error(ref error) => {
        match error {
          &HandshakeError::SetupFailure(_) => {
            self.front.as_ref().or_else(|| self.mid.as_ref().map(|mid| mid.get_ref()))
          },
          &HandshakeError::Failure(ref mid) | &HandshakeError::WouldBlock(ref mid) => Some(mid.get_ref())
        }
      }
    }
  }

  pub fn socket_mut(&mut self) -> Option<&mut TcpStream> {
    match self.state {
      TlsState::Initial => self.front.as_mut(),
      TlsState::Handshake => self.mid.as_mut().map(|mid| mid.get_mut()),
      TlsState::Established => self.stream.as_mut().map(|stream| stream.get_mut()),
      TlsState::Error(HandshakeError::Failure(ref mut mid)) |
      TlsState::Error(HandshakeError::WouldBlock(ref mut mid)) => {
          Some(mid.get_mut())
      },
      TlsState::Error(HandshakeError::SetupFailure(_)) => {
          self.front.as_mut().or(self.mid.as_mut().map(|mid| mid.get_mut()))
      }
    }
  }

  pub fn log_request_error(&mut self, metrics: &mut SessionMetrics, handshake: &MidHandshakeSslStream<TcpStream>) {
    metrics.service_stop();

    let session = match self.address.or_else(|| handshake.get_ref().peer_addr().ok()) {
      None => String::from("-"),
      Some(SocketAddr::V4(addr)) => format!("{}", addr),
      Some(SocketAddr::V6(addr)) => format!("{}", addr),
    };

    let backend = "-";

    let response_time = metrics.response_time();
    let service_time  = metrics.service_time();

    let proto = handshake.ssl().version2().map(|version| match version {
      SslVersion::SSL3 => "HTTPS-SSL3",
      SslVersion::TLS1 => "HTTPS-TLS1.0",
      SslVersion::TLS1_1 => "HTTPS-TLS1.1",
      SslVersion::TLS1_2 => "HTTPS-TLS1.2",
      _ => "HTTPS-TLS1.3",
    }).unwrap_or("HTTPS");

    let message = handshake.ssl().servername(NameType::HOST_NAME)
      .map(|s| format!("unknown server name: {}", s))
      .unwrap_or("no SNI".to_string());

    error_access!("{} - -\t{} -> {}\t{} {} {} {}\t{} | {}",
      self.request_id, session, backend,
      LogDuration(response_time), LogDuration(service_time), metrics.bin, metrics.bout,
      proto, message);
  }
}

<commit_msg>openssl handshake: display the user readable message before the error<commit_after>use mio::*;
use mio::net::*;
use rusty_ulid::Ulid;
use {SessionResult,Readiness};
use protocol::ProtocolResult;
use openssl::ssl::{HandshakeError,MidHandshakeSslStream,Ssl,SslStream,NameType,SslVersion};
use std::net::SocketAddr;
use LogDuration;
use SessionMetrics;
use sozu_command::ready::Ready;

pub enum TlsState {
  Initial,
  Handshake,
  Established,
  Error(HandshakeError<TcpStream>),
}

pub struct TlsHandshake {
  pub readiness:       Readiness,
  pub front:           Option<TcpStream>,
  pub request_id:      Ulid,
  pub ssl:             Option<Ssl>,
  pub stream:          Option<SslStream<TcpStream>>,
  mid:                 Option<MidHandshakeSslStream<TcpStream>>,
  state:               TlsState,
  address:             Option<SocketAddr>,
}

impl TlsHandshake {
  pub fn new(ssl:Ssl, sock: TcpStream, request_id: Ulid, address: Option<SocketAddr>) -> TlsHandshake {
    TlsHandshake {
      front:          Some(sock),
      ssl:            Some(ssl),
      mid:            None,
      stream:         None,
      state:          TlsState::Initial,
      readiness:      Readiness {
                        interest: Ready::readable() | Ready::hup() | Ready::error(),
                        event:    Ready::empty(),
      },
      request_id,
      address,
    }
  }

  pub fn readable(&mut self, metrics: &mut SessionMetrics) -> (ProtocolResult,SessionResult) {
    match self.state {
      TlsState::Error(_) => return (ProtocolResult::Continue, SessionResult::CloseSession),
      TlsState::Initial => {
        let ssl     = self.ssl.take().expect("TlsHandshake should have a Ssl backend");
        let sock    = self.front.take().expect("TlsHandshake should have a front socket");
        match ssl.accept(sock) {
          Ok(stream) => {
            self.stream = Some(stream);
            self.state = TlsState::Established;
            return (ProtocolResult::Upgrade, SessionResult::Continue);
          },
          Err(HandshakeError::SetupFailure(e)) => {
            error!("accept: handshake setup failed (client = {:?}): {:?}", self.address, e);
            self.state = TlsState::Error(HandshakeError::SetupFailure(e));
            return (ProtocolResult::Continue, SessionResult::CloseSession);
          },
          Err(HandshakeError::Failure(e)) => {
            {
              if let Some(error_stack) = e.error().ssl_error() {
                let errors = error_stack.errors();
                if errors.len() == 1 {
                  if errors[0].code() == 0x140A1175 {
                    incr!("openssl.inappropriate_fallback.error");
                  } else if errors[0].code() == 0x1408A10B {
                    incr!("openssl.wrong_version_number.error");
                  } else if errors[0].code() == 0x140760FC {
                    incr!("openssl.unknown_protocol.error");
                  } else if errors[0].code() == 0x1407609C {
                    //someone tried to connect in plain HTTP to a TLS server
                    incr!("openssl.http_request.error");
                  } else if errors[0].code() == 0x1422E0EA {
                    // SNI error
                    self.log_request_error(metrics, &e);
                  } else {
                    error!("accept: handshake failed (client = {:?}): {} ({:?})", self.address, e.error(), e);
                  }
                } else if errors.len() == 2 && errors[0].code() == 0x1412E0E2 && errors[1].code() == 0x1408A0E3 {
                  incr!("openssl.sni.error");
                } else {
                  error!("accept: handshake failed (client = {:?}): {} ({:?})", self.address, e.error(), e);
                }
              } else {
                error!("accept: handshake failed (client = {:?}): {} ({:?})", self.address, e.error(), e);
              }
            }
            self.state = TlsState::Error(HandshakeError::Failure(e));
            return (ProtocolResult::Continue, SessionResult::CloseSession);
          },
          Err(HandshakeError::WouldBlock(mid)) => {
            self.state = TlsState::Handshake;
            self.mid = Some(mid);
            self.readiness.event.remove(Ready::readable());
            return (ProtocolResult::Continue, SessionResult::Continue);
          }
        }
      },
      TlsState::Handshake => {
        let mid = self.mid.take().expect("TlsHandshake should have a MidHandshakeSslStream backend");
        match mid.handshake() {
          Ok(stream) => {
            self.stream = Some(stream);
            self.state = TlsState::Established;
            return (ProtocolResult::Upgrade, SessionResult::Continue);
          },
          Err(HandshakeError::SetupFailure(e)) => {
            debug!("mid handshake setup failed (client = {:?}): {:?}", self.address, e);
            self.state = TlsState::Error(HandshakeError::SetupFailure(e));
            return (ProtocolResult::Continue, SessionResult::CloseSession);
          },
          Err(HandshakeError::Failure(e)) => {
            debug!("mid handshake failed (client = {:?}): {:?}", self.address, e);
            self.state = TlsState::Error(HandshakeError::Failure(e));
            return (ProtocolResult::Continue, SessionResult::CloseSession);
          },
          Err(HandshakeError::WouldBlock(new_mid)) => {
            self.state = TlsState::Handshake;
            self.mid = Some(new_mid);
            self.readiness.event.remove(Ready::readable());
            return (ProtocolResult::Continue, SessionResult::Continue);
          }
        }
      },
      TlsState::Established => {
        return (ProtocolResult::Upgrade, SessionResult::Continue);
      }
    }

  }

  pub fn socket(&self) -> Option<&TcpStream> {
    match self.state {
      TlsState::Initial => self.front.as_ref(),
      TlsState::Handshake => self.mid.as_ref().map(|mid| mid.get_ref()),
      TlsState::Established => self.stream.as_ref().map(|stream| stream.get_ref()),
      TlsState::Error(ref error) => {
        match error {
          &HandshakeError::SetupFailure(_) => {
            self.front.as_ref().or_else(|| self.mid.as_ref().map(|mid| mid.get_ref()))
          },
          &HandshakeError::Failure(ref mid) | &HandshakeError::WouldBlock(ref mid) => Some(mid.get_ref())
        }
      }
    }
  }

  pub fn socket_mut(&mut self) -> Option<&mut TcpStream> {
    match self.state {
      TlsState::Initial => self.front.as_mut(),
      TlsState::Handshake => self.mid.as_mut().map(|mid| mid.get_mut()),
      TlsState::Established => self.stream.as_mut().map(|stream| stream.get_mut()),
      TlsState::Error(HandshakeError::Failure(ref mut mid)) |
      TlsState::Error(HandshakeError::WouldBlock(ref mut mid)) => {
          Some(mid.get_mut())
      },
      TlsState::Error(HandshakeError::SetupFailure(_)) => {
          self.front.as_mut().or(self.mid.as_mut().map(|mid| mid.get_mut()))
      }
    }
  }

  pub fn log_request_error(&mut self, metrics: &mut SessionMetrics, handshake: &MidHandshakeSslStream<TcpStream>) {
    metrics.service_stop();

    let session = match self.address.or_else(|| handshake.get_ref().peer_addr().ok()) {
      None => String::from("-"),
      Some(SocketAddr::V4(addr)) => format!("{}", addr),
      Some(SocketAddr::V6(addr)) => format!("{}", addr),
    };

    let backend = "-";

    let response_time = metrics.response_time();
    let service_time  = metrics.service_time();

    let proto = handshake.ssl().version2().map(|version| match version {
      SslVersion::SSL3 => "HTTPS-SSL3",
      SslVersion::TLS1 => "HTTPS-TLS1.0",
      SslVersion::TLS1_1 => "HTTPS-TLS1.1",
      SslVersion::TLS1_2 => "HTTPS-TLS1.2",
      _ => "HTTPS-TLS1.3",
    }).unwrap_or("HTTPS");

    let message = handshake.ssl().servername(NameType::HOST_NAME)
      .map(|s| format!("unknown server name: {}", s))
      .unwrap_or("no SNI".to_string());

    error_access!("{} - -\t{} -> {}\t{} {} {} {}\t{} | {}",
      self.request_id, session, backend,
      LogDuration(response_time), LogDuration(service_time), metrics.bin, metrics.bout,
      proto, message);
  }
}

<|endoftext|>


<commit_before>//! Copy-pasted from the internet

use std::fmt;

/// Available encoding character sets
#[derive(Clone, Copy, Debug)]
enum _CharacterSet {
    /// The standard character set (uses `+` and `/`)
    _Standard,
    /// The URL safe character set (uses `-` and `_`)
    _UrlSafe,
}

static STANDARD_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                        abcdefghijklmnopqrstuvwxyz\
                                        0123456789+/";

static _URLSAFE_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                       abcdefghijklmnopqrstuvwxyz\
                                       0123456789-_";

pub fn encode(input: &[u8]) -> String {
    let bytes = STANDARD_CHARS;

    let len = input.len();

    // Preallocate memory.
    let prealloc_len = (len + 2) / 3 * 4;
    let mut out_bytes = vec![b'='; prealloc_len];

    // Deal with padding bytes
    let mod_len = len % 3;

    // Use iterators to reduce branching
    {
        let mut s_in = input[..len - mod_len].iter().map(|&x| x as u32);
        let mut s_out = out_bytes.iter_mut();

        // Convenient shorthand
        let enc = |val| bytes[val as usize];
        let mut write = |val| *s_out.next().unwrap() = val;

        // Iterate though blocks of 4
        while let (Some(first), Some(second), Some(third)) = (s_in.next(), s_in.next(), s_in.next())
        {
            let n = first << 16 | second << 8 | third;

            // This 24-bit number gets separated into four 6-bit numbers.
            write(enc((n >> 18) & 63));
            write(enc((n >> 12) & 63));
            write(enc((n >> 6) & 63));
            write(enc((n >> 0) & 63));
        }

        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match mod_len {
            0 => (),
            1 => {
                let n = (input[len - 1] as u32) << 16;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
            }
            2 => {
                let n = (input[len - 2] as u32) << 16 | (input[len - 1] as u32) << 8;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
                write(enc((n >> 6) & 63));
            }
            _ => panic!("Algebra is broken, please alert the math police"),
        }
    }

    // `out_bytes` vec is prepopulated with `=` symbols and then only updated
    // with base64 chars, so this unsafe is safe.
    unsafe { String::from_utf8_unchecked(out_bytes) }
}

/// Errors that can occur when decoding a base64 encoded string
#[derive(Clone, Copy, Debug)]
pub enum FromBase64Error {
    /// The input contained a character not part of the base64 format
    InvalidBase64Byte(u8, usize),
    /// The input had an invalid length
    InvalidBase64Length,
}

impl fmt::Display for FromBase64Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FromBase64Error::InvalidBase64Byte(_, _) => write!(f, "Invalid base64 byte"),
            FromBase64Error::InvalidBase64Length => write!(f, "Invalid base64 length"),
        }
    }
}

impl std::error::Error for FromBase64Error {}

pub fn decode(input: &str) -> Result<Vec<u8>, FromBase64Error> {
    let mut r = Vec::with_capacity(input.len());
    let mut buf: u32 = 0;
    let mut modulus = 0;

    let mut it = input.as_bytes().iter();
    for byte in it.by_ref() {
        let code = DECODE_TABLE[*byte as usize];
        if code >= SPECIAL_CODES_START {
            match code {
                NEWLINE_CODE => continue,
                EQUALS_CODE => break,
                INVALID_CODE => {
                    return Err(FromBase64Error::InvalidBase64Byte(
                        *byte,
                        (byte as *const _ as usize) - input.as_ptr() as usize,
                    ))
                }
                _ => unreachable!(),
            }
        }
        buf = (buf | code as u32) << 6;
        modulus += 1;
        if modulus == 4 {
            modulus = 0;
            r.push((buf >> 22) as u8);
            r.push((buf >> 14) as u8);
            r.push((buf >> 6) as u8);
        }
    }

    for byte in it {
        match *byte {
            b'=' | b'\r' | b'\n' => continue,
            _ => {
                return Err(FromBase64Error::InvalidBase64Byte(
                    *byte,
                    (byte as *const _ as usize) - input.as_ptr() as usize,
                ))
            }
        }
    }

    match modulus {
        2 => {
            r.push((buf >> 10) as u8);
        }
        3 => {
            r.push((buf >> 16) as u8);
            r.push((buf >> 8) as u8);
        }
        0 => (),
        _ => return Err(FromBase64Error::InvalidBase64Length),
    }

    Ok(r)
}

const DECODE_TABLE: [u8; 256] = [
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0x3E, 0xFF, 0x3F,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
];
const INVALID_CODE: u8 = 0xFF;
const EQUALS_CODE: u8 = 0xFE;
const NEWLINE_CODE: u8 = 0xFD;
const SPECIAL_CODES_START: u8 = NEWLINE_CODE;

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

    #[test]
    fn test_encode_basic() {
        assert_eq!(encode(b""), "");
        assert_eq!(encode(b"f"), "Zg==");
        assert_eq!(encode(b"fo"), "Zm8=");
        assert_eq!(encode(b"foo"), "Zm9v");
        assert_eq!(encode(b"foob"), "Zm9vYg==");
        assert_eq!(encode(b"fooba"), "Zm9vYmE=");
        assert_eq!(encode(b"foobar"), "Zm9vYmFy");
    }

    #[test]
    fn test_encode_standard_safe() {
        assert_eq!(encode(&[251, 255]), "+/8=");
    }

    #[test]
    fn test_decode_basic() {
        assert_eq!(decode("").unwrap(), b"");
        assert_eq!(decode("Zg==").unwrap(), b"f");
        assert_eq!(decode("Zm8=").unwrap(), b"fo");
        assert_eq!(decode("Zm9v").unwrap(), b"foo");
        assert_eq!(decode("Zm9vYg==").unwrap(), b"foob");
        assert_eq!(decode("Zm9vYmE=").unwrap(), b"fooba");
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode() {
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode_newlines() {
        assert_eq!(decode("Zm9v\r\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\r\n").unwrap(), b"foob");
        assert_eq!(decode("Zm9v\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\n").unwrap(), b"foob");
    }

    #[test]
    fn test_decode_urlsafe() {
        assert_eq!(decode("-_8").unwrap(), decode("+/8=").unwrap());
    }

    #[test]
    fn test_from_base64_invalid_char() {
        assert!(decode("Zm$=").is_err());
        assert!(decode("Zg==$").is_err());
    }

    #[test]
    fn test_decode_invalid_padding() {
        assert!(decode("Z===").is_err());
    }
}
<commit_msg>Use thiserror for FromBase64Error<commit_after>//! Copy-pasted from the internet

use std::fmt;

/// Available encoding character sets
#[derive(Clone, Copy, Debug)]
enum _CharacterSet {
    /// The standard character set (uses `+` and `/`)
    _Standard,
    /// The URL safe character set (uses `-` and `_`)
    _UrlSafe,
}

static STANDARD_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                        abcdefghijklmnopqrstuvwxyz\
                                        0123456789+/";

static _URLSAFE_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                       abcdefghijklmnopqrstuvwxyz\
                                       0123456789-_";

pub fn encode(input: &[u8]) -> String {
    let bytes = STANDARD_CHARS;

    let len = input.len();

    // Preallocate memory.
    let prealloc_len = (len + 2) / 3 * 4;
    let mut out_bytes = vec![b'='; prealloc_len];

    // Deal with padding bytes
    let mod_len = len % 3;

    // Use iterators to reduce branching
    {
        let mut s_in = input[..len - mod_len].iter().map(|&x| x as u32);
        let mut s_out = out_bytes.iter_mut();

        // Convenient shorthand
        let enc = |val| bytes[val as usize];
        let mut write = |val| *s_out.next().unwrap() = val;

        // Iterate though blocks of 4
        while let (Some(first), Some(second), Some(third)) = (s_in.next(), s_in.next(), s_in.next())
        {
            let n = first << 16 | second << 8 | third;

            // This 24-bit number gets separated into four 6-bit numbers.
            write(enc((n >> 18) & 63));
            write(enc((n >> 12) & 63));
            write(enc((n >> 6) & 63));
            write(enc((n >> 0) & 63));
        }

        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match mod_len {
            0 => (),
            1 => {
                let n = (input[len - 1] as u32) << 16;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
            }
            2 => {
                let n = (input[len - 2] as u32) << 16 | (input[len - 1] as u32) << 8;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
                write(enc((n >> 6) & 63));
            }
            _ => panic!("Algebra is broken, please alert the math police"),
        }
    }

    // `out_bytes` vec is prepopulated with `=` symbols and then only updated
    // with base64 chars, so this unsafe is safe.
    unsafe { String::from_utf8_unchecked(out_bytes) }
}

/// Errors that can occur when decoding a base64 encoded string
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum FromBase64Error {
    /// The input contained a character not part of the base64 format
    #[error("Invalid base64 byte")]
    InvalidBase64Byte(u8, usize),
    /// The input had an invalid length
    #[error("Invalid base64 length")]
    InvalidBase64Length,
}

pub fn decode(input: &str) -> Result<Vec<u8>, FromBase64Error> {
    let mut r = Vec::with_capacity(input.len());
    let mut buf: u32 = 0;
    let mut modulus = 0;

    let mut it = input.as_bytes().iter();
    for byte in it.by_ref() {
        let code = DECODE_TABLE[*byte as usize];
        if code >= SPECIAL_CODES_START {
            match code {
                NEWLINE_CODE => continue,
                EQUALS_CODE => break,
                INVALID_CODE => {
                    return Err(FromBase64Error::InvalidBase64Byte(
                        *byte,
                        (byte as *const _ as usize) - input.as_ptr() as usize,
                    ))
                }
                _ => unreachable!(),
            }
        }
        buf = (buf | code as u32) << 6;
        modulus += 1;
        if modulus == 4 {
            modulus = 0;
            r.push((buf >> 22) as u8);
            r.push((buf >> 14) as u8);
            r.push((buf >> 6) as u8);
        }
    }

    for byte in it {
        match *byte {
            b'=' | b'\r' | b'\n' => continue,
            _ => {
                return Err(FromBase64Error::InvalidBase64Byte(
                    *byte,
                    (byte as *const _ as usize) - input.as_ptr() as usize,
                ))
            }
        }
    }

    match modulus {
        2 => {
            r.push((buf >> 10) as u8);
        }
        3 => {
            r.push((buf >> 16) as u8);
            r.push((buf >> 8) as u8);
        }
        0 => (),
        _ => return Err(FromBase64Error::InvalidBase64Length),
    }

    Ok(r)
}

const DECODE_TABLE: [u8; 256] = [
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0x3E, 0xFF, 0x3F,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
];
const INVALID_CODE: u8 = 0xFF;
const EQUALS_CODE: u8 = 0xFE;
const NEWLINE_CODE: u8 = 0xFD;
const SPECIAL_CODES_START: u8 = NEWLINE_CODE;

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

    #[test]
    fn test_encode_basic() {
        assert_eq!(encode(b""), "");
        assert_eq!(encode(b"f"), "Zg==");
        assert_eq!(encode(b"fo"), "Zm8=");
        assert_eq!(encode(b"foo"), "Zm9v");
        assert_eq!(encode(b"foob"), "Zm9vYg==");
        assert_eq!(encode(b"fooba"), "Zm9vYmE=");
        assert_eq!(encode(b"foobar"), "Zm9vYmFy");
    }

    #[test]
    fn test_encode_standard_safe() {
        assert_eq!(encode(&[251, 255]), "+/8=");
    }

    #[test]
    fn test_decode_basic() {
        assert_eq!(decode("").unwrap(), b"");
        assert_eq!(decode("Zg==").unwrap(), b"f");
        assert_eq!(decode("Zm8=").unwrap(), b"fo");
        assert_eq!(decode("Zm9v").unwrap(), b"foo");
        assert_eq!(decode("Zm9vYg==").unwrap(), b"foob");
        assert_eq!(decode("Zm9vYmE=").unwrap(), b"fooba");
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode() {
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode_newlines() {
        assert_eq!(decode("Zm9v\r\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\r\n").unwrap(), b"foob");
        assert_eq!(decode("Zm9v\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\n").unwrap(), b"foob");
    }

    #[test]
    fn test_decode_urlsafe() {
        assert_eq!(decode("-_8").unwrap(), decode("+/8=").unwrap());
    }

    #[test]
    fn test_from_base64_invalid_char() {
        assert!(decode("Zm$=").is_err());
        assert!(decode("Zg==$").is_err());
    }

    #[test]
    fn test_decode_invalid_padding() {
        assert!(decode("Z===").is_err());
    }
}
<|endoftext|>