<commit_before>use meta::sum::Sum;
use meta::prod::Prod;

pub trait ContinuationSet {
    type Emit;
    type Continue: State;
    type Head: Prod<Left = Self::Emit, Right = Self::Continue>;
    type Suspend: ContinuationSet;
    type Output: Sum<Left = Self::Head, Right = <Self::Suspend as ContinuationSet>::Output>;
}

impl ContinuationSet for ! {
    type Emit = !;
    type Continue = !;
    type Head = !;
    type Suspend = !;
    type Output = !;
}

pub trait State {
    type Input;
    type Exit;
    type Next: ContinuationSet;
    type Transition: Sum<Left = <Self::Next as ContinuationSet>::Output, Right = Self::Exit>;

    fn send(self, i: Self::Input) -> Self::Transition;
}

impl State for ! {
    type Input = !;
    type Exit = !;
    type Next = !;
    type Transition = !;

    fn send(self, _: Self::Input) -> Self::Transition {
        unreachable!()
    }
}

#[cfg(test)]
mod tests {

    #![macro_use]

    use super::{State, ContinuationSet};
    use meta::enums::Match::*;
    use meta::enums::Match;
    use meta::sum::{Sum, Either};
    use std::marker::PhantomData;
    use meta::prod::Prod;
    use std::fmt;

    pub enum Transition<Next, Exit>
    {
        Next(Next),
        Exit(Exit),
    }

    impl<Next, Exit> Sum for Transition<Next, Exit>
    {
        type Left = Next;
        type Right = Exit;

        fn to_canonical(self) -> Either<Self::Left, Self::Right> {
            match self {
                Transition::Next(next) => Either::Left(next),
                Transition::Exit(exit) => Either::Right(exit),
            }
        }
    }

    pub struct MatchContinuation<C>(PhantomData<C>);

    impl ContinuationSet for MatchContinuation<!> {
        type Emit = !;
        type Continue = !;
        type Head = !;
        type Suspend = !;
        type Output = !;
    }

    impl<H, T> ContinuationSet for MatchContinuation<Match<H, T>>
        where H: Prod,
              H::Right: State,
              MatchContinuation<T>: ContinuationSet
    {
        type Emit = H::Left;
        type Continue = H::Right;
        type Head = H;
        type Suspend = MatchContinuation<T>;
        type Output = Match<Self::Head, <Self::Suspend as ContinuationSet>::Output>;
    }

    struct TooSmall;

    impl fmt::Display for TooSmall {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "Too small!")
        }
    }

    struct TooBig;

    impl fmt::Display for TooBig {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "Too big!")
        }
    }

    struct Correct;

    impl fmt::Display for Correct {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "You guessed it!")
        }
    }

    struct Guess(i64);

    impl State for Guess {
        type Input = i64;
        type Exit = Correct;

        type Next = MatchContinuation<enums![(TooSmall, Guess), (TooBig, Guess)]>;
        type Transition = Transition<<Self::Next as ContinuationSet>::Output, Self::Exit>;

        fn send(self, i: Self::Input) -> Self::Transition {
            if self.0 == i {
                Transition::Exit(Correct {})
            } else if i < self.0 {
                Transition::Next(Variant((TooSmall {}, self)))
            } else {
                Transition::Next(Next(Variant((TooBig {}, self))))
            }
        }
    }

    struct Game;

    impl State for Game {
        type Input = i64;
        type Exit = !;

        type Next = MatchContinuation<enums![((), Guess)]>;
        type Transition = Transition<<Self::Next as ContinuationSet>::Output, Self::Exit>;

        fn send(self, secret: Self::Input) -> Self::Transition {
            Transition::Next(Variant(((), Guess(secret))))
        }
    }

    struct Quit;

    impl fmt::Display for Quit {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "I quit!")
        }
    }

    struct Maverick;

    impl State for Maverick {
        type Input = (i64, i64);
        type Exit = Quit;

        type Next = MatchContinuation<enums![(i64, Strategist)]>;
        type Transition = Transition<<Self::Next as ContinuationSet>::Output, Self::Exit>;

        fn send(self, range: Self::Input) -> Self::Transition {
            if range.0 > range.1 {
                Transition::Exit(Quit {})
            } else {
                let guess = (range.0 + range.1) / 2;
                Transition::Next(Variant((guess, Strategist(range.0, guess, range.1))))
            }
        }
    }

    struct Strategist(i64, i64, i64);

    impl State for Strategist {
        type Input = enums![TooSmall, TooBig];
        type Exit = Quit;

        type Next = MatchContinuation<enums![(i64, Strategist)]>;
        type Transition = Transition<<Self::Next as ContinuationSet>::Output, Self::Exit>;

        fn send(self, result: Self::Input) -> Self::Transition {
            let mav = Maverick {};
            let range = match result {
                Variant(TooSmall) => (self.1 + 1, self.2),
                Next(Variant(TooBig)) => (self.0, self.1 - 1),
            };
            mav.send(range)
        }
    }

    /*
    fn play_guess(secret: i64) {
        guess = yield;
        loop {
            guess = if guess == secret {
                return Correct;
            } else if guess < secret {
                yield TooSmall;
            } else {
                yield TooBig;
            }
        }
    }

    fn player(lo: i64, hi: i64) {
        while lo >= hi {
            let med = (lo + hi) / 2;
            let res = yield med;
            match res {
                TooBig => hi = med - 1,
                TooSmall => lo = med + 1
            }
        }
        return Quit;
    }
    */

    #[test]
    fn guess() {
        let game = Game {};

        let game = match game.send(16) {
            Transition::Next(Variant(((), game))) => game,
        };

        let suspend = match game.send(10) {
            Transition::Next(suspend) => suspend,
            _ => panic!("We didn't guess the number, so the coroutine should not return yet!"),
        };

        let game = match suspend {
            Variant((msg, guess_again)) => {
                assert_eq!(msg.to_string(), "Too small!");
                guess_again
            }
            _ => panic!("The number was clearly too small! Why do you not see this?"),
        };

        let suspend = match game.send(19) {
            Transition::Next(suspend) => suspend,
            _ => panic!("We didn't guess the number, so the coroutine should not return yet!"),
        };

        let game = match suspend {
            Next(Variant((msg, guess_again))) => {
                assert_eq!(msg.to_string(), "Too big!");
                guess_again
            }
            _ => panic!("The number was clearly too big! Why do you not see this?"),
        };

        match game.send(16) {
            Transition::Exit(msg) => assert_eq!(msg.to_string(), "You guessed it!"),
            _ => panic!("The answer was correct! The coroutine should return."),
        }
    }

    enum Finish<G, P> {
        GameWon(G),
        PlayerGaveUp(P),
    }

    fn play(secret: i64, lo: i64, hi: i64) -> Finish<Correct, Quit> {
        let (_, mut game) = match (Game {}.send(secret)) {
            Transition::Next(Variant(s)) => s,
        };

        let player = Maverick {};

        let (mut guess, mut player) = match player.send((lo, hi)) {
            Transition::Next(Variant(x)) => x,
            Transition::Exit(_) => panic!("The range is valid, why did you return?"),
        };

        loop {
            let (result, game_) = match game.send(guess) {
                Transition::Next(Variant((r, g))) => (Variant(r), g),
                Transition::Next(Next(Variant((r, g)))) => (Next(Variant(r)), g),
                Transition::Exit(r) => break Finish::GameWon(r),
            };
            game = game_;
            let (guess_, player_) = match player.send(result) {
                Transition::Next(Variant(s)) => s,
                Transition::Exit(r) => break Finish::PlayerGaveUp(r),
            };
            guess = guess_;
            player = player_;
        }
    }

    #[test]
    fn solo() {
        let msg = match play(75, 10, 100) {
            Finish::GameWon(e) => e.to_string(),
            Finish::PlayerGaveUp(q) => q.to_string(),
        };
        assert_eq!(msg, "You guessed it!");

        let msg = match play(5, 10, 100) {
            Finish::GameWon(e) => e.to_string(),
            Finish::PlayerGaveUp(q) => q.to_string(),
        };
        assert_eq!(msg, "I quit!");
    }
}
<commit_msg>Constrain State::Transition: ContinuationSet<commit_after>use meta::sum::Sum;
use meta::prod::Prod;

/// Computes the types of possible continuations from the current state of a fsm
///
/// A continuation is a pair-like (product) type of an emitted value and a new state. While the
/// emitted value can be anything, the new state must implement the State trait. A continuation
/// comes about when a state transitions into another as a result of a call to `State::send`.
///
/// The ContinuationSet computes a list of these continuation types as an enumeration.
pub trait ContinuationSet {
    /// The type to be emitted if the current continuation transition is activated
    type Emit;
    /// The type of the new state the fsm transitions into if the current continuation transition is
    /// activated
    type Continue: State;
    /// The type of the continuation resulting from the activation of the current continuation
    /// transition
    type Head: Prod<Left = Self::Emit, Right = Self::Continue>;
    /// The type of ContinuationSet to be used for computing the continuations resulting from the
    /// activation of the subsequent continuation transitions
    type Suspend: ContinuationSet;
    /// The discriminated union type used for holding one of the continuations
    type Output: Sum<Left = Self::Head, Right = <Self::Suspend as ContinuationSet>::Output>;
}

/// The never type can be used to show that there are no ensuing continuations
impl ContinuationSet for ! {
    type Emit = !;
    type Continue = !;
    type Head = !;
    type Suspend = !;
    type Output = !;
}

/// Models a fsm state
///
/// A state in a fsm has the sole characteristic that it can transition either into a value and a
/// child state or into a final exit value. The transition is triggered by the receival of an input
/// value.
pub trait State {
    /// The type of the input value which triggers the transition
    type Input;
    /// Tye type of the exit value that this state may transition into
    type Exit;
    /// This type models two concepts:
    ///  - The ContinuationSet of this state
    ///  - The result of a transition, which is a Sum type between the output of the
    ///  ContinuationSet and the Exit type
    type Transition: ContinuationSet + Sum<Left = <Self::Transition as ContinuationSet>::Output, Right = Self::Exit>;

    /// Implements the state transition
    fn send(self, i: Self::Input) -> Self::Transition;
}

/// The never type trivially represents a state which cannot be reached
impl State for ! {
    type Input = !;
    type Exit = !;
    type Transition = !;

    fn send(self, _: Self::Input) -> Self::Transition {
        unreachable!()
    }
}

#[cfg(test)]
mod tests {

    #![macro_use]

    use super::{State, ContinuationSet};
    use meta::enums::Match::*;
    use meta::enums::Match;
    use meta::sum::{Sum, Either};
    use std::marker::PhantomData;
    use meta::prod::Prod;
    use std::fmt;

    pub enum Transition<Next, Exit>
    {
        Next(Next),
        Exit(Exit),
    }

