<commit_before>//
// nfa.rs
// Nondeterministic Finite Automata
//

use std::collections::HashSet;

type StateID = usize;

struct State {
    id: StateID,
    accept: bool,
    trans: Vec<Transition>
}

impl State {
    // finds the outgoing transitions for a given label
    pub fn find_transition(&self, label: Label) -> Vec<&Transition> {
        let mut res : Vec<&Transition> = Vec::new();
        for t in self.trans.iter() {
            if t.label == label {
                res.push(&t);
            }
        }

        res
    }

    fn step(&self, c: char) -> HashSet<StateID> {
        let mut res : HashSet<StateID> = HashSet::new();
        for t in self.trans.iter() {
            match t.label {
                Label::Any => res.insert(t.target),
                Label::Symbol(s) if s == c => res.insert(t.target),
                _ => true
            };
        }

        res
    }
}

#[derive(PartialEq, Eq, Debug, Hash)]
enum Label {
    Epsilon,
    Any,
    Symbol(char)
}

struct Transition {
    label: Label,
    target: StateID
}

#[test]
fn states() {
    let mut ns = NFABuilder::new();
    let id1 = ns.new_state();
    let id2 = ns.new_state();
    assert_eq!(id1, 0);
    assert_eq!(id2, 1);

    assert!(ns.check_id(id1));
    assert!(ns.check_id(id2));
    assert!(!ns.check_id(100));
}

#[test]
fn add_transition() {
    let mut ns = NFABuilder::new();
    let id1 = ns.new_state();
    let id2 = ns.new_state();
    let id3 = ns.new_state();
    assert!(ns.add_transition(id1, id2, Label::Epsilon));
    assert!(ns.add_transition(id2, id3, Label::Symbol('a')));

    assert!(!ns.add_transition(id1, 1200, Label::Epsilon));
    assert!(!ns.add_transition(1020, id2, Label::Epsilon));
    assert!(!ns.add_transition(1320, 1200, Label::Epsilon));
}

#[test]
fn transitions() {
    let mut ns = NFABuilder::new();
    let id1 = ns.new_state();
    let id2 = ns.new_state();
    let id3 = ns.new_state();
    assert!(ns.add_transition(id1, id2, Label::Epsilon));
    assert!(ns.add_transition(id1, id3, Label::Epsilon));
    assert!(ns.add_transition(id2, id3, Label::Symbol('a')));

    let st1 = ns.get_state(id1).unwrap();
    let t0 = st1.find_transition(Label::Symbol('b'));
    assert_eq!(t0.len(), 0);
    let t1 = st1.find_transition(Label::Epsilon);
    assert_eq!(t1.len(), 2);

    let st2 = ns.get_state(t1[0].target).unwrap();
    let t2 = st2.find_transition(Label::Symbol('a'));
    assert_eq!(t2.len(), 1);

    assert_eq!(t2[0].target, id3);
    assert_eq!(t1[1].target, id3);
}

struct NFA {
    start: StateID,
    //accept: StateID,
    states: Vec<State>,
    //tables: Vec<HashMap<Label, Vec<StateID>>>
}

impl NFA {
    // pub fn new(st: Vec<State>, start: StateID, acc: StateID) -> NFA {
    //     let mut tbls : Vec<HashMap<Label, Vec<StateID>>> = Vec::with_capacity(st.len());
    //     for s in st.iter() {

    //     }
    // }

    pub fn get_state(&self, sid: StateID) -> Option<&State> {
        self.states.get(sid)
    }

    fn epsilon_closure(&self, states: HashSet<StateID>) -> HashSet<StateID> {
        let mut clos : HashSet<StateID> = HashSet::new();
        let mut stack : Vec<StateID> = Vec::with_capacity(states.len());

        stack.extend(states.clone());
        clos.extend(states.clone());

        while !stack.is_empty() {
            let s = stack.pop().unwrap();         // safe to unwrap because stack is not empty
            let st = self.get_state(s).unwrap();  // safe to unwrap because states on the stack must exist
            for t in st.find_transition(Label::Epsilon).iter() {
                if !clos.contains(&t.target) {
                    clos.insert(t.target);
                    stack.push(t.target);
                }
            }
        }

        clos
    }

    fn step(&self, s: StateID, c: char) -> HashSet<StateID> {
        let st = self.get_state(s).unwrap();
        st.step(c)
    }

    fn steps(&self, vs: HashSet<StateID>, c: char) -> HashSet<StateID> {
        let mut res : HashSet<StateID> = HashSet::new();
        for s in vs.iter() {
            let ss = self.step(*s, c);
            res = res.union(&ss).cloned().collect();
        }

        res
    }

    // this is terribly inefficient for the moment, will
    // optimize if/when necessary
    pub fn simulate(&self, word: &str) -> bool {
        let mut s = self.epsilon_closure(state_set(self.start));
        for c in word.chars() {
            s = self.epsilon_closure(self.steps(s, c));
        }
        s.iter().any(|&i| self.get_state(i).unwrap().accept)
        //s.contains(&self.accept)
    }
}

fn state_set(s: StateID) -> HashSet<StateID> {
    let mut hs : HashSet<StateID> = HashSet::new();
    hs.insert(s);
    hs
}

// specification for a NFA built by translation from a regexp
enum Spec {
    Single(Label),
    Union(Box<Spec>, Box<Spec>),
    Concat(Box<Spec>, Box<Spec>),
    Star(Box<Spec>)
}

impl Spec {
    // helper methods for building specs
    fn single(l: Label) -> Box<Spec> {
        Box::new(Spec::Single(l))
    }

    fn union(s1: Box<Spec>, s2: Box<Spec>) -> Box<Spec> {
        Box::new(Spec::Union(s1, s2))
    }

    fn concat(s1: Box<Spec>, s2: Box<Spec>) -> Box<Spec> {
        Box::new(Spec::Concat(s1, s2))
    }

    fn star(s: Box<Spec>) -> Box<Spec> {
        Box::new(Spec::Star(s))
    }
}

struct NFAid {
    start: StateID,
    accept: StateID
}

struct NFABuilder {
    states: Vec<State>
}

impl NFABuilder {
    fn new() -> NFABuilder {
        let st : Vec<State> = Vec::with_capacity(10);
        NFABuilder { states: st }
    }

    pub fn build_from_spec(s: Spec) -> NFA {
        let st : Vec<State> = Vec::with_capacity(10);
        let mut builder = NFABuilder { states: st };

        let nid = builder.build(s);
        builder.set_accepting(nid.accept);

        NFA { states: builder.states, start: nid.start }
    }

    fn build(&mut self, s: Spec) -> NFAid {
        match s {
            Spec::Single(l) => self.single(l),
            Spec::Union(s1, s2) => {
                let n1 = self.build(*s1);
                let n2 = self.build(*s2);
                self.union(n1, n2)
            },
            Spec::Concat(s1, s2) => {
                let n1 = self.build(*s1);
                let n2 = self.build(*s2);
                self.concat(n1, n2)
            },
            Spec::Star(s) => {
                let n = self.build(*s);
                self.star(n)
            }
        }
    }

    fn fuse_nfas(&mut self, ns: Vec<NFAid>) -> NFAid {
        let nstart = self.new_state();
        for n in ns {
            self.add_transition(nstart, n.start, Label::Epsilon);
            self.set_accepting(n.accept);
        }

        NFAid { start: nstart, accept: 0 }  // TODO: states must have accept flag
    }

    fn new_state(&mut self) -> StateID {
        let res = self.states.len();
        let trs : Vec<Transition> = Vec::with_capacity(2);
        let st = State { id: res, trans: trs, accept: false };

        self.states.push(st);
        res
    }

    pub fn check_id(&self, id: StateID) -> bool {
        id < self.states.len()
    }

    fn get_state_mut(&mut self, id: StateID) -> &mut State {
        // TODO: change to safe, checked access
        unsafe {
            let res : &mut State = self.states.get_unchecked_mut(id);
            res
        }
    }

    // TODO: check for deletion
    fn get_state(&self, id: StateID) -> Option<&State> {
        self.states.get(id)
    }

    fn set_accepting(&mut self, id: StateID) {
        let st = self.get_state_mut(id);
        st.accept = true;
    }

    fn add_transition(&mut self, src_id: StateID, dst_id: StateID, label: Label) -> bool {
        if self.check_id(src_id) && self.check_id(dst_id) {
            let src_st = self.get_state_mut(src_id);
            let trans = Transition { label: label, target: dst_id };
            src_st.trans.push(trans);
            true
        }
        else {
            false
        }
    }

    fn single(&mut self, l: Label) -> NFAid {
        let st = self.new_state();
        let acc = self.new_state();

        self.add_transition(st, acc, l);

        NFAid { start: st, accept: acc }
    }

    fn union(&mut self, n1: NFAid, n2: NFAid) -> NFAid {
        let start = self.new_state();
        self.add_transition(start, n1.start, Label::Epsilon);
        self.add_transition(start, n2.start, Label::Epsilon);

        let acc = self.new_state();
        self.add_transition(n1.accept, acc, Label::Epsilon);
        self.add_transition(n2.accept, acc, Label::Epsilon);

        NFAid { start: start, accept: acc }
    }

    fn concat(&mut self, n1: NFAid, n2: NFAid) -> NFAid {
        self.add_transition(n1.accept, n2.start, Label::Epsilon);
        NFAid { start: n1.start, accept: n2.accept }
    }

    fn star(&mut self, n: NFAid) -> NFAid {
        let start = self.new_state();
        let acc = self.new_state();

        // transitions connecting old and new start/accept states
        self.add_transition(start, n.start, Label::Epsilon);
        self.add_transition(n.accept, acc, Label::Epsilon);

        // transition connecting start and accept for 0 occurrences
        self.add_transition(start, acc, Label::Epsilon);

        // loop transition for any number of occurrences
        self.add_transition(n.accept, n.start, Label::Epsilon);

        NFAid { start: start, accept: acc }
    }

}

#[test]
fn test_single() {
    let n1 = NFABuilder::build_from_spec(Spec::Single(Label::Epsilon));

    let s0 = n1.get_state(n1.start).unwrap();
    assert_eq!(s0.trans.len(), 1);
    assert_eq!(s0.trans[0].label, Label::Epsilon);

    let ts = s0.find_transition(Label::Epsilon);
    assert_eq!(ts.len(), 1);
    assert!(n1.get_state(ts[0].target).unwrap().accept);

    let s1 = n1.get_state(ts[0].target).unwrap();
    assert_eq!(s1.trans.len(), 0);
    let t1 = s1.find_transition(Label::Epsilon);
    assert_eq!(t1.len(), 0);
}

#[test]
fn test_union() {
    use nfa::Label::{Epsilon, Any};

    let spec = Spec::union(Spec::single(Epsilon), Spec::single(Any));
    let nfa = NFABuilder::build_from_spec(*spec);

    let s0 = nfa.get_state(nfa.start).unwrap();
    assert_eq!(s0.trans.len(), 2);
    assert_eq!(s0.trans[0].label, Epsilon);
    assert_eq!(s0.trans[1].label, Epsilon);

    let t0 = s0.find_transition(Epsilon);
    assert_eq!(t0.len(), 2);

    let s1 = nfa.get_state(t0[0].target).unwrap();
    assert_eq!(s1.trans.len(), 1);
    let s2 = nfa.get_state(t0[1].target).unwrap();
    assert_eq!(s2.trans.len(), 1);

    let s3 = nfa.get_state(s1.trans[0].target).unwrap();
    assert_eq!(s3.trans.len(), 1);
    assert_eq!(s3.trans[0].label, Epsilon);
    let s4 = nfa.get_state(s2.trans[0].target).unwrap();
    assert_eq!(s4.trans.len(), 1);
    assert_eq!(s4.trans[0].label, Epsilon);

    assert_eq!(s3.trans[0].target, s4.trans[0].target);
    assert!(nfa.get_state(s3.trans[0].target).unwrap().accept);

    let acc = nfa.get_state(s4.trans[0].target).unwrap();
    assert_eq!(acc.trans.len(), 0);
}

