<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::{Term, TermError};
use term::Term::*;
use std::fmt;
use std::mem;
pub use self::Order::*;

/// The [evaluation order](http://www.cs.cornell.edu/courses/cs6110/2014sp/Handouts/Sestoft.pdf) of
/// β-reductions.
///
/// - The `NOR`, `HNO`, `APP` and `HAP` orders reduce expressions to their normal forms
/// - The `APP` order will fail to fully reduce expressions containing functions without a normal
/// form, e.g. the `Y` combinator (they will expand forever)
/// - The `CBN` order reduces to weak head normal form
/// - The `CBV` order reduces to weak normal form
/// - The `HSP` order reduces to head normal form
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Order {
    /// Normal - leftmost outermost; the most popular reduction strategy
    NOR,
    /// Call-by-name - leftmost outermost; no reductions inside abstractions
    CBN,
    /// Head spine - leftmost outermost; abstractions reduced only in head position
    HSP,
    /// Hybrid normal - a mix between `HSP` (head spine) and `NOR` (normal) strategies
    HNO,
    /// Applicative - leftmost innermost; the most eager strategy; unfit for recursion combinators
    APP,
    /// Call-by-value - leftmost innermost, no reductions inside abstractions
    CBV,
    /// Hybrid applicative - a mix between `CBV` (call-by-value) and `APP` (applicative) strategies;
    /// usually the fastest-reducing normalizing strategy
    HAP
}