    impl<Next, Exit> Sum for Transition<Next, Exit>
    {
        type Left = Next;
        type Right = Exit;

        fn to_canonical(self) -> Either<Self::Left, Self::Right> {
            match self {
                Transition::Next(next) => Either::Left(next),
                Transition::Exit(exit) => Either::Right(exit),
            }
        }
    }

    pub struct MatchContinuation<C>(PhantomData<C>);

    impl ContinuationSet for MatchContinuation<!> {
        type Emit = !;
        type Continue = !;
        type Head = !;
        type Suspend = !;
        type Output = !;
    }

    impl<H, T> ContinuationSet for MatchContinuation<Match<H, T>>
        where H: Prod,
              H::Right: State,
              MatchContinuation<T>: ContinuationSet
    {
        type Emit = H::Left;
        type Continue = H::Right;
        type Head = H;
        type Suspend = MatchContinuation<T>;
        type Output = Match<Self::Head, <Self::Suspend as ContinuationSet>::Output>;
    }

    impl<N, E> ContinuationSet for Transition<N, E>
        where MatchContinuation<N>: ContinuationSet
    {
        type Emit = <MatchContinuation<N> as ContinuationSet>::Emit;
        type Continue = <MatchContinuation<N> as ContinuationSet>::Continue;
        type Head = <MatchContinuation<N> as ContinuationSet>::Head;
        type Suspend = <MatchContinuation<N> as ContinuationSet>::Suspend;
        type Output = <MatchContinuation<N> as ContinuationSet>::Output;
    }

    struct TooSmall;

    impl fmt::Display for TooSmall {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "Too small!")
        }
    }

    struct TooBig;

    impl fmt::Display for TooBig {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "Too big!")
        }
    }

    struct Correct;

    impl fmt::Display for Correct {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "You guessed it!")
        }
    }

    struct Guess(i64);

    impl State for Guess {
        type Input = i64;
        type Exit = Correct;

        type Transition = Transition<enums![(TooSmall, Guess), (TooBig, Guess)], Self::Exit>;

        fn send(self, i: Self::Input) -> Self::Transition {
            if self.0 == i {
                Transition::Exit(Correct {})
            } else if i < self.0 {
                Transition::Next(Variant((TooSmall {}, self)))
            } else {
                Transition::Next(Next(Variant((TooBig {}, self))))
            }
        }
    }

    struct Game;

    impl State for Game {
        type Input = i64;
        type Exit = !;

        type Transition = Transition<enums![((), Guess)], Self::Exit>;

        fn send(self, secret: Self::Input) -> Self::Transition {
            Transition::Next(Variant(((), Guess(secret))))
        }
    }

    struct Quit;

    impl fmt::Display for Quit {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "I quit!")
        }
    }

    struct Maverick;

    impl State for Maverick {
        type Input = (i64, i64);
        type Exit = Quit;

        type Transition = Transition<enums![(i64, Strategist)], Self::Exit>;

        fn send(self, range: Self::Input) -> Self::Transition {
            if range.0 > range.1 {
                Transition::Exit(Quit {})
            } else {
                let guess = (range.0 + range.1) / 2;
                Transition::Next(Variant((guess, Strategist(range.0, guess, range.1))))
            }
        }
    }

    struct Strategist(i64, i64, i64);

    impl State for Strategist {
        type Input = enums![TooSmall, TooBig];
        type Exit = Quit;

        type Transition = Transition<enums![(i64, Strategist)], Self::Exit>;

        fn send(self, result: Self::Input) -> Self::Transition {
            let mav = Maverick {};
            let range = match result {
                Variant(TooSmall) => (self.1 + 1, self.2),
                Next(Variant(TooBig)) => (self.0, self.1 - 1),
            };
            mav.send(range)
        }
    }

    /*
    fn play_guess(secret: i64) {
        guess = yield;
        loop {
            guess = if guess == secret {
                return Correct;
            } else if guess < secret {
                yield TooSmall;
            } else {
                yield TooBig;
            }
        }
    }

    fn player(lo: i64, hi: i64) {
        while lo >= hi {
            let med = (lo + hi) / 2;
            let res = yield med;
            match res {
                TooBig => hi = med - 1,
                TooSmall => lo = med + 1
            }
        }
        return Quit;
    }
    */

    #[test]
    fn guess() {
        let game = Game {};

        let game = match game.send(16) {
            Transition::Next(Variant(((), game))) => game,
        };

        let suspend = match game.send(10) {
            Transition::Next(suspend) => suspend,
            _ => panic!("We didn't guess the number, so the coroutine should not return yet!"),
        };

        let game = match suspend {
            Variant((msg, guess_again)) => {
                assert_eq!(msg.to_string(), "Too small!");
                guess_again
            }
            _ => panic!("The number was clearly too small! Why do you not see this?"),
        };

        let suspend = match game.send(19) {
            Transition::Next(suspend) => suspend,
            _ => panic!("We didn't guess the number, so the coroutine should not return yet!"),
        };

        let game = match suspend {
            Next(Variant((msg, guess_again))) => {
                assert_eq!(msg.to_string(), "Too big!");
                guess_again
            }
            _ => panic!("The number was clearly too big! Why do you not see this?"),
        };

        match game.send(16) {
            Transition::Exit(msg) => assert_eq!(msg.to_string(), "You guessed it!"),
            _ => panic!("The answer was correct! The coroutine should return."),
        }
    }

    enum Finish<G, P> {
        GameWon(G),
        PlayerGaveUp(P),
    }

    fn play(secret: i64, lo: i64, hi: i64) -> Finish<Correct, Quit> {
        let (_, mut game) = match (Game {}.send(secret)) {
            Transition::Next(Variant(s)) => s,
        };

        let player = Maverick {};

        let (mut guess, mut player) = match player.send((lo, hi)) {
            Transition::Next(Variant(x)) => x,
            Transition::Exit(_) => panic!("The range is valid, why did you return?"),
        };

        loop {
            let (result, game_) = match game.send(guess) {
                Transition::Next(Variant((r, g))) => (Variant(r), g),
                Transition::Next(Next(Variant((r, g)))) => (Next(Variant(r)), g),
                Transition::Exit(r) => break Finish::GameWon(r),
            };
            game = game_;
            let (guess_, player_) = match player.send(result) {
                Transition::Next(Variant(s)) => s,
                Transition::Exit(r) => break Finish::PlayerGaveUp(r),
            };
            guess = guess_;
            player = player_;
        }
    }

    #[test]
    fn solo() {
        let msg = match play(75, 10, 100) {
            Finish::GameWon(e) => e.to_string(),
            Finish::PlayerGaveUp(q) => q.to_string(),
        };
        assert_eq!(msg, "You guessed it!");

        let msg = match play(5, 10, 100) {
            Finish::GameWon(e) => e.to_string(),
            Finish::PlayerGaveUp(q) => q.to_string(),
        };
        assert_eq!(msg, "I quit!");
    }
}
<|endoftext|>


<commit_before>use crate::error::{Error, ErrorKind, Result};
use std::ffi::OsStr;
use std::fmt::Debug;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;

pub struct Git<'a> {
    command: &'a str,
    cwd: &'a Path,
}

impl<'a> Git<'a> {
    pub fn command<S: AsRef<OsStr> + Debug>(&self, args: &[S]) -> Result<String> {
        let out = Command::new(&self.command)
            .arg("-C")
            .arg(self.cwd)
            .args(args)
            .output()?;
        if out.status.success() {
            Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
        } else {
            Error::err(ErrorKind::GitCommandError {
                stderr: String::from_utf8_lossy(&out.stderr)
                    .trim()
                    .replace('\n', " "),
                args: args.iter().map(|a| a.as_ref().to_owned()).collect(),
            })
        }
    }

    pub fn hash(&self, commit: impl AsRef<str>) -> Result<String> {
        self.command(&["rev-parse", commit.as_ref()])
            .map_err(|err| {
                Error::new(ErrorKind::GitObjectNotFound {
                    kind: "commit",
                    object: commit.as_ref().to_string(),
                    msg: format!("{}", err),
                })
            })
    }

    pub fn tag_hash(&self, tagname: impl AsRef<str>) -> Result<String> {
        let tagname = tagname.as_ref();
        let stdout = self
            .command(&["show-ref", "--tags", tagname])
            .map_err(|err| {
                Error::new(ErrorKind::GitObjectNotFound {
                    kind: "tag name",
                    object: tagname.to_string(),
                    msg: format!("{}", err),
                })
            })?;
        // Output must be in format '{rev} {ref name}'
        Ok(stdout.splitn(2, ' ').next().unwrap().to_string())
    }

    pub fn remote_url(&self, name: impl AsRef<str>) -> Result<String> {
        // XXX:
        // `git remote get-url {name}` is not available because it's added recently (at 2.6.1).
        // Note that git installed in Ubuntu 14.04 is 1.9.1.
        let name = name.as_ref();
        self.command(&["config", "--get", &format!("remote.{}.url", name)])
            .map_err(|err| {
                Error::new(ErrorKind::GitObjectNotFound {
                    kind: "remote",
                    object: name.to_string(),
                    msg: format!("{}", err),
                })
            })
    }

    pub fn tracking_remote_url(
        &self,
        branch: &Option<impl AsRef<str>>,
    ) -> Result<(String, String)> {
        let rev = match branch {
            Some(b) => format!("{}@{{u}}", b.as_ref()),
            None => "@{u}".to_string(),
        };

        let out = match self.command(&["rev-parse", "--abbrev-ref", "--symbolic", rev.as_str()]) {
            Ok(stdout) => stdout,
            Err(err) => match err.kind() {
                ErrorKind::GitCommandError { ref stderr, .. }
                    if stderr.contains("does not point to a branch") =>
                {
                    return Ok((self.remote_url("origin")?, "origin".to_string()));
                }
                _ => return Err(err),
            },
        };

        // out is formatted as '{remote-name}/{branch-name}'
        match out.splitn(2, '/').next() {
            Some(u) => Ok((self.remote_url(u)?, u.to_string())),
            None => Error::err(ErrorKind::UnexpectedRemoteName(out.clone())),
        }
    }

    pub fn root_dir(&self) -> Result<PathBuf> {
        match self.command(&["rev-parse", "--show-toplevel"]) {
            Ok(stdout) => Ok(fs::canonicalize(stdout)?),
            Err(err) => match err.kind() {
                ErrorKind::GitCommandError { stderr, .. } => {
                    Error::err(ErrorKind::GitRootDirNotFound {
                        cwd: self.cwd.to_owned(),
                        stderr: stderr.clone(),
                    })
                }
                _ => Err(err),
            },
        }
    }

    pub fn current_branch(&self) -> Result<String> {
        self.command(&["rev-parse", "--abbrev-ref", "--symbolic", "HEAD"])
    }