#[test]
fn test_eps_clos() {
    use nfa::Label::{Epsilon,Any};

    let n1 = NFABuilder::build_from_spec(Spec::Single(Any));
    let cls1 = n1.epsilon_closure(state_set(n1.start));
    assert_eq!(cls1.len(), 1);
    assert!(cls1.contains(&n1.start));

    let n2 = NFABuilder::build_from_spec(Spec::Single(Epsilon));
    let cls2 = n2.epsilon_closure(state_set(n2.start));
    assert_eq!(cls2.len(), 2);
    assert!(cls2.contains(&n2.start));
    //assert!(cls2.contains(&n2.accept));
}

#[test]
fn test_simulation() {
    use nfa::Label::*;

    let sp1 = Spec::single(Symbol('a'));

    let n1 = NFABuilder::build_from_spec(*sp1);
    assert!(n1.simulate("a"));
    assert!(!n1.simulate("x"));
    assert!(!n1.simulate("aaa"));

    let sym_a = Spec::single(Symbol('a'));
    let sym_b = Spec::single(Symbol('b'));
    let sp2 = Spec::star(Spec::union(sym_a, sym_b));
    let n2 = NFABuilder::build_from_spec(*sp2);
    assert!(n2.simulate("aabb"));
    assert!(n2.simulate("bbaa"));
    assert!(n2.simulate(""));
    assert!(n2.simulate("aaaaaaaba"));
    assert!(!n2.simulate("aaaaaaabbbbcaaa"));
}
<commit_msg>final nfa combining multiple nfas<commit_after>//
// nfa.rs
// Nondeterministic Finite Automata
//

use std::collections::HashSet;

type StateID = usize;

struct State {
    //id: StateID,
    accept: bool,
    trans: Vec<Transition>
}

impl State {
    // finds the outgoing transitions for a given label
    pub fn find_transition(&self, label: Label) -> Vec<&Transition> {
        let mut res : Vec<&Transition> = Vec::new();
        for t in self.trans.iter() {
            if t.label == label {
                res.push(&t);
            }
        }

        res
    }

    fn step(&self, c: char) -> HashSet<StateID> {
        let mut res : HashSet<StateID> = HashSet::new();
        for t in self.trans.iter() {
            match t.label {
                Label::Any => res.insert(t.target),
                Label::Symbol(s) if s == c => res.insert(t.target),
                _ => true
            };
        }

        res
    }
}

#[derive(PartialEq, Eq, Debug, Hash)]
enum Label {
    Epsilon,
    Any,
    Symbol(char)
}

struct Transition {
    label: Label,
    target: StateID
}

#[test]
fn states() {
    let mut ns = NFABuilder::new();
    let id1 = ns.new_state();
    let id2 = ns.new_state();
    assert_eq!(id1, 0);
    assert_eq!(id2, 1);

    assert!(ns.check_id(id1));
    assert!(ns.check_id(id2));
    assert!(!ns.check_id(100));
}

#[test]
fn add_transition() {
    let mut ns = NFABuilder::new();
    let id1 = ns.new_state();
    let id2 = ns.new_state();
    let id3 = ns.new_state();
    assert!(ns.add_transition(id1, id2, Label::Epsilon));
    assert!(ns.add_transition(id2, id3, Label::Symbol('a')));

    assert!(!ns.add_transition(id1, 1200, Label::Epsilon));
    assert!(!ns.add_transition(1020, id2, Label::Epsilon));
    assert!(!ns.add_transition(1320, 1200, Label::Epsilon));
}

#[test]
fn transitions() {
    let mut ns = NFABuilder::new();
    let id1 = ns.new_state();
    let id2 = ns.new_state();
    let id3 = ns.new_state();
    assert!(ns.add_transition(id1, id2, Label::Epsilon));
    assert!(ns.add_transition(id1, id3, Label::Epsilon));
    assert!(ns.add_transition(id2, id3, Label::Symbol('a')));

    let st1 = ns.get_state(id1).unwrap();
    let t0 = st1.find_transition(Label::Symbol('b'));
    assert_eq!(t0.len(), 0);
    let t1 = st1.find_transition(Label::Epsilon);
    assert_eq!(t1.len(), 2);

    let st2 = ns.get_state(t1[0].target).unwrap();
    let t2 = st2.find_transition(Label::Symbol('a'));
    assert_eq!(t2.len(), 1);

    assert_eq!(t2[0].target, id3);
    assert_eq!(t1[1].target, id3);
}

struct NFA {
    start: StateID,
    //accept: StateID,
    states: Vec<State>,
    //tables: Vec<HashMap<Label, Vec<StateID>>>
}

impl NFA {
    // pub fn new(st: Vec<State>, start: StateID, acc: StateID) -> NFA {
    //     let mut tbls : Vec<HashMap<Label, Vec<StateID>>> = Vec::with_capacity(st.len());
    //     for s in st.iter() {

    //     }
    // }

    pub fn get_state(&self, sid: StateID) -> Option<&State> {
        self.states.get(sid)
    }

    fn epsilon_closure(&self, states: HashSet<StateID>) -> HashSet<StateID> {
        let mut clos : HashSet<StateID> = HashSet::new();
        let mut stack : Vec<StateID> = Vec::with_capacity(states.len());

        stack.extend(states.clone());
        clos.extend(states.clone());

        while !stack.is_empty() {
            let s = stack.pop().unwrap();         // safe to unwrap because stack is not empty
            let st = self.get_state(s).unwrap();  // safe to unwrap because states on the stack must exist
            for t in st.find_transition(Label::Epsilon).iter() {
                if !clos.contains(&t.target) {
                    clos.insert(t.target);
                    stack.push(t.target);
                }
            }
        }

        clos
    }

    fn step(&self, s: StateID, c: char) -> HashSet<StateID> {
        let st = self.get_state(s).unwrap();
        st.step(c)
    }

    fn steps(&self, vs: HashSet<StateID>, c: char) -> HashSet<StateID> {
        let mut res : HashSet<StateID> = HashSet::new();
        for s in vs.iter() {
            let ss = self.step(*s, c);
            res = res.union(&ss).cloned().collect();
        }

        res
    }

    // this is terribly inefficient for the moment, will
    // optimize if/when necessary
    pub fn simulate(&self, word: &str) -> bool {
        let mut s = self.epsilon_closure(state_set(self.start));
        for c in word.chars() {
            s = self.epsilon_closure(self.steps(s, c));
        }
        s.iter().any(|&i| self.get_state(i).unwrap().accept)
        //s.contains(&self.accept)
    }
}

fn state_set(s: StateID) -> HashSet<StateID> {
    let mut hs : HashSet<StateID> = HashSet::new();
    hs.insert(s);
    hs
}

// specification for a NFA built by translation from a regexp
enum Spec {
    Single(Label),
    Union(Box<Spec>, Box<Spec>),
    Concat(Box<Spec>, Box<Spec>),
    Star(Box<Spec>)
}

impl Spec {
    // helper methods for building specs
    fn single(l: Label) -> Box<Spec> {
        Box::new(Spec::Single(l))
    }

    fn union(s1: Box<Spec>, s2: Box<Spec>) -> Box<Spec> {
        Box::new(Spec::Union(s1, s2))
    }

    fn concat(s1: Box<Spec>, s2: Box<Spec>) -> Box<Spec> {
        Box::new(Spec::Concat(s1, s2))
    }

    fn star(s: Box<Spec>) -> Box<Spec> {
        Box::new(Spec::Star(s))
    }
}

struct NFAid {
    start: StateID,
    accept: StateID
}

struct NFABuilder {
    states: Vec<State>
}

impl NFABuilder {
    fn new() -> NFABuilder {
        let st : Vec<State> = Vec::with_capacity(10);
        NFABuilder { states: st }
    }

    pub fn build_from_spec(s: Spec) -> NFA {
        let st : Vec<State> = Vec::with_capacity(10);
        let mut builder = NFABuilder { states: st };

        let nid = builder.build(s);
        builder.set_accepting(nid.accept);

        NFA { states: builder.states, start: nid.start }
    }

    // build NFA integrating all the specs; consumes (drains) the vector ss
    pub fn build_from_specs(ss: &mut Vec<Spec>) -> NFA {
        let mut builder = NFABuilder::new();
        let ns = ss.drain(..).map(|s| builder.build(s)).collect();
        let start = builder.fuse_nfas(ns);

        NFA { states: builder.states, start: start }
    }

    fn build(&mut self, s: Spec) -> NFAid {
        match s {
            Spec::Single(l) => self.single(l),
            Spec::Union(s1, s2) => {
                let n1 = self.build(*s1);
                let n2 = self.build(*s2);
                self.union(n1, n2)
            },
            Spec::Concat(s1, s2) => {
                let n1 = self.build(*s1);
                let n2 = self.build(*s2);
                self.concat(n1, n2)
            },
            Spec::Star(s) => {
                let n = self.build(*s);
                self.star(n)
            }
        }
    }

    // fuse all nfas creating a new start state; return ID of new start state
    fn fuse_nfas(&mut self, ns: Vec<NFAid>) -> StateID {
        let nstart = self.new_state();
        for n in ns {
            self.add_transition(nstart, n.start, Label::Epsilon);
            self.set_accepting(n.accept);
        }

        nstart
    }

    fn new_state(&mut self) -> StateID {
        let res = self.states.len();
        let trs : Vec<Transition> = Vec::with_capacity(2);
        //let st = State { id: res, trans: trs, accept: false };
        let st = State { trans: trs, accept: false };

        self.states.push(st);
        res
    }

    pub fn check_id(&self, id: StateID) -> bool {
        id < self.states.len()
    }

    fn get_state_mut(&mut self, id: StateID) -> &mut State {
        // TODO: change to safe, checked access
        unsafe {
            let res : &mut State = self.states.get_unchecked_mut(id);
            res
        }
    }

    // TODO: check for deletion
    fn get_state(&self, id: StateID) -> Option<&State> {
        self.states.get(id)
    }

    fn set_accepting(&mut self, id: StateID) {
        let st = self.get_state_mut(id);
        st.accept = true;
    }

    fn add_transition(&mut self, src_id: StateID, dst_id: StateID, label: Label) -> bool {
        if self.check_id(src_id) && self.check_id(dst_id) {
            let src_st = self.get_state_mut(src_id);
            let trans = Transition { label: label, target: dst_id };
            src_st.trans.push(trans);
            true
        }
        else {
            false
        }
    }

    fn single(&mut self, l: Label) -> NFAid {
        let st = self.new_state();
        let acc = self.new_state();

        self.add_transition(st, acc, l);

        NFAid { start: st, accept: acc }
    }

    fn union(&mut self, n1: NFAid, n2: NFAid) -> NFAid {
        let start = self.new_state();
        self.add_transition(start, n1.start, Label::Epsilon);
        self.add_transition(start, n2.start, Label::Epsilon);

        let acc = self.new_state();
        self.add_transition(n1.accept, acc, Label::Epsilon);
        self.add_transition(n2.accept, acc, Label::Epsilon);

        NFAid { start: start, accept: acc }
    }

    fn concat(&mut self, n1: NFAid, n2: NFAid) -> NFAid {
        self.add_transition(n1.accept, n2.start, Label::Epsilon);
        NFAid { start: n1.start, accept: n2.accept }
    }