/// Applies two `Term`s with substitution and variable update, consuming the first one in the
/// process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::*;
///
/// // these are valid terms, but be careful with unwraps in your code
/// let lhs    = parse(&"λλ42(λ13)", DeBruijn).unwrap();
/// let rhs    = parse(&"λ51", DeBruijn).unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))", DeBruijn).unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
/// # Errors
///
/// Returns a `TermError` if `lhs` is not an `Abs`traction.
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, TermError> {
    lhs.unabs_ref()?;

    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.to_owned(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(ref mut abstracted) => {
            _apply(abstracted, rhs, depth + 1)
        },
        App(ref mut lhs_lhs, ref mut lhs_rhs) => {
            _apply(lhs_lhs, rhs, depth);
            _apply(lhs_rhs, rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(ref mut i) => if *i > own_depth {
            *i += added_depth
        },
        Abs(ref mut abstracted) => {
            update_free_variables(abstracted, added_depth, own_depth + 1)
        },
        App(ref mut lhs, ref mut rhs) => {
            update_free_variables(lhs, added_depth, own_depth);
            update_free_variables(rhs, added_depth, own_depth)
        }
    }
}

/// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit on
/// the number of reductions (`0` means no limit) and returns the reduced `Term`.
///
/// # Example
///
/// ```
/// use lambda_calculus::*;
///
/// let expr    = parse(&"(λa.λb.λc.a (λd.λe.e (d b)) (λd.c) (λd.d)) (λa.λb.a b)", Classic).unwrap();
/// let reduced = parse(&"λa.λb.b", Classic).unwrap();
///
/// assert_eq!(beta(expr, NOR, 0), reduced);
/// ```
pub fn beta(mut term: Term, order: Order, limit: usize) -> Term {
    term.reduce(order, limit);
    term
}

impl Term {
    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::*;
    /// use lambda_calculus::combinators::I;
    ///
    /// assert_eq!(app(I(), Var(1)).eval(), Ok(Var(1)));
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not an `App`lication or if its left hand
    /// side term is not an `Abs`traction.
    pub fn eval(self) -> Result<Term, TermError> {
        let (lhs, rhs) = self.unapp()?;

        apply(lhs, &rhs)
    }

    fn _eval(&mut self, count: &mut usize) {
        let mut to_eval = mem::replace(self, Var(0)); // replace self with a dummy
        to_eval = to_eval.eval().unwrap(); // safe; only called in reduction sites
        mem::replace(self, to_eval); // move self back to its place

        *count += 1;
    }

    fn is_reducible(&self, limit: usize, count: &usize) -> bool {
        self.lhs_ref().and_then(|t| t.unabs_ref()).is_ok() && (limit == 0 || *count < limit )
    }

    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit
    /// on the number of reductions (`0` means no limit) and returns the number of performed
    /// reductions.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::*;
    ///
    /// let mut expression = parse(&"(λa.λb.λc.b (a b c)) (λa.λb.b)", Classic).unwrap();
    /// let reduced        = parse(&"λa.λb.a b", Classic).unwrap();
    ///
    /// expression.reduce(NOR, 0);
    ///
    /// assert_eq!(expression, reduced);
    /// ```
    pub fn reduce(&mut self, order: Order, limit: usize) -> usize {
        let mut count = 0;

        match order {
            CBN => self.beta_cbn(limit, &mut count),
            NOR => self.beta_nor(limit, &mut count),
            CBV => self.beta_cbv(limit, &mut count),
            APP => self.beta_app(limit, &mut count),
            HSP => self.beta_hsp(limit, &mut count),
            HNO => self.beta_hno(limit, &mut count),
            HAP => self.beta_hap(limit, &mut count)
        }

        count
    }

    fn beta_cbn(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        if let App(_, _) = *self {
            self.lhs_mut().unwrap().beta_cbn(limit, count);

            if self.is_reducible(limit, count) {
                self._eval(count);
                self.beta_cbn(limit, count);
            }
        }
    }

    fn beta_nor(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_nor(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_cbn(limit, count);

                if self.is_reducible(limit, count) {
                    self._eval(count);
                    self.beta_nor(limit, count);
                } else {
                    self.lhs_mut().unwrap().beta_nor(limit, count);
                    self.rhs_mut().unwrap().beta_nor(limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_cbv(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        if let App(_, _) = *self {
            self.lhs_mut().unwrap().beta_cbv(limit, count);
            self.rhs_mut().unwrap().beta_cbv(limit, count);

            if self.is_reducible(limit, count) {
                self._eval(count);
                self.beta_cbv(limit, count);
            }
        }
    }

    fn beta_app(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_app(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_app(limit, count);
                self.rhs_mut().unwrap().beta_app(limit, count);

                if self.is_reducible(limit, count) {
                    self._eval(count);
                    self.beta_app(limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_hap(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_hap(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_cbv(limit, count);
                self.rhs_mut().unwrap().beta_hap(limit, count);

                if self.is_reducible(limit, count) {
                    self._eval(count);
                    self.beta_hap(limit, count);
                } else {
                    self.lhs_mut().unwrap().beta_hap(limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_hsp(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_hsp(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_hsp(limit, count);

                if self.is_reducible(limit, count) {
                    self._eval(count);
                    self.beta_hsp(limit, count)
                }
            },
            _ => ()
        }
    }

    fn beta_hno(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_hno(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_hsp(limit, count);

                if self.is_reducible(limit, count) {
                    self._eval(count);
                    self.beta_hno(limit, count)
                } else {
                    self.lhs_mut().unwrap().beta_hno(limit, count);
                    self.rhs_mut().unwrap().beta_hno(limit, count);
                }
            },
            _ => ()
        }
    }
}

impl fmt::Display for Order {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            NOR => "normal",
            CBN => "call-by-name",
            HSP => "head spine",
            HNO => "hybrid normal",
            APP => "applicative",
            CBV => "call-by-value",
            HAP => "hybrid applicative"
        })
    }
}
<commit_msg>remove Term::eval<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::{Term, TermError};
use term::Term::*;
use std::fmt;
use std::mem;
pub use self::Order::*;

/// The [evaluation order](http://www.cs.cornell.edu/courses/cs6110/2014sp/Handouts/Sestoft.pdf) of
/// β-reductions.
///
/// - The `NOR`, `HNO`, `APP` and `HAP` orders reduce expressions to their normal forms
/// - The `APP` order will fail to fully reduce expressions containing functions without a normal
/// form, e.g. the `Y` combinator (they will expand forever)
/// - The `CBN` order reduces to weak head normal form
/// - The `CBV` order reduces to weak normal form
/// - The `HSP` order reduces to head normal form
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Order {
    /// Normal - leftmost outermost; the most popular reduction strategy
    NOR,
    /// Call-by-name - leftmost outermost; no reductions inside abstractions
    CBN,
    /// Head spine - leftmost outermost; abstractions reduced only in head position
    HSP,
    /// Hybrid normal - a mix between `HSP` (head spine) and `NOR` (normal) strategies
    HNO,
    /// Applicative - leftmost innermost; the most eager strategy; unfit for recursion combinators
    APP,
    /// Call-by-value - leftmost innermost, no reductions inside abstractions
    CBV,
    /// Hybrid applicative - a mix between `CBV` (call-by-value) and `APP` (applicative) strategies;
    /// usually the fastest-reducing normalizing strategy
    HAP
}

/// Applies two `Term`s with substitution and variable update, consuming the first one in the
/// process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::*;
///
/// // these are valid terms, but be careful with unwraps in your code
/// let lhs    = parse(&"λλ42(λ13)", DeBruijn).unwrap();
/// let rhs    = parse(&"λ51", DeBruijn).unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))", DeBruijn).unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
/// # Errors
///
/// Returns a `TermError` if `lhs` is not an `Abs`traction.
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, TermError> {
    lhs.unabs_ref()?;

    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.to_owned(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(ref mut abstracted) => {
            _apply(abstracted, rhs, depth + 1)
        },
        App(ref mut lhs_lhs, ref mut lhs_rhs) => {
            _apply(lhs_lhs, rhs, depth);
            _apply(lhs_rhs, rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(ref mut i) => if *i > own_depth {
            *i += added_depth
        },
        Abs(ref mut abstracted) => {
            update_free_variables(abstracted, added_depth, own_depth + 1)
        },
        App(ref mut lhs, ref mut rhs) => {
            update_free_variables(lhs, added_depth, own_depth);
            update_free_variables(rhs, added_depth, own_depth)
        }
    }
}

/// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit on
/// the number of reductions (`0` means no limit) and returns the reduced `Term`.
///
/// # Example
///
/// ```
/// use lambda_calculus::*;
///
/// let expr    = parse(&"(λa.λb.λc.a (λd.λe.e (d b)) (λd.c) (λd.d)) (λa.λb.a b)", Classic).unwrap();
/// let reduced = parse(&"λa.λb.b", Classic).unwrap();
///
/// assert_eq!(beta(expr, NOR, 0), reduced);
/// ```
pub fn beta(mut term: Term, order: Order, limit: usize) -> Term {
    term.reduce(order, limit);
    term
}

impl Term {
    fn eval(&mut self, count: &mut usize) {
        let to_apply = mem::replace(self, Var(0)); // replace self with a dummy
        let (lhs, rhs) = to_apply.unapp().unwrap(); // safe; only called in reduction sites
        let applied = apply(lhs, &rhs).unwrap(); // ditto
        mem::replace(self, applied); // move self back to its place

        *count += 1;
    }

    fn is_reducible(&self, limit: usize, count: &usize) -> bool {
        self.lhs_ref().and_then(|t| t.unabs_ref()).is_ok() && (limit == 0 || *count < limit )
    }

    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit
    /// on the number of reductions (`0` means no limit) and returns the number of performed
    /// reductions.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::*;
    ///
    /// let mut expression = parse(&"(λa.λb.λc.b (a b c)) (λa.λb.b)", Classic).unwrap();
    /// let reduced        = parse(&"λa.λb.a b", Classic).unwrap();
    ///
    /// expression.reduce(NOR, 0);
    ///
    /// assert_eq!(expression, reduced);
    /// ```
    pub fn reduce(&mut self, order: Order, limit: usize) -> usize {
        let mut count = 0;

        match order {
            CBN => self.beta_cbn(limit, &mut count),
            NOR => self.beta_nor(limit, &mut count),
            CBV => self.beta_cbv(limit, &mut count),
            APP => self.beta_app(limit, &mut count),
            HSP => self.beta_hsp(limit, &mut count),
            HNO => self.beta_hno(limit, &mut count),
            HAP => self.beta_hap(limit, &mut count)
        }

        count
    }

    fn beta_cbn(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        if let App(_, _) = *self {
            self.lhs_mut().unwrap().beta_cbn(limit, count);

            if self.is_reducible(limit, count) {
                self.eval(count);
                self.beta_cbn(limit, count);
            }
        }
    }

    fn beta_nor(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_nor(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_cbn(limit, count);

                if self.is_reducible(limit, count) {
                    self.eval(count);
                    self.beta_nor(limit, count);
                } else {
                    self.lhs_mut().unwrap().beta_nor(limit, count);
                    self.rhs_mut().unwrap().beta_nor(limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_cbv(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        if let App(_, _) = *self {
            self.lhs_mut().unwrap().beta_cbv(limit, count);
            self.rhs_mut().unwrap().beta_cbv(limit, count);

            if self.is_reducible(limit, count) {
                self.eval(count);
                self.beta_cbv(limit, count);
            }
        }
    }

    fn beta_app(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_app(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_app(limit, count);
                self.rhs_mut().unwrap().beta_app(limit, count);

                if self.is_reducible(limit, count) {
                    self.eval(count);
                    self.beta_app(limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_hap(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_hap(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_cbv(limit, count);
                self.rhs_mut().unwrap().beta_hap(limit, count);

                if self.is_reducible(limit, count) {
                    self.eval(count);
                    self.beta_hap(limit, count);
                } else {
                    self.lhs_mut().unwrap().beta_hap(limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_hsp(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_hsp(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_hsp(limit, count);

                if self.is_reducible(limit, count) {
                    self.eval(count);
                    self.beta_hsp(limit, count)
                }
            },
            _ => ()
        }
    }

    fn beta_hno(&mut self, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

        match *self {
            Abs(ref mut abstracted) => abstracted.beta_hno(limit, count),
            App(_, _) => {
                self.lhs_mut().unwrap().beta_hsp(limit, count);

                if self.is_reducible(limit, count) {
                    self.eval(count);
                    self.beta_hno(limit, count)
                } else {
                    self.lhs_mut().unwrap().beta_hno(limit, count);
                    self.rhs_mut().unwrap().beta_hno(limit, count);
                }
            },
            _ => ()
        }
    }
}

impl fmt::Display for Order {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            NOR => "normal",
            CBN => "call-by-name",
            HSP => "head spine",
            HNO => "hybrid normal",
            APP => "applicative",
            CBV => "call-by-value",
            HAP => "hybrid applicative"
        })
    }
}
<|endoftext|>


<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction strategies) of
/// β-reductions. The `Applicative` order will not fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator. The default is `Normal`.
pub const EVAL_ORDER: Order = Normal;

#[derive(Debug, PartialEq)]
pub enum Order {
    Normal,
    Applicative
}

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        match EVAL_ORDER {
            Normal => self._beta_once_normal(0),
            Applicative => self._beta_once_applicative(0)
        };
    }

    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_normal(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    fn _beta_once_applicative(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative(depth + 1),
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible() &&
                   !self.rhs_ref().unwrap().is_beta_reducible() &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_applicative(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_applicative(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Checks whether `self` is β-reducible.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::{succ, zero};
    ///
    /// let reducible     = succ().app(1.into());
    /// let not_reducible = zero();
    ///
    /// assert!(reducible.is_beta_reducible());
    /// assert!(!not_reducible.is_beta_reducible());
    /// ```
    pub fn is_beta_reducible(&self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref().unwrap().is_beta_reducible(),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    true
                } else {
                    self.lhs_ref().unwrap().is_beta_reducible() ||
                    self.rhs_ref().unwrap().is_beta_reducible()
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full normal-order β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single normal-order β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn normal_order() {
        if EVAL_ORDER == Normal {
            let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
            assert_eq!(beta_full(should_reduce), Var(1))
        }
    }
}
<commit_msg>add applicative order test<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction strategies) of
/// β-reductions. The `Applicative` order will not fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator. The default is `Normal`.
pub const EVAL_ORDER: Order = Normal;

#[derive(Debug, PartialEq)]
pub enum Order {
    Normal,
    Applicative
}

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        match EVAL_ORDER {
            Normal => self._beta_once_normal(0),
            Applicative => self._beta_once_applicative(0)
        };
    }

    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_normal(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    fn _beta_once_applicative(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative(depth + 1),
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible() &&
                   !self.rhs_ref().unwrap().is_beta_reducible() &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_applicative(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_applicative(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Checks whether `self` is β-reducible.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::{succ, zero};
    ///
    /// let reducible     = succ().app(1.into());
    /// let not_reducible = zero();
    ///
    /// assert!(reducible.is_beta_reducible());
    /// assert!(!not_reducible.is_beta_reducible());
    /// ```
    pub fn is_beta_reducible(&self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref().unwrap().is_beta_reducible(),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    true
                } else {
                    self.lhs_ref().unwrap().is_beta_reducible() ||
                    self.rhs_ref().unwrap().is_beta_reducible()
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full normal-order β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single normal-order β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn normal_order() {
        if EVAL_ORDER == Normal {
            let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
            assert_eq!(beta_full(should_reduce), Var(1))
        }
    }

    #[test]
    fn applicative_order() {
        if EVAL_ORDER == Applicative {
            let shouldnt_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
            assert_eq!(beta_once(shouldnt_reduce), parse(&"(λ2)((λ111)(λ111)(λ111))").unwrap())
        }
    }
}
<|endoftext|>


<commit_before>//! Reminisce is a lightweight library intended to be used for detecting and
//! reading from joysticks.
#![feature(into_cow)]
#![cfg_attr(feature = "mappings", feature(alloc))]
#![cfg_attr(target_os = "linux", feature(libc, fs_walk))]
extern crate libc;
#[cfg(target_os = "windows")]
#[macro_use] extern crate rustc_bitflags;

#[cfg(feature = "sdl")]
extern crate sdl2;

/// Someday, somehow
#[cfg(all(feature = "emscripten", not(feature = "sdl")))]
pub mod emscripten;

#[cfg(all(feature = "emscripten", not(feature = "sdl")))]
pub use emscripten as native;

#[cfg(all(target_os = "linux", not(feature = "sdl")))]
pub mod linux;

#[cfg(all(target_os = "linux", not(feature = "sdl")))]
pub use linux as native;

#[cfg(all(target_os = "windows", not(feature = "sdl")))]
pub mod windows;

#[cfg(all(target_os = "windows", not(feature = "sdl")))]
pub use windows as native;

#[cfg(feature = "sdl")]
pub mod sdl;

#[cfg(feature = "sdl")]
pub use sdl as native;

#[cfg(feature = "mappings")]
pub mod mapper;

#[cfg(feature = "mappings")]
pub use mapper::JoystickMapper;
pub use native::{NativeJoystick, scan};

/// The maximum axis value
pub static MAX_AXIS_VALUE:i16 = 32767;
/// The minimum axis value
pub static MIN_AXIS_VALUE:i16 = -32767;

use std::borrow::Cow;
use std::error::Error;
use std::fmt::Debug;
use std::mem::transmute as cast;

#[cfg(feature = "mappings")]
macro_rules! text_enum(
    ($name:ident, $($enumer:ident => $text:expr),+) => (
        impl ::std::fmt::Display for $name {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                write!(fmt, "{}", match *self {
                    $($name::$enumer => $text),+
                })
            }
        }
        impl ::std::str::FromStr for $name {
            type Err = String;
            fn from_str(s: &str) -> Result<$name, String> {
                match s {
                    $($text => Ok($name::$enumer),)+
                    _ => Err(format!("Could not make {} from {}", stringify!($name), s))
                }
            }
        }
    )
);
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
/// A direction on a joystick
///
/// This uses the order that Linux drivers and the HTML5 Gamepad API uses so it should ring a bell
pub enum Axis {
	/// The x direction of the left stick
    ///
    /// This is usually used for horizontal movement in a game
    LeftX,
	/// The y direction of the left stick
    ///
    /// This is usually used for vertical movement in a game
    LeftY,
	/// The x direction of the right stick
    //
    /// This is usually used for looking around in a game
    RightX,
	/// The y direction of the right stick
    //
    /// This is usually used for looking around in a game
    RightY,
	/// How far down the left trigger is pressed
    ///
    /// This is only used as a button on some platforms so don't rely on just this
    TriggerLeft,
	/// How far down the right trigger is pressed
    ///
    /// This is only used as a button on some platforms so don't rely on just this
    TriggerRight
}
#[cfg(feature = "mappings")]
text_enum!(Axis,
    LeftX => "leftx",
    LeftY => "lefty",
    RightX => "rightx",
    RightY => "righty",
    TriggerLeft => "triggerleft",
    TriggerRight => "triggerright"
);

#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
/// A button on a joystick
///
/// This uses the order that Linux drivers and the HTML5 Gamepad API uses so it should be familiar
pub enum Button {
	/// The A button
    ///
    /// This is typically used for jumping
	A,
	/// The B button
    ///
    /// This is typically used for attacking
	B,
	/// The X button
	X,
	/// The Y button
	Y,
	/// The left top shoulder button
    ///
    /// This is usually used in FPS games for throwing a certain kind of grenade
	LeftShoulder,
	/// The right top shoulder button
	RightShoulder,
	/// The left bottom shoulder / trigger button
    ///
    /// This is typically used in FPS games for aiming down the sights in a weapon
	LeftTrigger,
	/// The right bottom shoulder / trigger button
    ///
    /// This is typically used in FPS games for shooting with a weapon
	RightTrigger,
	/// The back / select button
    ///
    /// This is usually used as an alternate pause menu or to open a menu in-game.
	Select,
	/// The start / forward button
    ///
    /// This is usually used to start and pause a game
	Start,
	/// The left stick button
	LeftStick,
	/// The right stick button
	RightStick,
	/// The up button on the directional pad
	DPadUp,
	/// The down button on the directional pad
	DPadDown,
	/// The left button on the directional pad
	DPadLeft,
	/// The right button on the directional pad
	DPadRight
}
#[cfg(feature = "mappings")]
text_enum!(Button,
    A => "a",
    B => "b",
    X => "x",
    Y => "y",
    LeftShoulder => "leftshoulder",
    RightShoulder => "rightshoulder",
    LeftTrigger => "lefttrigger",
    RightTrigger => "righttrigger",
    Select => "select",
    Start => "start",
    LeftStick => "leftstick",
    RightStick => "rightstick",
    DPadUp => "dpadup",
    DPadDown => "dpaddown",
    DPadLeft => "dpadleft",
    DPadRight => "dpadright"
);

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
/// An event emitted by a joystick
pub enum Event {
	/// Fired when a button is pressed with the button's index
	ButtonPressed(Button),
	/// Fired when a button is released with the button's index
	ButtonReleased(Button),
	/// Fired when a axis is moved with the axis index and its value,
	/// which is between `MIN_JOYSTICK_VALUE` and `MAX_JOYSTICK_VALUE`
	AxisMoved(Axis, i16)
}
/// Convert a raw event into a Reminisce event
pub trait IntoEvent {
	/// Convert this into a Reminisce event
	fn into_event(self) -> Event;
}

impl IntoEvent for Event {
    /// Convert the event into itself
    ///
    /// The only reason this exists is because windows doesn't have events so the backend has to
    /// make reminisce events directly and pretend that they are the native events.
    fn into_event(self) -> Event {
        self
    }
}

/// A joystick or gamepad
///
/// Each `Joystick` has its own event queue that can be polled repeatedly
/// using the `iter()` method or by calling the `poll()` method repeatedly.
///
/// ``` rust
/// use reminisce::{scan, Axis, Event, Joystick};
/// let mut joysticks = scan();
/// let (mut x, mut y) = (0, 0);
/// for joystick in &mut joysticks {
///     for event in joystick.iter() {
///         match event {
///             Event::AxisMoved(Axis::LeftX, nx) =>
///                 x += nx,
///             Event::AxisMoved(Axis::LeftY, ny) =>
///                 y += ny,
///             _ => ()
///         }
///     }
/// }
/// println!("{}, {}", x, y);
/// ```
pub trait Joystick : Sized {
	/// The version of this joystick that includes the state
    ///
    /// On Linux, this is a wrapper around the joystick, but on Windows,
    /// this is the same stucture because the joystick always includes
    /// state on it.
	type WithState : StatefulJoystick;

    /// The event that this joystick processes
    type NativeEvent: IntoEvent;

    /// The error that could be thrown while trying to open the joystick
    type OpenError: Error + Debug;

	/// Attempts to open a joystick from its index
    ///
    /// If an error occurs, this will return the textual representation of that error.
    ///
    /// ``` rust
    /// use reminisce::{NativeJoystick, Joystick};
    /// let joystick:NativeJoystick = Joystick::open(0);
    /// if let Ok(joystick) = joystick {
    ///     println!("{}", joystick.get_id())
    /// } else {
    ///     println!("No joystick plugged in")
    /// }
    /// ```
	fn open(index: u8) -> Result<Self, Self::OpenError>;

	/// Check if the joystick is still connected
	fn is_connected(&self) -> bool;

	/// Get the identifier of this joystick
    ///
    /// This is copy-on-write because it can be a borrowed or owned String depending on the
    /// implementation.
	fn get_id(&self) -> Cow<str>;

	/// Get the index of this joystick
	fn get_index(&self) -> u8;

	/// Get the number of axes this joystick has
    ///
    /// This is capped at 6 axes for now.
	fn get_num_axes(&self) -> u8;

	/// Get the number of buttons this joystick has
    ///
    /// This is capped at 16 buttons currently.
	fn get_num_buttons(&self) -> u8;

    /// Get the battery level of this joystick
    ///
    /// Returns none if the joystick is wired or this operation is not supported
    /// by the backend
    fn get_battery(&self) -> Option<f32>;

    /// Poll the joystick for events in non-blocking mode and return the native event
    /// as returned from the backend
	fn poll_native(&mut self) -> Option<Self::NativeEvent>;

    /// Poll the joystick for events in non-blocking mode
    ///
    /// This runs `self.poll_native()` then converts it into an event using
    /// the `into_event` method.
    fn poll(&mut self) -> Option<Event> {
        self.poll_native().map(|e| e.into_event())
    }

    /// Map the axes and buttons of this joystick by wrapping it in a `JoystickMapper`
    #[cfg(feature = "mappings")]
    fn into_mapper(self) -> JoystickMapper<Self> {
        JoystickMapper::new(self)
    }

    /// Iterate through the events that haven't been processed yet
    fn iter(&mut self) -> Poller<Self> {
        Poller {
            joystick: self
        }
    }

	/// Get the version of this joystick which includes state
    ///
    /// On the Linux backend, a wrapper is created that records the changes in state, but on the
    /// Windows backend no wrapper is needed so this just returns the `NativeJoystick` structure
	fn with_state(self) -> Self::WithState;
}

/// A joystick that tracks its state
///
/// This makes it really easy to write the input for games, etc because its just a matter
/// of calling a single method to query the current value of the axes
///
/// ``` rust
/// use reminisce::{scan, Axis, NativeJoystick, Joystick, StatefulJoystick};
/// let mut joysticks = scan().into_iter().map(|js| js.with_state()).collect::<Vec<_>>();
/// for joystick in &joysticks {
///     let x = joystick.get_axis(Axis::LeftX).unwrap_or(0);
///     let y = joystick.get_axis(Axis::LeftY).unwrap_or(0);
///     println!("{}, {}", x, y)
/// }
/// ```
pub trait StatefulJoystick : Joystick + Sized {
	/// Get the value of a specific axis as an integer between `MIN_AXIS_VALUE` and `MAX_AXIS_VALUE`
    ///
    /// If the axis is horizontal, a negative value indicates it is pointing leftwards and a positive
    /// value indicates it is pointing rightwards. However, is the axis is vertical a negative value
    /// indicates it is pointing upwards and a positive value indicates it is pointing downwards
	fn get_axis(&self, index: Axis) -> Option<i16>;

	/// Get the value of a specific axis normalised to between -1.0 and 1.0
    ///
    /// This is equivalent to calling `get_axis` then dividing the value by the `MAX_AXIS_VALUE`
	fn get_normalised_axis(&self, index: Axis) -> Option<f32> {
		self.get_axis(index).map(|v| v as f32 / MAX_AXIS_VALUE as f32)
	}

	/// Iterate over the axes in this joystick
	fn axes(&self) -> Axes<Self> {
		use std::mem;
		Axes {
			joystick: self,
			axis: unsafe { mem::zeroed() }
		}
	}

	/// Iterate over the buttons in this joystick
	fn buttons(&self) -> Buttons<Self> {
		use std::mem;
		Buttons {
			joystick: self,
			button: unsafe { mem::zeroed() }
		}
	}

	/// Get the pressed state of a specific button
	fn get_button(&self, index: Button) -> Option<bool>;

	/// Update the state of this joystick by polling the native backend
	fn update(&mut self);
}

/// An iterator over a joystick's axes
pub struct Axes<'a, J> where J:StatefulJoystick+'a {
	joystick: &'a J,
	axis: u8
}
impl<'a, J> Iterator for Axes<'a, J> where J:StatefulJoystick {
    /// The axis index and state, respectively
	type Item = (Axis, i16);
	fn next(&mut self) -> Option<(Axis, i16)> {
		self.axis += 1;
		let axis = unsafe { cast(self.axis - 1) };
		self.joystick.get_axis(axis).map(|v| (axis, v))
	}
}

/// An iterator over a joystick's buttons
pub struct Buttons<'a, J> where J:StatefulJoystick+'a {
	joystick: &'a J,
	button: u8
}
impl<'a, J> Iterator for Buttons<'a, J> where J:StatefulJoystick {
    /// The button index and state, respectively
	type Item = (Button, bool);
	fn next(&mut self) -> Option<(Button, bool)> {
		self.button += 1;
		let button = unsafe { cast(self.button - 1) };
		self.joystick.get_button(button).map(|v| (button, v))
	}
}

/// An iterator over a joystick's event queue
pub struct Poller<'a, J> where J:Joystick+'a {
    joystick: &'a mut J
}

impl<'a, J> Iterator for Poller<'a, J> where J:Joystick {
	type Item = Event;
    /// This calls the `joystick.poll()` method to poll for the next event
	fn next(&mut self) -> Option<Event> {
		self.joystick.poll()
	}
}
<commit_msg>Fix example<commit_after>//! Reminisce is a lightweight library intended to be used for detecting and
//! reading from joysticks.
#![feature(into_cow)]
#![cfg_attr(feature = "mappings", feature(alloc))]
#![cfg_attr(target_os = "linux", feature(libc, fs_walk))]
extern crate libc;
#[cfg(target_os = "windows")]
#[macro_use] extern crate rustc_bitflags;

#[cfg(feature = "sdl")]
extern crate sdl2;

/// Someday, somehow
#[cfg(all(feature = "emscripten", not(feature = "sdl")))]
pub mod emscripten;

#[cfg(all(feature = "emscripten", not(feature = "sdl")))]
pub use emscripten as native;

#[cfg(all(target_os = "linux", not(feature = "sdl")))]
pub mod linux;

#[cfg(all(target_os = "linux", not(feature = "sdl")))]
pub use linux as native;

#[cfg(all(target_os = "windows", not(feature = "sdl")))]
pub mod windows;

#[cfg(all(target_os = "windows", not(feature = "sdl")))]
pub use windows as native;

#[cfg(feature = "sdl")]
pub mod sdl;

#[cfg(feature = "sdl")]
pub use sdl as native;

#[cfg(feature = "mappings")]
pub mod mapper;

#[cfg(feature = "mappings")]
pub use mapper::JoystickMapper;
pub use native::{NativeJoystick, scan};

/// The maximum axis value
pub static MAX_AXIS_VALUE:i16 = 32767;
/// The minimum axis value
pub static MIN_AXIS_VALUE:i16 = -32767;

use std::borrow::Cow;
use std::error::Error;
use std::fmt::Debug;
use std::mem::transmute as cast;

#[cfg(feature = "mappings")]
macro_rules! text_enum(
    ($name:ident, $($enumer:ident => $text:expr),+) => (
        impl ::std::fmt::Display for $name {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                write!(fmt, "{}", match *self {
                    $($name::$enumer => $text),+
                })
            }
        }
        impl ::std::str::FromStr for $name {
            type Err = String;
            fn from_str(s: &str) -> Result<$name, String> {
                match s {
                    $($text => Ok($name::$enumer),)+
                    _ => Err(format!("Could not make {} from {}", stringify!($name), s))
                }
            }
        }
    )
);
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
/// A direction on a joystick
///
/// This uses the order that Linux drivers and the HTML5 Gamepad API uses so it should ring a bell
pub enum Axis {
	/// The x direction of the left stick
    ///
    /// This is usually used for horizontal movement in a game
    LeftX,
	/// The y direction of the left stick
    ///
    /// This is usually used for vertical movement in a game
    LeftY,
	/// The x direction of the right stick
    //
    /// This is usually used for looking around in a game
    RightX,
	/// The y direction of the right stick
    //
    /// This is usually used for looking around in a game
    RightY,
	/// How far down the left trigger is pressed
    ///
    /// This is only used as a button on some platforms so don't rely on just this
    TriggerLeft,
	/// How far down the right trigger is pressed
    ///
    /// This is only used as a button on some platforms so don't rely on just this
    TriggerRight
}
#[cfg(feature = "mappings")]
text_enum!(Axis,
    LeftX => "leftx",
    LeftY => "lefty",
    RightX => "rightx",
    RightY => "righty",
    TriggerLeft => "triggerleft",
    TriggerRight => "triggerright"
);

#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
/// A button on a joystick
///
/// This uses the order that Linux drivers and the HTML5 Gamepad API uses so it should be familiar
pub enum Button {
	/// The A button
    ///
    /// This is typically used for jumping
	A,
	/// The B button
    ///
    /// This is typically used for attacking
	B,
	/// The X button
	X,
	/// The Y button
	Y,
	/// The left top shoulder button
    ///
    /// This is usually used in FPS games for throwing a certain kind of grenade
	LeftShoulder,
	/// The right top shoulder button
	RightShoulder,
	/// The left bottom shoulder / trigger button
    ///
    /// This is typically used in FPS games for aiming down the sights in a weapon
	LeftTrigger,
	/// The right bottom shoulder / trigger button
    ///
    /// This is typically used in FPS games for shooting with a weapon
	RightTrigger,
	/// The back / select button
    ///
    /// This is usually used as an alternate pause menu or to open a menu in-game.
	Select,
	/// The start / forward button
    ///
    /// This is usually used to start and pause a game
	Start,
	/// The left stick button
	LeftStick,
	/// The right stick button
	RightStick,
	/// The up button on the directional pad
	DPadUp,
	/// The down button on the directional pad
	DPadDown,
	/// The left button on the directional pad
	DPadLeft,
	/// The right button on the directional pad
	DPadRight
}
#[cfg(feature = "mappings")]
text_enum!(Button,
    A => "a",
    B => "b",
    X => "x",
    Y => "y",
    LeftShoulder => "leftshoulder",
    RightShoulder => "rightshoulder",
    LeftTrigger => "lefttrigger",
    RightTrigger => "righttrigger",
    Select => "select",
    Start => "start",
    LeftStick => "leftstick",
    RightStick => "rightstick",
    DPadUp => "dpadup",
    DPadDown => "dpaddown",
    DPadLeft => "dpadleft",
    DPadRight => "dpadright"
);

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
/// An event emitted by a joystick
pub enum Event {
	/// Fired when a button is pressed with the button's index
	ButtonPressed(Button),
	/// Fired when a button is released with the button's index
	ButtonReleased(Button),
	/// Fired when a axis is moved with the axis index and its value,
	/// which is between `MIN_JOYSTICK_VALUE` and `MAX_JOYSTICK_VALUE`
	AxisMoved(Axis, i16)
}
/// Convert a raw event into a Reminisce event
pub trait IntoEvent {
	/// Convert this into a Reminisce event
	fn into_event(self) -> Event;
}

impl IntoEvent for Event {
    /// Convert the event into itself
    ///
    /// The only reason this exists is because windows doesn't have events so the backend has to
    /// make reminisce events directly and pretend that they are the native events.
    fn into_event(self) -> Event {
        self
    }
}

/// A joystick or gamepad
///
/// Each `Joystick` has its own event queue that can be polled repeatedly
/// using the `iter()` method or by calling the `poll()` method repeatedly.
///
/// ``` rust
/// use reminisce::{scan, Axis, Event, Joystick};
/// let mut joysticks = scan();
/// let (mut x, mut y) = (0, 0);
/// for joystick in &mut joysticks {
///     for event in joystick.iter() {
///         match event {
///             Event::AxisMoved(Axis::LeftX, nx) =>
///                 x += nx,
///             Event::AxisMoved(Axis::LeftY, ny) =>
///                 y += ny,
///             _ => ()
///         }
///     }
/// }
/// println!("{}, {}", x, y);
/// ```
pub trait Joystick : Sized {
	/// The version of this joystick that includes the state
    ///
    /// On Linux, this is a wrapper around the joystick, but on Windows,
    /// this is the same stucture because the joystick always includes
    /// state on it.
	type WithState : StatefulJoystick;

    /// The event that this joystick processes
    type NativeEvent: IntoEvent;

    /// The error that could be thrown while trying to open the joystick
    type OpenError: Error + Debug;

	/// Attempts to open a joystick from its index
    ///
    /// If an error occurs, this will return the textual representation of that error.
    ///
    /// ``` rust
    /// use reminisce::{NativeJoystick, Joystick};
    /// if let Ok(joystick) = <NativeJoystick as Joystick>::open(0) {
    ///     println!("{}", joystick.get_id())
    /// } else {
    ///     println!("No joystick plugged in")
    /// }
    /// ```
	fn open(index: u8) -> Result<Self, Self::OpenError>;

	/// Check if the joystick is still connected
	fn is_connected(&self) -> bool;

	/// Get the identifier of this joystick
    ///
    /// This is copy-on-write because it can be a borrowed or owned String depending on the
    /// implementation.
	fn get_id(&self) -> Cow<str>;

	/// Get the index of this joystick
	fn get_index(&self) -> u8;

	/// Get the number of axes this joystick has
    ///
    /// This is capped at 6 axes for now.
	fn get_num_axes(&self) -> u8;

	/// Get the number of buttons this joystick has
    ///
    /// This is capped at 16 buttons currently.
	fn get_num_buttons(&self) -> u8;

    /// Get the battery level of this joystick
    ///
    /// Returns none if the joystick is wired or this operation is not supported
    /// by the backend
    fn get_battery(&self) -> Option<f32>;

    /// Poll the joystick for events in non-blocking mode and return the native event
    /// as returned from the backend
	fn poll_native(&mut self) -> Option<Self::NativeEvent>;

    /// Poll the joystick for events in non-blocking mode
    ///
    /// This runs `self.poll_native()` then converts it into an event using
    /// the `into_event` method.
    fn poll(&mut self) -> Option<Event> {
        self.poll_native().map(|e| e.into_event())
    }

    /// Map the axes and buttons of this joystick by wrapping it in a `JoystickMapper`
    #[cfg(feature = "mappings")]
    fn into_mapper(self) -> JoystickMapper<Self> {
        JoystickMapper::new(self)
    }

    /// Iterate through the events that haven't been processed yet
    fn iter(&mut self) -> Poller<Self> {
        Poller {
            joystick: self
        }
    }

	/// Get the version of this joystick which includes state
    ///
    /// On the Linux backend, a wrapper is created that records the changes in state, but on the
    /// Windows backend no wrapper is needed so this just returns the `NativeJoystick` structure
	fn with_state(self) -> Self::WithState;
}

/// A joystick that tracks its state
///
/// This makes it really easy to write the input for games, etc because its just a matter
/// of calling a single method to query the current value of the axes
///
/// ``` rust
/// use reminisce::{scan, Axis, NativeJoystick, Joystick, StatefulJoystick};
/// let mut joysticks = scan().into_iter().map(|js| js.with_state()).collect::<Vec<_>>();
/// for joystick in &joysticks {
///     let x = joystick.get_axis(Axis::LeftX).unwrap_or(0);
///     let y = joystick.get_axis(Axis::LeftY).unwrap_or(0);
///     println!("{}, {}", x, y)
/// }
/// ```
pub trait StatefulJoystick : Joystick + Sized {
	/// Get the value of a specific axis as an integer between `MIN_AXIS_VALUE` and `MAX_AXIS_VALUE`
    ///
    /// If the axis is horizontal, a negative value indicates it is pointing leftwards and a positive
    /// value indicates it is pointing rightwards. However, is the axis is vertical a negative value
    /// indicates it is pointing upwards and a positive value indicates it is pointing downwards
	fn get_axis(&self, index: Axis) -> Option<i16>;

	/// Get the value of a specific axis normalised to between -1.0 and 1.0
    ///
    /// This is equivalent to calling `get_axis` then dividing the value by the `MAX_AXIS_VALUE`
	fn get_normalised_axis(&self, index: Axis) -> Option<f32> {
		self.get_axis(index).map(|v| v as f32 / MAX_AXIS_VALUE as f32)
	}

	/// Iterate over the axes in this joystick
	fn axes(&self) -> Axes<Self> {
		use std::mem;
		Axes {
			joystick: self,
			axis: unsafe { mem::zeroed() }
		}
	}

	/// Iterate over the buttons in this joystick
	fn buttons(&self) -> Buttons<Self> {
		use std::mem;
		Buttons {
			joystick: self,
			button: unsafe { mem::zeroed() }
		}
	}

	/// Get the pressed state of a specific button
	fn get_button(&self, index: Button) -> Option<bool>;

	/// Update the state of this joystick by polling the native backend
	fn update(&mut self);
}

/// An iterator over a joystick's axes
pub struct Axes<'a, J> where J:StatefulJoystick+'a {
	joystick: &'a J,
	axis: u8
}
impl<'a, J> Iterator for Axes<'a, J> where J:StatefulJoystick {
    /// The axis index and state, respectively
	type Item = (Axis, i16);
	fn next(&mut self) -> Option<(Axis, i16)> {
		self.axis += 1;
		let axis = unsafe { cast(self.axis - 1) };
		self.joystick.get_axis(axis).map(|v| (axis, v))
	}
}

/// An iterator over a joystick's buttons
pub struct Buttons<'a, J> where J:StatefulJoystick+'a {
	joystick: &'a J,
	button: u8
}
impl<'a, J> Iterator for Buttons<'a, J> where J:StatefulJoystick {
    /// The button index and state, respectively
	type Item = (Button, bool);
	fn next(&mut self) -> Option<(Button, bool)> {
		self.button += 1;
		let button = unsafe { cast(self.button - 1) };
		self.joystick.get_button(button).map(|v| (button, v))
	}
}

/// An iterator over a joystick's event queue
pub struct Poller<'a, J> where J:Joystick+'a {
    joystick: &'a mut J
}

impl<'a, J> Iterator for Poller<'a, J> where J:Joystick {
	type Item = Event;
    /// This calls the `joystick.poll()` method to poll for the next event
	fn next(&mut self) -> Option<Event> {
		self.joystick.poll()
	}
}
<|endoftext|>


<commit_before>use std::process::Command;
use std::process::Child;
use std::process::Stdio;
use std::rc::Rc;
use std::sync::RwLock;
use std::ffi::OsStr;
use std::convert::AsRef;
use core::workspaces::Workspaces;
use config::Config;
use window_system::*;

#[macro_export]
macro_rules! add_key_handler_str(
    ($config: expr, $w:expr, $key:expr, $modkey:expr, $inp:expr) => (
        $config.add_key_handler($w.get_keycode_from_string($key), $modkey, box $inp);
    )
);

#[macro_export]
macro_rules! add_key_handler_code(
    ($config: expr, $key:expr, $modkey:expr, $inp:expr) => (
        $config.add_key_handler($key, $modkey, box $inp);
    )
);

#[macro_export]
macro_rules! add_mouse_handler(
    ($config: expr, $button:expr, $modkey:expr, $inp:expr) => (
        $config.add_mouse_handler($button, $modkey, box $inp);
    )
);

#[macro_export]
macro_rules! send_layout_message(
    ($message: expr) => (
        |m, w, c| m.send_layout_message($message, w.deref(), c).windows(w.deref(), c, &|x| x.clone())
    )
);

#[macro_export]
macro_rules! run(
    ($command: expr, $options: expr) => (
        |w, _, _| { run($command, String::from_str($options).split(' ').map(String::from_str).collect()); w }
    )
);

pub fn run<S: AsRef<OsStr>>(program: S, args: Vec<String>) {
    match Command::new(program).args(&args).spawn() {
        _ => ()
    }
}

pub fn spawn_pipe<S: AsRef<OsStr>>(config: &mut Config, program: S, args: Vec<String>) -> Rc<RwLock<Child>> {
    let result = Command::new(program)
        .args(&args).stdin(Stdio::piped()).spawn().unwrap();
    let rc = Rc::new(RwLock::new(result));
    config.general.pipes.push(rc.clone());
    rc
}

pub fn spawn_on(workspaces: Workspaces, _: &WindowSystem,
                window: Window, workspace_id: u32) -> Workspaces {
    workspaces.focus_window(window).shift(workspace_id)
}
<commit_msg>replaced deprecated String::from_str with String::from<commit_after>use std::process::Command;
use std::process::Child;
use std::process::Stdio;
use std::rc::Rc;
use std::sync::RwLock;
use std::ffi::OsStr;
use std::convert::AsRef;
use core::workspaces::Workspaces;
use config::Config;
use window_system::*;

#[macro_export]
macro_rules! add_key_handler_str(
    ($config: expr, $w:expr, $key:expr, $modkey:expr, $inp:expr) => (
        $config.add_key_handler($w.get_keycode_from_string($key), $modkey, box $inp);
    )
);

#[macro_export]
macro_rules! add_key_handler_code(
    ($config: expr, $key:expr, $modkey:expr, $inp:expr) => (
        $config.add_key_handler($key, $modkey, box $inp);
    )
);

#[macro_export]
macro_rules! add_mouse_handler(
    ($config: expr, $button:expr, $modkey:expr, $inp:expr) => (
        $config.add_mouse_handler($button, $modkey, box $inp);
    )
);

#[macro_export]
macro_rules! send_layout_message(
    ($message: expr) => (
        |m, w, c| m.send_layout_message($message, w.deref(), c).windows(w.deref(), c, &|x| x.clone())
    )
);

#[macro_export]
macro_rules! run(
    ($command: expr, $options: expr) => (
        |w, _, _| { run($command, String::from($options).split(' ').map(String::from).collect()); w }
    )
);

pub fn run<S: AsRef<OsStr>>(program: S, args: Vec<String>) {
    match Command::new(program).args(&args).spawn() {
        _ => ()
    }
}

pub fn spawn_pipe<S: AsRef<OsStr>>(config: &mut Config, program: S, args: Vec<String>) -> Rc<RwLock<Child>> {
    let result = Command::new(program)
        .args(&args).stdin(Stdio::piped()).spawn().unwrap();
    let rc = Rc::new(RwLock::new(result));
    config.general.pipes.push(rc.clone());
    rc
}

pub fn spawn_on(workspaces: Workspaces, _: &WindowSystem,
                window: Window, workspace_id: u32) -> Workspaces {
    workspaces.focus_window(window).shift(workspace_id)
}
<|endoftext|>


<commit_before>extern crate regex;
use credentials::AWSCredentials;
use hyper::client::Response;
use hyper::status::StatusCode;
use openssl::crypto::hash::Type::SHA256;
use openssl::crypto::hash::hash;
use openssl::crypto::hmac::hmac;
use params::Params;
use serialize::hex::ToHex;
use std::ascii::AsciiExt;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::str;
use time::Tm;
use time::now_utc;
use url::percent_encoding::{percent_encode_to, FORM_URLENCODED_ENCODE_SET};
use regions::*;
use request::send_request;
use xmlutil::*;
use error::*;
// Debug:
// use std::io::Read;

const HTTP_TEMPORARY_REDIRECT: StatusCode = StatusCode::TemporaryRedirect;

/// A data structure for all the elements of an HTTP request that are involved in
/// the Amazon Signature Version 4 signing process
#[derive(Debug)]
pub struct SignedRequest<'a> {
	method: String,
	service: String,
	region: &'a Region,
	path: String,
	headers: BTreeMap<String, Vec<Vec<u8>>>,
	params: Params,
	hostname: Option<String>,
	payload: Option<&'a [u8]>,
	canonical_query_string: String,
	canonical_uri: String,
}

impl <'a> SignedRequest <'a> {
	/// Default constructor
	pub fn new<'b>(method: &str, service: &str, region: &'a Region, path: &str) -> SignedRequest<'a> {
		SignedRequest {
			method: method.to_string(),
			service: service.to_string(),
			region: region,
			path: path.to_string(),
			headers: BTreeMap::new(),
			params: Params::new(),
			hostname: None,
			payload: None,
			canonical_query_string: String::new(),
			canonical_uri: String::new(),
		 }
	}

	pub fn set_hostname(&mut self, hostname: Option<String>) {
		self.hostname = hostname;
	}

	pub fn set_payload(&mut self, payload: Option<&'a [u8]>) {
		self.payload = payload;
	}

	pub fn get_method(&self) -> &str {
		&self.method
	}

	pub fn get_canonical_uri(&self) -> &str {
		&self.canonical_uri
	}

	pub fn get_canonical_query_string(&self) -> &str {
		&self.canonical_query_string
	}

	pub fn get_payload(&self) -> Option<&'a [u8]> {
		self.payload
	}

	pub fn get_headers(&'a self) -> &'a BTreeMap<String, Vec<Vec<u8>>> {
		&self.headers
	}

	pub fn get_hostname(&self) -> String {
		match self.hostname {
			Some(ref h) => h.to_string(),
			None => build_hostname(&self.service, &self.region)
		}
	}

	/// Add a value to the array of headers for the specified key.
	/// Headers are kept sorted by key name for use at signing (BTreeMap)
	pub fn add_header(&mut self, key: &str, value: &str) {
		let key_lower = key.to_ascii_lowercase().to_string();
		let value_vec = value.as_bytes().to_vec();

		match self.headers.entry(key_lower) {
			Entry::Vacant(entry) => {
				let mut values = Vec::new();
				values.push(value_vec);
				entry.insert(values);
			}
			Entry::Occupied(entry) => {
				entry.into_mut().push(value_vec);
			}
		}
	}

	pub fn add_param<S>(&mut self, key: S, value: S)  where S: Into<String> {
		self.params.insert(key.into(), value.into());
	}

	pub fn set_params(&mut self, params: Params){
		self.params = params;
	}

	/// Calculate the signature from the credentials provided and the request data
	/// Add the calculated signature to the request headers and execute it
	/// Return the hyper HTTP response
	pub fn sign_and_execute(&mut self, creds: &AWSCredentials) -> Response {
		let hostname = match self.hostname {
			Some(ref h) => h.to_string(),
			None => build_hostname(&self.service, &self.region)
		};
		self.add_header("host", &hostname);

		if let Some(ref token) = *creds.get_token() {
			self.add_header("X-Amz-Security-Token", token);
		}

		self.canonical_query_string = build_canonical_query_string(&self.params);

		let date = now_utc();
		self.add_header("x-amz-date", &date.strftime("%Y%m%dT%H%M%SZ").unwrap().to_string());

		// build the canonical request
		let signed_headers = signed_headers(&self.headers);
		self.canonical_uri = canonical_uri(&self.path);
		let canonical_headers = canonical_headers(&self.headers);

		let mut canonical_request : String;

		match self.payload {
			None => {
				canonical_request = format!("{}\n{}\n{}\n{}\n{}\n{}",
					&self.method,
					self.canonical_uri,
					self.canonical_query_string,
					canonical_headers,
					signed_headers,
					&to_hexdigest_from_string(""));
				self.add_header("x-amz-content-sha256", &to_hexdigest_from_string(""));
			}
			Some(payload) => {
				// This is hashing the payload twice, booo:
				canonical_request = format!("{}\n{}\n{}\n{}\n{}\n{}",
					&self.method,
					self.canonical_uri,
					self.canonical_query_string,
					canonical_headers,
					signed_headers,
					&to_hexdigest_from_bytes(payload));
				self.add_header("x-amz-content-sha256", &to_hexdigest_from_bytes(payload));
				self.add_header("content-length", &format!("{}", payload.len()));
			}
		}
		self.add_header("content-type", "application/octet-stream");

		// use the hashed canonical request to build the string to sign
		let hashed_canonical_request = to_hexdigest_from_string(&canonical_request);
		let scope = format!("{}/{}/{}/aws4_request", date.strftime("%Y%m%d").unwrap(), region_in_aws_format(&self.region), &self.service);
		let string_to_sign = string_to_sign(date, &hashed_canonical_request, &scope);

		// construct the signing key and sign the string with it
		let signing_key = signing_key(&creds.get_aws_secret_key(), date, &region_in_aws_format(&self.region), &self.service);
		let signature = signature(&string_to_sign, signing_key);

		// build the actual auth header
		let auth_header = format!("AWS4-HMAC-SHA256 Credential={}/{}, SignedHeaders={}, Signature={}",
	               &creds.get_aws_access_key_id(), scope, signed_headers, signature);
		self.add_header("authorization", &auth_header);

		let response = send_request(&self);

		if response.status == HTTP_TEMPORARY_REDIRECT {
			// We have to re-sign and resend the request.

			// extract location from response, modify request and re-sign and resend.

			self.set_hostname(Some("new host".to_string()));

			return self.sign_and_execute(creds);
		}

		response
	}
}

fn signature(string_to_sign: &str, signing_key: Vec<u8>) -> String {
	hmac(SHA256, &signing_key, string_to_sign.as_bytes()).to_hex().to_string()
}

fn signing_key(secret: &str, date: Tm, region: &str, service: &str) -> Vec<u8> {
	let k_date = hmac(SHA256, format!("AWS4{}", secret).as_bytes(), date.strftime("%Y%m%d").unwrap().to_string().as_bytes());
	let k_region = hmac(SHA256, &k_date, region.as_bytes());
	let k_service = hmac(SHA256, &k_region, service.as_bytes());
	hmac(SHA256, &k_service, "aws4_request".as_bytes())
}

pub fn string_to_sign(date: Tm, hashed_canonical_request: &str, scope: &str) -> String {
	format!("AWS4-HMAC-SHA256\n{}\n{}\n{}",
		date.strftime("%Y%m%dT%H%M%SZ").unwrap(),
		scope,
		hashed_canonical_request)
}

fn signed_headers(headers: &BTreeMap<String, Vec<Vec<u8>>>) -> String {
	let mut signed = String::new();

	for (key,_) in headers.iter() {
		if signed.len() > 0 {
			signed.push(';')
		}

		if skipped_headers(key) {
			continue;
		}
		signed.push_str(&key.to_ascii_lowercase());
	}
    signed
}

fn canonical_headers(headers: &BTreeMap<String, Vec<Vec<u8>>>) -> String {
	let mut canonical = String::new();

	for item in headers.iter() {
		if skipped_headers(item.0) {
			continue;
		}
		canonical.push_str(format!("{}:{}\n", item.0.to_ascii_lowercase(), canonical_values(item.1)).as_ref());
	}
	canonical
}

fn canonical_values(values: &Vec<Vec<u8>>) -> String {
	let mut st = String::new();
    for v in values {
        let s = str::from_utf8(v).unwrap();
        if st.len() > 0 {
            st.push(',')
        }
        if s.starts_with("\""){
            st.push_str(&s);
        } else {
            st.push_str(s.replace("  ", " ").trim());
        }
    }
    st
}

fn skipped_headers(header: &str) -> bool {
    ["authorization", "content-length", "user-agent"].contains(&header)
}

fn canonical_uri(path: &str) -> String {
	match path {
		"" => "/".to_string(),
		_ => path.to_string()
	}
}

fn build_canonical_query_string(params: &Params) -> String {
	if params.len() == 0 {
		return String::new();
    }

	let mut output = String::new();
	for item in params.iter() {
		if output.len() > 0 {
			output.push_str("&");
		}
		byte_serialize(item.0, &mut output);
		output.push_str("=");
		byte_serialize(item.1, &mut output);
	}

	output
}

#[inline]
fn byte_serialize(input: &str, output: &mut String) {
	for &byte in input.as_bytes().iter() {
		percent_encode_to(&[byte], FORM_URLENCODED_ENCODE_SET, output)
	}
}

// TODO: consolidate these functions
fn to_hexdigest_from_string(val: &str) -> String {
    let h = hash(SHA256, val.as_bytes());
    h.to_hex().to_string()
}

fn to_hexdigest_from_bytes(val: &[u8]) -> String {
	let h = hash(SHA256, val);
    h.to_hex().to_string()
}

fn build_hostname(service: &str, region: &Region) -> String {
	//iam has only 1 endpoint, other services have region-based endpoints
	match service {
		"iam" => format!("{}.amazonaws.com", service),
		"s3" => {
				match *region {
					Region::UsEast1 => return "s3.amazonaws.com".to_string(),
					_ => return format!("s3-{}.amazonaws.com", region_in_aws_format(region)),
				}
			}
		_ => format!("{}.{}.amazonaws.com", service, region_in_aws_format(region))
	}
}

/// extract_s3_redirect_location takes a Hyper Response and attempts to pull out the temporary endpoint.
fn extract_s3_redirect_location(response: Response) -> Result<String, AWSError> {
	// Double checking this feels like belts and suspenders since we're checking the status code
	// before calling this.  Remove this check?

	// Verify it's a 307 temporary redirect
	if response.status != HTTP_TEMPORARY_REDIRECT {
		return Err(AWSError::new("Trying to find temporary location when status is not 307 temp redirect."))
	}

	// read response body
	let mut body = String::new();
	response.read_to_string(&mut body).unwrap();

	// extract and return temporary endpoint location
	extract_s3_temporary_endpoint_from_xml(body)
}

/// extract_s3_temporary_endpoint_from_xml takes in XML and tries to find the value of the Endpoint node.
fn extract_s3_temporary_endpoint_from_xml(body: &str) -> Result<String, AWSError> {

	try!(start_element(tag_name, stack));
	let obj = try!(characters(stack));
	try!(end_element(tag_name, stack));
	Ok("meh")
}

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

	#[test]
	fn get_hostname_none_present() {
		let region = Region::UsEast1;
		let request = SignedRequest::new("POST", "sqs", &region, "/");
		assert_eq!("sqs.us-east-1.amazonaws.com", request.get_hostname());
	}

	#[test]
	fn get_hostname_happy_path() {
		let region = Region::UsEast1;
		let mut request = SignedRequest::new("POST", "sqs", &region, "/");
		request.set_hostname(Some("test-hostname".to_string()));
		assert_eq!("test-hostname", request.get_hostname());
	}

}
<commit_msg>Redirect feature now compiles, working on unit tests.<commit_after>extern crate regex;
use credentials::AWSCredentials;
use hyper::client::Response;
use hyper::status::StatusCode;
use openssl::crypto::hash::Type::SHA256;
use openssl::crypto::hash::hash;
use openssl::crypto::hmac::hmac;
use params::Params;
use serialize::hex::ToHex;
use std::ascii::AsciiExt;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::str;
use time::Tm;
use time::now_utc;
use url::percent_encoding::{percent_encode_to, FORM_URLENCODED_ENCODE_SET};
use regions::*;
use request::send_request;
use xmlutil::*;
use error::*;
use xml::reader::*;
// Debug:
// use std::io::Read;

const HTTP_TEMPORARY_REDIRECT: StatusCode = StatusCode::TemporaryRedirect;

/// A data structure for all the elements of an HTTP request that are involved in
/// the Amazon Signature Version 4 signing process
#[derive(Debug)]
pub struct SignedRequest<'a> {
	method: String,
	service: String,
	region: &'a Region,
	path: String,
	headers: BTreeMap<String, Vec<Vec<u8>>>,
	params: Params,
	hostname: Option<String>,
	payload: Option<&'a [u8]>,
	canonical_query_string: String,
	canonical_uri: String,
}

impl <'a> SignedRequest <'a> {
	/// Default constructor
	pub fn new<'b>(method: &str, service: &str, region: &'a Region, path: &str) -> SignedRequest<'a> {
		SignedRequest {
			method: method.to_string(),
			service: service.to_string(),
			region: region,
			path: path.to_string(),
			headers: BTreeMap::new(),
			params: Params::new(),
			hostname: None,
			payload: None,
			canonical_query_string: String::new(),
			canonical_uri: String::new(),
		 }
	}

	pub fn set_hostname(&mut self, hostname: Option<String>) {
		self.hostname = hostname;
	}

	pub fn set_payload(&mut self, payload: Option<&'a [u8]>) {
		self.payload = payload;
	}

	pub fn get_method(&self) -> &str {
		&self.method
	}

	pub fn get_canonical_uri(&self) -> &str {
		&self.canonical_uri
	}

	pub fn get_canonical_query_string(&self) -> &str {
		&self.canonical_query_string
	}

	pub fn get_payload(&self) -> Option<&'a [u8]> {
		self.payload
	}

	pub fn get_headers(&'a self) -> &'a BTreeMap<String, Vec<Vec<u8>>> {
		&self.headers
	}

	pub fn get_hostname(&self) -> String {
		match self.hostname {
			Some(ref h) => h.to_string(),
			None => build_hostname(&self.service, &self.region)
		}
	}

	/// Add a value to the array of headers for the specified key.
	/// Headers are kept sorted by key name for use at signing (BTreeMap)
	pub fn add_header(&mut self, key: &str, value: &str) {
		let key_lower = key.to_ascii_lowercase().to_string();
		let value_vec = value.as_bytes().to_vec();

		match self.headers.entry(key_lower) {
			Entry::Vacant(entry) => {
				let mut values = Vec::new();
				values.push(value_vec);
				entry.insert(values);
			}
			Entry::Occupied(entry) => {
				entry.into_mut().push(value_vec);
			}
		}
	}

	pub fn add_param<S>(&mut self, key: S, value: S)  where S: Into<String> {
		self.params.insert(key.into(), value.into());
	}

	pub fn set_params(&mut self, params: Params){
		self.params = params;
	}

	/// Calculate the signature from the credentials provided and the request data
	/// Add the calculated signature to the request headers and execute it
	/// Return the hyper HTTP response
	pub fn sign_and_execute(&mut self, creds: &AWSCredentials) -> Response {
		let hostname = match self.hostname {
			Some(ref h) => h.to_string(),
			None => build_hostname(&self.service, &self.region)
		};
		self.add_header("host", &hostname);

		if let Some(ref token) = *creds.get_token() {
			self.add_header("X-Amz-Security-Token", token);
		}

		self.canonical_query_string = build_canonical_query_string(&self.params);

		let date = now_utc();
		self.add_header("x-amz-date", &date.strftime("%Y%m%dT%H%M%SZ").unwrap().to_string());

		// build the canonical request
		let signed_headers = signed_headers(&self.headers);
		self.canonical_uri = canonical_uri(&self.path);
		let canonical_headers = canonical_headers(&self.headers);

		let mut canonical_request : String;

		match self.payload {
			None => {
				canonical_request = format!("{}\n{}\n{}\n{}\n{}\n{}",
					&self.method,
					self.canonical_uri,
					self.canonical_query_string,
					canonical_headers,
					signed_headers,
					&to_hexdigest_from_string(""));
				self.add_header("x-amz-content-sha256", &to_hexdigest_from_string(""));
			}
			Some(payload) => {
				// This is hashing the payload twice, booo:
				canonical_request = format!("{}\n{}\n{}\n{}\n{}\n{}",
					&self.method,
					self.canonical_uri,
					self.canonical_query_string,
					canonical_headers,
					signed_headers,
					&to_hexdigest_from_bytes(payload));
				self.add_header("x-amz-content-sha256", &to_hexdigest_from_bytes(payload));
				self.add_header("content-length", &format!("{}", payload.len()));
			}
		}
		self.add_header("content-type", "application/octet-stream");

		// use the hashed canonical request to build the string to sign
		let hashed_canonical_request = to_hexdigest_from_string(&canonical_request);
		let scope = format!("{}/{}/{}/aws4_request", date.strftime("%Y%m%d").unwrap(), region_in_aws_format(&self.region), &self.service);
		let string_to_sign = string_to_sign(date, &hashed_canonical_request, &scope);

		// construct the signing key and sign the string with it
		let signing_key = signing_key(&creds.get_aws_secret_key(), date, &region_in_aws_format(&self.region), &self.service);
		let signature = signature(&string_to_sign, signing_key);

		// build the actual auth header
		let auth_header = format!("AWS4-HMAC-SHA256 Credential={}/{}, SignedHeaders={}, Signature={}",
	               &creds.get_aws_access_key_id(), scope, signed_headers, signature);
		self.add_header("authorization", &auth_header);

		let response = send_request(&self);

		if response.status == HTTP_TEMPORARY_REDIRECT {
			// We have to re-sign and resend the request.

			// extract location from response, modify request and re-sign and resend.

			self.set_hostname(Some("new host".to_string()));

			return self.sign_and_execute(creds);
		}

		response
	}
}

fn signature(string_to_sign: &str, signing_key: Vec<u8>) -> String {
	hmac(SHA256, &signing_key, string_to_sign.as_bytes()).to_hex().to_string()
}

fn signing_key(secret: &str, date: Tm, region: &str, service: &str) -> Vec<u8> {
	let k_date = hmac(SHA256, format!("AWS4{}", secret).as_bytes(), date.strftime("%Y%m%d").unwrap().to_string().as_bytes());
	let k_region = hmac(SHA256, &k_date, region.as_bytes());
	let k_service = hmac(SHA256, &k_region, service.as_bytes());
	hmac(SHA256, &k_service, "aws4_request".as_bytes())
}

pub fn string_to_sign(date: Tm, hashed_canonical_request: &str, scope: &str) -> String {
	format!("AWS4-HMAC-SHA256\n{}\n{}\n{}",
		date.strftime("%Y%m%dT%H%M%SZ").unwrap(),
		scope,
		hashed_canonical_request)
}

fn signed_headers(headers: &BTreeMap<String, Vec<Vec<u8>>>) -> String {
	let mut signed = String::new();

	for (key,_) in headers.iter() {
		if signed.len() > 0 {
			signed.push(';')
		}

		if skipped_headers(key) {
			continue;
		}
		signed.push_str(&key.to_ascii_lowercase());
	}
    signed
}

fn canonical_headers(headers: &BTreeMap<String, Vec<Vec<u8>>>) -> String {
	let mut canonical = String::new();

	for item in headers.iter() {
		if skipped_headers(item.0) {
			continue;
		}
		canonical.push_str(format!("{}:{}\n", item.0.to_ascii_lowercase(), canonical_values(item.1)).as_ref());
	}
	canonical
}

fn canonical_values(values: &Vec<Vec<u8>>) -> String {
	let mut st = String::new();
    for v in values {
        let s = str::from_utf8(v).unwrap();
        if st.len() > 0 {
            st.push(',')
        }
        if s.starts_with("\""){
            st.push_str(&s);
        } else {
            st.push_str(s.replace("  ", " ").trim());
        }
    }
    st
}

fn skipped_headers(header: &str) -> bool {
    ["authorization", "content-length", "user-agent"].contains(&header)
}

fn canonical_uri(path: &str) -> String {
	match path {
		"" => "/".to_string(),
		_ => path.to_string()
	}
}

fn build_canonical_query_string(params: &Params) -> String {
	if params.len() == 0 {
		return String::new();
    }

	let mut output = String::new();
	for item in params.iter() {
		if output.len() > 0 {
			output.push_str("&");
		}
		byte_serialize(item.0, &mut output);
		output.push_str("=");
		byte_serialize(item.1, &mut output);
	}

	output
}

#[inline]
fn byte_serialize(input: &str, output: &mut String) {
	for &byte in input.as_bytes().iter() {
		percent_encode_to(&[byte], FORM_URLENCODED_ENCODE_SET, output)
	}
}

// TODO: consolidate these functions
fn to_hexdigest_from_string(val: &str) -> String {
    let h = hash(SHA256, val.as_bytes());
    h.to_hex().to_string()
}

fn to_hexdigest_from_bytes(val: &[u8]) -> String {
	let h = hash(SHA256, val);
    h.to_hex().to_string()
}

fn build_hostname(service: &str, region: &Region) -> String {
	//iam has only 1 endpoint, other services have region-based endpoints
	match service {
		"iam" => format!("{}.amazonaws.com", service),
		"s3" => {
				match *region {
					Region::UsEast1 => return "s3.amazonaws.com".to_string(),
					_ => return format!("s3-{}.amazonaws.com", region_in_aws_format(region)),
				}
			}
		_ => format!("{}.{}.amazonaws.com", service, region_in_aws_format(region))
	}
}

/// extract_s3_redirect_location takes a Hyper Response and attempts to pull out the temporary endpoint.
fn extract_s3_redirect_location(response: Response) -> Result<String, AWSError> {
	// Double checking this feels like belts and suspenders since we're checking the status code
	// before calling this.  Remove this check?

	// Verify it's a 307 temporary redirect
	if response.status != HTTP_TEMPORARY_REDIRECT {
		return Err(AWSError::new("Trying to find temporary location when status is not 307 temp redirect."))
	}

	let mut reader = EventReader::new(response);
	let mut stack = XmlResponseFromAws::new(reader.events().peekable());
	stack.next(); // xml start tag

	// extract and return temporary endpoint location
	extract_s3_temporary_endpoint_from_xml(&mut stack)
}

/// extract_s3_temporary_endpoint_from_xml takes in XML and tries to find the value of the Endpoint node.
fn extract_s3_temporary_endpoint_from_xml<'a, T: Peek + Next>(stack: &mut T) -> Result<String, AWSError> {

	try!(start_element(&"Error".to_string(), stack));
	// now find Endpoint contents
	let obj = try!(characters(stack));
	println!("obj is {}", obj);

	try!(end_element(&"Error".to_string(), stack));

	Ok("meh".to_string()) // endpoint location
}

#[cfg(test)]
mod tests {
    use super::SignedRequest;
	use super::extract_s3_temporary_endpoint_from_xml;
	use xmlutil::*;
	use regions::*;
	use std::io::BufReader;
	use std::fs::File;
	use xml::reader::*;

	#[test]
	fn get_hostname_none_present() {
		let region = Region::UsEast1;
		let request = SignedRequest::new("POST", "sqs", &region, "/");
		assert_eq!("sqs.us-east-1.amazonaws.com", request.get_hostname());
	}

	#[test]
	fn get_hostname_happy_path() {
		let region = Region::UsEast1;
		let mut request = SignedRequest::new("POST", "sqs", &region, "/");
		request.set_hostname(Some("test-hostname".to_string()));
		assert_eq!("test-hostname", request.get_hostname());
	}

	#[test]
	fn get_redirect_location_from_s3() {
		let file = File::open("tests/sample-data/s3_temp_redirect.xml").unwrap();
	    let file = BufReader::new(file);
	    let mut my_parser  = EventReader::new(file);
	    let my_stack = my_parser.events().peekable();
	    let mut reader = XmlResponseFromFile::new(my_stack);
		reader.next(); // xml start node
		let result = extract_s3_temporary_endpoint_from_xml(&mut reader);

		match result {
			Err(_) => panic!("Couldn't parse s3_temp_redirect.xml"),
			Ok(location) => {
				assert_eq!(location, "rusoto1441045966.s3-us-west-1.amazonaws.com");
			}
		}
	}

}
<|endoftext|>