<commit_before>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    Var(usize),
    Abs(Box<Term>),
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp(), Ok((Var(0), Var(1))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref(), Ok((&Var(0), &Var(1))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref_mut(), Ok((&mut Var(0), &mut Var(1))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs(), Ok(Var(0)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref(), Ok(&Var(0)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref_mut(), Ok(&mut Var(0)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs(), Ok(Var(1)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Performs 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()).evaluate(), Ok(abs(abs(Var(1)))));
    /// ```
    pub fn evaluate(self) -> Result<Term, Error> {
        let (mut lhs, rhs) = try!(self.unapp());

        _apply(&mut lhs, rhs, 0);

        Ok(try!(lhs.unabs()))
    }
}

/// Wraps a term in an abstraction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an application of its arguments without substitution or reduction, consuming them in
/// the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(0), Var(1)), App(Box::new(Var(0)), Box::new(Var(1))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

/// Applies two terms with substitution and variable update (but without reduction), consuming them
/// in the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::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);

    Ok(try!(lhs.unabs()))
}

fn _apply(lhs: &mut Term, rhs: Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs; // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1) // update free variables' indices 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.clone(), depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth);
        }
    }
}

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

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(0, self))
    }
}

fn show_precedence(context_precedence: usize, term: &Term) -> String {
    match *term {
        Var(i) => format!("{:X}", i), // max. index = 15
        Abs(ref t) => {
            let ret = format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t);
            parenthesize_if(context_precedence > 1, &ret).into()
        },
        App(ref t1, ref t2) => {
            let ret = format!("{}{}", show_precedence(2, t1), show_precedence(3, t2));
            parenthesize_if(context_precedence == 3, &ret).into()
        }
    }
}

fn parenthesize_if(condition: bool, input: &str) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

#[cfg(test)]
mod test {
    use arithmetic::{zero, succ, pred};
    use combinators::i;
    use parser::parse;
    use term::{apply, abs};
    use term::Term::Var;

    #[test]
    fn applying() {
        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));

        assert_eq!(apply(i(), zero()).unwrap(), abs(abs(Var(1))));
    }

    #[test]
    fn displaying_terms() {
        assert_eq!(&format!("{}", zero()), "λλ1");
        assert_eq!(&format!("{}", succ()), "λλλ2(321)");
        assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
    }
}
<commit_msg>Change evaluate() to apply()<commit_after>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    Var(usize),
    Abs(Box<Term>),
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp(), Ok((Var(0), Var(1))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref(), Ok((&Var(0), &Var(1))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref_mut(), Ok((&mut Var(0), &mut Var(1))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs(), Ok(Var(0)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref(), Ok(&Var(0)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref_mut(), Ok(&mut Var(0)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs(), Ok(Var(1)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Performs 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()).apply(), Ok(abs(abs(Var(1)))));
    /// ```
    pub fn apply(self) -> Result<Term, Error> {
        let (mut lhs, rhs) = try!(self.unapp());

        _apply(&mut lhs, rhs, 0);

        Ok(try!(lhs.unabs()))
    }
}

/// Wraps a term in an abstraction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an application of its arguments without substitution or reduction, consuming them in
/// the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(0), Var(1)), App(Box::new(Var(0)), Box::new(Var(1))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

/// Applies two terms with substitution and variable update (but without reduction), consuming them
/// in the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::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);

    Ok(try!(lhs.unabs()))
}

fn _apply(lhs: &mut Term, rhs: Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs; // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1) // update free variables' indices 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.clone(), depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth);
        }
    }
}

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

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(0, self))
    }
}

fn show_precedence(context_precedence: usize, term: &Term) -> String {
    match *term {
        Var(i) => format!("{:X}", i), // max. index = 15
        Abs(ref t) => {
            let ret = format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t);
            parenthesize_if(context_precedence > 1, &ret).into()
        },
        App(ref t1, ref t2) => {
            let ret = format!("{}{}", show_precedence(2, t1), show_precedence(3, t2));
            parenthesize_if(context_precedence == 3, &ret).into()
        }
    }
}

fn parenthesize_if(condition: bool, input: &str) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

#[cfg(test)]
mod test {
    use arithmetic::{zero, succ, pred};
    use combinators::i;
    use parser::parse;
    use term::{apply, abs};
    use term::Term::Var;

    #[test]
    fn applying() {
        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));

        assert_eq!(apply(i(), zero()).unwrap(), abs(abs(Var(1))));
    }

    #[test]
    fn displaying_terms() {
        assert_eq!(&format!("{}", zero()), "λλ1");
        assert_eq!(&format!("{}", succ()), "λλλ2(321)");
        assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
    }
}
<|endoftext|>


<commit_before>//! Implementation of Reversi rules to play a turn.

use board::*;
use ::Result;

/// A turn can be in two states: either running (with a side to play next) or ended.
pub type State = Option<::Side>;

/// A turn is given by a board and by which player has to move next.
/// For convenience we also annotate current scores.
#[derive(Debug, Clone)]
pub struct Turn {
    board: Board,
    state: State,
    score_dark: u8,
    score_light: u8,
}

impl Turn {
    /// Initializing a new first turn: starting positions on the board and Dark is the first to play
    pub fn first_turn() -> Turn {
        let mut board = Board::new(&[[None; BOARD_SIZE]; BOARD_SIZE]);
        board.place_disk(::Side::Dark, Coord::new(3, 4)).expect("This cannot fail");
        board.place_disk(::Side::Dark, Coord::new(4, 3)).expect("This cannot fail");
        board.place_disk(::Side::Light, Coord::new(3, 3)).expect("This cannot fail");
        board.place_disk(::Side::Light, Coord::new(4, 4)).expect("This cannot fail");

        Turn {
            board: board,
            state: Some(::Side::Dark),
            score_dark: 2,
            score_light: 2,
        }
    }

    /// Returns the turn's board
    pub fn get_board(&self) -> &Board {
        &self.board
    }

    /// Returns the board's cell corresponding to the given coordinates.
    pub fn get_cell(&self, coord: Coord) -> Result<Cell> {
        self.board.get_cell(coord)
    }

    /// Returns the turn's status
    pub fn get_state(&self) -> State {
        self.state
    }

    /// Returns the current score of the match.
    pub fn get_score(&self) -> (u8, u8) {
        (self.score_dark, self.score_light)
    }

    /// Returns the difference in score between Light and Dark.
    pub fn get_score_diff(&self) -> i16 {
        self.score_light as i16 - self.score_dark as i16
    }

    /// Returns turn's tempo (how many disks there are on the board).
    pub fn get_tempo(&self) -> u8 {
        self.score_light + self.score_dark
    }

    /// Check whether a given move is legal
    pub fn check_move (&self, coord: Coord) -> Result<()> {

        if self.state.is_none() {
            // If the game is ended, no further moves are possible
            Err(::ReversiError::EndedGame)
        } else if try!(self.board.get_cell(coord)).is_some() { // This also checks `coord`
            // If a cell is already taken, it's not possible to move there
            Err(::ReversiError::CellAlreadyTaken(coord))
        } else {
            // If a move leads to eat in at least one direction, then it is legal
            for &dir in &DIRECTIONS {
                if self.check_move_along_direction(coord, dir) {
                    return Ok(());
                }
            }
            // Otherwise, the move is not legal
            Err(::ReversiError::IllegalMove(coord))
        }
    }

    /// Check whether a move leads to eat in a specified direction
    fn check_move_along_direction (&self, coord: Coord, dir: Direction) -> bool {
        let mut next_coord = coord;
        if let Ok(Ok(Some(next_disk))) = next_coord.step(dir).map(|()| self.get_cell(next_coord)) {
            if Some(next_disk.get_side().opposite()) == self.state {
                while let Ok(Ok(Some(successive_disk))) = next_coord.step(dir).map(|()| self.get_cell(next_coord)) {
                    if Some(successive_disk.get_side()) == self.state {
                        return true;
                    }
                }
            }
        }
        false
    }