    fn star(&mut self, n: NFAid) -> NFAid {
        let start = self.new_state();
        let acc = self.new_state();

        // transitions connecting old and new start/accept states
        self.add_transition(start, n.start, Label::Epsilon);
        self.add_transition(n.accept, acc, Label::Epsilon);

        // transition connecting start and accept for 0 occurrences
        self.add_transition(start, acc, Label::Epsilon);

        // loop transition for any number of occurrences
        self.add_transition(n.accept, n.start, Label::Epsilon);

        NFAid { start: start, accept: acc }
    }

}

#[test]
fn test_single() {
    let n1 = NFABuilder::build_from_spec(Spec::Single(Label::Epsilon));

    let s0 = n1.get_state(n1.start).unwrap();
    assert_eq!(s0.trans.len(), 1);
    assert_eq!(s0.trans[0].label, Label::Epsilon);

    let ts = s0.find_transition(Label::Epsilon);
    assert_eq!(ts.len(), 1);
    assert!(n1.get_state(ts[0].target).unwrap().accept);

    let s1 = n1.get_state(ts[0].target).unwrap();
    assert_eq!(s1.trans.len(), 0);
    let t1 = s1.find_transition(Label::Epsilon);
    assert_eq!(t1.len(), 0);
}

#[test]
fn test_union() {
    use nfa::Label::{Epsilon, Any};

    let spec = Spec::union(Spec::single(Epsilon), Spec::single(Any));
    let nfa = NFABuilder::build_from_spec(*spec);

    let s0 = nfa.get_state(nfa.start).unwrap();
    assert_eq!(s0.trans.len(), 2);
    assert_eq!(s0.trans[0].label, Epsilon);
    assert_eq!(s0.trans[1].label, Epsilon);

    let t0 = s0.find_transition(Epsilon);
    assert_eq!(t0.len(), 2);

    let s1 = nfa.get_state(t0[0].target).unwrap();
    assert_eq!(s1.trans.len(), 1);
    let s2 = nfa.get_state(t0[1].target).unwrap();
    assert_eq!(s2.trans.len(), 1);

    let s3 = nfa.get_state(s1.trans[0].target).unwrap();
    assert_eq!(s3.trans.len(), 1);
    assert_eq!(s3.trans[0].label, Epsilon);
    let s4 = nfa.get_state(s2.trans[0].target).unwrap();
    assert_eq!(s4.trans.len(), 1);
    assert_eq!(s4.trans[0].label, Epsilon);

    assert_eq!(s3.trans[0].target, s4.trans[0].target);
    assert!(nfa.get_state(s3.trans[0].target).unwrap().accept);

    let acc = nfa.get_state(s4.trans[0].target).unwrap();
    assert_eq!(acc.trans.len(), 0);
}

#[test]
fn test_eps_clos() {
    use nfa::Label::{Epsilon,Any};

    let n1 = NFABuilder::build_from_spec(Spec::Single(Any));
    let cls1 = n1.epsilon_closure(state_set(n1.start));
    assert_eq!(cls1.len(), 1);
    assert!(cls1.contains(&n1.start));

    let n2 = NFABuilder::build_from_spec(Spec::Single(Epsilon));
    let cls2 = n2.epsilon_closure(state_set(n2.start));
    assert_eq!(cls2.len(), 2);
    assert!(cls2.contains(&n2.start));
    //assert!(cls2.contains(&n2.accept));
}

#[test]
fn test_simulation() {
    use nfa::Label::*;

    let sp1 = Spec::single(Symbol('a'));

    let n1 = NFABuilder::build_from_spec(*sp1);
    assert!(n1.simulate("a"));
    assert!(!n1.simulate("x"));
    assert!(!n1.simulate("aaa"));

    let sym_a = Spec::single(Symbol('a'));
    let sym_b = Spec::single(Symbol('b'));
    let sp2 = Spec::star(Spec::union(sym_a, sym_b));
    let n2 = NFABuilder::build_from_spec(*sp2);
    assert!(n2.simulate("aabb"));
    assert!(n2.simulate("bbaa"));
    assert!(n2.simulate(""));
    assert!(n2.simulate("aaaaaaaba"));
    assert!(!n2.simulate("aaaaaaabbbbcaaa"));
}

#[test]
fn test_build_specs() {
    use nfa::Label::*;

    let sp1 = Spec::single(Symbol('a'));
    let sp2 = Spec::concat(Spec::single(Symbol('b')), Spec::single(Symbol('a')));

    let mut v : Vec<Spec> = Vec::with_capacity(2);
    v.push(*sp1);
    v.push(*sp2);

    let n = NFABuilder::build_from_specs(&mut v);

    assert!(n.simulate("ba"));
    assert!(n.simulate("a"));
    assert!(!n.simulate("baa"));
}
<|endoftext|>


<commit_before>#![feature(catch_panic)]

extern crate itertools;
extern crate numeric_float;

#[macro_use] mod util;

use std::thread::catch_panic;
use itertools::Itertools;
use numeric_float::{Numeric, n32f, n64f, n32p, n64p};
use util::corner_cases as ccs;

#[test]
fn test_arith() {
    macro_rules! test_bin_op {
        ($op:tt; $ty:ty; option) => {
            {
                type F = <$ty as Numeric>::Float;
                for (af, bf) in ccs::<F>().cartesian_product(ccs::<F>()) {
                    let cf = as_expr!(af $op bf);
                    let an = <$ty>::from_float(af).unwrap();
                    let bn = <$ty>::from_float(bf).unwrap();
                    let cn = <$ty>::from_float(as_expr!(an $op bn));
                    match cn {
                        Some(cn) => {
                            let cnf = cn.into_float();
                            assert!(!cnf.is_nan());
                            assert_eq!(cnf, cf);
                        },
                        None => {
                            assert!(cf.is_nan());
                        }
                    }
                }
            }
        };

        ($op:tt; $ty:ty; panic) => {
            {
                type F = <$ty as Numeric>::Float;
                for (af, bf) in ccs::<F>().cartesian_product(ccs::<F>()) {
                    let cf = as_expr!(af $op bf);
                    let an = <$ty>::from_float(af).unwrap();
                    let bn = <$ty>::from_float(bf).unwrap();
                    let cn = catch_panic(move || as_expr!(an $op bn)).ok();
                    match cn {
                        Some(cn) => {
                            let cnf = cn.into_float();
                            assert!(!cnf.is_nan());
                            assert_eq!(cnf, cf);
                        },
                        None => {
                            assert!(cf.is_nan());
                        }
                    }
                }
            }
        };
    }

    test_bin_op!(+; n32f; option);
    test_bin_op!(-; n32f; option);
    test_bin_op!(*; n32f; option);
    test_bin_op!(/; n32f; option);
    test_bin_op!(%; n32f; option);

    test_bin_op!(+; n64f; option);
    test_bin_op!(-; n64f; option);
    test_bin_op!(*; n64f; option);
    test_bin_op!(/; n64f; option);
    test_bin_op!(%; n64f; option);

    test_bin_op!(+; n32p; panic);
    test_bin_op!(-; n32p; panic);
    test_bin_op!(*; n32p; panic);
    test_bin_op!(/; n32p; panic);
    test_bin_op!(%; n32p; panic);

    test_bin_op!(+; n64p; panic);
    test_bin_op!(-; n64p; panic);
    test_bin_op!(*; n64p; panic);
    test_bin_op!(/; n64p; panic);
    test_bin_op!(%; n64p; panic);
}
<commit_msg>Changed test to use thread::spawn i/o catch_panic.<commit_after>extern crate itertools;
extern crate numeric_float;

#[macro_use] mod util;

use std::thread::spawn;
use itertools::Itertools;
use numeric_float::{Numeric, n32f, n64f, n32p, n64p};
use util::corner_cases as ccs;

#[test]
fn test_arith() {
    macro_rules! test_bin_op {
        ($op:tt; $ty:ty; option) => {
            {
                type F = <$ty as Numeric>::Float;
                for (af, bf) in ccs::<F>().cartesian_product(ccs::<F>()) {
                    let cf = as_expr!(af $op bf);
                    let an = <$ty>::from_float(af).unwrap();
                    let bn = <$ty>::from_float(bf).unwrap();
                    let cn = <$ty>::from_float(as_expr!(an $op bn));
                    match cn {
                        Some(cn) => {
                            let cnf = cn.into_float();
                            assert!(!cnf.is_nan());
                            assert_eq!(cnf, cf);
                        },
                        None => {
                            assert!(cf.is_nan());
                        }
                    }
                }
            }
        };

        ($op:tt; $ty:ty; panic) => {
            {
                type F = <$ty as Numeric>::Float;
                for (af, bf) in ccs::<F>().cartesian_product(ccs::<F>()) {
                    let cf = as_expr!(af $op bf);
                    let an = <$ty>::from_float(af).unwrap();
                    let bn = <$ty>::from_float(bf).unwrap();
                    let cn = spawn(move || as_expr!(an $op bn)).join().ok();
                    match cn {
                        Some(cn) => {
                            let cnf = cn.into_float();
                            assert!(!cnf.is_nan());
                            assert_eq!(cnf, cf);
                        },
                        None => {
                            assert!(cf.is_nan());
                        }
                    }
                }
            }
        };
    }

    test_bin_op!(+; n32f; option);
    test_bin_op!(-; n32f; option);
    test_bin_op!(*; n32f; option);
    test_bin_op!(/; n32f; option);
    test_bin_op!(%; n32f; option);

    test_bin_op!(+; n64f; option);
    test_bin_op!(-; n64f; option);
    test_bin_op!(*; n64f; option);
    test_bin_op!(/; n64f; option);
    test_bin_op!(%; n64f; option);

    test_bin_op!(+; n32p; panic);
    test_bin_op!(-; n32p; panic);
    test_bin_op!(*; n32p; panic);
    test_bin_op!(/; n32p; panic);
    test_bin_op!(%; n32p; panic);

    test_bin_op!(+; n64p; panic);
    test_bin_op!(-; n64p; panic);
    test_bin_op!(*; n64p; panic);
    test_bin_op!(/; n64p; panic);
    test_bin_op!(%; n64p; panic);
}
<|endoftext|>


<commit_before>/*!

Things for the construction and sending of HTTP requests.

If you want to make a request, `RequestWriter::new` is where you start, and
`RequestWriter.read_response` is where you will send the request and read the response.

```rust
extern crate http;
extern crate url;

use http::client::RequestWriter;
use http::method::Get;
use url::Url;

fn main() {
    let url = Url::parse("http://example.com/").unwrap();
    let request: RequestWriter = match RequestWriter::new(Get, url) {
        Ok(request) => request,
        Err(error) => fail!(":-( {}", error),
    };

    let mut response = match request.read_response() {
        Ok(response) => response,
        Err((_request, error)) => fail!(":-( {}", error),
    };
    // Now you have a `ResponseReader`; see http::client::response for docs on that.
}
```

If you wish to send a request body (e.g. POST requests), I'm sorry to have to tell you that there is
not *good* support for this yet. However, it can be done; here is an example:

```rust
# extern crate url;
# extern crate http;
# use http::client::RequestWriter;
# use http::method::Get;
# use url::Url;
# #[allow(unused_must_use)]
# fn main() {
# let url = Url::parse("http://example.com/").unwrap();
let data = b"var1=val1&var2=val2";
let mut request: RequestWriter = match RequestWriter::new(Get, url) {
    Ok(request) => request,
    Err(error) => fail!(":-( {}", error),
};

request.headers.content_length = Some(data.len());
request.write(data);
let response = match request.read_response() {
    Ok(response) => response,
    Err((_request, error)) => fail!(":-( {}", error),
};
# }
```

*/