    pub fn remote_contains(
        &self,
        spec: impl AsRef<str>,
        remote_branch: impl AsRef<str>,
    ) -> Result<bool> {
        self.command(&[
            "branch",
            "--remote",
            remote_branch.as_ref(),
            "--contains",
            spec.as_ref(),
        ])
        .map(|o| !o.is_empty())
    }

    // Returns {remote}/{branch}
    pub fn remote_branch(
        &self,
        remote_name: &Option<impl AsRef<str>>,
        local_branch: &Option<impl AsRef<str>>,
    ) -> Result<String> {
        let remote = match remote_name {
            Some(r) => r.as_ref(),
            None => "{u}",
        };
        let branch = match local_branch {
            Some(b) => b.as_ref(),
            None => "",
        };
        let rev = format!("{}@{}", branch, remote);
        self.command(&["rev-parse", "--abbrev-ref", "--symbolic", rev.as_str()])
    }
}

impl<'a> Git<'a> {
    pub fn new(cwd: &'a Path, command: &'a str) -> Git<'a> {
        Git { command, cwd }
    }
}
<commit_msg>refactor creating 'object not found' error<commit_after>use crate::error::{Error, ErrorKind, Result};
use std::ffi::OsStr;
use std::fmt::{Debug, Display};
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;

fn object_not_found(kind: &'static str, inner: impl Display, object: impl ToString) -> Box<Error> {
    Error::new(ErrorKind::GitObjectNotFound {
        kind,
        object: object.to_string(),
        msg: format!("{}", inner),
    })
}

pub struct Git<'a> {
    command: &'a str,
    cwd: &'a Path,
}

impl<'a> Git<'a> {
    pub fn command<S: AsRef<OsStr> + Debug>(&self, args: &[S]) -> Result<String> {
        let out = Command::new(&self.command)
            .arg("-C")
            .arg(self.cwd)
            .args(args)
            .output()?;
        if out.status.success() {
            Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
        } else {
            Error::err(ErrorKind::GitCommandError {
                stderr: String::from_utf8_lossy(&out.stderr)
                    .trim()
                    .replace('\n', " "),
                args: args.iter().map(|a| a.as_ref().to_owned()).collect(),
            })
        }
    }

    pub fn hash(&self, commit: impl AsRef<str>) -> Result<String> {
        let commit = commit.as_ref();
        self.command(&["rev-parse", commit])
            .map_err(|e| object_not_found("commit", e, commit))
    }

    pub fn tag_hash(&self, tagname: impl AsRef<str>) -> Result<String> {
        let tagname = tagname.as_ref();
        let stdout = self
            .command(&["show-ref", "--tags", tagname])
            .map_err(|e| object_not_found("tag name", e, tagname))?;
        // Output must be in format '{rev} {ref name}'
        Ok(stdout.splitn(2, ' ').next().unwrap().to_string())
    }

    pub fn remote_url(&self, name: impl AsRef<str>) -> Result<String> {
        // XXX:
        // `git remote get-url {name}` is not available because it's added recently (at 2.6.1).
        // Note that git installed in Ubuntu 14.04 is 1.9.1.
        let name = name.as_ref();
        self.command(&["config", "--get", &format!("remote.{}.url", name)])
            .map_err(|e| object_not_found("remote", e, name))
    }

    pub fn tracking_remote_url(
        &self,
        branch: &Option<impl AsRef<str>>,
    ) -> Result<(String, String)> {
        let rev = match branch {
            Some(b) => format!("{}@{{u}}", b.as_ref()),
            None => "@{u}".to_string(),
        };

        let out = match self.command(&["rev-parse", "--abbrev-ref", "--symbolic", rev.as_str()]) {
            Ok(stdout) => stdout,
            Err(err) => match err.kind() {
                ErrorKind::GitCommandError { stderr, .. }
                    if stderr.contains("does not point to a branch") =>
                {
                    return Ok((self.remote_url("origin")?, "origin".to_string()));
                }
                _ => return Err(err),
            },
        };

        // out is formatted as '{remote-name}/{branch-name}'
        match out.splitn(2, '/').next() {
            Some(u) => Ok((self.remote_url(u)?, u.to_string())),
            None => Error::err(ErrorKind::UnexpectedRemoteName(out.clone())),
        }
    }

    pub fn root_dir(&self) -> Result<PathBuf> {
        match self.command(&["rev-parse", "--show-toplevel"]) {
            Ok(stdout) => Ok(fs::canonicalize(stdout)?),
            Err(err) => match err.kind() {
                ErrorKind::GitCommandError { stderr, .. } => {
                    Error::err(ErrorKind::GitRootDirNotFound {
                        cwd: self.cwd.to_owned(),
                        stderr: stderr.clone(),
                    })
                }
                _ => Err(err),
            },
        }
    }

    pub fn current_branch(&self) -> Result<String> {
        self.command(&["rev-parse", "--abbrev-ref", "--symbolic", "HEAD"])
    }

    pub fn remote_contains(
        &self,
        spec: impl AsRef<str>,
        remote_branch: impl AsRef<str>,
    ) -> Result<bool> {
        self.command(&[
            "branch",
            "--remote",
            remote_branch.as_ref(),
            "--contains",
            spec.as_ref(),
        ])
        .map(|o| !o.is_empty())
    }

    // Returns {remote}/{branch}
    pub fn remote_branch(
        &self,
        remote_name: &Option<impl AsRef<str>>,
        local_branch: &Option<impl AsRef<str>>,
    ) -> Result<String> {
        let remote = match remote_name {
            Some(r) => r.as_ref(),
            None => "{u}",
        };
        let branch = match local_branch {
            Some(b) => b.as_ref(),
            None => "",
        };
        let rev = format!("{}@{}", branch, remote);
        self.command(&["rev-parse", "--abbrev-ref", "--symbolic", rev.as_str()])
    }
}

impl<'a> Git<'a> {
    pub fn new(cwd: &'a Path, command: &'a str) -> Git<'a> {
        Git { command, cwd }
    }
}
<|endoftext|>


<commit_before>//!  Dynamic parsers within a tree
#![deny(missing_docs)]

#[macro_use]
extern crate nom;
#[macro_use]
extern crate log;
extern crate term;

pub mod examples;
pub mod structures;
pub mod traits;
mod logger;

use nom::IResult;
use log::LogLevelFilter;

use structures::{Arena, NodeId};
use traits::{ParserBox, Parser};
use logger::Logger;

/// A parser tree description for a result R and a variant V
pub struct Tree<R, V> {
    /// The memory arena of the tree
    pub arena: Arena<ParserBox<R, V>>,

    /// The current result stack of the parsers
    pub result: Vec<R>,
}

impl<R, V> Tree<R, V> {
    /// Create a new emtpy `Tree`
    pub fn new() -> Self {
        Tree {
            arena: Arena::new(),
            result: vec![],
        }
    }

    /// Set the global log level for reporting
    pub fn set_log_level(&mut self, level: LogLevelFilter) {
        // Setup the logger if not already set
        if log::set_logger(|max_log_level| {
                max_log_level.set(level);
                Box::new(Logger)
            })
            .is_err() {
            warn!("Logger already set.");
        };
        trace!("Log level set to: {:?}", level);
    }

    /// Create a new boxed Parser and return a corresponding Node
    pub fn new_parser<T>(&mut self, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + 'static
    {
        // Create a new Parser in a Box
        let parser_box = Box::new(parser);

        // Create a new node and return the node ID
        self.arena.new_node(parser_box)
    }

    /// Append the second node to the first one within the current tree structure
    pub fn link(&mut self, left: NodeId, right: NodeId) {
        left.append(right, &mut self.arena);
    }

    /// Do parsing until all possible paths failed. This is equivalent in finding the deepest
    /// possible parsing result within the tree. The result will be assembled together in the
    /// given result vector, which will be returned at the end.
    pub fn traverse(&self, start_node: NodeId, input: &[u8], mut result: Vec<R>) -> Vec<R> {
        for node_id in start_node.following_siblings(&self.arena) {
            // Get the initial values from the arena
            let ref node = self.arena[node_id];
            let ref parser = node.data;
            trace!("Parsing: {:?}", node_id);

            // Do the actual parsing work
            match parser.parse(input, node, &self.arena) {
                IResult::Done(input_left, parser_result) => {
                    // Adapt the result
                    trace!("Parsing succeed, left input length: {}", input_left.len());
                    result.push(parser_result);

                    // Check for further child nodes
                    match node.first_child() {
                        Some(node) => {
                            trace!("Continue traversal at first child of the node.");
                            result = self.traverse(node, input_left, result);
                        }
                        None => trace!("No child left any more, parsing done."),
                    }
                }
                IResult::Error(err) => trace!("Parser failed with error: {:?}", err),
                IResult::Incomplete(err) => trace!("Parser failed: {:?}", err),
            }
        }
        result
    }
}
<commit_msg>Update lib.rs<commit_after>//!  Dynamic parsers within a tree
#![deny(missing_docs)]

#[macro_use]
extern crate nom;
#[macro_use]
extern crate log;
extern crate term;

pub mod examples;
pub mod structures;
pub mod traits;
mod logger;

use nom::IResult;
use log::LogLevelFilter;

use structures::{Arena, NodeId};
use traits::{ParserBox, Parser};
use logger::Logger;

/// A parser tree description for a result R and a variant V
pub struct Tree<R, V> {
    /// The memory arena of the tree
    pub arena: Arena<ParserBox<R, V>>,

    /// The current result stack of the parsers
    pub result: Vec<R>,
}

impl<R, V> Tree<R, V> {
    /// Create a new emtpy `Tree`
    pub fn new() -> Self {
        Tree {
            arena: Arena::new(),
            result: vec![],
        }
    }

    /// Set the global log level for reporting
    pub fn set_log_level(&mut self, level: LogLevelFilter) {
        // Setup the logger if not already set
        if log::set_logger(|max_log_level| {
                max_log_level.set(level);
                Box::new(Logger)
            })
            .is_err() {
            warn!("Logger already set.");
        };
        trace!("Log level set to: {:?}", level);
    }