    /// Eats all of the opponent's occupied cells from a specified cell (given by its coordinates) in a specified direction until it finds a cell of the current player.
    fn make_move_along_direction (&mut self, coord: Coord, dir: Direction) -> Result<()> {

        let side = try!(self.state.ok_or(::ReversiError::EndedGame));
        let mut next_coord = coord;
        let _ = try!(next_coord.step(dir).map(|()| self.board.flip_disk(next_coord)));
        let mut eating: u8 = 1;

        while side != try!(try!(next_coord.step(dir).map(|()| self.board.get_disk(next_coord))).map(|disk| disk.get_side())) {
            try!(self.board.flip_disk(next_coord));
            eating += 1;
        }

        match side {
            ::Side::Light => {
                self.score_light += eating;
                self.score_dark  -= eating;
            }
            ::Side::Dark => {
                self.score_light -= eating;
                self.score_dark  += eating;
            }
        };

        Ok(())
    }

    /// Current player performs a move, after verifying that it is legal.
    /// It returns either the new turn or the error preventing the move to be performed.
    pub fn make_move (&self, coord: Coord) -> Result<Turn> {

        if let Ok(None) = self.board.get_cell(coord) {
            let mut turn_after_move = self.clone();
            let mut legal = false;

            if let Some(turn_side) = self.state {
                for &dir in &DIRECTIONS {
                    if self.check_move_along_direction(coord, dir) {
                        try!(turn_after_move.make_move_along_direction(coord, dir));
                        legal = true;
                    }
                }

                if legal {
                    try!(turn_after_move.board.place_disk(turn_side, coord));
                    match turn_side {
                        ::Side::Dark  => turn_after_move.score_dark  += 1,
                        ::Side::Light => turn_after_move.score_light += 1,
                    }

                    // If a move is legal, the next player to play has to be determined
                    // If the opposite player can make any move at all, it gets the turn
                    // If not, if the previous player can make any move at all, it gets the turn
                    // If not (that is, if no player can make any move at all) the game is ended
                    if turn_after_move.get_tempo() == NUM_CELLS as u8 {
                        // Quick check to rule out games with filled up boards as ended.
                        turn_after_move.state = None;
                    } else {
                        // Turn passes to the other player.
                        turn_after_move.state = Some(turn_side.opposite());
                        if !turn_after_move.can_move() {
                            // If the other player cannot move, turn passes back to the first player.
                            turn_after_move.state = Some(turn_side);
                            if !turn_after_move.can_move() {
                                // If neither platers can move, game is ended.
                                turn_after_move.state = None;
                            }
                        }
                    }
                    Ok(turn_after_move)
                } else {
                    Err(::ReversiError::IllegalMove(coord))
                }
            } else {
                Err(::ReversiError::EndedGame)
            }
        } else {
            Err(::ReversiError::CellAlreadyTaken(coord))
        }
    }

    /// Returns whether or not next_player can make any move at all.
    /// To be used privately. User should rather look at turn's state.
    fn can_move(&self) -> bool {
        for (row, &row_array) in self.board.get_all_cells().into_iter().enumerate() {
            for (col, &cell) in row_array.into_iter().enumerate() {
                if cell.is_none() {
                    for &dir in &DIRECTIONS {
                        if self.check_move_along_direction(Coord::new(row, col), dir) {
                            return true;
                        }
                    }
                }
            }
        }
        false
    }

}
<commit_msg>add `is_endgame` function<commit_after>//! Implementation of Reversi rules to play a turn.

use board::*;
use ::Result;

/// A turn can be in two states: either running (with a side to play next) or ended.
pub type State = Option<::Side>;

/// A turn is given by a board and by which player has to move next.
/// For convenience we also annotate current scores.
#[derive(Debug, Clone)]
pub struct Turn {
    board: Board,
    state: State,
    score_dark: u8,
    score_light: u8,
}

impl Turn {
    /// Initializing a new first turn: starting positions on the board and Dark is the first to play
    pub fn first_turn() -> Turn {
        let mut board = Board::new(&[[None; BOARD_SIZE]; BOARD_SIZE]);
        board.place_disk(::Side::Dark, Coord::new(3, 4)).expect("This cannot fail");
        board.place_disk(::Side::Dark, Coord::new(4, 3)).expect("This cannot fail");
        board.place_disk(::Side::Light, Coord::new(3, 3)).expect("This cannot fail");
        board.place_disk(::Side::Light, Coord::new(4, 4)).expect("This cannot fail");

        Turn {
            board: board,
            state: Some(::Side::Dark),
            score_dark: 2,
            score_light: 2,
        }
    }

    /// Returns the turn's board
    pub fn get_board(&self) -> &Board {
        &self.board
    }

    /// Returns the board's cell corresponding to the given coordinates.
    pub fn get_cell(&self, coord: Coord) -> Result<Cell> {
        self.board.get_cell(coord)
    }

    /// Returns the turn's status
    pub fn get_state(&self) -> State {
        self.state
    }

    /// Returns whether the turn is an endgame
    pub fn is_endgame(&self) -> bool {
        self.state == None
    }

    /// Returns the current score of the match.
    pub fn get_score(&self) -> (u8, u8) {
        (self.score_dark, self.score_light)
    }

    /// Returns the difference in score between Light and Dark.
    pub fn get_score_diff(&self) -> i16 {
        self.score_light as i16 - self.score_dark as i16
    }

    /// Returns turn's tempo (how many disks there are on the board).
    pub fn get_tempo(&self) -> u8 {
        self.score_light + self.score_dark
    }

    /// Check whether a given move is legal
    pub fn check_move (&self, coord: Coord) -> Result<()> {

        if self.state.is_none() {
            // If the game is ended, no further moves are possible
            Err(::ReversiError::EndedGame)
        } else if try!(self.board.get_cell(coord)).is_some() { // This also checks `coord`
            // If a cell is already taken, it's not possible to move there
            Err(::ReversiError::CellAlreadyTaken(coord))
        } else {
            // If a move leads to eat in at least one direction, then it is legal
            for &dir in &DIRECTIONS {
                if self.check_move_along_direction(coord, dir) {
                    return Ok(());
                }
            }
            // Otherwise, the move is not legal
            Err(::ReversiError::IllegalMove(coord))
        }
    }

    /// Check whether a move leads to eat in a specified direction
    fn check_move_along_direction (&self, coord: Coord, dir: Direction) -> bool {
        let mut next_coord = coord;
        if let Ok(Ok(Some(next_disk))) = next_coord.step(dir).map(|()| self.get_cell(next_coord)) {
            if Some(next_disk.get_side().opposite()) == self.state {
                while let Ok(Ok(Some(successive_disk))) = next_coord.step(dir).map(|()| self.get_cell(next_coord)) {
                    if Some(successive_disk.get_side()) == self.state {
                        return true;
                    }
                }
            }
        }
        false
    }

    /// Eats all of the opponent's occupied cells from a specified cell (given by its coordinates) in a specified direction until it finds a cell of the current player.
    fn make_move_along_direction (&mut self, coord: Coord, dir: Direction) -> Result<()> {

        let side = try!(self.state.ok_or(::ReversiError::EndedGame));
        let mut next_coord = coord;
        let _ = try!(next_coord.step(dir).map(|()| self.board.flip_disk(next_coord)));
        let mut eating: u8 = 1;

        while side != try!(try!(next_coord.step(dir).map(|()| self.board.get_disk(next_coord))).map(|disk| disk.get_side())) {
            try!(self.board.flip_disk(next_coord));
            eating += 1;
        }

        match side {
            ::Side::Light => {
                self.score_light += eating;
                self.score_dark  -= eating;
            }
            ::Side::Dark => {
                self.score_light -= eating;
                self.score_dark  += eating;
            }
        };

        Ok(())
    }