use url::Url;
use method::Method;
use std::io::{IoError, IoResult};
use std::io::net::get_host_addresses;
use std::io::net::ip::{SocketAddr, Ipv4Addr};
use buffer::BufferedStream;
use headers::request::HeaderCollection;
use headers::host::Host;
use connecter::Connecter;

use client::response::ResponseReader;

/*impl ResponseReader {
    {
        let mut buf = [0u8, ..2000];
        match stream.read(buf) {
            None => fail!("Read error :-("),  // conditions for error interception, again
            Some(bytes_read) => {
                println!(str::from_bytes(buf.slice_to(bytes_read)));
            }
        }

        match response {
            Some(response) => Ok(response),
            None => Err(self),
        }
    }
}*/

pub struct RequestWriter<S = super::NetworkStream> {
    // The place to write to (typically a network stream, which is
    // io::net::tcp::TcpStream or an SSL wrapper around that)
    stream: Option<BufferedStream<S>>,
    headers_written: bool,

    /// The originating IP address of the request.
    pub remote_addr: Option<SocketAddr>,

    /// The host name and IP address that the request was sent to; this must always be specified for
    /// HTTP/1.1 requests (or the request will be rejected), but for HTTP/1.0 requests the Host
    /// header was not defined, and so this field will probably be None in such cases.
    //host: Host,  // Now headers.host

    /// The headers sent with the request.
    pub headers: Box<HeaderCollection>,

    /// The HTTP method for the request.
    pub method: Method,

    /// The URL being requested.
    pub url: Url,

    /// Should we use SSL?
    use_ssl: bool,
}

/// Low-level HTTP request writing support
///
/// Moderately hacky, and due to current limitations in the TcpStream arrangement reading cannot
/// take place until writing is completed.
///
/// At present, this only supports making one request per connection.
impl<S: Reader + Writer = super::NetworkStream> RequestWriter<S> {
    /// Create a `RequestWriter` writing to the specified location
    pub fn new(method: Method, url: Url) -> IoResult<RequestWriter<S>> {
        RequestWriter::new_request(method, url, false, true)
    }

    pub fn new_request(method: Method, url: Url, use_ssl: bool, auto_detect_ssl: bool) -> IoResult<RequestWriter<S>> {
        let host = Host {
            name: url.domain().unwrap().to_string(),
            port: url.port(),
        };

        let remote_addr = try!(url_to_socket_addr(&url, &host));
        info!("using ip address {} for {}", remote_addr, host.name.as_slice());

        fn url_to_socket_addr(url: &Url, host: &Host) -> IoResult<SocketAddr> {
            // Just grab the first IPv4 address
            let addrs = try!(get_host_addresses(host.name.as_slice()));
            let addr = addrs.move_iter().find(|&a| {
                match a {
                    Ipv4Addr(..) => true,
                    _ => false
                }
            });

            // TODO: Error handling
            let addr = addr.unwrap();

            // Default to 80, using the port specified or 443 if the protocol is HTTPS.
            let port = match host.port {
                Some(p) => p,
                // FIXME: case insensitivity?
                None => if url.scheme.as_slice() == "https" { 443 } else { 80 },
            };

            Ok(SocketAddr {
                ip: addr,
                port: port
            })
        }

        let mut request = RequestWriter {
            stream: None,
            headers_written: false,
            remote_addr: Some(remote_addr),
            headers: box HeaderCollection::new(),
            method: method,
            url: url,
            use_ssl: use_ssl,
        };

        if auto_detect_ssl {
            // FIXME: case insensitivity?
            request.use_ssl = request.url.scheme.as_slice() == "https";
        }

        request.headers.host = Some(host);
        Ok(request)
    }
}

impl<S: Connecter + Reader + Writer = super::NetworkStream> RequestWriter<S> {

    /// Connect to the remote host if not already connected.
    pub fn try_connect(&mut self) -> IoResult<()> {
        if self.stream.is_none() {
            self.connect()
        } else {
            Ok(())
        }
    }

    /// Connect to the remote host; fails if already connected.
    /// Returns ``true`` upon success and ``false`` upon failure (also use conditions).
    pub fn connect(&mut self) -> IoResult<()> {
        if !self.stream.is_none() {
            fail!("I don't think you meant to call connect() twice, you know.");
        }

        self.stream = match self.remote_addr {
            Some(addr) => {
                let stream = try!(Connecter::connect(
                    addr, self.headers.host.as_ref().unwrap().name.as_slice(), self.use_ssl));
                Some(BufferedStream::new(stream))
            },
            None => fail!("connect() called before remote_addr was set"),
        };
        Ok(())
    }

    /// Write the Request-Line and headers of the response, if we have not already done so.
    pub fn try_write_headers(&mut self) -> IoResult<()> {
        if !self.headers_written {
            self.write_headers()
        } else {
            Ok(())
        }
    }

    /// Write the Status-Line and headers of the response, in preparation for writing the body.
    ///
    /// If the headers have already been written, this will fail. See also `try_write_headers`.
    pub fn write_headers(&mut self) -> IoResult<()> {
        // This marks the beginning of the response (RFC2616 §5)
        if self.headers_written {
            fail!("RequestWriter.write_headers() called, but headers already written");
        }
        if self.stream.is_none() {
            try!(self.connect());
        }

        // Write the Request-Line (RFC2616 §5.1)
        // TODO: get to the point where we can say HTTP/1.1 with good conscience
        let (question_mark, query) = match self.url.query {
            Some(ref query) => ("?", query.as_slice()),
            None => ("", "")
        };
        try!(write!(self.stream.get_mut_ref() as &mut Writer,
            "{} {}{}{} HTTP/1.0\r\n",
            self.method, self.url.serialize_path().unwrap(), question_mark, query));

        try!(self.headers.write_all(self.stream.get_mut_ref()));
        self.headers_written = true;
        Ok(())
    }

    /**
     * Send the request and construct a `ResponseReader` out of it.
     *
     * If the request sending fails in any way, a condition will be raised; if handled, the original
     * request will be returned as an `Err`.
     */
    pub fn read_response(mut self) -> Result<ResponseReader<S>, (RequestWriter<S>, IoError)> {
        match self.try_write_headers() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.flush() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.stream.take() {
            Some(stream) => ResponseReader::construct(stream, self),
            None => unreachable!(), // TODO: is it genuinely unreachable?
        }
    }
}

/// Write the request body. Note that any calls to `write()` will cause the headers to be sent.
impl<S: Reader + Writer + Connecter = super::NetworkStream> Writer for RequestWriter<S> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        if !self.headers_written {
            try!(self.write_headers());
        }
        // TODO: decide whether using get_mut_ref() is sound
        // (it will cause failure if None)
        self.stream.as_mut().unwrap().write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        // TODO: ditto
        self.stream.as_mut().unwrap().flush()
    }
}
<commit_msg>Unbox client Request::headers.<commit_after>/*!

Things for the construction and sending of HTTP requests.

If you want to make a request, `RequestWriter::new` is where you start, and
`RequestWriter.read_response` is where you will send the request and read the response.

```rust
extern crate http;
extern crate url;

use http::client::RequestWriter;
use http::method::Get;
use url::Url;

fn main() {
    let url = Url::parse("http://example.com/").unwrap();
    let request: RequestWriter = match RequestWriter::new(Get, url) {
        Ok(request) => request,
        Err(error) => fail!(":-( {}", error),
    };

    let mut response = match request.read_response() {
        Ok(response) => response,
        Err((_request, error)) => fail!(":-( {}", error),
    };
    // Now you have a `ResponseReader`; see http::client::response for docs on that.
}
```

If you wish to send a request body (e.g. POST requests), I'm sorry to have to tell you that there is
not *good* support for this yet. However, it can be done; here is an example:

```rust
# extern crate url;
# extern crate http;
# use http::client::RequestWriter;
# use http::method::Get;
# use url::Url;
# #[allow(unused_must_use)]
# fn main() {
# let url = Url::parse("http://example.com/").unwrap();
let data = b"var1=val1&var2=val2";
let mut request: RequestWriter = match RequestWriter::new(Get, url) {
    Ok(request) => request,
    Err(error) => fail!(":-( {}", error),
};

request.headers.content_length = Some(data.len());
request.write(data);
let response = match request.read_response() {
    Ok(response) => response,
    Err((_request, error)) => fail!(":-( {}", error),
};
# }
```

*/

use url::Url;
use method::Method;
use std::io::{IoError, IoResult};
use std::io::net::get_host_addresses;
use std::io::net::ip::{SocketAddr, Ipv4Addr};
use buffer::BufferedStream;
use headers::request::HeaderCollection;
use headers::host::Host;
use connecter::Connecter;

use client::response::ResponseReader;

/*impl ResponseReader {
    {
        let mut buf = [0u8, ..2000];
        match stream.read(buf) {
            None => fail!("Read error :-("),  // conditions for error interception, again
            Some(bytes_read) => {
                println!(str::from_bytes(buf.slice_to(bytes_read)));
            }
        }

        match response {
            Some(response) => Ok(response),
            None => Err(self),
        }
    }
}*/

pub struct RequestWriter<S = super::NetworkStream> {
    // The place to write to (typically a network stream, which is
    // io::net::tcp::TcpStream or an SSL wrapper around that)
    stream: Option<BufferedStream<S>>,
    headers_written: bool,

    /// The originating IP address of the request.
    pub remote_addr: Option<SocketAddr>,

    /// The host name and IP address that the request was sent to; this must always be specified for
    /// HTTP/1.1 requests (or the request will be rejected), but for HTTP/1.0 requests the Host
    /// header was not defined, and so this field will probably be None in such cases.
    //host: Host,  // Now headers.host

    /// The headers sent with the request.
    pub headers: HeaderCollection,

    /// The HTTP method for the request.
    pub method: Method,

    /// The URL being requested.
    pub url: Url,

    /// Should we use SSL?
    use_ssl: bool,
}

/// Low-level HTTP request writing support
///
/// Moderately hacky, and due to current limitations in the TcpStream arrangement reading cannot
/// take place until writing is completed.
///
/// At present, this only supports making one request per connection.
impl<S: Reader + Writer = super::NetworkStream> RequestWriter<S> {
    /// Create a `RequestWriter` writing to the specified location
    pub fn new(method: Method, url: Url) -> IoResult<RequestWriter<S>> {
        RequestWriter::new_request(method, url, false, true)
    }

    pub fn new_request(method: Method, url: Url, use_ssl: bool, auto_detect_ssl: bool) -> IoResult<RequestWriter<S>> {
        let host = Host {
            name: url.domain().unwrap().to_string(),
            port: url.port(),
        };

        let remote_addr = try!(url_to_socket_addr(&url, &host));
        info!("using ip address {} for {}", remote_addr, host.name.as_slice());

        fn url_to_socket_addr(url: &Url, host: &Host) -> IoResult<SocketAddr> {
            // Just grab the first IPv4 address
            let addrs = try!(get_host_addresses(host.name.as_slice()));
            let addr = addrs.move_iter().find(|&a| {
                match a {
                    Ipv4Addr(..) => true,
                    _ => false
                }
            });

            // TODO: Error handling
            let addr = addr.unwrap();

            // Default to 80, using the port specified or 443 if the protocol is HTTPS.
            let port = match host.port {
                Some(p) => p,
                // FIXME: case insensitivity?
                None => if url.scheme.as_slice() == "https" { 443 } else { 80 },
            };

            Ok(SocketAddr {
                ip: addr,
                port: port
            })
        }

        let mut request = RequestWriter {
            stream: None,
            headers_written: false,
            remote_addr: Some(remote_addr),
            headers: HeaderCollection::new(),
            method: method,
            url: url,
            use_ssl: use_ssl,
        };

        if auto_detect_ssl {
            // FIXME: case insensitivity?
            request.use_ssl = request.url.scheme.as_slice() == "https";
        }

        request.headers.host = Some(host);
        Ok(request)
    }
}