    /// Create a new boxed Parser and return a corresponding Node
    pub fn new_parser<T>(&mut self, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + 'static
    {
        // Create a new Parser in a Box
        let parser_box = Box::new(parser);

        // Create a new node and return the node ID
        self.arena.new_node(parser_box)
    }

    /// Append the second node to the first one within the current tree structure
    pub fn link(&mut self, left: NodeId, right: NodeId) {
        left.append(right, &mut self.arena);
    }

    /// Do parsing until all possible paths failed. This is equivalent in finding the deepest
    /// possible parsing result within the tree. The result will be assembled together in the
    /// given result vector, which will be returned at the end.
    pub fn traverse(&self, start_node: NodeId, input: &[u8], mut result: Vec<R>) -> Vec<R> {
        for node_id in start_node.following_siblings(&self.arena) {
            // Get the initial values from the arena
            let ref node = self.arena[node_id];
            let ref parser = node.data;
            trace!("Parsing: {:?}", node_id);

            // Do the actual parsing work
            match parser.parse(input, node, &self.arena) {
                IResult::Done(input_left, parser_result) => {
                    // Adapt the result
                    trace!("Parsing succeed, left input length: {}", input_left.len());
                    result.push(parser_result);

                    // Check for further child nodes
                    match node.first_child() {
                        Some(node) => {
                            trace!("Continue traversal at first child of the node.");
                            result = self.traverse(node, input_left, result);
                        }
                        None => {
                            trace!("No child nodes left any more, parsing done.");
                            break;
                        }
                    }
                }
                IResult::Error(err) => trace!("Parser failed with error: {:?}", err),
                IResult::Incomplete(err) => trace!("Parser failed: {:?}", err),
            }
        }
        result
    }
}
<|endoftext|>


<commit_before>//! # Russet
//!
//! __Russet__ is a simple string tokenising library for Rust.  It supports
//! POSIX shell-style separation of a line of _words_ into a vector of
//! strings.
#![experimental]

#![feature(phase)]
#[phase(plugin)]
extern crate quickcheck_macros;
extern crate quickcheck;

use std::char::is_whitespace;
use std::collections::hashmap::HashMap;

/// A tokeniser object.
///
/// A Tokeniser can be fed characters from an iterator, string, or individually.
/// It is an _immutable_ object: actions on a Tokeniser consume the Tokeniser,
/// and produce a fresh copy of the Tokeniser.
///
/// At any stage, a Tokeniser can be consumed to produce the vector of words
/// it has read, using the `into_strings` method.  This method may fail if the
/// Tokeniser ended in a bad state (in the middle of a quoted string, or in
/// the middle of an escape sequence).
#[deriving(Clone)]
pub struct Tokeniser<Q, E> {
    /// The current vector of parsed words.
    vec: Vec<String>,

    /// Whether or not we are currently in a word.
    in_word: bool,

    /// The current closing quote character and quote mode, if any.
    quote: Option<( char, QuoteMode )>,

    /// Whether the tokeniser is currently processing an escape character.
    escaping: bool,

    /// Maps from quote openers to quote closers.
    quote_pairs: Q,

    /// Map from escape characters to their replacements.
    escape_pairs: E,

    /// The character preceding escape characters.
    escape_leader: Option<char>
}

/// A quote mode.
#[deriving(Clone)]
pub enum QuoteMode {
    /// All characters except the closing character have their literal value.
    /// This is equivalent to single-quoting in POSIX shell.
    IgnoreEscapes,

    /// All characters except the closing character and escape sequences
    /// have their literal value.  This is roughly equivalent to
    /// double-quoting in POSIX shell.
    ParseEscapes
}

/// A tokeniser error.
///
/// A Tokeniser's `into_strings` method can fail with one of the following
/// errors if called while the Tokeniser is in an unfinished state.
#[deriving(Eq, PartialEq, Show)]
pub enum Error {
    /// A quotation was opened, but not closed.
    UnmatchedQuote,

    /// An escape sequence was started, but not finished.
    UnfinishedEscape
}

impl<Q: Map<char, ( char, QuoteMode )>+Collection+Clone,
     E: Map<char, char>+Clone> Tokeniser<Q, E> {
    /// Creates a new, blank Tokeniser.
    ///
    /// # Arguments
    ///
    /// * `quote_pairs`   - A map, mapping characters that serve as opening
    ///                     quotes to their closing quotes and quote modes.
    /// * `escape_pairs`  - A map, mapping escape characters to the characters
    ///                     they represent.  An empty map is treated as a
    ///                     _shell-style escape_: the
    /// * `escape_leader` - The character, if any, that starts an escape
    ///                     sequence.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, with an empty state.  Attempting to take the
    /// string vector of the Tokeniser yields the empty vector.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::hashmap::HashMap;
    /// use russet::{ Tokeniser, ParseEscapes, QuoteMode };
    ///
    /// let quote_pairs: HashMap<char, ( char, QuoteMode )> =
    ///     vec![ ( '\"', ( '\"', ParseEscapes ) ) ].move_iter().collect();
    /// let escape_pairs: HashMap<char, char> =
    ///     vec![ ( 'n', '\n' ) ].move_iter().collect();
    /// let tok = Tokeniser::new(quote_pairs, escape_pairs, Some('\\'));
    /// assert_eq!(tok.into_strings(), Ok(vec![]));
    /// ```
    pub fn new(quote_pairs: Q, escape_pairs: E, escape_leader: Option<char>)
               -> Tokeniser<Q, E> {
        Tokeniser {
            vec: vec![ String::new() ],
            in_word: false,
            quote: None,
            escaping: false,
            quote_pairs: quote_pairs,
            escape_pairs: escape_pairs,
            escape_leader: escape_leader
        }
    }

    /// Feeds a single character `chr` to a Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `chr`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use russet::whitespace_split_tokeniser;
    ///
    /// let tok = whitespace_split_tokeniser();
    /// let tok2 = tok.add_char('a').add_char('b').add_char('c');
    /// assert_eq!(tok2.into_strings(), Ok(vec![ "abc".into_string() ]));
    /// ```
    pub fn add_char(self, chr: char) -> Tokeniser<Q, E> {
        let mut new = self.clone();

        match (chr, self) {
            // ESCAPE SEQUENCES
            //   Shell-style escaping (no escapes defined)
            //   -> Echo character
            ( c, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.is_empty() => new.emit(c),
            // Known escaped character, otherwise
            // -> Escape character
            ( e, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.contains_key(&e) => {
                let x = es.find(&e).unwrap();

                new.emit(x.clone());
            },

            // ESCAPE LEADER
            //   Escape leader, not in quotes
            //   -> Begin escape (and word if not in one already)
            ( c, Tokeniser {
                escaping: false,
                quote: None,
                escape_leader: Some(e),
                ..
            } ) if e == c => new.start_escaping(),
            //   Escape leader, in escape-permitting quotes
            //   -> Begin escape (and word if not in one already)
            ( c, Tokeniser {
                escaping: false,
                quote: Some(( _, ParseEscapes )),
                escape_leader: Some(e),
                ..
            } ) if e == c => new.start_escaping(),

            // QUOTE OPENING
            //   Quote opening character, not currently in quoted word
            //   -> Start quoting
            ( q, Tokeniser {
                escaping: false,
                quote: None,
                quote_pairs: ref qs,
                ..
            } ) if qs.contains_key(&q) => {
                new.quote = Some(qs.find(&q).unwrap().clone());
                new.in_word = true;
            },

            // QUOTE CLOSING
            //   Quote closing character, in quoted word, quotes ok
            //   -> Stop quoting
            ( c, Tokeniser { escaping: false, quote: Some(( cc, _ )), .. } )
                if c == cc => {
                new.quote = None;
                new.in_word = true;
            },

            // UNESCAPED WHITESPACE
            //   Unescaped whitespace, while not in a word
            //   -> Ignore
            ( a, Tokeniser { escaping: false, in_word: false, .. } )
                if is_whitespace(a) => (),
            //   Unescaped whitespace, while in a non-quoted word
            //   -> End word
            ( a, Tokeniser { escaping: false, in_word: true, quote: None, .. } )
                if is_whitespace(a) => {
                new.in_word = false;
                new.vec.push(String::new());
            },

            // DEFAULT
            //   Anything else
            //   -> Echo
            ( a, _ ) => new.emit(a)
        }

        new
    }

    /// Feeds a line into the Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `line`.
    pub fn add_line(self, line: &str) -> Tokeniser<Q, E> {
        line.trim().chars().fold(self, |s, chr| s.add_char(chr))
    }

    /// Destroys the tokeniser, extracting the string vector.
    ///
    /// # Return value
    ///
    /// A Result, containing the tokenised string vector if the Tokeniser
    /// was in a valid ending state, and an Error otherwise.
    pub fn into_strings(mut self) -> Result<Vec<String>, Error> {
        if self.in_word && self.quote.is_some() {
            Err(UnmatchedQuote)
        } else if self.escaping {
            Err(UnfinishedEscape)
        } else {
            self.drop_empty_current_string();
            Ok(self.vec)
        }
    }

    /// Adds a character into a Tokeniser's current string.
    /// This automatically sets the Tokeniser's state to be in a word,
    /// and clears any escape sequence flag.
    fn emit(&mut self, c: char) {
        self.in_word = true;
        self.escaping = false;
        self.vec.mut_last().mutate(|s| { s.push_char(c); s });
    }

    /// Switches on escape mode.
    /// This automatically sets the Tokeniser to be in a word, if it isn't
    /// already.
    fn start_escaping(&mut self) {
        self.escaping = true;
        self.in_word = true;
    }

    /// Drops the current working string, if it is empty.
    fn drop_empty_current_string(&mut self) {
        if self.vec.last().map(|s| s.is_empty()).unwrap_or(false) {
            self.vec.pop();
        }
    }
}

/// A type for tokenisers returned by Russet.
pub type StockTokeniser = Tokeniser<HashMap<char, ( char, QuoteMode )>,
                                    HashMap<char, char>>;

/// Creates a Tokeniser that doesn't support quoting or escaping.
///
/// This is effectively equivalent to the Words iterator on string slices.
///
/// # Return value
///
/// A Tokeniser with no quoting or escaping.
///
/// # Example
///
/// ```rust
/// use russet::whitespace_split_tokeniser;
///
/// let tok = whitespace_split_tokeniser();
/// let tok2 = tok.add_line("this \"ignores quotes\"  and\n  \\slashes");
/// assert_eq!(tok2.into_strings(), Ok(vec!("this".into_string(),
///                                         "\"ignores".into_string(),
///                                         "quotes\"".into_string(),
///                                         "and".into_string(),
///                                         "\\slashes".into_string())));
/// ```
#[experimental]
pub fn whitespace_split_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> = HashMap::new();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, None)
}

/// Creates a Tokeniser that provides shell-style quoting.
///
/// This recognises pairs of " and ' as delineating words, and parses
/// anything following a \ as its literal value.  Anything in single quotes
/// is returned verbatim.
///
/// # Return value
///
/// A Tokeniser with shell-style quoting.
///
/// # Example
///
/// ```rust
/// use russet::shell_style_tokeniser;
///
/// let tok = shell_style_tokeniser();
/// let tok2 = tok.add_line("word1 word\\ 2 \"word\\ 3\" 'word\\ \"4\"'");
/// assert_eq!(tok2.into_strings(), Ok(vec!("word1".into_string(),
///                                         "word 2".into_string(),
///                                         "word 3".into_string(),
///                                         "word\\ \"4\"".into_string())));
/// ```
#[experimental]
pub fn shell_style_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', IgnoreEscapes ) ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, Some('\\'))
}

/// Unpacks a line into its constituent words.
#[experimental]
pub fn unpack(line: &str) -> Result<Vec<String>, Error> {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', ParseEscapes ) ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> =
        vec![ ( 'n', '\n' ) ].move_iter().collect();

    line.trim().chars().fold(
        Tokeniser::new(quote_pairs, escape_pairs, Some('\\')),
        |s, chr| s.add_char(chr)
    ).into_strings()
}

#[cfg(test)]
mod test {
    use super::{
        unpack,
        whitespace_split_tokeniser,
        Error,
        UnmatchedQuote,
        UnfinishedEscape
    };