    /// Current player performs a move, after verifying that it is legal.
    /// It returns either the new turn or the error preventing the move to be performed.
    pub fn make_move (&self, coord: Coord) -> Result<Turn> {

        if let Ok(None) = self.board.get_cell(coord) {
            let mut turn_after_move = self.clone();
            let mut legal = false;

            if let Some(turn_side) = self.state {
                for &dir in &DIRECTIONS {
                    if self.check_move_along_direction(coord, dir) {
                        try!(turn_after_move.make_move_along_direction(coord, dir));
                        legal = true;
                    }
                }

                if legal {
                    try!(turn_after_move.board.place_disk(turn_side, coord));
                    match turn_side {
                        ::Side::Dark  => turn_after_move.score_dark  += 1,
                        ::Side::Light => turn_after_move.score_light += 1,
                    }

                    // If a move is legal, the next player to play has to be determined
                    // If the opposite player can make any move at all, it gets the turn
                    // If not, if the previous player can make any move at all, it gets the turn
                    // If not (that is, if no player can make any move at all) the game is ended
                    if turn_after_move.get_tempo() == NUM_CELLS as u8 {
                        // Quick check to rule out games with filled up boards as ended.
                        turn_after_move.state = None;
                    } else {
                        // Turn passes to the other player.
                        turn_after_move.state = Some(turn_side.opposite());
                        if !turn_after_move.can_move() {
                            // If the other player cannot move, turn passes back to the first player.
                            turn_after_move.state = Some(turn_side);
                            if !turn_after_move.can_move() {
                                // If neither platers can move, game is ended.
                                turn_after_move.state = None;
                            }
                        }
                    }
                    Ok(turn_after_move)
                } else {
                    Err(::ReversiError::IllegalMove(coord))
                }
            } else {
                Err(::ReversiError::EndedGame)
            }
        } else {
            Err(::ReversiError::CellAlreadyTaken(coord))
        }
    }

    /// Returns whether or not next_player can make any move at all.
    /// To be used privately. User should rather look at turn's state.
    fn can_move(&self) -> bool {
        for (row, &row_array) in self.board.get_all_cells().into_iter().enumerate() {
            for (col, &cell) in row_array.into_iter().enumerate() {
                if cell.is_none() {
                    for &dir in &DIRECTIONS {
                        if self.check_move_along_direction(Coord::new(row, col), dir) {
                            return true;
                        }
                    }
                }
            }
        }
        false
    }

}
<|endoftext|>


<commit_before>/// unit creates a type representing a single unit.
///
/// # Examples
///
/// ```
/// #[macro_use(unit)]
/// extern crate fiz_math;
/// use std::fmt::Debug;
///
/// // GoldCoins are golden!
/// unit!(GoldCoins);
///
/// // SilverCoins are like gold coins, only worse!
/// unit!(SilverCoins);
///
/// fn collect<T: Debug>(coins: GoldCoins<T>) {
///     println!("We've collected {:?} golden coins!", coins.0);
/// }
///
/// fn main() {
///     let x = GoldCoins(5);
///     collect(x);
///
///     let y = SilverCoins(6);
///
///     // Try uncommenting the line below and see:
///     //
///     // error: mismatched types: expected `GoldCoins<_>`, found `SilverCoins<_>`"
///     //
///     //collect(y);
///
///     // add, subtract, multiply, divide:
///     assert_eq!(y + SilverCoins(4), SilverCoins(10));
///     assert_eq!(y - SilverCoins(3), SilverCoins(3));
///     assert_eq!(y * SilverCoins(3), SilverCoins(18));
///     assert_eq!(y / SilverCoins(2), SilverCoins(3));
///
///     // ordering and equality:
///     assert!(y < SilverCoins(7));
///     assert!(y > SilverCoins(5));
///     assert!(y <= SilverCoins(6));
///     assert!(y >= SilverCoins(6));
///     assert!(y == SilverCoins(6));
/// }
/// ```
///
#[macro_export]
macro_rules! unit {
    ( $ident:ident ) => {
        #[derive(Copy, Clone, Debug)]
        pub struct $ident<T>(pub T);

        // addition
        impl<T: ::std::ops::Add<Output = T>> ::std::ops::Add for $ident<T> {
            type Output = Self;

            fn add(self, _rhs: Self) -> Self {
                $ident(self.0 + _rhs.0)
            }
        }

        // subtraction
        impl<T: ::std::ops::Sub<Output = T>> ::std::ops::Sub for $ident<T> {
            type Output = Self;

            fn sub(self, _rhs: Self) -> Self {
                $ident(self.0 - _rhs.0)
            }
        }

        // multiplication
        impl<T: ::std::ops::Mul<Output = T>> ::std::ops::Mul for $ident<T> {
            type Output = Self;

            fn mul(self, _rhs: Self) -> Self {
                $ident(self.0 * _rhs.0)
            }
        }

        // division
        impl<T: ::std::ops::Div<Output = T>> ::std::ops::Div for $ident<T> {
            type Output = Self;

            fn div(self, _rhs: Self) -> Self {
                $ident(self.0 / _rhs.0)
            }
        }

        // remainder
        impl<T: ::std::ops::Rem<Output = T>> ::std::ops::Rem for $ident<T> {
            type Output = Self;

            fn rem(self, rhs: Self) -> Self { $ident(self.0 % rhs.0) }
        }

        // negation
        impl<T: ::std::ops::Neg<Output = T>> ::std::ops::Neg for $ident<T> {
            type Output = Self;

            fn neg(self) -> Self::Output { $ident(-self.0) }
        }

        // partial equality
        impl<T: ::std::cmp::PartialEq> ::std::cmp::PartialEq for $ident<T> {
            fn eq(&self, _rhs: &Self) -> bool {
                self.0 == _rhs.0
            }
        }

        // equality
        impl<T: ::std::cmp::Eq> ::std::cmp::Eq for $ident<T> {}

        // partial ordering
        impl<T: ::std::cmp::PartialOrd> ::std::cmp::PartialOrd for $ident<T> {
            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
                if self.0 < other.0 {
                    Some(::std::cmp::Ordering::Less)
                } else if self.0 > other.0 {
                    Some(::std::cmp::Ordering::Greater)
                } else if self == other {
                    Some(::std::cmp::Ordering::Equal)
                } else {
                    None
                }
            }
        }

        // total ordering
        impl<T: ::std::cmp::Ord> ::std::cmp::Ord for $ident<T> {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                if self.0 < other.0 {
                    ::std::cmp::Ordering::Less
                } else if self.0 > other.0 {
                    ::std::cmp::Ordering::Greater
                } else {
                    ::std::cmp::Ordering::Equal
                }
            }
        }
    };
}
<commit_msg>unit! implement not operator<commit_after>/// unit creates a type representing a single unit.
///
/// # Examples
///
/// ```
/// #[macro_use(unit)]
/// extern crate fiz_math;
/// use std::fmt::Debug;
///
/// // GoldCoins are golden!
/// unit!(GoldCoins);
///
/// // SilverCoins are like gold coins, only worse!
/// unit!(SilverCoins);
///
/// fn collect<T: Debug>(coins: GoldCoins<T>) {
///     println!("We've collected {:?} golden coins!", coins.0);
/// }
///
/// fn main() {
///     let x = GoldCoins(5);
///     collect(x);
///
///     let y = SilverCoins(6);
///
///     // Try uncommenting the line below and see:
///     //
///     // error: mismatched types: expected `GoldCoins<_>`, found `SilverCoins<_>`"
///     //
///     //collect(y);
///
///     // add, subtract, multiply, divide:
///     assert_eq!(y + SilverCoins(4), SilverCoins(10));
///     assert_eq!(y - SilverCoins(3), SilverCoins(3));
///     assert_eq!(y * SilverCoins(3), SilverCoins(18));
///     assert_eq!(y / SilverCoins(2), SilverCoins(3));
///
///     // ordering and equality:
///     assert!(y < SilverCoins(7));
///     assert!(y > SilverCoins(5));
///     assert!(y <= SilverCoins(6));
///     assert!(y >= SilverCoins(6));
///     assert!(y == SilverCoins(6));
/// }
/// ```
///
#[macro_export]
macro_rules! unit {
    ( $ident:ident ) => {
        #[derive(Copy, Clone, Debug)]
        pub struct $ident<T>(pub T);

        // addition
        impl<T: ::std::ops::Add<Output = T>> ::std::ops::Add for $ident<T> {
            type Output = Self;

            fn add(self, _rhs: Self) -> Self {
                $ident(self.0 + _rhs.0)
            }
        }

        // subtraction
        impl<T: ::std::ops::Sub<Output = T>> ::std::ops::Sub for $ident<T> {
            type Output = Self;

            fn sub(self, _rhs: Self) -> Self {
                $ident(self.0 - _rhs.0)
            }
        }

        // multiplication
        impl<T: ::std::ops::Mul<Output = T>> ::std::ops::Mul for $ident<T> {
            type Output = Self;

            fn mul(self, _rhs: Self) -> Self {
                $ident(self.0 * _rhs.0)
            }
        }

        // division
        impl<T: ::std::ops::Div<Output = T>> ::std::ops::Div for $ident<T> {
            type Output = Self;

            fn div(self, _rhs: Self) -> Self {
                $ident(self.0 / _rhs.0)
            }
        }

        // remainder
        impl<T: ::std::ops::Rem<Output = T>> ::std::ops::Rem for $ident<T> {
            type Output = Self;

            fn rem(self, rhs: Self) -> Self { $ident(self.0 % rhs.0) }
        }

        // negation
        impl<T: ::std::ops::Neg<Output = T>> ::std::ops::Neg for $ident<T> {
            type Output = Self;

            fn neg(self) -> Self::Output { $ident(-self.0) }
        }

        // not
        impl<T: ::std::ops::Not<Output = T>> ::std::ops::Not for $ident<T> {
            type Output = Self;

            fn not(self) -> Self::Output { $ident(!self.0) }
        }

        // partial equality
        impl<T: ::std::cmp::PartialEq> ::std::cmp::PartialEq for $ident<T> {
            fn eq(&self, _rhs: &Self) -> bool {
                self.0 == _rhs.0
            }
        }

        // equality
        impl<T: ::std::cmp::Eq> ::std::cmp::Eq for $ident<T> {}

        // partial ordering
        impl<T: ::std::cmp::PartialOrd> ::std::cmp::PartialOrd for $ident<T> {
            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
                if self.0 < other.0 {
                    Some(::std::cmp::Ordering::Less)
                } else if self.0 > other.0 {
                    Some(::std::cmp::Ordering::Greater)
                } else if self == other {
                    Some(::std::cmp::Ordering::Equal)
                } else {
                    None
                }
            }
        }

        // total ordering
        impl<T: ::std::cmp::Ord> ::std::cmp::Ord for $ident<T> {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                if self.0 < other.0 {
                    ::std::cmp::Ordering::Less
                } else if self.0 > other.0 {
                    ::std::cmp::Ordering::Greater
                } else {
                    ::std::cmp::Ordering::Equal
                }
            }
        }
    };
}
<|endoftext|>