impl<S: Connecter + Reader + Writer = super::NetworkStream> RequestWriter<S> {

    /// Connect to the remote host if not already connected.
    pub fn try_connect(&mut self) -> IoResult<()> {
        if self.stream.is_none() {
            self.connect()
        } else {
            Ok(())
        }
    }

    /// Connect to the remote host; fails if already connected.
    /// Returns ``true`` upon success and ``false`` upon failure (also use conditions).
    pub fn connect(&mut self) -> IoResult<()> {
        if !self.stream.is_none() {
            fail!("I don't think you meant to call connect() twice, you know.");
        }

        self.stream = match self.remote_addr {
            Some(addr) => {
                let stream = try!(Connecter::connect(
                    addr, self.headers.host.as_ref().unwrap().name.as_slice(), self.use_ssl));
                Some(BufferedStream::new(stream))
            },
            None => fail!("connect() called before remote_addr was set"),
        };
        Ok(())
    }

    /// Write the Request-Line and headers of the response, if we have not already done so.
    pub fn try_write_headers(&mut self) -> IoResult<()> {
        if !self.headers_written {
            self.write_headers()
        } else {
            Ok(())
        }
    }

    /// Write the Status-Line and headers of the response, in preparation for writing the body.
    ///
    /// If the headers have already been written, this will fail. See also `try_write_headers`.
    pub fn write_headers(&mut self) -> IoResult<()> {
        // This marks the beginning of the response (RFC2616 §5)
        if self.headers_written {
            fail!("RequestWriter.write_headers() called, but headers already written");
        }
        if self.stream.is_none() {
            try!(self.connect());
        }

        // Write the Request-Line (RFC2616 §5.1)
        // TODO: get to the point where we can say HTTP/1.1 with good conscience
        let (question_mark, query) = match self.url.query {
            Some(ref query) => ("?", query.as_slice()),
            None => ("", "")
        };
        try!(write!(self.stream.get_mut_ref() as &mut Writer,
            "{} {}{}{} HTTP/1.0\r\n",
            self.method, self.url.serialize_path().unwrap(), question_mark, query));

        try!(self.headers.write_all(self.stream.get_mut_ref()));
        self.headers_written = true;
        Ok(())
    }

    /**
     * Send the request and construct a `ResponseReader` out of it.
     *
     * If the request sending fails in any way, a condition will be raised; if handled, the original
     * request will be returned as an `Err`.
     */
    pub fn read_response(mut self) -> Result<ResponseReader<S>, (RequestWriter<S>, IoError)> {
        match self.try_write_headers() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.flush() {
            Ok(()) => (),
            Err(err) => return Err((self, err)),
        };
        match self.stream.take() {
            Some(stream) => ResponseReader::construct(stream, self),
            None => unreachable!(), // TODO: is it genuinely unreachable?
        }
    }
}

/// Write the request body. Note that any calls to `write()` will cause the headers to be sent.
impl<S: Reader + Writer + Connecter = super::NetworkStream> Writer for RequestWriter<S> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        if !self.headers_written {
            try!(self.write_headers());
        }
        // TODO: decide whether using get_mut_ref() is sound
        // (it will cause failure if None)
        self.stream.as_mut().unwrap().write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        // TODO: ditto
        self.stream.as_mut().unwrap().flush()
    }
}
<|endoftext|>


<commit_before>/// low level wrapper for the state machine, encoding and decoding from lapin-async
use lapin_async::connection::*;
use lapin_async::format::frame::*;

use nom::{IResult,Offset};
use cookie_factory::GenError;
use bytes::BytesMut;
use std::iter::repeat;
use std::io::{self,Error,ErrorKind};
use std::time::Duration;
use futures::{Async,Poll,Sink,Stream,StartSend,Future,future};
use tokio_io::{AsyncRead,AsyncWrite};
use tokio_io::codec::{Decoder,Encoder,Framed};
use tokio_timer::{Interval,Timer};
use client::ConnectionOptions;

/// implements tokio-io's Decoder and Encoder
pub struct AMQPCodec;

impl Decoder for AMQPCodec {
    type Item = Frame;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Frame>, io::Error> {
        let (consumed, f) = match frame(buf) {
          IResult::Incomplete(_) => {
            return Ok(None)
          },
          IResult::Error(e) => {
            return Err(io::Error::new(io::ErrorKind::Other, format!("parse error: {:?}", e)))
          },
          IResult::Done(i, frame) => {
            (buf.offset(i), frame)
          }
        };

        trace!("decoded frame: {:?}", f);

        buf.split_to(consumed);

        Ok(Some(f))
    }
}

impl Encoder for AMQPCodec {
    type Item = Frame;
    type Error = io::Error;

    fn encode(&mut self, frame: Frame, buf: &mut BytesMut) -> Result<(), Self::Error> {
      let length = buf.len();
      if length < 8192 {
        //reserve more capacity and intialize it
        buf.extend(repeat(0).take(8192 - length));
      }
      trace!("will encode and write frame: {:?}", frame);

      loop {
        let gen_res = match &frame {
          &Frame::ProtocolHeader => {
            gen_protocol_header((buf, 0)).map(|tup| tup.1)
          },
          &Frame::Heartbeat(_) => {
            gen_heartbeat_frame((buf, 0)).map(|tup| tup.1)
          },
          &Frame::Method(channel, ref method) => {
            gen_method_frame((buf, 0), channel, method).map(|tup| tup.1)
          },
          &Frame::Header(channel_id, class_id, ref header) => {
            gen_content_header_frame((buf, 0), channel_id, class_id, header.body_size, &header.properties).map(|tup| tup.1)
          },
          &Frame::Body(channel_id, ref data) => {
            gen_content_body_frame((buf, 0), channel_id, data).map(|tup| tup.1)
          }
        };

        match gen_res {
          Ok(sz) => {
            buf.truncate(sz);
            trace!("serialized frame: {} bytes", sz);
            return Ok(());
          },
          Err(e) => {
            error!("error generating frame: {:?}", e);
            match e {
              GenError::BufferTooSmall(sz) => {
                buf.extend(repeat(0).take(sz - length));
                //return Err(Error::new(ErrorKind::InvalidData, "send buffer too small"));
              },
              GenError::InvalidOffset | GenError::CustomError(_) | GenError::NotYetImplemented => {
                return Err(Error::new(ErrorKind::InvalidData, "could not generate"));
              }
            }
          }
        }
      }
    }
}

/// Wrappers over a `Framed` stream using `AMQPCodec` and lapin-async's `Connection`
pub struct AMQPTransport<T> {
  upstream: Framed<T,AMQPCodec>,
  heartbeat: Interval,
  pub conn: Connection,
}

impl<T> AMQPTransport<T>
   where T: AsyncRead+AsyncWrite,
         T: 'static               {

  /// starts the connection process
  ///
  /// returns a future of a `AMQPTransport` that is connected
  pub fn connect(upstream: Framed<T,AMQPCodec>, options: &ConnectionOptions) -> Box<Future<Item = AMQPTransport<T>, Error = io::Error>> {
    let mut conn = Connection::new();
    conn.set_credentials(&options.username, &options.password);
    conn.set_vhost(&options.vhost);
    conn.set_heartbeat(options.heartbeat);
    if let Err(e) = conn.connect() {
      let err = format!("Failed to connect: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }

    let mut t = AMQPTransport {
      upstream:  upstream,
      heartbeat: Timer::default().interval(Duration::from_secs(conn.configuration.heartbeat as u64)),
      conn:      conn,
    };

    t.send_and_handle_frames();

    let mut connector = AMQPTransportConnector {
      transport: Some(t)
    };

    trace!("pre-poll");
    connector.poll();
    trace!("post-poll");

    Box::new(connector)
  }

  fn poll_heartbeat(&mut self) {
    if let Ok(Async::Ready(_)) = self.heartbeat.poll() {
      trace!("Sending heartbeat");
      if let Err(e) = self.send_frame(Frame::Heartbeat(0)) {
        error!("Failed to send heartbeat: {:?}", e);
      }
    }
  }

  fn poll_upstream(&mut self) -> Poll<Option<()>, io::Error> {
    let value = match self.upstream.poll() {
      Ok(Async::Ready(t)) => t,
      Ok(Async::NotReady) => {
        trace!("upstream poll gave NotReady");
        return Ok(Async::NotReady);
      },
      Err(e) => {
        error!("upstream poll gave error: {:?}", e);
        return Err(From::from(e));
      },
    };

    if let Some(frame) = value {
      trace!("upstream poll gave frame: {:?}", frame);
      self.conn.handle_frame(frame);
      self.send_frames();
      Ok(Async::Ready(Some(())))
    } else {
      error!("upstream poll gave Ready(None)");
      Ok(Async::Ready(None))
    }
  }

  pub fn send_and_handle_frames(&mut self) -> Poll<Option<()>, io::Error> {
    self.send_frames();
    self.handle_frames()
  }

  pub fn send_frames(&mut self) {
    //FIXME: find a way to use a future here
    while let Some(f) = self.conn.next_frame() {
      if let Err(e) = self.send_frame(f) {
        error!("Failed to send frame: {:?}", e);
        break;
      }
    }
  }

  fn send_frame(&mut self, frame: Frame) -> Poll<(), io::Error> {
      self.start_send(frame).and_then(|_| self.poll_complete())
  }

  pub fn handle_frames(&mut self) -> Poll<Option<()>, io::Error> {
    for _ in 0..30 {
      if try_ready!(self.poll()).is_none() {
        return Ok(Async::Ready(None));
      }
    }
    self.poll()
  }
}

/// implements a future of `AMQPTransport`
///
/// this structure is used to perform the AMQP handshake and provide
/// a connected transport afterwards
pub struct AMQPTransportConnector<T> {
  pub transport: Option<AMQPTransport<T>>,
}

impl<T> Future for AMQPTransportConnector<T>
    where T: AsyncRead + AsyncWrite,
          T: 'static {

  type Item  = AMQPTransport<T>;
  type Error = io::Error;

  fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
    debug!("AMQPTransportConnector poll transport is none? {}", self.transport.is_none());
    let mut transport = self.transport.take().unwrap();

    //we might have received a frame before here
    transport.send_and_handle_frames();

    debug!("conn state: {:?}", transport.conn.state);
    if transport.conn.state == ConnectionState::Connected {
      debug!("already connected");
      return Ok(Async::Ready(transport))
    }

    trace!("waiting before poll");
    transport.poll().map(|value| {
      match value {
        Async::Ready(Some(_)) => {
          if transport.conn.state == ConnectionState::Connected {
            // Upstream had frames available and we're connected, the transport is ready
            Async::Ready(transport)
          } else {
            // Upstream had frames but we're not yet connected, continue polling
            transport.poll();
            self.transport = Some(transport);
            Async::NotReady
          }
        },
        _ => {
          // Upstream had no frames
          self.transport = Some(transport);
          Async::NotReady
        },
      }
    })
  }
}

impl<T> Stream for AMQPTransport<T>
    where T: AsyncRead + AsyncWrite,
          T: 'static {
    type Item = ();
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<()>, io::Error> {
      trace!("stream poll");
      self.poll_heartbeat();
      self.poll_upstream()
    }
}

impl<T> Sink for AMQPTransport<T>
    where T: AsyncWrite {
    type SinkItem = Frame;
    type SinkError = io::Error;

    fn start_send(&mut self, item: Frame) -> StartSend<Frame, io::Error> {
        trace!("sink start send");
        self.upstream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), io::Error> {
        trace!("sink poll_complete");
        self.upstream.poll_complete()
    }
}