    #[test]
    fn unpack_unmatched_quote() {
        assert_eq!(unpack("\"abcde"), Err(UnmatchedQuote));
    }

    #[test]
    fn unpack_unfinished_escape() {
        assert_eq!(unpack("zxcvbn m\\"), Err(UnfinishedEscape));
    }

    #[test]
    fn unpack_empty_string() {
        assert_eq!(unpack(""), Ok(vec![]));
    }

    #[test]
    fn unpack_leading_whitespace() {
        let rhs = vec![ "abc".into_string(), "def".into_string() ];
        assert_eq!(unpack("     abc def"), Ok(rhs));
    }

    #[test]
    fn unpack_trailing_whitespace() {
        let rhs = vec![ "ghi".into_string(), "jkl".into_string() ];
        assert_eq!(unpack("ghi jkl     \n"), Ok(rhs));
    }

    #[test]
    fn unpack_enqueue_command() {
        let lhs =
            "enqueue file \"C:\\\\Users\\\\Test\\\\Artist - Title.mp3\" 1";
        let rhs = vec![ "enqueue".into_string(),
                        "file".into_string(),
                        "C:\\Users\\Test\\Artist - Title.mp3".into_string(),
                        "1".into_string() ];
        assert_eq!(unpack(lhs), Ok(rhs));
    }

    #[test]
    fn unpack_escaped_newline() {
        assert_eq!(unpack("abc\\nde"),     Ok(vec![ "abc\nde".into_string() ]));
        assert_eq!(unpack("\"abc\\nde\""), Ok(vec![ "abc\nde".into_string() ]));
    }

    /// The whitespace_split_tokeniser should provide the same strings as
    /// the Words iterator for an arbitrary string.
    #[quickcheck]
    fn whitespace_split_tokeniser_words_equivalence(line : String) -> bool {
        let line_slice = line.as_slice();

        let lhs: Result<Vec<String>, Error> =
            whitespace_split_tokeniser().add_line(line_slice).into_strings();
        let rhs: Result<Vec<String>, Error> =
            Ok(line_slice.words().map(|x| x.into_string()).collect());

        lhs == rhs
    }
}
<commit_msg>Add Tokeniser.add_iterator.<commit_after>//! # Russet
//!
//! __Russet__ is a simple string tokenising library for Rust.  It supports
//! POSIX shell-style separation of a line of _words_ into a vector of
//! strings.
#![experimental]

#![feature(phase)]
#[phase(plugin)]
extern crate quickcheck_macros;
extern crate quickcheck;

use std::char::is_whitespace;
use std::collections::hashmap::HashMap;

/// A tokeniser object.
///
/// A Tokeniser can be fed characters from an iterator, string, or individually.
/// It is an _immutable_ object: actions on a Tokeniser consume the Tokeniser,
/// and produce a fresh copy of the Tokeniser.
///
/// At any stage, a Tokeniser can be consumed to produce the vector of words
/// it has read, using the `into_strings` method.  This method may fail if the
/// Tokeniser ended in a bad state (in the middle of a quoted string, or in
/// the middle of an escape sequence).
#[deriving(Clone)]
pub struct Tokeniser<Q, E> {
    /// The current vector of parsed words.
    vec: Vec<String>,

    /// Whether or not we are currently in a word.
    in_word: bool,

    /// The current closing quote character and quote mode, if any.
    quote: Option<( char, QuoteMode )>,

    /// Whether the tokeniser is currently processing an escape character.
    escaping: bool,

    /// Maps from quote openers to quote closers.
    quote_pairs: Q,

    /// Map from escape characters to their replacements.
    escape_pairs: E,

    /// The character preceding escape characters.
    escape_leader: Option<char>
}

/// A quote mode.
#[deriving(Clone)]
pub enum QuoteMode {
    /// All characters except the closing character have their literal value.
    /// This is equivalent to single-quoting in POSIX shell.
    IgnoreEscapes,

    /// All characters except the closing character and escape sequences
    /// have their literal value.  This is roughly equivalent to
    /// double-quoting in POSIX shell.
    ParseEscapes
}

/// A tokeniser error.
///
/// A Tokeniser's `into_strings` method can fail with one of the following
/// errors if called while the Tokeniser is in an unfinished state.
#[deriving(Eq, PartialEq, Show)]
pub enum Error {
    /// A quotation was opened, but not closed.
    UnmatchedQuote,

    /// An escape sequence was started, but not finished.
    UnfinishedEscape
}

impl<Q: Map<char, ( char, QuoteMode )>+Collection+Clone,
     E: Map<char, char>+Clone> Tokeniser<Q, E> {
    /// Creates a new, blank Tokeniser.
    ///
    /// # Arguments
    ///
    /// * `quote_pairs`   - A map, mapping characters that serve as opening
    ///                     quotes to their closing quotes and quote modes.
    /// * `escape_pairs`  - A map, mapping escape characters to the characters
    ///                     they represent.  An empty map is treated as a
    ///                     _shell-style escape_: the
    /// * `escape_leader` - The character, if any, that starts an escape
    ///                     sequence.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, with an empty state.  Attempting to take the
    /// string vector of the Tokeniser yields the empty vector.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::hashmap::HashMap;
    /// use russet::{ Tokeniser, ParseEscapes, QuoteMode };
    ///
    /// let quote_pairs: HashMap<char, ( char, QuoteMode )> =
    ///     vec![ ( '\"', ( '\"', ParseEscapes ) ) ].move_iter().collect();
    /// let escape_pairs: HashMap<char, char> =
    ///     vec![ ( 'n', '\n' ) ].move_iter().collect();
    /// let tok = Tokeniser::new(quote_pairs, escape_pairs, Some('\\'));
    /// assert_eq!(tok.into_strings(), Ok(vec![]));
    /// ```
    pub fn new(quote_pairs: Q, escape_pairs: E, escape_leader: Option<char>)
               -> Tokeniser<Q, E> {
        Tokeniser {
            vec: vec![ String::new() ],
            in_word: false,
            quote: None,
            escaping: false,
            quote_pairs: quote_pairs,
            escape_pairs: escape_pairs,
            escape_leader: escape_leader
        }
    }

    /// Feeds a single character `chr` to a Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `chr`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use russet::whitespace_split_tokeniser;
    ///
    /// let tok = whitespace_split_tokeniser();
    /// let tok2 = tok.add_char('a').add_char('b').add_char('c');
    /// assert_eq!(tok2.into_strings(), Ok(vec![ "abc".into_string() ]));
    /// ```
    pub fn add_char(self, chr: char) -> Tokeniser<Q, E> {
        let mut new = self.clone();

        match (chr, self) {
            // ESCAPE SEQUENCES
            //   Shell-style escaping (no escapes defined)
            //   -> Echo character
            ( c, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.is_empty() => new.emit(c),
            // Known escaped character, otherwise
            // -> Escape character
            ( e, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.contains_key(&e) => {
                let x = es.find(&e).unwrap();

                new.emit(x.clone());
            },

            // ESCAPE LEADER
            //   Escape leader, not in quotes
            //   -> Begin escape (and word if not in one already)
            ( c, Tokeniser {
                escaping: false,
                quote: None,
                escape_leader: Some(e),
                ..
            } ) if e == c => new.start_escaping(),
            //   Escape leader, in escape-permitting quotes
            //   -> Begin escape (and word if not in one already)
            ( c, Tokeniser {
                escaping: false,
                quote: Some(( _, ParseEscapes )),
                escape_leader: Some(e),
                ..
            } ) if e == c => new.start_escaping(),

            // QUOTE OPENING
            //   Quote opening character, not currently in quoted word
            //   -> Start quoting
            ( q, Tokeniser {
                escaping: false,
                quote: None,
                quote_pairs: ref qs,
                ..
            } ) if qs.contains_key(&q) => {
                new.quote = Some(qs.find(&q).unwrap().clone());
                new.in_word = true;
            },

            // QUOTE CLOSING
            //   Quote closing character, in quoted word, quotes ok
            //   -> Stop quoting
            ( c, Tokeniser { escaping: false, quote: Some(( cc, _ )), .. } )
                if c == cc => {
                new.quote = None;
                new.in_word = true;
            },

            // UNESCAPED WHITESPACE
            //   Unescaped whitespace, while not in a word
            //   -> Ignore
            ( a, Tokeniser { escaping: false, in_word: false, .. } )
                if is_whitespace(a) => (),
            //   Unescaped whitespace, while in a non-quoted word
            //   -> End word
            ( a, Tokeniser { escaping: false, in_word: true, quote: None, .. } )
                if is_whitespace(a) => {
                new.in_word = false;
                new.vec.push(String::new());
            },

            // DEFAULT
            //   Anything else
            //   -> Echo
            ( a, _ ) => new.emit(a)
        }

        new
    }

    /// Feeds an Iterator of chars, `it`, into the Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming the characters in `it`.
    pub fn add_iterator<I: Iterator<char>>(self, mut it: I) -> Tokeniser<Q, E> {
        it.fold(self, |s, chr| s.add_char(chr))
    }

    /// Feeds a line, `line`, into the Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `line`.
    pub fn add_line(self, line: &str) -> Tokeniser<Q, E> {
        self.add_iterator(line.trim().chars())
    }

    /// Destroys the tokeniser, extracting the string vector.
    ///
    /// # Return value
    ///
    /// A Result, containing the tokenised string vector if the Tokeniser
    /// was in a valid ending state, and an Error otherwise.
    pub fn into_strings(mut self) -> Result<Vec<String>, Error> {
        if self.in_word && self.quote.is_some() {
            Err(UnmatchedQuote)
        } else if self.escaping {
            Err(UnfinishedEscape)
        } else {
            self.drop_empty_current_string();
            Ok(self.vec)
        }
    }

    /// Adds a character into a Tokeniser's current string.
    /// This automatically sets the Tokeniser's state to be in a word,
    /// and clears any escape sequence flag.
    fn emit(&mut self, c: char) {
        self.in_word = true;
        self.escaping = false;
        self.vec.mut_last().mutate(|s| { s.push_char(c); s });
    }

    /// Switches on escape mode.
    /// This automatically sets the Tokeniser to be in a word, if it isn't
    /// already.
    fn start_escaping(&mut self) {
        self.escaping = true;
        self.in_word = true;
    }

    /// Drops the current working string, if it is empty.
    fn drop_empty_current_string(&mut self) {
        if self.vec.last().map(|s| s.is_empty()).unwrap_or(false) {
            self.vec.pop();
        }
    }
}

/// A type for tokenisers returned by Russet.
pub type StockTokeniser = Tokeniser<HashMap<char, ( char, QuoteMode )>,
                                    HashMap<char, char>>;

/// Creates a Tokeniser that doesn't support quoting or escaping.
///
/// This is effectively equivalent to the Words iterator on string slices.
///
/// # Return value
///
/// A Tokeniser with no quoting or escaping.
///
/// # Example
///
/// ```rust
/// use russet::whitespace_split_tokeniser;
///
/// let tok = whitespace_split_tokeniser();
/// let tok2 = tok.add_line("this \"ignores quotes\"  and\n  \\slashes");
/// assert_eq!(tok2.into_strings(), Ok(vec!("this".into_string(),
///                                         "\"ignores".into_string(),
///                                         "quotes\"".into_string(),
///                                         "and".into_string(),
///                                         "\\slashes".into_string())));
/// ```
#[experimental]
pub fn whitespace_split_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> = HashMap::new();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, None)
}

/// Creates a Tokeniser that provides shell-style quoting.
///
/// This recognises pairs of " and ' as delineating words, and parses
/// anything following a \ as its literal value.  Anything in single quotes
/// is returned verbatim.
///
/// # Return value
///
/// A Tokeniser with shell-style quoting.
///
/// # Example
///
/// ```rust
/// use russet::shell_style_tokeniser;
///
/// let tok = shell_style_tokeniser();
/// let tok2 = tok.add_line("word1 word\\ 2 \"word\\ 3\" 'word\\ \"4\"'");
/// assert_eq!(tok2.into_strings(), Ok(vec!("word1".into_string(),
///                                         "word 2".into_string(),
///                                         "word 3".into_string(),
///                                         "word\\ \"4\"".into_string())));
/// ```
#[experimental]
pub fn shell_style_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', IgnoreEscapes ) ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, Some('\\'))
}