<commit_before>extern crate libc;

use std::ffi::CString;
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::Path;

pub fn duplicate(file: &File) -> Result<File> {
    unsafe {
        let fd = libc::dup(file.as_raw_fd());

        if fd < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(File::from_raw_fd(fd))
        }
    }
}

pub fn lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH)
}

pub fn lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX)
}

pub fn try_lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH | libc::LOCK_NB)
}

pub fn try_lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX | libc::LOCK_NB)
}

pub fn unlock(file: &File) -> Result<()> {
    flock(file, libc::LOCK_UN)
}

pub fn lock_error() -> Error {
    Error::from_raw_os_error(libc::EWOULDBLOCK)
}

fn flock(file: &File, flag: libc::c_int) -> Result<()> {
    let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
    if ret < 0 { Err(Error::last_os_error()) } else { Ok(()) }
}

pub fn allocated_size(file: &File) -> Result<u64> {
    file.metadata().map(|m| m.blocks() as u64 * 512)
}

#[cfg(any(target_os = "linux",
          target_os = "freebsd",
          target_os = "android",
          target_os = "nacl"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let ret = unsafe { libc::posix_fallocate(file.as_raw_fd(), 0, len as libc::off_t) };
    if ret == 0 { Ok(()) } else { Err(Error::last_os_error()) }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let stat = try!(file.metadata());

    if len > stat.blocks() as u64 * 512 {
        let mut fstore = libc::fstore_t {
            fst_flags: libc::F_ALLOCATECONTIG,
            fst_posmode: libc::F_PEOFPOSMODE,
            fst_offset: 0,
            fst_length: len as libc::off_t,
            fst_bytesalloc: 0,
        };

        let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
        if ret == -1 {
            // Unable to allocate contiguous disk space; attempt to allocate non-contiguously.
            fstore.fst_flags = libc::F_ALLOCATEALL;
            let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
            if ret == -1 {
                return Err(Error::last_os_error());
            }
        }
    }

    if len > stat.size() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

#[cfg(any(target_os = "openbsd",
          target_os = "netbsd",
          target_os = "dragonfly"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    // No file allocation API available, just set the length if necessary.
    if len > try!(file.metadata()).len() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

fn statvfs<P>(path: P) -> Result<libc::statvfs> where P: AsRef<Path> {
    let cstr = match CString::new(path.as_ref().as_os_str().as_bytes()) {
        Ok(cstr) => cstr,
        Err(..) => return Err(Error::new(ErrorKind::InvalidInput, "path contained a null")),
    };

    unsafe {
        let mut stat: libc::statvfs = mem::zeroed();
        // danburkert/fs2-rs#1: cast is necessary for platforms where c_char != u8.
        if libc::statvfs(cstr.as_ptr() as *const _, &mut stat) == -1 {
            Err(Error::last_os_error())
        } else {
            Ok(stat)
        }
    }
}

pub fn free_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
    statvfs(path).map(|statvfs| statvfs.f_frsize as u64 * statvfs.f_bfree as u64)
}

pub fn available_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
    statvfs(path).map(|statvfs| statvfs.f_frsize as u64 * statvfs.f_bavail as u64)
}

pub fn total_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
    statvfs(path).map(|statvfs| statvfs.f_frsize as u64 * statvfs.f_blocks as u64)
}

pub fn allocation_granularity<P>(path: P) -> Result<u64> where P: AsRef<Path> {
    statvfs(path).map(|statvfs| statvfs.f_frsize as u64)
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    extern crate libc;

    use std::fs::{self, File};
    use std::os::unix::io::AsRawFd;

    use {FileExt, lock_contended_error};

    /// The duplicate method returns a file with a new file descriptor.
    #[test]
    fn duplicate_new_fd() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        assert!(file1.as_raw_fd() != file2.as_raw_fd());
    }

    /// The duplicate method should preservesthe close on exec flag.
    #[test]
    fn duplicate_cloexec() {

        fn flags(file: &File) -> libc::c_int {
            unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL, 0) }
        }

        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();

        assert_eq!(flags(&file1), flags(&file2));
    }

    /// Tests that locking a file descriptor will replace any existing locks
    /// held on the file descriptor.
    #[test]
    fn lock_replace() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Creating a shared lock will drop an exclusive lock.
        file1.lock_exclusive().unwrap();
        file1.lock_shared().unwrap();
        file2.lock_shared().unwrap();

        // Attempting to replace a shared lock with an exclusive lock will fail
        // with multiple lock holders, and remove the original shared lock.
        assert_eq!(file2.try_lock_exclusive().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());
        file1.lock_shared().unwrap();
    }

    /// Tests that locks are shared among duplicated file descriptors.
    #[test]
    fn lock_duplicate() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        let file3 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Create a lock through fd1, then replace it through fd2.
        file1.lock_shared().unwrap();
        file2.lock_exclusive().unwrap();
        assert_eq!(file3.try_lock_shared().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());

        // Either of the file descriptors should be able to unlock.
        file1.unlock().unwrap();
        file3.lock_shared().unwrap();
    }
}
<commit_msg>Fix compilation on Solaris/Illumos (#8)<commit_after>extern crate libc;

use std::ffi::CString;
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::Path;

pub fn duplicate(file: &File) -> Result<File> {
    unsafe {
        let fd = libc::dup(file.as_raw_fd());

        if fd < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(File::from_raw_fd(fd))
        }
    }
}