<commit_msg>futures: don't eat poll retval in transport<commit_after>/// low level wrapper for the state machine, encoding and decoding from lapin-async
use lapin_async::connection::*;
use lapin_async::format::frame::*;

use nom::{IResult,Offset};
use cookie_factory::GenError;
use bytes::BytesMut;
use std::iter::repeat;
use std::io::{self,Error,ErrorKind};
use std::time::Duration;
use futures::{Async,Poll,Sink,Stream,StartSend,Future,future};
use tokio_io::{AsyncRead,AsyncWrite};
use tokio_io::codec::{Decoder,Encoder,Framed};
use tokio_timer::{Interval,Timer};
use client::ConnectionOptions;

/// implements tokio-io's Decoder and Encoder
pub struct AMQPCodec;

impl Decoder for AMQPCodec {
    type Item = Frame;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Frame>, io::Error> {
        let (consumed, f) = match frame(buf) {
          IResult::Incomplete(_) => {
            return Ok(None)
          },
          IResult::Error(e) => {
            return Err(io::Error::new(io::ErrorKind::Other, format!("parse error: {:?}", e)))
          },
          IResult::Done(i, frame) => {
            (buf.offset(i), frame)
          }
        };

        trace!("decoded frame: {:?}", f);

        buf.split_to(consumed);

        Ok(Some(f))
    }
}

impl Encoder for AMQPCodec {
    type Item = Frame;
    type Error = io::Error;

    fn encode(&mut self, frame: Frame, buf: &mut BytesMut) -> Result<(), Self::Error> {
      let length = buf.len();
      if length < 8192 {
        //reserve more capacity and intialize it
        buf.extend(repeat(0).take(8192 - length));
      }
      trace!("will encode and write frame: {:?}", frame);

      loop {
        let gen_res = match &frame {
          &Frame::ProtocolHeader => {
            gen_protocol_header((buf, 0)).map(|tup| tup.1)
          },
          &Frame::Heartbeat(_) => {
            gen_heartbeat_frame((buf, 0)).map(|tup| tup.1)
          },
          &Frame::Method(channel, ref method) => {
            gen_method_frame((buf, 0), channel, method).map(|tup| tup.1)
          },
          &Frame::Header(channel_id, class_id, ref header) => {
            gen_content_header_frame((buf, 0), channel_id, class_id, header.body_size, &header.properties).map(|tup| tup.1)
          },
          &Frame::Body(channel_id, ref data) => {
            gen_content_body_frame((buf, 0), channel_id, data).map(|tup| tup.1)
          }
        };

        match gen_res {
          Ok(sz) => {
            buf.truncate(sz);
            trace!("serialized frame: {} bytes", sz);
            return Ok(());
          },
          Err(e) => {
            error!("error generating frame: {:?}", e);
            match e {
              GenError::BufferTooSmall(sz) => {
                buf.extend(repeat(0).take(sz - length));
                //return Err(Error::new(ErrorKind::InvalidData, "send buffer too small"));
              },
              GenError::InvalidOffset | GenError::CustomError(_) | GenError::NotYetImplemented => {
                return Err(Error::new(ErrorKind::InvalidData, "could not generate"));
              }
            }
          }
        }
      }
    }
}

/// Wrappers over a `Framed` stream using `AMQPCodec` and lapin-async's `Connection`
pub struct AMQPTransport<T> {
  upstream: Framed<T,AMQPCodec>,
  heartbeat: Interval,
  pub conn: Connection,
}