/// Unpacks a line into its constituent words.
#[experimental]
pub fn unpack(line: &str) -> Result<Vec<String>, Error> {
    let quote_pairs: HashMap<char, ( char, QuoteMode )> =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', ParseEscapes ) ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> =
        vec![ ( 'n', '\n' ) ].move_iter().collect();

    line.trim().chars().fold(
        Tokeniser::new(quote_pairs, escape_pairs, Some('\\')),
        |s, chr| s.add_char(chr)
    ).into_strings()
}

#[cfg(test)]
mod test {
    use super::{
        unpack,
        whitespace_split_tokeniser,
        Error,
        UnmatchedQuote,
        UnfinishedEscape
    };

    #[test]
    fn unpack_unmatched_quote() {
        assert_eq!(unpack("\"abcde"), Err(UnmatchedQuote));
    }

    #[test]
    fn unpack_unfinished_escape() {
        assert_eq!(unpack("zxcvbn m\\"), Err(UnfinishedEscape));
    }

    #[test]
    fn unpack_empty_string() {
        assert_eq!(unpack(""), Ok(vec![]));
    }

    #[test]
    fn unpack_leading_whitespace() {
        let rhs = vec![ "abc".into_string(), "def".into_string() ];
        assert_eq!(unpack("     abc def"), Ok(rhs));
    }

    #[test]
    fn unpack_trailing_whitespace() {
        let rhs = vec![ "ghi".into_string(), "jkl".into_string() ];
        assert_eq!(unpack("ghi jkl     \n"), Ok(rhs));
    }

    #[test]
    fn unpack_enqueue_command() {
        let lhs =
            "enqueue file \"C:\\\\Users\\\\Test\\\\Artist - Title.mp3\" 1";
        let rhs = vec![ "enqueue".into_string(),
                        "file".into_string(),
                        "C:\\Users\\Test\\Artist - Title.mp3".into_string(),
                        "1".into_string() ];
        assert_eq!(unpack(lhs), Ok(rhs));
    }

    #[test]
    fn unpack_escaped_newline() {
        assert_eq!(unpack("abc\\nde"),     Ok(vec![ "abc\nde".into_string() ]));
        assert_eq!(unpack("\"abc\\nde\""), Ok(vec![ "abc\nde".into_string() ]));
    }

    /// The whitespace_split_tokeniser should provide the same strings as
    /// the Words iterator for an arbitrary string.
    #[quickcheck]
    fn whitespace_split_tokeniser_words_equivalence(line : String) -> bool {
        let line_slice = line.as_slice();

        let lhs: Result<Vec<String>, Error> =
            whitespace_split_tokeniser().add_line(line_slice).into_strings();
        let rhs: Result<Vec<String>, Error> =
            Ok(line_slice.words().map(|x| x.into_string()).collect());

        lhs == rhs
    }
}
<|endoftext|>


<commit_before>//! Reliably remove a directory and all of its children.
//!
//! This library provides a reliable implementation of `remove_dir_all` for Windows.
//! For Unix systems, it re-exports `std::fs::remove_dir_all`.

#![deny(missing_debug_implementations)]
#![deny(missing_docs)]

#[cfg(windows)]
extern crate winapi;

#[cfg(test)]
#[macro_use]
extern crate doc_comment;

#[cfg(test)]
doctest!("../README.md");

#[cfg(windows)]
mod fs;

#[cfg(windows)]
pub use self::fs::remove_dir_all;