pub fn lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH)
}

pub fn lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX)
}

pub fn try_lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH | libc::LOCK_NB)
}

pub fn try_lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX | libc::LOCK_NB)
}

pub fn unlock(file: &File) -> Result<()> {
    flock(file, libc::LOCK_UN)
}

pub fn lock_error() -> Error {
    Error::from_raw_os_error(libc::EWOULDBLOCK)
}

fn flock(file: &File, flag: libc::c_int) -> Result<()> {
    let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
    if ret < 0 { Err(Error::last_os_error()) } else { Ok(()) }
}

pub fn allocated_size(file: &File) -> Result<u64> {
    file.metadata().map(|m| m.blocks() as u64 * 512)
}

#[cfg(any(target_os = "linux",
          target_os = "freebsd",
          target_os = "android",
          target_os = "nacl"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let ret = unsafe { libc::posix_fallocate(file.as_raw_fd(), 0, len as libc::off_t) };
    if ret == 0 { Ok(()) } else { Err(Error::last_os_error()) }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let stat = try!(file.metadata());

    if len > stat.blocks() as u64 * 512 {
        let mut fstore = libc::fstore_t {
            fst_flags: libc::F_ALLOCATECONTIG,
            fst_posmode: libc::F_PEOFPOSMODE,
            fst_offset: 0,
            fst_length: len as libc::off_t,
            fst_bytesalloc: 0,
        };

        let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
        if ret == -1 {
            // Unable to allocate contiguous disk space; attempt to allocate non-contiguously.
            fstore.fst_flags = libc::F_ALLOCATEALL;
            let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
            if ret == -1 {
                return Err(Error::last_os_error());
            }
        }
    }

    if len > stat.size() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

#[cfg(any(target_os = "openbsd",
          target_os = "netbsd",
          target_os = "dragonfly",
          target_os = "solaris"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    // No file allocation API available, just set the length if necessary.
    if len > try!(file.metadata()).len() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

fn statvfs<P>(path: P) -> Result<libc::statvfs> where P: AsRef<Path> {
    let cstr = match CString::new(path.as_ref().as_os_str().as_bytes()) {
        Ok(cstr) => cstr,
        Err(..) => return Err(Error::new(ErrorKind::InvalidInput, "path contained a null")),
    };

    unsafe {
        let mut stat: libc::statvfs = mem::zeroed();
        // danburkert/fs2-rs#1: cast is necessary for platforms where c_char != u8.
        if libc::statvfs(cstr.as_ptr() as *const _, &mut stat) == -1 {
            Err(Error::last_os_error())
        } else {
            Ok(stat)
        }
    }
}

pub fn free_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
    statvfs(path).map(|statvfs| statvfs.f_frsize as u64 * statvfs.f_bfree as u64)
}

pub fn available_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
    statvfs(path).map(|statvfs| statvfs.f_frsize as u64 * statvfs.f_bavail as u64)
}

pub fn total_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
    statvfs(path).map(|statvfs| statvfs.f_frsize as u64 * statvfs.f_blocks as u64)
}

pub fn allocation_granularity<P>(path: P) -> Result<u64> where P: AsRef<Path> {
    statvfs(path).map(|statvfs| statvfs.f_frsize as u64)
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    extern crate libc;

    use std::fs::{self, File};
    use std::os::unix::io::AsRawFd;

    use {FileExt, lock_contended_error};

    /// The duplicate method returns a file with a new file descriptor.
    #[test]
    fn duplicate_new_fd() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        assert!(file1.as_raw_fd() != file2.as_raw_fd());
    }

    /// The duplicate method should preservesthe close on exec flag.
    #[test]
    fn duplicate_cloexec() {

        fn flags(file: &File) -> libc::c_int {
            unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL, 0) }
        }

        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();

        assert_eq!(flags(&file1), flags(&file2));
    }

    /// Tests that locking a file descriptor will replace any existing locks
    /// held on the file descriptor.
    #[test]
    fn lock_replace() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Creating a shared lock will drop an exclusive lock.
        file1.lock_exclusive().unwrap();
        file1.lock_shared().unwrap();
        file2.lock_shared().unwrap();

        // Attempting to replace a shared lock with an exclusive lock will fail
        // with multiple lock holders, and remove the original shared lock.
        assert_eq!(file2.try_lock_exclusive().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());
        file1.lock_shared().unwrap();
    }

    /// Tests that locks are shared among duplicated file descriptors.
    #[test]
    fn lock_duplicate() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        let file3 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Create a lock through fd1, then replace it through fd2.
        file1.lock_shared().unwrap();
        file2.lock_exclusive().unwrap();
        assert_eq!(file3.try_lock_shared().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());

        // Either of the file descriptors should be able to unlock.
        file1.unlock().unwrap();
        file3.lock_shared().unwrap();
    }
}
<|endoftext|>


<commit_before>use super::NodeId;
use super::MutableNode;

///
/// A `Node` builder that provides more control over how a `Node` is created.
///
/// _Note: Right now `Node`s aren't that complicated, but they might get more complicated later.
/// This allows us to not break the `Node` API itself, while admittedly being a bit over-
/// engineered at this point in the development process._
///
pub struct NodeBuilder<T> {
    data: T,
    child_capacity: usize,
}

impl<T> NodeBuilder<T> {

    ///
    /// Creates a new `NodeBuilder` with the required data.
    ///
    /// ```
    /// use id_tree::NodeBuilder;
    ///
    /// let _node_builder = NodeBuilder::new(5);
    /// ```
    ///
    pub fn new(data: T) -> NodeBuilder<T> {
        NodeBuilder {
            data: data,
            child_capacity: 0,
        }
    }

    ///
    /// Set the child capacity of the `NodeBuilder`.
    ///
    /// ```
    /// use id_tree::NodeBuilder;
    ///
    /// let _node_builder = NodeBuilder::new(5).with_child_capacity(3);
    /// ```
    ///
    pub fn with_child_capacity(mut self, child_capacity: usize) -> NodeBuilder<T> {
        self.child_capacity = child_capacity;
        self
    }

    ///
    /// Build a `Node` based upon the current settings in the `NodeBuilder`.
    ///
    /// ```
    /// use id_tree::NodeBuilder;
    /// use id_tree::Node;
    ///
    /// let node: Node<i32> = NodeBuilder::new(5)
    ///         .with_child_capacity(3)
    ///         .build();
    ///
    /// assert_eq!(node.data(), &5);
    /// assert_eq!(node.children().capacity(), 3);
    /// ```
    ///
    pub fn build(self) -> Node<T> {
        Node {
            data: self.data,
            parent: None,
            children: Vec::with_capacity(self.child_capacity),
        }
    }
}

///
/// A container that wraps data in a given Tree.
///
pub struct Node<T> {
    data: T,
    parent: Option<NodeId>,
    children: Vec<NodeId>,
}