impl<T> AMQPTransport<T>
   where T: AsyncRead+AsyncWrite,
         T: 'static               {

  /// starts the connection process
  ///
  /// returns a future of a `AMQPTransport` that is connected
  pub fn connect(upstream: Framed<T,AMQPCodec>, options: &ConnectionOptions) -> Box<Future<Item = AMQPTransport<T>, Error = io::Error>> {
    let mut conn = Connection::new();
    conn.set_credentials(&options.username, &options.password);
    conn.set_vhost(&options.vhost);
    conn.set_heartbeat(options.heartbeat);
    if let Err(e) = conn.connect() {
      let err = format!("Failed to connect: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }

    let mut t = AMQPTransport {
      upstream:  upstream,
      heartbeat: Timer::default().interval(Duration::from_secs(conn.configuration.heartbeat as u64)),
      conn:      conn,
    };

    if let Err(e) = t.send_and_handle_frames() {
      let err = format!("Failed to handle frames: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }

    let mut connector = AMQPTransportConnector {
      transport: Some(t)
    };

    trace!("pre-poll");
    if let Err(e) = connector.poll() {
      let err = format!("Failed to handle frames: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }
    trace!("post-poll");

    Box::new(connector)
  }

  fn poll_heartbeat(&mut self) {
    if let Ok(Async::Ready(_)) = self.heartbeat.poll() {
      trace!("Sending heartbeat");
      if let Err(e) = self.send_frame(Frame::Heartbeat(0)) {
        error!("Failed to send heartbeat: {:?}", e);
      }
    }
  }

  fn poll_upstream(&mut self) -> Poll<Option<()>, io::Error> {
    let value = match self.upstream.poll() {
      Ok(Async::Ready(t)) => t,
      Ok(Async::NotReady) => {
        trace!("upstream poll gave NotReady");
        return Ok(Async::NotReady);
      },
      Err(e) => {
        error!("upstream poll gave error: {:?}", e);
        return Err(From::from(e));
      },
    };

    if let Some(frame) = value {
      trace!("upstream poll gave frame: {:?}", frame);
      if let Err(e) = self.conn.handle_frame(frame) {
        let err = format!("failed to handle frame: {:?}", e);
        return Err(io::Error::new(io::ErrorKind::Other, err));
      }
      self.send_frames();
      Ok(Async::Ready(Some(())))
    } else {
      error!("upstream poll gave Ready(None)");
      Ok(Async::Ready(None))
    }
  }

  pub fn send_and_handle_frames(&mut self) -> Poll<Option<()>, io::Error> {
    self.send_frames();
    self.handle_frames()
  }

  pub fn send_frames(&mut self) {
    //FIXME: find a way to use a future here
    while let Some(f) = self.conn.next_frame() {
      if let Err(e) = self.send_frame(f) {
        error!("Failed to send frame: {:?}", e);
        break;
      }
    }
  }

  fn send_frame(&mut self, frame: Frame) -> Poll<(), io::Error> {
      self.start_send(frame).and_then(|_| self.poll_complete())
  }

  pub fn handle_frames(&mut self) -> Poll<Option<()>, io::Error> {
    for _ in 0..30 {
      if try_ready!(self.poll()).is_none() {
        return Ok(Async::Ready(None));
      }
    }
    self.poll()
  }
}

/// implements a future of `AMQPTransport`
///
/// this structure is used to perform the AMQP handshake and provide
/// a connected transport afterwards
pub struct AMQPTransportConnector<T> {
  pub transport: Option<AMQPTransport<T>>,
}

impl<T> Future for AMQPTransportConnector<T>
    where T: AsyncRead + AsyncWrite,
          T: 'static {

  type Item  = AMQPTransport<T>;
  type Error = io::Error;

  fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
    debug!("AMQPTransportConnector poll transport is none? {}", self.transport.is_none());
    let mut transport = self.transport.take().unwrap();

    //we might have received a frame before here
    transport.send_and_handle_frames()?;

    debug!("conn state: {:?}", transport.conn.state);
    if transport.conn.state == ConnectionState::Connected {
      debug!("already connected");
      return Ok(Async::Ready(transport))
    }

    trace!("waiting before poll");
    transport.poll().map(|value| {
      match value {
        Async::Ready(Some(_)) => {
          if transport.conn.state == ConnectionState::Connected {
            // Upstream had frames available and we're connected, the transport is ready
            Async::Ready(transport)
          } else {
            // Upstream had frames but we're not yet connected, continue polling
            transport.poll();
            self.transport = Some(transport);
            Async::NotReady
          }
        },
        _ => {
          // Upstream had no frames
          self.transport = Some(transport);
          Async::NotReady
        },
      }
    })
  }
}

impl<T> Stream for AMQPTransport<T>
    where T: AsyncRead + AsyncWrite,
          T: 'static {
    type Item = ();
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<()>, io::Error> {
      trace!("stream poll");
      self.poll_heartbeat();
      self.poll_upstream()
    }
}

impl<T> Sink for AMQPTransport<T>
    where T: AsyncWrite {
    type SinkItem = Frame;
    type SinkError = io::Error;

    fn start_send(&mut self, item: Frame) -> StartSend<Frame, io::Error> {
        trace!("sink start send");
        self.upstream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), io::Error> {
        trace!("sink poll_complete");
        self.upstream.poll_complete()
    }
}

<|endoftext|>


<commit_before>import comm::port;
import result::result;
import std::net::tcp::{tcp_err_data, tcp_connect_err_data};
import std::net::ip::ip_addr;

type ReadPort = port<result<~[u8], tcp_err_data>>;

/**
An abstract client socket connection. This mirrors the bits
of the net::tcp::tcp_socket interface that we care about
while letting us have additional implementations for
mocking
*/
trait Connection {
    fn read_start() -> ReadPort;
    fn read_stop(-read_port: ReadPort) -> result<(), tcp_err_data>;
}

trait ConnectionFactory<C/*: connection*/> {
    fn connect(ip: ip_addr, port: uint) -> result<C, tcp_connect_err_data>;
}

impl Connection for tcp_socket {
    fn read_start() -> result<ReadPort, tcp_err_data> {
        import std::net::tcp::tcp_socket;
        self.read_start()
    }

    fn read_stop(-read_port: ReadPort) -> result<(), tcp_err_data> {
        import std::net::tcp::tcp_socket;
        self.read_stop(read_port)
    }
}

type UvConnectionFactory = ();

<commit_msg>Finish ConnectionFactory and mocks<commit_after>import comm::port;
import result::result;
import std::net::tcp::{tcp_err_data, tcp_connect_err_data};
import std::net::ip::ip_addr;

type ReadPort = port<result<~[u8], tcp_err_data>>;

/**
An abstract client socket connection. This mirrors the bits
of the net::tcp::tcp_socket interface that we care about
while letting us have additional implementations for
mocking
*/
trait Connection {
    fn read_start() -> result<ReadPort, tcp_err_data>;
    fn read_stop(-read_port: ReadPort) -> result<(), tcp_err_data>;
}

trait ConnectionFactory<C: Connection> {
    fn connect(ip: ip_addr, port: uint) -> result<C, tcp_connect_err_data>;
}

impl of Connection for tcp_socket {
    fn read_start() -> result<ReadPort, tcp_err_data> {
        import std::net::tcp::tcp_socket;
        self.read_start()
    }

    fn read_stop(-read_port: ReadPort) -> result<(), tcp_err_data> {
        import std::net::tcp::tcp_socket;
        self.read_stop(read_port)
    }
}

type UvConnectionFactory = ();

impl of ConnectionFactory<tcp_socket> for UvConnectionFactory {
    fn connect(ip: ip_addr, port: uint) -> result<tcp_socket, tcp_connect_err_data> {
        import std::uv_global_loop;
        import std::net::tcp::connect;
        let iotask = uv_global_loop::get();
        ret connect(copy ip, port, iotask);
    }
}

type MockConnection = {
    read_start_fn: fn@() -> result<ReadPort, tcp_err_data>,
    read_stop_fn: fn@(-ReadPort) -> result<(), tcp_err_data>
};

impl of Connection for MockConnection {
    fn read_start() -> result<ReadPort, tcp_err_data> {
        self.read_start_fn()
    }

    fn read_stop(-read_port: ReadPort) -> result<(), tcp_err_data> {
        self.read_stop_fn(read_port)
    }
}

type MockConnectionFactory = {
    connect_fn: fn@(ip_addr, uint) -> result<MockConnection, tcp_connect_err_data>
};

impl of ConnectionFactory<MockConnection> for MockConnectionFactory {
    fn connect(ip: ip_addr, port: uint) -> result<MockConnection, tcp_connect_err_data> {
        self.connect_fn(ip, port)
    }
}
<|endoftext|>


<commit_before>use self::grammar::job_list;

#[derive(Debug, PartialEq)]
pub struct Job {
    pub command: String,
    pub args: Vec<String>,
}

impl Job {
    fn new(command: String, args: Vec<String>) -> Job {
        Job {
            command: command,
            args: args,
        }
    }
}

pub fn parse(code: &str) -> Vec<Job> {
    job_list(code).unwrap()  // TODO don't unwrap, handle parse error
}

peg! grammar(r#"
use super::Job;

#[pub]
job_list -> Vec<Job>
    = (unused* newline)* jobs:job ++ ((job_ending+ unused*)+) (newline unused*)* { jobs }
    / (unused*) ** newline { vec![] }

job -> Job
    = whitespace? res:_job whitespace? comment? { res }

_job -> Job
    = args:word ++ whitespace { let mut args = args.clone(); Job::new(args.remove(0), args) }

word -> String
    = double_quoted_word
    / single_quoted_word
    / [^ \t\r\n#;]+ { match_str.to_string() }

double_quoted_word -> String
    = ["] word:_double_quoted_word ["] { word }

_double_quoted_word -> String
    = [^"]+ { match_str.to_string() }

single_quoted_word -> String
    = ['] word:_single_quoted_word ['] { word }

_single_quoted_word -> String
    = [^']+ { match_str.to_string() }

unused -> ()
    = whitespace comment? { () }
    / comment { () }

comment -> ()
    = [#] [^\r\n]*

whitespace -> ()
    = [ \t]+

job_ending -> ()
    = [;]
    / newline
    / newline

newline -> ()
    = [\r\n]
"#);

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

    #[test]
    fn single_job_no_args() {
        let jobs = job_list("cat").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("cat", jobs[0].command);
        assert_eq!(0, jobs[0].args.len());
    }

    #[test]
    fn single_job_with_args() {
        let jobs = job_list("ls -al dir").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("ls", jobs[0].command);
        assert_eq!("-al", jobs[0].args[0]);
        assert_eq!("dir", jobs[0].args[1]);
    }

    #[test]
    fn multiple_jobs_with_args() {
        let jobs = job_list("ls -al;cat tmp.txt").unwrap();
        assert_eq!(2, jobs.len());
        assert_eq!("ls", jobs[0].command);
        assert_eq!("-al", jobs[0].args[0]);
        assert_eq!("cat", jobs[1].command);
        assert_eq!("tmp.txt", jobs[1].args[0]);
    }

    #[test]
    fn parse_empty_string() {
        let jobs = job_list("").unwrap();
        assert_eq!(0, jobs.len());
    }

    #[test]
    fn multiple_white_space_between_words() {
        let jobs = job_list("ls \t -al\t\tdir").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("ls", jobs[0].command);
        assert_eq!("-al", jobs[0].args[0]);
        assert_eq!("dir", jobs[0].args[1]);
    }

    #[test]
    fn trailing_whitespace() {
        let jobs = job_list("ls -al\t ").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("ls", jobs[0].command);
        assert_eq!("-al", jobs[0].args[0]);
    }

    #[test]
    fn double_quoting() {
        let jobs = job_list("echo \"Hello World\"").unwrap();
        assert_eq!(1, jobs[0].args.len());
        assert_eq!("Hello World", jobs[0].args[0]);
    }

    #[test]
    fn all_whitespace() {
        let jobs = job_list("  \t ").unwrap();
        assert_eq!(0, jobs.len());
    }

    #[test]
    fn lone_comment() {
        let jobs = job_list("# ; \t as!!+dfa").unwrap();
        assert_eq!(0, jobs.len());
    }

    #[test]
    fn command_followed_by_comment() {
        let jobs = job_list("cat # ; \t as!!+dfa").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!(0, jobs[0].args.len());
    }

    #[test]
    fn comments_in_multiline_script() {
        let jobs = job_list("echo\n# a comment;\necho#asfasdf").unwrap();
        assert_eq!(2, jobs.len());
    }

    #[test]
    fn multiple_newlines() {
        let jobs = job_list("echo\n\ncat").unwrap();
        assert_eq!(2, jobs.len());
    }

    #[test]
    fn leading_whitespace() {
        let jobs = job_list("    \techo").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("echo", jobs[0].command);
    }

    #[test]
    fn indentation_on_multiple_lines() {
        let jobs = job_list("echo\n  cat").unwrap();
        assert_eq!(2, jobs.len());
        assert_eq!("echo", jobs[0].command);
        assert_eq!("cat", jobs[1].command);
    }

    #[test]
    fn single_quoting() {
        let jobs = job_list("echo '#!!;\"\\'").unwrap();
        assert_eq!("#!!;\"\\", jobs[0].args[0]);
    }

    #[test]
    fn mixed_quoted_and_unquoted() {
        let jobs = job_list("echo '#!!;\"\\' and \t some \"more' 'stuff\"").unwrap();
        assert_eq!("#!!;\"\\", jobs[0].args[0]);
        assert_eq!("and", jobs[0].args[1]);
        assert_eq!("some", jobs[0].args[2]);
        assert_eq!("more' 'stuff", jobs[0].args[3]);
    }

    #[test]
    fn several_blank_lines() {
        let jobs = parse("\n\n\n");
        assert_eq!(0, jobs.len());
    }

    #[test]
    fn full_script() {
        job_list(r#"if a == a
  echo true a == a

  if b != b
    echo true b != b
  else
    echo false b != b

    if 3 > 2
      echo true 3 > 2
    else
      echo false 3 > 2
    fi
  fi
else
  echo false a == a
fi
"#)
            .unwrap();  // Make sure it parses
    }

    #[test]
    fn leading_and_trailing_junk() {
        job_list(r#"

# comment
   # comment

    if a == a
  echo true a == a  # Line ending commment

  if b != b
    echo true b != b
  else
    echo false b != b

    if 3 > 2
      echo true 3 > 2
    else
      echo false 3 > 2
    fi
  fi
else
  echo false a == a
      fi

# comment

"#)
            .unwrap();  // Make sure it parses
    }

}
<commit_msg>Finish a todo<commit_after>use self::grammar::job_list;

#[derive(Debug, PartialEq)]
pub struct Job {
    pub command: String,
    pub args: Vec<String>,
}

impl Job {
    fn new(command: String, args: Vec<String>) -> Job {
        Job {
            command: command,
            args: args,
        }
    }
}

pub fn parse(code: &str) -> Vec<Job> {
    job_list(code).unwrap_or(vec![])
}

peg! grammar(r#"
use super::Job;

#[pub]
job_list -> Vec<Job>
    = (unused* newline)* jobs:job ++ ((job_ending+ unused*)+) (newline unused*)* { jobs }
    / (unused*) ** newline { vec![] }

job -> Job
    = whitespace? res:_job whitespace? comment? { res }

_job -> Job
    = args:word ++ whitespace { let mut args = args.clone(); Job::new(args.remove(0), args) }

word -> String
    = double_quoted_word
    / single_quoted_word
    / [^ \t\r\n#;]+ { match_str.to_string() }

double_quoted_word -> String
    = ["] word:_double_quoted_word ["] { word }

_double_quoted_word -> String
    = [^"]+ { match_str.to_string() }

single_quoted_word -> String
    = ['] word:_single_quoted_word ['] { word }

_single_quoted_word -> String
    = [^']+ { match_str.to_string() }

unused -> ()
    = whitespace comment? { () }
    / comment { () }

comment -> ()
    = [#] [^\r\n]*

whitespace -> ()
    = [ \t]+

job_ending -> ()
    = [;]
    / newline
    / newline

newline -> ()
    = [\r\n]
"#);

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

    #[test]
    fn single_job_no_args() {
        let jobs = job_list("cat").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("cat", jobs[0].command);
        assert_eq!(0, jobs[0].args.len());
    }

    #[test]
    fn single_job_with_args() {
        let jobs = job_list("ls -al dir").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("ls", jobs[0].command);
        assert_eq!("-al", jobs[0].args[0]);
        assert_eq!("dir", jobs[0].args[1]);
    }

    #[test]
    fn multiple_jobs_with_args() {
        let jobs = job_list("ls -al;cat tmp.txt").unwrap();
        assert_eq!(2, jobs.len());
        assert_eq!("ls", jobs[0].command);
        assert_eq!("-al", jobs[0].args[0]);
        assert_eq!("cat", jobs[1].command);
        assert_eq!("tmp.txt", jobs[1].args[0]);
    }

    #[test]
    fn parse_empty_string() {
        let jobs = job_list("").unwrap();
        assert_eq!(0, jobs.len());
    }

    #[test]
    fn multiple_white_space_between_words() {
        let jobs = job_list("ls \t -al\t\tdir").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("ls", jobs[0].command);
        assert_eq!("-al", jobs[0].args[0]);
        assert_eq!("dir", jobs[0].args[1]);
    }

    #[test]
    fn trailing_whitespace() {
        let jobs = job_list("ls -al\t ").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("ls", jobs[0].command);
        assert_eq!("-al", jobs[0].args[0]);
    }

    #[test]
    fn double_quoting() {
        let jobs = job_list("echo \"Hello World\"").unwrap();
        assert_eq!(1, jobs[0].args.len());
        assert_eq!("Hello World", jobs[0].args[0]);
    }

    #[test]
    fn all_whitespace() {
        let jobs = job_list("  \t ").unwrap();
        assert_eq!(0, jobs.len());
    }

    #[test]
    fn lone_comment() {
        let jobs = job_list("# ; \t as!!+dfa").unwrap();
        assert_eq!(0, jobs.len());
    }

    #[test]
    fn command_followed_by_comment() {
        let jobs = job_list("cat # ; \t as!!+dfa").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!(0, jobs[0].args.len());
    }

    #[test]
    fn comments_in_multiline_script() {
        let jobs = job_list("echo\n# a comment;\necho#asfasdf").unwrap();
        assert_eq!(2, jobs.len());
    }

    #[test]
    fn multiple_newlines() {
        let jobs = job_list("echo\n\ncat").unwrap();
        assert_eq!(2, jobs.len());
    }

    #[test]
    fn leading_whitespace() {
        let jobs = job_list("    \techo").unwrap();
        assert_eq!(1, jobs.len());
        assert_eq!("echo", jobs[0].command);
    }

    #[test]
    fn indentation_on_multiple_lines() {
        let jobs = job_list("echo\n  cat").unwrap();
        assert_eq!(2, jobs.len());
        assert_eq!("echo", jobs[0].command);
        assert_eq!("cat", jobs[1].command);
    }

    #[test]
    fn single_quoting() {
        let jobs = job_list("echo '#!!;\"\\'").unwrap();
        assert_eq!("#!!;\"\\", jobs[0].args[0]);
    }

    #[test]
    fn mixed_quoted_and_unquoted() {
        let jobs = job_list("echo '#!!;\"\\' and \t some \"more' 'stuff\"").unwrap();
        assert_eq!("#!!;\"\\", jobs[0].args[0]);
        assert_eq!("and", jobs[0].args[1]);
        assert_eq!("some", jobs[0].args[2]);
        assert_eq!("more' 'stuff", jobs[0].args[3]);
    }

    #[test]
    fn several_blank_lines() {
        let jobs = parse("\n\n\n");
        assert_eq!(0, jobs.len());
    }

    #[test]
    fn full_script() {
        job_list(r#"if a == a
  echo true a == a

  if b != b
    echo true b != b
  else
    echo false b != b

    if 3 > 2
      echo true 3 > 2
    else
      echo false 3 > 2
    fi
  fi
else
  echo false a == a
fi
"#)
            .unwrap();  // Make sure it parses
    }

    #[test]
    fn leading_and_trailing_junk() {
        job_list(r#"

# comment
   # comment

    if a == a
  echo true a == a  # Line ending commment

  if b != b
    echo true b != b
  else
    echo false b != b

    if 3 > 2
      echo true 3 > 2
    else
      echo false 3 > 2
    fi
  fi
else
  echo false a == a
      fi

# comment

"#)
            .unwrap();  // Make sure it parses
    }

}
<|endoftext|>


<commit_before>//! Data types for text representations of board positions and moves, which may be used for [Portable game notation][1].
//!
//! The terminology used in this module is specific to chess and chess variants, but it can be implemented for any game.
//!
//! [1]: https://en.wikipedia.org/wiki/Portable_Game_Notation

use search_algorithms::board;
use std::error;
use std::fmt;

/// A list of general categories of errors related to pgn parsing.
///
/// This list is intended to grow over time and it is not recommended to exhaustively match against it.
///
/// It is used with the [`Error`] type.
///
/// [`Error`]: struct.Error.html
#[derive(Clone, Copy, Eq, PartialEq, Debug, PartialOrd, Ord)]
pub enum ErrorKind {
    ParseError,
    AmbiguousMove,
    IllegalMove,
    IllegalPosition,
    IOError,
    Other,
}

/// The error type for operations on a `PgnBoard`.
///
/// The error can be created with an arbitrary payload and optionally an underlying source error for error chaining.
#[derive(Debug)]
pub struct Error {
    kind: ErrorKind,
    error: Box<dyn error::Error + Send + Sync>,
    source: Option<Box<dyn error::Error + Send + Sync>>,
}

impl Error {
    /// Returns a new error of the specific `ErrorKind` with an arbitrary payload.
    pub fn new<E>(kind: ErrorKind, error: E) -> Error
    where E: Into<Box<dyn error::Error + Send + Sync>> {
        Error { kind, error: error.into(), source: None}
    }

    /// Returns a new error of the specific `ErrorKind` with an arbitrary payload and source error.
    pub fn new_caused_by<E, F>(kind: ErrorKind, error: E, source: F) -> Error
    where E: Into<Box<dyn error::Error + Send + Sync>>,
          F: Into<Box<dyn error::Error + Send + Sync>> {
        Error { kind, error: error.into(), source: Some(source.into())}
    }
}

impl error::Error for Error {}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.kind {
            ErrorKind::ParseError => write!(fmt, "Parse error. "),
            ErrorKind::AmbiguousMove => write!(fmt, "Ambiguous move. "),
            ErrorKind::IllegalMove => write!(fmt, "Illegal move. "),
            ErrorKind::IllegalPosition => write!(fmt, "Illegal position. "),
            ErrorKind::IOError => write!(fmt, "IO error. "),
            ErrorKind::Other => Ok(()),
        }?;
        write!(fmt, "{}", self.error)?;
        if let Some(ref source) = self.source {
            write!(fmt, "\nCaused by: {}", source)?;
        }
        Ok(())
    }
}

/// Trait for text representations of board positions and moves.
///
/// The terminology used in this trait is specific to chess and chess variants, but it can be implemented for any game.
pub trait PgnBoard: Sized + board::Board {
    /// Constructs a board from [Forsyth–Edwards Notation][1].
    ///
    /// Extensions to this notation exist for all large chess variants
    ///
    /// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
    fn from_fen(fen: &str) -> Result<Self, Error>;

    /// Returns a string representation of the board in [Forsyth–Edwards Notation][1].
    ///
    /// Extensions to this notation exist for all large chess variants.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
    fn to_fen(&self) -> String;

    /// Construct a game move from [Standard Algebraic Notation][1], specifically the format used in [pgn notation][2].
    ///
    /// Extensions to this notation exist for all large chess variants.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
    /// [2]: https://en.wikipedia.org/wiki/Portable_Game_Notation
    fn move_from_san(&self, input: &str) -> Result<Self::Move, Error>;

    /// Returns a string representation of the move in [Standard Algebraic Notation][1], specifically the format used in [pgn notation][2].
    ///
    /// Extensions to this notation exist for all large chess variants.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
    /// [2]: https://en.wikipedia.org/wiki/Portable_Game_Notation
    fn move_to_san(&self, mv: &Self::Move) -> String;

    /// Construct a move from an alternative, [long algebraic notation][1].
    ///
    /// This is mostly used for chess and chess variations in the uci interface, or for convenient debugging.
    /// Implementations may simply wrap this function around `move_from_san` where appropriate.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation
    fn move_from_lan(&self, input: &str) -> Result<Self::Move, Error>;

    /// Returns a string representation of the move in an alternative, [long algebraic notation][1].
    ///
    /// This is mostly used for chess and chess variations in the uci interface, or for convenient debugging.
    /// Implementations may simply wrap this function around `move_to_san` where appropriate.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation
    fn move_to_lan(&self, mv: &Self::Move) -> String;

}<commit_msg>Add game_to_pgn() method to pgn<commit_after>//! Data types for text representations of board positions and moves, which may be used for [Portable game notation][1].
//!
//! The terminology used in this module is specific to chess and chess variants, but it can be implemented for any game.
//!
//! [1]: https://en.wikipedia.org/wiki/Portable_Game_Notation

use search_algorithms::board;
use std::error;
use std::fmt;
use search_algorithms::board::GameResult;
use search_algorithms::board::Color;
use std::io::Write;
use std::io;

/// A list of general categories of errors related to pgn parsing.
///
/// This list is intended to grow over time and it is not recommended to exhaustively match against it.
///
/// It is used with the [`Error`] type.
///
/// [`Error`]: struct.Error.html
#[derive(Clone, Copy, Eq, PartialEq, Debug, PartialOrd, Ord)]
pub enum ErrorKind {
    ParseError,
    AmbiguousMove,
    IllegalMove,
    IllegalPosition,
    IOError,
    Other,
}

/// The error type for operations on a `PgnBoard`.
///
/// The error can be created with an arbitrary payload and optionally an underlying source error for error chaining.
#[derive(Debug)]
pub struct Error {
    kind: ErrorKind,
    error: Box<dyn error::Error + Send + Sync>,
    source: Option<Box<dyn error::Error + Send + Sync>>,
}

impl Error {
    /// Returns a new error of the specific `ErrorKind` with an arbitrary payload.
    pub fn new<E>(kind: ErrorKind, error: E) -> Error
    where E: Into<Box<dyn error::Error + Send + Sync>> {
        Error { kind, error: error.into(), source: None}
    }

    /// Returns a new error of the specific `ErrorKind` with an arbitrary payload and source error.
    pub fn new_caused_by<E, F>(kind: ErrorKind, error: E, source: F) -> Error
    where E: Into<Box<dyn error::Error + Send + Sync>>,
          F: Into<Box<dyn error::Error + Send + Sync>> {
        Error { kind, error: error.into(), source: Some(source.into())}
    }
}

impl error::Error for Error {}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.kind {
            ErrorKind::ParseError => write!(fmt, "Parse error. "),
            ErrorKind::AmbiguousMove => write!(fmt, "Ambiguous move. "),
            ErrorKind::IllegalMove => write!(fmt, "Illegal move. "),
            ErrorKind::IllegalPosition => write!(fmt, "Illegal position. "),
            ErrorKind::IOError => write!(fmt, "IO error. "),
            ErrorKind::Other => Ok(()),
        }?;
        write!(fmt, "{}", self.error)?;
        if let Some(ref source) = self.source {
            write!(fmt, "\nCaused by: {}", source)?;
        }
        Ok(())
    }
}

/// Trait for text representations of board positions and moves.
///
/// The terminology used in this trait is specific to chess and chess variants, but it can be implemented for any game.
pub trait PgnBoard: Sized + board::Board + PartialEq {
    /// Constructs a board from [Forsyth–Edwards Notation][1].
    ///
    /// Extensions to this notation exist for all large chess variants
    ///
    /// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
    fn from_fen(fen: &str) -> Result<Self, Error>;

    /// Returns a string representation of the board in [Forsyth–Edwards Notation][1].
    ///
    /// Extensions to this notation exist for all large chess variants.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
    fn to_fen(&self) -> String;

    /// Construct a game move from [Standard Algebraic Notation][1], specifically the format used in [pgn notation][2].
    ///
    /// Extensions to this notation exist for all large chess variants.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
    /// [2]: https://en.wikipedia.org/wiki/Portable_Game_Notation
    fn move_from_san(&self, input: &str) -> Result<Self::Move, Error>;

    /// Returns a string representation of the move in [Standard Algebraic Notation][1], specifically the format used in [pgn notation][2].
    ///
    /// Extensions to this notation exist for all large chess variants.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
    /// [2]: https://en.wikipedia.org/wiki/Portable_Game_Notation
    fn move_to_san(&self, mv: &Self::Move) -> String;

    /// Construct a move from an alternative, [long algebraic notation][1].
    ///
    /// This is mostly used for chess and chess variations in the uci interface, or for convenient debugging.
    /// Implementations may simply wrap this function around `move_from_san` where appropriate.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation
    fn move_from_lan(&self, input: &str) -> Result<Self::Move, Error>;

    /// Returns a string representation of the move in an alternative, [long algebraic notation][1].
    ///
    /// This is mostly used for chess and chess variations in the uci interface, or for convenient debugging.
    /// Implementations may simply wrap this function around `move_to_san` where appropriate.
    ///
    /// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation
    fn move_to_lan(&self, mv: &Self::Move) -> String;

    fn game_to_pgn<W: Write>(mut self, moves: &[Self::Move], event: &str, site: &str, date: &str,
                   round: &str, white: &str, black: &str, result: Option<GameResult>,
                   tags_pairs: &[(&str, &str)], f: &mut W) -> Result<(), io::Error> {
        writeln!(f, "[Event \"{}\"]", event)?;
        writeln!(f, "[Site \"{}\"]", site)?;
        writeln!(f, "[Date \"{}\"]", date)?;
        writeln!(f, "[Round \"{}\"]", round)?;
        writeln!(f, "[White \"{}\"]", white)?;
        writeln!(f, "[Black \"{}\"]", black)?;
        writeln!(f, "[Result \"{}\"]", match result {
            None => "*",
            Some(GameResult::WhiteWin) => "1-0",
            Some(GameResult::BlackWin) => "0-1",
            Some(GameResult::Draw) => "1/2-1/2",
        })?;

        if tags_pairs.iter().find(|(tag, _)| *tag == "FEN" ).is_none()
            && self != Self::start_board() {
            writeln!(f, "[FEN \"{}\"", self.to_fen())?;
        }

        for (i, mv) in moves.into_iter().enumerate() {
            if i % 20 == 0 {
                writeln!(f)?;
            }
            if i == 0 && self.side_to_move() == Color::Black {
                write!(f, "{}... {} ", 1, self.move_to_san(&mv))?;
            }
            else if self.side_to_move() == Color::White {
                write!(f, "{}. {} ", (i + 1) / 2 + 1, self.move_to_san(&mv))?;
            }
            else {
                write!(f, "{} ", self.move_to_san(&mv))?;
            }
            self.do_move(mv.clone());
        }

        write!(f, "{}", match result {
            None => "*",
            Some(GameResult::WhiteWin) => "1-0",
            Some(GameResult::BlackWin) => "0-1",
            Some(GameResult::Draw) => "1/2-1/2",
        })?;
        writeln!(f)?;
        writeln!(f)?;
        Ok(())
    }
}<|endoftext|>