#[cfg(not(windows))]
pub use std::fs::remove_dir_all;
<commit_msg>Replace "cfg(test)" with "cfg(doctest)" for readme testing (#17)<commit_after>//! Reliably remove a directory and all of its children.
//!
//! This library provides a reliable implementation of `remove_dir_all` for Windows.
//! For Unix systems, it re-exports `std::fs::remove_dir_all`.

#![deny(missing_debug_implementations)]
#![deny(missing_docs)]

#[cfg(windows)]
extern crate winapi;

#[cfg(doctest)]
#[macro_use]
extern crate doc_comment;

#[cfg(doctest)]
doctest!("../README.md");

#[cfg(windows)]
mod fs;

#[cfg(windows)]
pub use self::fs::remove_dir_all;

#[cfg(not(windows))]
pub use std::fs::remove_dir_all;
<|endoftext|>


<commit_before>#![unstable]
#![feature(core)]
#![feature(collections)]
#![feature(unicode)]

//!This crate contains parser combinators, roughly based on the Haskell library [parsec](http://hackage.haskell.org/package/parsec).
//!
//!A parser in this library can be described as a function which takes some input and if it
//!is succesful, returns a value together with the remaining input.
//!A parser combinator is a function which takes one or more parsers and returns a new parser.
//!For instance the `many` parser can be used to convert a parser for single digits into one that
//!parses multiple digits.
//!
//!# Examples:
//!
//!```
//! extern crate "parser-combinators" as parser_combinators;
//! use parser_combinators::{spaces, many1, sep_by, digit, satisfy, Parser, ParserExt, ParseError};
//!
//! fn main() {
//!     let input = "1234, 45,78";
//!     let spaces = spaces();
//!     let integer = spaces.clone()//Parse spaces first and use the with method to only keep the result of the next parser
//!         .with(many1(digit()).map(|string: String| string.parse::<i32>().unwrap()));//parse a string of digits into an i32
//!     //Parse integers separated by commas, skipping whitespace
//!     let mut integer_list = sep_by(integer, spaces.skip(satisfy(|c| c == ',')));
//!
//!     //Call parse with the input to execute the parser
//!     let result: Result<(Vec<i32>, &str), ParseError> = integer_list.parse(input);
//!     match result {
//!         Ok((value, _remaining_input)) => println!("{:?}", value),
//!         Err(err) => println!("{}", err)
//!     }
//! }
//!```
//!
//!If we need a parser that is mutually recursive we can define a free function which internally
//!can in turn be used as a parser (Note that we need to explicitly cast the function, this should
//!not be necessary once changes in rustc to make orphan checking less restrictive gets implemented)
//!
//!`expr` is written fully general here which may not be necessary in a specific implementation
//!The `Stream` trait is predefined to work with array slices, string slices and iterators
//!meaning that in this case it could be defined as
//!`fn expr(input: State<&str>) -> ParseResult<Expr, &str>`
//!
//!```
//! extern crate "parser-combinators" as parser_combinators;
//! use parser_combinators::{between, spaces, many1, sep_by, satisfy, Parser, ParserExt,
//! ParseResult};
//! use parser_combinators::primitives::{State, Stream};
//!
//! #[derive(Debug, PartialEq)]
//! enum Expr {
//!     Id(String),
//!     Array(Vec<Expr>),
//! }
//! fn expr<I>(input: State<I>) -> ParseResult<Expr, I>
//!     where I: Stream<Item=char> {
//!     let word = many1(satisfy(|c| c.is_alphabetic()));
//!     let comma_list = sep_by(expr as fn (_) -> _, satisfy(|c| c == ','));
//!     let array = between(satisfy(|c| c == '['), satisfy(|c| c == ']'), comma_list);
//!     let spaces = spaces();
//!     spaces.clone().with(
//!             word.map(Expr::Id)
//!             .or(array.map(Expr::Array))
//!         ).parse_state(input)
//! }
//!
//! fn main() {
//!     let result = (expr as fn (_) -> _)
//!         .parse("[[], hello, [world]]");
//!     let expr = Expr::Array(vec![
//!           Expr::Array(Vec::new())
//!         , Expr::Id("hello".to_string())
//!         , Expr::Array(vec![Expr::Id("world".to_string())])
//!     ]);
//!     assert_eq!(result, Ok((expr, "")));
//! }
//!```

#[cfg(test)]
extern crate test;

#[doc(inline)]
pub use primitives::{Parser, ParseResult, ParseError};
#[doc(inline)]
pub use char::{
    any_char,
    digit,
    space,
    spaces,
    newline,
    crlf,
    tab,
    upper,
    lower,
    letter,
    alpha_num,
    hex_digit,
    oct_digit,
    string,
    satisfy,
};
#[doc(inline)]
pub use combinator::{
    between,
    chainl1,
    many,
    many1,
    optional,
    sep_by,
    try,
    value,
    unexpected,
    not_followed_by,

    ParserExt
};

macro_rules! static_fn {
    (($($arg: pat, $arg_ty: ty),*) -> $ret: ty { $body: expr }) => { { fn temp($($arg: $arg_ty),*) -> $ret { $body } temp } }
}

///Module containing the primitive types which is used to create and compose more advanced parsers
pub mod primitives;
///Module containing all specific parsers
pub mod combinator;
///Module containg parsers specialized on character streams
pub mod char;

#[cfg(test)]
mod tests {
    use super::*;
    use super::primitives::{SourcePosition, State, Stream, Error, Consumed};

    fn integer<'a, I>(input: State<I>) -> ParseResult<i64, I>
        where I: Stream<Item=char> {
        let (s, input) = try!(many1::<String, _>(digit())
            .parse_state(input));
        let mut n = 0;
        for c in s.chars() {
            n = n * 10 + (c as i64 - '0' as i64);
        }
        Ok((n, input))
    }

    #[test]
    fn test_integer() {
        let result = (integer as fn(_) -> _).parse("123");
        assert_eq!(result, Ok((123i64, "")));
    }
    #[test]
    fn list() {
        let mut p = sep_by(integer as fn(_) -> _, satisfy(|c| c == ','));
        let result = p.parse("123,4,56");
        assert_eq!(result, Ok((vec![123, 4, 56], "")));
    }
    #[test]
    fn iterator() {
        let result = (integer as fn(_) -> _).parse("123".chars())
            .map(|(i, mut input)| (i, input.next()));
        assert_eq!(result, Ok((123i64, None)));
    }
    #[test]
    fn field() {
        let word = many(satisfy(|c| c.is_alphanumeric()));
        let word2 = many(satisfy(|c| c.is_alphanumeric()));
        let spaces = spaces();
        let c_decl = word
            .skip(spaces.clone())
            .skip(satisfy(|c| c == ':'))
            .skip(spaces)
            .and(word2)
            .parse("x: int");
        assert_eq!(c_decl, Ok((("x".to_string(), "int".to_string()), "")));
    }
    #[test]
    fn source_position() {
        let source =
r"
123
";
        let result = spaces()
            .with(integer as fn(_) -> _)
            .skip(spaces())
            .parse_state(State::new(source));
        let state = Consumed::Consumed(State {
            position: SourcePosition { line: 3, column: 1 },
            input: ""
        });
        assert_eq!(result, Ok((123i64, state)));
    }

    #[derive(Show, PartialEq)]
    enum Expr {
        Id(String),
        Int(i64),
        Array(Vec<Expr>),
        Plus(Box<Expr>, Box<Expr>),
        Times(Box<Expr>, Box<Expr>),
    }
    fn expr(input: State<&str>) -> ParseResult<Expr, &str> {
        let word = many1(satisfy(|c| c.is_alphabetic()));
        let integer = integer as fn (_) -> _;
        let array = between(satisfy(|c| c == '['), satisfy(|c| c == ']'), sep_by(expr as fn (_) -> _, satisfy(|c| c == ',')));
        let spaces = spaces();
        spaces.clone().with(
                word.map(Expr::Id)
                .or(integer.map(Expr::Int))
                .or(array.map(Expr::Array))
                .or(between(satisfy(|c| c == '('), satisfy(|c| c == ')'), term as fn (_) -> _))
            ).skip(spaces)
            .parse_state(input)
    }

    #[test]
    fn expression() {
        let result = sep_by(expr as fn (_) -> _, satisfy(|c| c == ','))
            .parse("int, 100, [[], 123]");
        let exprs = vec![
              Expr::Id("int".to_string())
            , Expr::Int(100)
            , Expr::Array(vec![Expr::Array(vec![]), Expr::Int(123)])
        ];
        assert_eq!(result, Ok((exprs, "")));
    }

    #[test]
    fn expression_error() {
        let input =
r"
,123
";
        let result = (expr as fn (_) -> _)
            .parse(input);
        let err = ParseError {
            position: SourcePosition { line: 2, column: 1 },
            errors: vec![Error::Unexpected(','), Error::Message("Expected digit".to_string())]
        };
        assert_eq!(result, Err(err));
    }

    fn term(input: State<&str>) -> ParseResult<Expr, &str> {

        let mul = satisfy(|c| c == '*')
            .map(|_| Box::new(|&mut:l, r| Expr::Times(Box::new(l), Box::new(r))) as Box<FnMut(_, _) -> _>);
        let add = satisfy(|c| c == '+')
            .map(|_| Box::new(|&mut:l, r| Expr::Plus(Box::new(l), Box::new(r))) as Box<FnMut(_, _) -> _>);
        let factor = chainl1(expr as fn (_) -> _, mul);
        chainl1(factor, add)
            .parse_state(input)
    }

    #[test]
    fn operators() {
        let input =
r"
1 * 2 + 3 * test
";
        let (result, _) = (term as fn (_) -> _)
            .parse(input)
            .unwrap();

        let e1 = Expr::Times(Box::new(Expr::Int(1)), Box::new(Expr::Int(2)));
        let e2 = Expr::Times(Box::new(Expr::Int(3)), Box::new(Expr::Id("test".to_string())));
        assert_eq!(result, Expr::Plus(Box::new(e1), Box::new(e2)));
    }

    fn follow(input: State<&str>) -> ParseResult<(), &str> {
        match input.clone().uncons_char() {
            Ok((c, _)) => {
                if c.is_alphanumeric() {
                    Err(Consumed::Empty(ParseError::new(input.position, Error::Unexpected(c))))
                }
                else {
                    Ok(((), Consumed::Empty(input)))
                }
            }
            Err(_) => Ok(((), Consumed::Empty(input)))
        }
    }
    #[test]
    fn error_position() {
        let mut p = string("let").skip(follow as fn (_) -> _).map(|x| x.to_string())
            .or(many1(satisfy(|c| c.is_digit(10))));
        match p.parse("le123") {
            Ok(_) => assert!(false),
            Err(err) => assert_eq!(err.position, SourcePosition { line: 1, column: 1 })
        }
    }

static LONG_EXPR: &'static str =
r"(3 * 4) + 2 * 4 * test + 4 * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* (a + 2 * ((476128368 + i * (((3 * 4) + 2 * 4 * test + 4 * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* (a + 2 * ((476128368 + i * ((476128368 + 21476)+ 21476)) + 21476)) * 2123 * 214 + (476128368 + 21476) * hello + 42 + 21476)+ 21476)) + 21476)) * 2123 * 214 + (476128368 + 21476) * hello + 42 +
(3 * 4) + 2 * 4 * test + 4 * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* (a + 2 * ((476128368 + i * (((3 * 4) + 2 * 4 * test + 4 * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* (a + 2 * ((476128368 + i * ((476128368 + 21476)+ 21476)) + 21476)) * 2123 * 214 + (476128368 + 21476) * hello + 42
+ 21476)+ 21476)) + 21476)) * 2123 * 214 + (476128368 + 21476) * hello + 42";
    #[bench]
    fn bench_expression(bench: &mut ::test::Bencher) {

        let result = (term as fn (_) -> _)
            .parse(LONG_EXPR);
        assert!(result.is_ok());
        assert_eq!(result.unwrap().1, "");
        bench.iter(|| {
            let result = (term as fn (_) -> _)
                .parse(LONG_EXPR);
            let _ = ::test::black_box(result);
        })
    }

    #[test]
    fn sep_by_error_consume() {
        let mut p = sep_by::<Vec<_>, _, _>(string("abc"), satisfy(|c| c == ','));
        let err = p.parse("ab,abc")
            .map(|x| format!("{:?}", x))
            .unwrap_err();
        assert_eq!(err.position, SourcePosition { line: 1, column: 1});
    }

    #[test]
    fn optional_error_consume() {
        let mut p = optional(string("abc"));
        let err = p.parse("ab")
            .map(|x| format!("{:?}", x))
            .unwrap_err();
        assert_eq!(err.position, SourcePosition { line: 1, column: 1});
    }
    #[test]
    fn chainl1_error_consume() {
        let mut p = chainl1(string("abc"), satisfy(|c| c == ',').map(|_| Box::new(|&:l, _| l) as Box<FnMut(_, _) -> _>));
        assert!(p.parse("abc,ab").is_err());
    }

    #[test]
    fn inner_error_consume() {
        let mut p = many::<Vec<_>, _>(between(string("["), string("]"), satisfy(|c| c.is_digit(10))));
        let result = p.parse("[1][2][]");
        assert!(result.is_err(), format!("{:?}", result));
        let error = result
            .map(|x| format!("{:?}", x))
            .unwrap_err();
        assert_eq!(error.position, SourcePosition { line: 1, column: 8 });
    }

    #[test]
    fn infinite_recursion_in_box_parser() {
        let _: Result<(Vec<_>, _), _> = (many(Box::new(digit())))
            .parse("1");
    }
}
<commit_msg>Added feature(test) to prepare for stabilization changes<commit_after>#![unstable]
#![feature(core, collections, unicode)]
#![cfg_attr(test, feature(test))]

//!This crate contains parser combinators, roughly based on the Haskell library [parsec](http://hackage.haskell.org/package/parsec).
//!
//!A parser in this library can be described as a function which takes some input and if it
//!is succesful, returns a value together with the remaining input.
//!A parser combinator is a function which takes one or more parsers and returns a new parser.
//!For instance the `many` parser can be used to convert a parser for single digits into one that
//!parses multiple digits.
//!
//!# Examples:
//!
//!```
//! extern crate "parser-combinators" as parser_combinators;
//! use parser_combinators::{spaces, many1, sep_by, digit, satisfy, Parser, ParserExt, ParseError};
//!
//! fn main() {
//!     let input = "1234, 45,78";
//!     let spaces = spaces();
//!     let integer = spaces.clone()//Parse spaces first and use the with method to only keep the result of the next parser
//!         .with(many1(digit()).map(|string: String| string.parse::<i32>().unwrap()));//parse a string of digits into an i32
//!     //Parse integers separated by commas, skipping whitespace
//!     let mut integer_list = sep_by(integer, spaces.skip(satisfy(|c| c == ',')));
//!
//!     //Call parse with the input to execute the parser
//!     let result: Result<(Vec<i32>, &str), ParseError> = integer_list.parse(input);
//!     match result {
//!         Ok((value, _remaining_input)) => println!("{:?}", value),
//!         Err(err) => println!("{}", err)
//!     }
//! }
//!```
//!
//!If we need a parser that is mutually recursive we can define a free function which internally
//!can in turn be used as a parser (Note that we need to explicitly cast the function, this should
//!not be necessary once changes in rustc to make orphan checking less restrictive gets implemented)
//!
//!`expr` is written fully general here which may not be necessary in a specific implementation
//!The `Stream` trait is predefined to work with array slices, string slices and iterators
//!meaning that in this case it could be defined as
//!`fn expr(input: State<&str>) -> ParseResult<Expr, &str>`
//!
//!```
//! extern crate "parser-combinators" as parser_combinators;
//! use parser_combinators::{between, spaces, many1, sep_by, satisfy, Parser, ParserExt,
//! ParseResult};
//! use parser_combinators::primitives::{State, Stream};
//!
//! #[derive(Debug, PartialEq)]
//! enum Expr {
//!     Id(String),
//!     Array(Vec<Expr>),
//! }
//! fn expr<I>(input: State<I>) -> ParseResult<Expr, I>
//!     where I: Stream<Item=char> {
//!     let word = many1(satisfy(|c| c.is_alphabetic()));
//!     let comma_list = sep_by(expr as fn (_) -> _, satisfy(|c| c == ','));
//!     let array = between(satisfy(|c| c == '['), satisfy(|c| c == ']'), comma_list);
//!     let spaces = spaces();
//!     spaces.clone().with(
//!             word.map(Expr::Id)
//!             .or(array.map(Expr::Array))
//!         ).parse_state(input)
//! }
//!
//! fn main() {
//!     let result = (expr as fn (_) -> _)
//!         .parse("[[], hello, [world]]");
//!     let expr = Expr::Array(vec![
//!           Expr::Array(Vec::new())
//!         , Expr::Id("hello".to_string())
//!         , Expr::Array(vec![Expr::Id("world".to_string())])
//!     ]);
//!     assert_eq!(result, Ok((expr, "")));
//! }
//!```

#[cfg(test)]
extern crate test;

#[doc(inline)]
pub use primitives::{Parser, ParseResult, ParseError};
#[doc(inline)]
pub use char::{
    any_char,
    digit,
    space,
    spaces,
    newline,
    crlf,
    tab,
    upper,
    lower,
    letter,
    alpha_num,
    hex_digit,
    oct_digit,
    string,
    satisfy,
};
#[doc(inline)]
pub use combinator::{
    between,
    chainl1,
    many,
    many1,
    optional,
    sep_by,
    try,
    value,
    unexpected,
    not_followed_by,

    ParserExt
};

macro_rules! static_fn {
    (($($arg: pat, $arg_ty: ty),*) -> $ret: ty { $body: expr }) => { { fn temp($($arg: $arg_ty),*) -> $ret { $body } temp } }
}

///Module containing the primitive types which is used to create and compose more advanced parsers
pub mod primitives;
///Module containing all specific parsers
pub mod combinator;
///Module containg parsers specialized on character streams
pub mod char;

#[cfg(test)]
mod tests {
    use super::*;
    use super::primitives::{SourcePosition, State, Stream, Error, Consumed};

    fn integer<'a, I>(input: State<I>) -> ParseResult<i64, I>
        where I: Stream<Item=char> {
        let (s, input) = try!(many1::<String, _>(digit())
            .parse_state(input));
        let mut n = 0;
        for c in s.chars() {
            n = n * 10 + (c as i64 - '0' as i64);
        }
        Ok((n, input))
    }

    #[test]
    fn test_integer() {
        let result = (integer as fn(_) -> _).parse("123");
        assert_eq!(result, Ok((123i64, "")));
    }
    #[test]
    fn list() {
        let mut p = sep_by(integer as fn(_) -> _, satisfy(|c| c == ','));
        let result = p.parse("123,4,56");
        assert_eq!(result, Ok((vec![123, 4, 56], "")));
    }
    #[test]
    fn iterator() {
        let result = (integer as fn(_) -> _).parse("123".chars())
            .map(|(i, mut input)| (i, input.next()));
        assert_eq!(result, Ok((123i64, None)));
    }
    #[test]
    fn field() {
        let word = many(satisfy(|c| c.is_alphanumeric()));
        let word2 = many(satisfy(|c| c.is_alphanumeric()));
        let spaces = spaces();
        let c_decl = word
            .skip(spaces.clone())
            .skip(satisfy(|c| c == ':'))
            .skip(spaces)
            .and(word2)
            .parse("x: int");
        assert_eq!(c_decl, Ok((("x".to_string(), "int".to_string()), "")));
    }
    #[test]
    fn source_position() {
        let source =
r"
123
";
        let result = spaces()
            .with(integer as fn(_) -> _)
            .skip(spaces())
            .parse_state(State::new(source));
        let state = Consumed::Consumed(State {
            position: SourcePosition { line: 3, column: 1 },
            input: ""
        });
        assert_eq!(result, Ok((123i64, state)));
    }

    #[derive(Debug, PartialEq)]
    enum Expr {
        Id(String),
        Int(i64),
        Array(Vec<Expr>),
        Plus(Box<Expr>, Box<Expr>),
        Times(Box<Expr>, Box<Expr>),
    }
    fn expr(input: State<&str>) -> ParseResult<Expr, &str> {
        let word = many1(satisfy(|c| c.is_alphabetic()));
        let integer = integer as fn (_) -> _;
        let array = between(satisfy(|c| c == '['), satisfy(|c| c == ']'), sep_by(expr as fn (_) -> _, satisfy(|c| c == ',')));
        let spaces = spaces();
        spaces.clone().with(
                word.map(Expr::Id)
                .or(integer.map(Expr::Int))
                .or(array.map(Expr::Array))
                .or(between(satisfy(|c| c == '('), satisfy(|c| c == ')'), term as fn (_) -> _))
            ).skip(spaces)
            .parse_state(input)
    }

    #[test]
    fn expression() {
        let result = sep_by(expr as fn (_) -> _, satisfy(|c| c == ','))
            .parse("int, 100, [[], 123]");
        let exprs = vec![
              Expr::Id("int".to_string())
            , Expr::Int(100)
            , Expr::Array(vec![Expr::Array(vec![]), Expr::Int(123)])
        ];
        assert_eq!(result, Ok((exprs, "")));
    }

    #[test]
    fn expression_error() {
        let input =
r"
,123
";
        let result = (expr as fn (_) -> _)
            .parse(input);
        let err = ParseError {
            position: SourcePosition { line: 2, column: 1 },
            errors: vec![Error::Unexpected(','), Error::Message("Expected digit".to_string())]
        };
        assert_eq!(result, Err(err));
    }

    fn term(input: State<&str>) -> ParseResult<Expr, &str> {

        let mul = satisfy(|c| c == '*')
            .map(|_| Box::new(|&mut:l, r| Expr::Times(Box::new(l), Box::new(r))) as Box<FnMut(_, _) -> _>);
        let add = satisfy(|c| c == '+')
            .map(|_| Box::new(|&mut:l, r| Expr::Plus(Box::new(l), Box::new(r))) as Box<FnMut(_, _) -> _>);
        let factor = chainl1(expr as fn (_) -> _, mul);
        chainl1(factor, add)
            .parse_state(input)
    }

    #[test]
    fn operators() {
        let input =
r"
1 * 2 + 3 * test
";
        let (result, _) = (term as fn (_) -> _)
            .parse(input)
            .unwrap();

        let e1 = Expr::Times(Box::new(Expr::Int(1)), Box::new(Expr::Int(2)));
        let e2 = Expr::Times(Box::new(Expr::Int(3)), Box::new(Expr::Id("test".to_string())));
        assert_eq!(result, Expr::Plus(Box::new(e1), Box::new(e2)));
    }

    fn follow(input: State<&str>) -> ParseResult<(), &str> {
        match input.clone().uncons_char() {
            Ok((c, _)) => {
                if c.is_alphanumeric() {
                    Err(Consumed::Empty(ParseError::new(input.position, Error::Unexpected(c))))
                }
                else {
                    Ok(((), Consumed::Empty(input)))
                }
            }
            Err(_) => Ok(((), Consumed::Empty(input)))
        }
    }
    #[test]
    fn error_position() {
        let mut p = string("let").skip(follow as fn (_) -> _).map(|x| x.to_string())
            .or(many1(satisfy(|c| c.is_digit(10))));
        match p.parse("le123") {
            Ok(_) => assert!(false),
            Err(err) => assert_eq!(err.position, SourcePosition { line: 1, column: 1 })
        }
    }

static LONG_EXPR: &'static str =
r"(3 * 4) + 2 * 4 * test + 4 * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* (a + 2 * ((476128368 + i * (((3 * 4) + 2 * 4 * test + 4 * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* (a + 2 * ((476128368 + i * ((476128368 + 21476)+ 21476)) + 21476)) * 2123 * 214 + (476128368 + 21476) * hello + 42 + 21476)+ 21476)) + 21476)) * 2123 * 214 + (476128368 + 21476) * hello + 42 +
(3 * 4) + 2 * 4 * test + 4 * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* (a + 2 * ((476128368 + i * (((3 * 4) + 2 * 4 * test + 4 * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
* (a + 2 * ((476128368 + i * ((476128368 + 21476)+ 21476)) + 21476)) * 2123 * 214 + (476128368 + 21476) * hello + 42
+ 21476)+ 21476)) + 21476)) * 2123 * 214 + (476128368 + 21476) * hello + 42";
    #[bench]
    fn bench_expression(bench: &mut ::test::Bencher) {

        let result = (term as fn (_) -> _)
            .parse(LONG_EXPR);
        assert!(result.is_ok());
        assert_eq!(result.unwrap().1, "");
        bench.iter(|| {
            let result = (term as fn (_) -> _)
                .parse(LONG_EXPR);
            let _ = ::test::black_box(result);
        })
    }

    #[test]
    fn sep_by_error_consume() {
        let mut p = sep_by::<Vec<_>, _, _>(string("abc"), satisfy(|c| c == ','));
        let err = p.parse("ab,abc")
            .map(|x| format!("{:?}", x))
            .unwrap_err();
        assert_eq!(err.position, SourcePosition { line: 1, column: 1});
    }

    #[test]
    fn optional_error_consume() {
        let mut p = optional(string("abc"));
        let err = p.parse("ab")
            .map(|x| format!("{:?}", x))
            .unwrap_err();
        assert_eq!(err.position, SourcePosition { line: 1, column: 1});
    }
    #[test]
    fn chainl1_error_consume() {
        let mut p = chainl1(string("abc"), satisfy(|c| c == ',').map(|_| Box::new(|&:l, _| l) as Box<FnMut(_, _) -> _>));
        assert!(p.parse("abc,ab").is_err());
    }

    #[test]
    fn inner_error_consume() {
        let mut p = many::<Vec<_>, _>(between(string("["), string("]"), satisfy(|c| c.is_digit(10))));
        let result = p.parse("[1][2][]");
        assert!(result.is_err(), format!("{:?}", result));
        let error = result
            .map(|x| format!("{:?}", x))
            .unwrap_err();
        assert_eq!(error.position, SourcePosition { line: 1, column: 8 });
    }

    #[test]
    fn infinite_recursion_in_box_parser() {
        let _: Result<(Vec<_>, _), _> = (many(Box::new(digit())))
            .parse("1");
    }
}
<|endoftext|>