impl<T> Node<T> {
    ///
    /// Creates a new Node with the data provided.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let _one: Node<i32> = Node::new(1);
    /// ```
    ///
    pub fn new(data: T) -> Node<T> {
        NodeBuilder::new(data).build()
    }

    ///
    /// Returns an immutable reference to the data contained within the Node.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let node_three: Node<i32> = Node::new(3);
    /// let three = 3;
    ///
    /// assert_eq!(node_three.data(), &three);
    /// ```
    ///
    pub fn data(&self) -> &T {
        &self.data
    }

    ///
    /// Returns a mutable reference to the data contained within the Node.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let mut node_four: Node<i32> = Node::new(4);
    /// let mut four = 4;
    ///
    /// assert_eq!(node_four.data_mut(), &mut four);
    /// ```
    ///
    pub fn data_mut(&mut self) -> &mut T {
        &mut self.data
    }

    ///
    /// Returns a `Some` value containing the NodeId of this Node's parent if it exists; returns `None` if it does not.
    ///
    /// **Note:** A Node cannot have a parent until after it has been inserted into a Tree.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let five: Node<i32> = Node::new(5);
    ///
    /// assert!(five.parent().is_none());
    /// ```
    ///
    pub fn parent(&self) -> Option<NodeId> {
        self.parent
    }

    ///
    /// Returns an immutable reference to a Vec containing the NodeIds of this Node's children.
    ///
    /// **Note:** A Node cannot have any children until after it has been inserted into a Tree.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let six: Node<i32> = Node::new(6);
    ///
    /// assert_eq!(six.children().len(), 0);
    /// ```
    ///
    pub fn children(&self) -> &Vec<NodeId> {
        &self.children
    }
}

impl<T> MutableNode for Node<T> {
    fn set_parent(&mut self, parent: Option<NodeId>) {
        self.parent = parent;
    }

    fn add_child(&mut self, child: NodeId) {
        self.children.push(child);
    }

    fn children_mut(&mut self) -> &mut Vec<NodeId> {
        &mut self.children
    }
}

#[cfg(test)]
mod node_bulder_tests {
    use super::NodeBuilder;

    #[test]
    fn test_new() {
        let five = 5;
        let node = NodeBuilder::new(5).build();
        assert_eq!(node.data(), &five);
        assert_eq!(node.children.capacity(), 0);
    }

    #[test]
    fn test_with_child_capacity() {
        let five = 5;
        let node = NodeBuilder::new(5).with_child_capacity(10).build();
        assert_eq!(node.data(), &five);
        assert_eq!(node.children.capacity(), 10);
    }
}

#[cfg(test)]
mod node_tests {
    use super::Node;
    use super::super::NodeId;
    use super::super::snowflake::ProcessUniqueId;
    use super::super::MutableNode;

    #[test]
    fn test_new() {
        let node = Node::new(5);
        assert_eq!(node.children.capacity(), 0);
    }

    #[test]
    fn test_data() {
        let five = 5;
        let node = Node::new(five);
        assert_eq!(node.data(), &five);
    }

    #[test]
    fn test_data_mut() {
        let mut five = 5;
        let mut node = Node::new(five);
        assert_eq!(node.data_mut(), &mut five);
    }

    #[test]
    fn test_parent() {
        let mut node = Node::new(5);
        assert!(node.parent().is_none());

        let parent_id: NodeId = NodeId {
            tree_id: ProcessUniqueId::new(),
            index: 0,
        };

        node.set_parent(Some(parent_id));
        assert_eq!(node.parent(), Some(parent_id));
    }

    #[test]
    fn test_children() {
        let mut node = Node::new(5);
        assert_eq!(node.children().len(), 0);

        let child_id: NodeId = NodeId {
            tree_id: ProcessUniqueId::new(),
            index: 0,
        };
        node.add_child(child_id);

        assert_eq!(node.children().len(), 1);
        assert_eq!(node.children().get(0).unwrap(), &child_id);

        let mut node = Node::new(5);
        assert_eq!(node.children().len(), 0);

        let child_id: NodeId = NodeId {
            tree_id: ProcessUniqueId::new(),
            index: 0,
        };
        node.children_mut().push(child_id);

        assert_eq!(node.children().len(), 1);
        assert_eq!(node.children().get(0).unwrap(), &child_id);
    }
}
<commit_msg>make the Node documenation a bit more consistent with newer Tree docs.`<commit_after>use super::NodeId;
use super::MutableNode;

///
/// A `Node` builder that provides more control over how a `Node` is created.
///
pub struct NodeBuilder<T> {
    data: T,
    child_capacity: usize,
}

impl<T> NodeBuilder<T> {

    ///
    /// Creates a new `NodeBuilder` with the required data.
    ///
    /// ```
    /// use id_tree::NodeBuilder;
    ///
    /// let _node_builder = NodeBuilder::new(5);
    /// ```
    ///
    pub fn new(data: T) -> NodeBuilder<T> {
        NodeBuilder {
            data: data,
            child_capacity: 0,
        }
    }

    ///
    /// Set the child capacity of the `NodeBuilder`.
    ///
    /// As `Node`s are added to a `Tree`, parent and child references must be maintained. To do this,
    /// an allocation must be made every time a child is added to a `Node`.  Using this setting
    /// allows the `Node` to pre-allocate space for its children so that the allocations aren't made
    /// as children are added.
    ///
    /// _Use of this setting is recommended if you know the **maximum number** of children (not
    /// including grandchildren, great-grandchildren, etc.) that a `Node` will have **at any given
    /// time**_.
    ///
    /// ```
    /// use id_tree::NodeBuilder;
    ///
    /// let _node_builder = NodeBuilder::new(5).with_child_capacity(3);
    /// ```
    ///
    pub fn with_child_capacity(mut self, child_capacity: usize) -> NodeBuilder<T> {
        self.child_capacity = child_capacity;
        self
    }

    ///
    /// Build a `Node` based upon the current settings in the `NodeBuilder`.
    ///
    /// ```
    /// use id_tree::NodeBuilder;
    /// use id_tree::Node;
    ///
    /// let node: Node<i32> = NodeBuilder::new(5)
    ///         .with_child_capacity(3)
    ///         .build();
    ///
    /// assert_eq!(node.data(), &5);
    /// assert_eq!(node.children().capacity(), 3);
    /// ```
    ///
    pub fn build(self) -> Node<T> {
        Node {
            data: self.data,
            parent: None,
            children: Vec::with_capacity(self.child_capacity),
        }
    }
}

///
/// A container that wraps data in a given `Tree`.
///
pub struct Node<T> {
    data: T,
    parent: Option<NodeId>,
    children: Vec<NodeId>,
}

impl<T> Node<T> {
    ///
    /// Creates a new `Node` with the data provided.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let _one: Node<i32> = Node::new(1);
    /// ```
    ///
    pub fn new(data: T) -> Node<T> {
        NodeBuilder::new(data).build()
    }

    ///
    /// Returns an immutable reference to the data contained within the `Node`.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let node_three: Node<i32> = Node::new(3);
    /// let three = 3;
    ///
    /// assert_eq!(node_three.data(), &three);
    /// ```
    ///
    pub fn data(&self) -> &T {
        &self.data
    }

    ///
    /// Returns a mutable reference to the data contained within the `Node`.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let mut node_four: Node<i32> = Node::new(4);
    /// let mut four = 4;
    ///
    /// assert_eq!(node_four.data_mut(), &mut four);
    /// ```
    ///
    pub fn data_mut(&mut self) -> &mut T {
        &mut self.data
    }

    ///
    /// Returns a `Some` value containing the `NodeId` of this `Node`'s parent if it exists; returns `None` if it does not.
    ///
    /// **Note:** A `Node` cannot have a parent until after it has been inserted into a `Tree`.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let five: Node<i32> = Node::new(5);
    ///
    /// assert!(five.parent().is_none());
    /// ```
    ///
    pub fn parent(&self) -> Option<NodeId> {
        self.parent
    }

    ///
    /// Returns an immutable reference to a `Vec` containing the `NodeId`s of this `Node`'s children.
    ///
    /// **Note:** A `Node` cannot have any children until after it has been inserted into a `Tree`.
    ///
    /// ```
    /// use id_tree::Node;
    ///
    /// let six: Node<i32> = Node::new(6);
    ///
    /// assert_eq!(six.children().len(), 0);
    /// ```
    ///
    pub fn children(&self) -> &Vec<NodeId> {
        &self.children
    }
}

impl<T> MutableNode for Node<T> {
    fn set_parent(&mut self, parent: Option<NodeId>) {
        self.parent = parent;
    }

    fn add_child(&mut self, child: NodeId) {
        self.children.push(child);
    }

    fn children_mut(&mut self) -> &mut Vec<NodeId> {
        &mut self.children
    }
}

#[cfg(test)]
mod node_bulder_tests {
    use super::NodeBuilder;

    #[test]
    fn test_new() {
        let five = 5;
        let node = NodeBuilder::new(5).build();
        assert_eq!(node.data(), &five);
        assert_eq!(node.children.capacity(), 0);
    }

    #[test]
    fn test_with_child_capacity() {
        let five = 5;
        let node = NodeBuilder::new(5).with_child_capacity(10).build();
        assert_eq!(node.data(), &five);
        assert_eq!(node.children.capacity(), 10);
    }
}

#[cfg(test)]
mod node_tests {
    use super::Node;
    use super::super::NodeId;
    use super::super::snowflake::ProcessUniqueId;
    use super::super::MutableNode;

    #[test]
    fn test_new() {
        let node = Node::new(5);
        assert_eq!(node.children.capacity(), 0);
    }

    #[test]
    fn test_data() {
        let five = 5;
        let node = Node::new(five);
        assert_eq!(node.data(), &five);
    }

    #[test]
    fn test_data_mut() {
        let mut five = 5;
        let mut node = Node::new(five);
        assert_eq!(node.data_mut(), &mut five);
    }

    #[test]
    fn test_parent() {
        let mut node = Node::new(5);
        assert!(node.parent().is_none());

        let parent_id: NodeId = NodeId {
            tree_id: ProcessUniqueId::new(),
            index: 0,
        };

        node.set_parent(Some(parent_id));
        assert_eq!(node.parent(), Some(parent_id));
    }

    #[test]
    fn test_children() {
        let mut node = Node::new(5);
        assert_eq!(node.children().len(), 0);

        let child_id: NodeId = NodeId {
            tree_id: ProcessUniqueId::new(),
            index: 0,
        };
        node.add_child(child_id);

        assert_eq!(node.children().len(), 1);
        assert_eq!(node.children().get(0).unwrap(), &child_id);

        let mut node = Node::new(5);
        assert_eq!(node.children().len(), 0);

        let child_id: NodeId = NodeId {
            tree_id: ProcessUniqueId::new(),
            index: 0,
        };
        node.children_mut().push(child_id);

        assert_eq!(node.children().len(), 1);
        assert_eq!(node.children().get(0).unwrap(), &child_id);
    }
}
<|endoftext|>


<commit_before>use ::*;
use relm::{Relm, RemoteRelm, Widget};
use gtk::prelude::*;
use gtk::{Window, WindowType, DrawingArea, Button};
use gtk::{FileChooserDialog};
use tokio_core::reactor::Interval;
use std::time::Duration;

use map::*;

#[derive(Clone)]
pub struct MyModel {
    map: Map,
    size: Pos,
    center: Pos,
    scale: i32,
    mouse: Option<Pos>,
}
impl MyModel {
    fn new() -> Self {
        MyModel {
            map: Map::acorn(),
            size: pos(250, 250),
            center: pos(0, 0),
            scale: 2,
            mouse: None,
        }
    }
}

#[derive(SimpleMsg)]
pub enum MyMsg {
    Motion(((f64, f64), gdk::ModifierType)),
    Save,
    Open,
    Tick,
    Quit,
}

#[derive(Clone)]
pub struct Win {
    hbox: gtk::Box,
    button_box: gtk::ButtonBox,
    open_button: Button,
    save_button: Button,
    area: DrawingArea,
    window: Window,
}

impl Win {
    fn draw(&mut self, cells: Vec<&Pos>, model: &MyModel, top_left: &Pos) {
        use gdk::prelude::ContextExt;
        let cr = cairo::Context::create_from_window(&self.area.get_window().unwrap());
        cr.set_source_rgb(1., 1., 1.);
        cr.paint();
        cr.scale(model.scale as f64, model.scale as f64);
        cr.set_source_rgb(0., 0., 0.);
        for pos in cells {
            cr.rectangle((pos.x - top_left.x) as f64, (pos.y - top_left.y) as f64, 1., 1.);
        }
        cr.fill();
    }
}

impl Widget for Win {
    type Root = Window;
    type Model = MyModel;
    type ModelParam = ();
    type Msg = MyMsg;

    fn root(&self) -> &Self::Root {
        &self.window
    }

    fn model(_: ()) -> MyModel {
        MyModel::new()
    }

    fn subscriptions(relm: &Relm<MyMsg>) {
        let stream = Interval::new(Duration::from_millis(30), relm.handle()).unwrap();
        relm.connect_exec_ignore_err(stream, MyMsg::Tick);
    }

    fn update(&mut self, event: MyMsg, model: &mut MyModel) {
        match event {
            MyMsg::Tick => {
                model.map.next_generation();
                let top_left = pos(model.center.x - model.size.x / 2, model.center.y - model.size.y / 2);
                let cells = model.map.get_alive_cells_in(top_left, model.size);
                self.draw(cells, model, &top_left);
            },
            MyMsg::Save => {
                let dialog = FileChooserDialog::new(
                    Some("Save File"),
                    Some(&self.window),
                    gtk::FileChooserAction::Save);
                let cancel: i32 = gtk::ResponseType::Cancel.into();
                let accept: i32 = gtk::ResponseType::Accept.into();
                dialog.add_button("Cancel", cancel);
                dialog.add_button("Save", accept);
                if accept == dialog.run() {
                    if let Some(path) = dialog.get_filename() {
                        model.map.save(path).unwrap();
                    }
                }
                dialog.close();
            },
            MyMsg::Open => {
                let dialog = FileChooserDialog::new(
                    Some("Open File"),
                    Some(&self.window),
                    gtk::FileChooserAction::Open);
                let cancel: i32 = gtk::ResponseType::Cancel.into();
                let accept: i32 = gtk::ResponseType::Accept.into();
                dialog.add_button("Cancel", cancel);
                dialog.add_button("Open", accept);
                if accept == dialog.run() {
                    if let Some(path) = dialog.get_filename() {
                        model.map = Map::open(path).unwrap();
                    }
                }
                dialog.close();
            },
            MyMsg::Motion(((x, y), t)) => {
                let p = pos(x as i32, y as i32);
                if (t & gdk::BUTTON1_MASK).bits() != 0 {
                    if model.mouse != None {
                        let mut old_pos = model.mouse.unwrap();
                        let new_center = pos(
                            model.center.x + (old_pos.x - p.x) / model.scale,
                            model.center.y + (old_pos.y - p.y) / model.scale
                            );
                        if new_center.x != model.center.x {
                            old_pos.x = p.x;
                        }
                        if new_center.y != model.center.y {
                            old_pos.y = p.y;
                        }
                        model.center = new_center;
                        model.mouse = Some(old_pos);
                    } else {
                        model.mouse = Some(p);
                    }
                } else {
                    model.mouse = None;
                }
            },
            MyMsg::Quit => gtk::main_quit(),
        }
    }

    fn view(relm: &RemoteRelm<Self>, model: &MyModel) -> Self {
        let window = Window::new(WindowType::Toplevel);
        let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        let button_box = gtk::ButtonBox::new(gtk::Orientation::Vertical);
        let open_button = Button::new_with_label("Open");
        let save_button = Button::new_with_label("Save");
        let area = DrawingArea::new();
        area.set_size_request(model.size.x * model.scale, model.size.y * model.scale);
        area.set_events(area.get_events() | gdk::POINTER_MOTION_MASK.bits() as i32);
        area.set_events(area.get_events() | gdk::BUTTON_PRESS_MASK.bits() as i32);
        button_box.set_layout(gtk::ButtonBoxStyle::Start);

        button_box.pack_start(&open_button, false, false, 0);
        button_box.pack_start(&save_button, false, false, 0);
        hbox.pack_start(&area, false, false, 0);
        hbox.pack_start(&button_box, false, false, 0);
        window.add(&hbox);
        window.set_title("Game of Life");
        window.show_all();

        connect!(relm, window, connect_delete_event(_, _) (Some(MyMsg::Quit), Inhibit(false)));
        connect!(relm, area, connect_motion_notify_event(_, ev) (Some(MyMsg::Motion((ev.get_position(), ev.get_state()))), Inhibit(false)));
        connect!(relm, save_button, connect_clicked(_), MyMsg::Save);
        connect!(relm, open_button, connect_clicked(_), MyMsg::Open);

        Win {
            hbox: hbox,
            button_box: button_box,
            open_button: open_button,
            save_button: save_button,
            area: area,
            window: window,
        }
    }
}
<commit_msg>Shorten code<commit_after>use ::*;
use relm::{Relm, RemoteRelm, Widget};
use gtk::prelude::*;
use gtk::{Window, WindowType, DrawingArea, Button};
use gtk::{FileChooserDialog};
use tokio_core::reactor::Interval;
use std::time::Duration;

use map::*;

#[derive(Clone)]
pub struct MyModel {
    map: Map,
    size: Pos,
    center: Pos,
    scale: i32,
    mouse: Option<Pos>,
}
impl MyModel {
    fn new() -> Self {
        MyModel {
            map: Map::acorn(),
            size: pos(250, 250),
            center: pos(0, 0),
            scale: 2,
            mouse: None,
        }
    }
}

#[derive(SimpleMsg)]
pub enum MyMsg {
    Motion(((f64, f64), gdk::ModifierType)),
    Save,
    Open,
    Tick,
    Quit,
}

#[derive(Clone)]
pub struct Win {
    hbox: gtk::Box,
    button_box: gtk::ButtonBox,
    open_button: Button,
    save_button: Button,
    area: DrawingArea,
    window: Window,
}

impl Win {
    fn draw(&mut self, cells: Vec<&Pos>, model: &MyModel, top_left: &Pos) {
        use gdk::prelude::ContextExt;
        let cr = cairo::Context::create_from_window(&self.area.get_window().unwrap());
        cr.set_source_rgb(1., 1., 1.);
        cr.paint();
        cr.scale(model.scale as f64, model.scale as f64);
        cr.set_source_rgb(0., 0., 0.);
        for pos in cells {
            cr.rectangle((pos.x - top_left.x) as f64, (pos.y - top_left.y) as f64, 1., 1.);
        }
        cr.fill();
    }
}

impl Widget for Win {
    type Root = Window;
    type Model = MyModel;
    type ModelParam = ();
    type Msg = MyMsg;

    fn root(&self) -> &Self::Root {
        &self.window
    }

    fn model(_: ()) -> MyModel {
        MyModel::new()
    }

    fn subscriptions(relm: &Relm<MyMsg>) {
        let stream = Interval::new(Duration::from_millis(30), relm.handle()).unwrap();
        relm.connect_exec_ignore_err(stream, MyMsg::Tick);
    }

    fn update(&mut self, event: MyMsg, model: &mut MyModel) {
        use self::MyMsg::*;
        match event {
            Tick => {
                model.map.next_generation();
                let top_left = pos(model.center.x - model.size.x / 2, model.center.y - model.size.y / 2);
                let cells = model.map.get_alive_cells_in(top_left, model.size);
                self.draw(cells, model, &top_left);
            },
            Save => {
                let dialog = FileChooserDialog::new(
                    Some("Save File"),
                    Some(&self.window),
                    gtk::FileChooserAction::Save);
                let cancel: i32 = gtk::ResponseType::Cancel.into();
                let accept: i32 = gtk::ResponseType::Accept.into();
                dialog.add_button("Cancel", cancel);
                dialog.add_button("Save", accept);
                if accept == dialog.run() {
                    if let Some(path) = dialog.get_filename() {
                        model.map.save(path).unwrap();
                    }
                }
                dialog.close();
            },
            Open => {
                let dialog = FileChooserDialog::new(
                    Some("Open File"),
                    Some(&self.window),
                    gtk::FileChooserAction::Open);
                let cancel: i32 = gtk::ResponseType::Cancel.into();
                let accept: i32 = gtk::ResponseType::Accept.into();
                dialog.add_button("Cancel", cancel);
                dialog.add_button("Open", accept);
                if accept == dialog.run() {
                    if let Some(path) = dialog.get_filename() {
                        model.map = Map::open(path).unwrap();
                    }
                }
                dialog.close();
            },
            Motion(((x, y), t)) => {
                let p = pos(x as i32, y as i32);
                if (t & gdk::BUTTON1_MASK).bits() != 0 {
                    if model.mouse != None {
                        let mut old_pos = model.mouse.unwrap();
                        let new_center = pos(
                            model.center.x + (old_pos.x - p.x) / model.scale,
                            model.center.y + (old_pos.y - p.y) / model.scale
                            );
                        if new_center.x != model.center.x {
                            old_pos.x = p.x;
                        }
                        if new_center.y != model.center.y {
                            old_pos.y = p.y;
                        }
                        model.center = new_center;
                        model.mouse = Some(old_pos);
                    } else {
                        model.mouse = Some(p);
                    }
                } else {
                    model.mouse = None;
                }
            },
            Quit => gtk::main_quit(),
        }
    }

    fn view(relm: &RemoteRelm<Self>, model: &MyModel) -> Self {
        let window = Window::new(WindowType::Toplevel);
        let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        let button_box = gtk::ButtonBox::new(gtk::Orientation::Vertical);
        let open_button = Button::new_with_label("Open");
        let save_button = Button::new_with_label("Save");
        let area = DrawingArea::new();
        area.set_size_request(model.size.x * model.scale, model.size.y * model.scale);
        area.set_events(area.get_events() | gdk::POINTER_MOTION_MASK.bits() as i32);
        area.set_events(area.get_events() | gdk::BUTTON_PRESS_MASK.bits() as i32);
        button_box.set_layout(gtk::ButtonBoxStyle::Start);

        button_box.pack_start(&open_button, false, false, 0);
        button_box.pack_start(&save_button, false, false, 0);
        hbox.pack_start(&area, false, false, 0);
        hbox.pack_start(&button_box, false, false, 0);
        window.add(&hbox);
        window.set_title("Game of Life");
        window.show_all();

        use self::MyMsg::*;
        connect!(relm, window, connect_delete_event(_, _) (Some(Quit), Inhibit(false)));
        connect!(relm, area, connect_motion_notify_event(_, ev) (Some(Motion((ev.get_position(), ev.get_state()))), Inhibit(false)));
        connect!(relm, save_button, connect_clicked(_), Save);
        connect!(relm, open_button, connect_clicked(_), Open);

        Win {
            hbox: hbox,
            button_box: button_box,
            open_button: open_button,
            save_button: save_button,
            area: area,
            window: window,
        }
    }
}
<|endoftext|>