<commit_before>#![allow(unstable)]
use libc::*;

use std::os;

use input::*;
use controls::*;
use constants::*;
use util::*;
use signal::*;
use types::*;

pub struct LineReader {
    pub line: InputLine,
    pub controls: Controls,
    pub bpart: String,
    pub escape: bool,
    pub escape_chars: String,
    pub finished: bool,
    pub eof: bool,
    pub restarted: bool
}

impl LineReader {
    pub fn new() -> LineReader {
        LineReader {
            line: InputLine::new(),
            controls: Controls::new(),
            bpart: String::new(),
            escape: false,
            escape_chars: String::new(),
            finished: false,
            eof: false,
            restarted: false
        }
    }

    pub fn clear(&mut self) {
        self.line.clear();
        self.bpart.clear();
        self.escape = false;
        self.escape_chars.clear();
        self.finished = false;
        self.eof = false;
        self.restarted = false;
    }

    pub fn restart(&mut self) {
        self.finished = false;
        self.eof = false;
        self.restarted = true;
    }

    pub fn draw_ps1(&mut self) {
        let cwd = os::getcwd().unwrap();
        self.controls.outf(format_args!("{}$ ", condense_path(cwd).display()));
    }

    fn handle_signal(&mut self, set:&SigSet) {
        let sig = match signal_wait_set(set, None) {
            Err(e) => panic!("Didn't get signal: {}", e),
            Ok(s) => s
        };
        if sig.signo != SIGINT {
            panic!("Caught bad signal: {}", sig.signo);
        }
        self.controls.outs("\nInterrupt\n");
        self.clear();
        self.draw_ps1();
    }

    fn read_character(&mut self) {
        match self.controls.read() {
            Err(e) => panic!("\nError: {}\n", e),
            Ok(ch) => match
                if self.escape {
                    self.handle_escape(ch)
                } else if ch.is_control() {
                    self.handle_control(ch)
                } else {
                    self.handle_ch(ch)
                } {
                    false => self.controls.outc(BEL),
                    _ => {}
                }
        }
    }

    pub fn read_line(&mut self) -> Option<InputValue> {
        // these panic because if we can't do this we can't run wash at all
        let mut set = tryp!(empty_sigset());
        tryp!(sigset_add(&mut set, SIGINT));
        let sigfd = tryp!(signal_fd(&set));
        // the file descriptors we want to watch
        let read = vec![sigfd, STDIN];
        let emvc = vec![];
        let mut sread;
        let old_set = tryp!(signal_proc_mask(SIG_BLOCK, &set));
        while !self.finished && !self.eof {
            sread = tryp!(select(&read, &emvc, &emvc,
                                 None, &set));
            if sread.len() == 2 {
                // prefer SIGINT
                self.handle_signal(&set);
            } else {
                match sread.pop() {
                    None => self.handle_signal(&set),
                    Some(ref fd) if *fd == sigfd =>
                        self.handle_signal(&set),
                    Some(ref fd) if *fd == STDIN =>
                        self.read_character(),
                    _ => panic!("select returned unknown file descriptor")
                }
            }
            self.controls.flush();
        }
        tryp!(signal_proc_mask(SIG_SETMASK, &old_set));
        if self.eof {
            return None;
        } else {
            return self.line.process();
        }
    }

    pub fn draw_part(&mut self) {
        // quick out if part is empty
        if self.line.part.is_empty() {
            return;
        }
        if self.bpart.is_empty() {
            // only calculate bpart when it needs to be recalculated
            let mut cpart = self.line.part.clone();
            loop {
                match cpart.pop() {
                    Some(c) => self.bpart.push(c),
                    None => break
                }
            }
        }
        self.controls.outs(self.bpart.as_slice());
    }

    pub fn idraw_part(&mut self) {
        // in-place draw of the line part
        self.draw_part();
        self.controls.cursors_left(self.line.part.len());
    }

    pub fn handle_ch(&mut self, ch:char) -> bool {
        if self.line.push(ch) {
            self.controls.outc(ch);
            self.idraw_part();
            return true;
        } else {
            return false;
        }
    }

    pub fn handle_control(&mut self, ch:char) -> bool {
        match ch {
            CEOF => {
                if self.line.is_empty() {
                    self.finished = true;
                    self.eof = true;
                }
            },
            NL => {
                self.finished = true;
            },
            ESC => {
                self.escape = true;
                self.escape_chars = String::new();
            },
            DEL => {
                match self.line.pop() {
                    None => return false,
                    Some(_) => {
                        self.controls.cursor_left();
                        self.draw_part();
                        self.controls.outc(SPC);
                        self.controls.cursors_left(self.line.part.len() + 1);
                    }
                }
            },
            _ => return false
        }
        return true;
    }

    pub fn handle_escape(&mut self, ch:char) -> bool {
        match ch {
            ESC => {
                self.escape = false;
            },
            ANSI => {
                if self.escape_chars.is_empty() {
                    self.escape_chars.push(ANSI);
                } else {
                    self.escape = false;
                }
            },
            'D' => {
                if self.line.left() {
                    self.bpart.clear();
                    self.controls.cursor_left();
                }
                self.escape = false;
            },
            'C' => {
                if self.line.right() {
                    self.bpart.clear();
                    self.controls.cursor_right();
                }
                self.escape = false;
            },
            _ => {
                self.escape = false;
                return false;
            }
        }
        return true;
    }
}

<commit_msg>Handle error in reader when SIGCHLD gets handled<commit_after>#![allow(unstable)]
use libc::*;

use std::os;

use input::*;
use controls::*;
use constants::*;
use util::*;
use signal::*;
use types::*;

pub struct LineReader {
    pub line: InputLine,
    pub controls: Controls,
    pub bpart: String,
    pub escape: bool,
    pub escape_chars: String,
    pub finished: bool,
    pub eof: bool,
    pub restarted: bool
}

impl LineReader {
    pub fn new() -> LineReader {
        LineReader {
            line: InputLine::new(),
            controls: Controls::new(),
            bpart: String::new(),
            escape: false,
            escape_chars: String::new(),
            finished: false,
            eof: false,
            restarted: false
        }
    }

    pub fn clear(&mut self) {
        self.line.clear();
        self.bpart.clear();
        self.escape = false;
        self.escape_chars.clear();
        self.finished = false;
        self.eof = false;
        self.restarted = false;
    }

    pub fn restart(&mut self) {
        self.finished = false;
        self.eof = false;
        self.restarted = true;
    }

    pub fn draw_ps1(&mut self) {
        let cwd = os::getcwd().unwrap();
        self.controls.outf(format_args!("{}$ ", condense_path(cwd).display()));
    }

    fn handle_signal(&mut self, set:&SigSet) {
        let sig = match signal_wait_set(set, None) {
            Err(e) => panic!("Didn't get signal: {}", e),
            Ok(s) => s
        };
        if sig.signo != SIGINT {
            panic!("Caught bad signal: {}", sig.signo);
        }
        self.controls.outs("\nInterrupt\n");
        self.clear();
        self.draw_ps1();
    }

    fn read_character(&mut self) {
        match self.controls.read() {
            Err(e) => panic!("\nError: {}\n", e),
            Ok(ch) => match
                if self.escape {
                    self.handle_escape(ch)
                } else if ch.is_control() {
                    self.handle_control(ch)
                } else {
                    self.handle_ch(ch)
                } {
                    false => self.controls.outc(BEL),
                    _ => {}
                }
        }
    }

    pub fn read_line(&mut self) -> Option<InputValue> {
        // these panic because if we can't do this we can't run wash at all
        let mut set = tryp!(empty_sigset());
        tryp!(sigset_add(&mut set, SIGINT));
        let sigfd = tryp!(signal_fd(&set));
        // the file descriptors we want to watch
        let read = vec![sigfd, STDIN];
        let emvc = vec![];
        let mut sread;
        let old_set = tryp!(signal_proc_mask(SIG_BLOCK, &set));
        while !self.finished && !self.eof {
            sread = match select(&read, &emvc, &emvc,
                                 None, &set) {
                Err(_) => continue, // try again
                Ok(v) => v
            };
            if sread.len() == 2 {
                // prefer SIGINT
                self.handle_signal(&set);
            } else {
                match sread.pop() {
                    None => self.handle_signal(&set),
                    Some(ref fd) if *fd == sigfd =>
                        self.handle_signal(&set),
                    Some(ref fd) if *fd == STDIN =>
                        self.read_character(),
                    _ => panic!("select returned unknown file descriptor")
                }
            }
            self.controls.flush();
        }
        tryp!(signal_proc_mask(SIG_SETMASK, &old_set));
        if self.eof {
            return None;
        } else {
            return self.line.process();
        }
    }

    pub fn draw_part(&mut self) {
        // quick out if part is empty
        if self.line.part.is_empty() {
            return;
        }
        if self.bpart.is_empty() {
            // only calculate bpart when it needs to be recalculated
            let mut cpart = self.line.part.clone();
            loop {
                match cpart.pop() {
                    Some(c) => self.bpart.push(c),
                    None => break
                }
            }
        }
        self.controls.outs(self.bpart.as_slice());
    }

    pub fn idraw_part(&mut self) {
        // in-place draw of the line part
        self.draw_part();
        self.controls.cursors_left(self.line.part.len());
    }

    pub fn handle_ch(&mut self, ch:char) -> bool {
        if self.line.push(ch) {
            self.controls.outc(ch);
            self.idraw_part();
            return true;
        } else {
            return false;
        }
    }

    pub fn handle_control(&mut self, ch:char) -> bool {
        match ch {
            CEOF => {
                if self.line.is_empty() {
                    self.finished = true;
                    self.eof = true;
                }
            },
            NL => {
                self.finished = true;
            },
            ESC => {
                self.escape = true;
                self.escape_chars = String::new();
            },
            DEL => {
                match self.line.pop() {
                    None => return false,
                    Some(_) => {
                        self.controls.cursor_left();
                        self.draw_part();
                        self.controls.outc(SPC);
                        self.controls.cursors_left(self.line.part.len() + 1);
                    }
                }
            },
            _ => return false
        }
        return true;
    }

    pub fn handle_escape(&mut self, ch:char) -> bool {
        match ch {
            ESC => {
                self.escape = false;
            },
            ANSI => {
                if self.escape_chars.is_empty() {
                    self.escape_chars.push(ANSI);
                } else {
                    self.escape = false;
                }
            },
            'D' => {
                if self.line.left() {
                    self.bpart.clear();
                    self.controls.cursor_left();
                }
                self.escape = false;
            },
            'C' => {
                if self.line.right() {
                    self.bpart.clear();
                    self.controls.cursor_right();
                }
                self.escape = false;
            },
            _ => {
                self.escape = false;
                return false;
            }
        }
        return true;
    }
}

<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
mod buffer;

enum SqlType {
	Char(uint),
	Varchar(uint),
	Integer,
}

enum SqlAttribute {
	Null,
	NotNull,
}

struct Column {
	name: ~str,
	datatype: SqlType,
	attributes: Vec<SqlAttribute>,
}

struct Relation {
	name: ~str,
	columns: Vec<Column>,
}

impl Relation {
	pub fn new(name: ~str) -> Relation {
		Relation {name: name, columns: Vec::new()}
	}

	pub fn add_column(&mut self, column: Column) {
		self.columns.push(column);
	}
}

struct Schema {
	relations: Vec<Relation>,
}

impl Schema {
	pub fn new() -> Schema {
		Schema {relations: Vec::new()}
	}

	pub fn new_from_disk(bufmanager: buffer::BufferManager) -> Schema {
		// TODO
		Schema {relations: Vec::new()}
	}

	pub fn add_relation(&mut self, relation: Relation) {
		self.relations.push(relation);
	}
}

struct Record {
	// TODO: do we even need this?
	len: uint,
	data: Vec<u8>,
}

impl Record {
	/* move semantics, no copy */
	pub fn new(len: uint, data: Vec<u8>) -> Record {
		Record {len: len, data: data}
	}

	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

struct SPSegment;

/* our newtype struct: create a TID type as an alias to u64 */
struct TID(u64);

impl SPSegment {
	pub fn insert(r: Record) -> TID {
		TID(0_u64)
	}

	pub fn remove(tid: TID) -> bool {
		false
	}

	pub fn lookup(tid: TID) -> Record {
		// TODO
		Record {len: 1, data: vec!(1)}
	}

	pub fn update(tid: TID, r: Record) -> bool {
		false
	}
}

#[test]
fn main() {
}

#[test]
fn create_schema() {
	let name = Column {name: ~"name", datatype: Varchar(128), attributes: vec!(NotNull)};
	let age = Column {name: ~"age", datatype: Integer, attributes: vec!(NotNull)};
	let mut relation = Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	let mut schema = Schema::new();
	schema.add_relation(relation);
}
<commit_msg>Added code for simpleserializing into EBML<commit_after>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
extern crate serialize;
use std::io::MemWriter;
use serialize::ebml::writer;
use serialize::ebml::reader;
use serialize::{Encodable, Decodable};
mod buffer;

enum SqlType {
	Char(uint),
	Varchar(uint),
	Integer,
}

enum SqlAttribute {
	Null,
	NotNull,
}

struct Column {
	name: ~str,
	datatype: SqlType,
	attributes: Vec<SqlAttribute>,
}

struct Relation {
	name: ~str,
	columns: Vec<Column>,
}

impl Relation {
	pub fn new(name: ~str) -> Relation {
		Relation {name: name, columns: Vec::new()}
	}

	pub fn add_column(&mut self, column: Column) {
		self.columns.push(column);
	}
}

struct Schema {
	relations: Vec<Relation>,
}

impl Schema {
	pub fn new() -> Schema {
		Schema {relations: Vec::new()}
	}

	pub fn new_from_disk(bufmanager: buffer::BufferManager) -> Schema {
		// TODO
		Schema {relations: Vec::new()}
	}

	pub fn add_relation(&mut self, relation: Relation) {
		self.relations.push(relation);
	}

	pub fn save_to_disk(&self) {
		let mut wr = MemWriter::new();
		let v: u64 = 42;
		{
			let mut ebml_w = writer::Encoder(&mut wr);
			let _ = v.encode(&mut ebml_w);
		}
		let ebml_doc = reader::Doc(wr.get_ref());
		let mut deser = reader::Decoder(ebml_doc);
		let v1: u64 = Decodable::decode(&mut deser).unwrap();
		println!("v1 == {:?}", v1);

		for rel in self.relations.iter() {
			//rel.save_to_disk()
		}
	}
}

struct Record {
	// TODO: do we even need this?
	len: uint,
	data: Vec<u8>,
}

impl Record {
	/* move semantics, no copy */
	pub fn new(len: uint, data: Vec<u8>) -> Record {
		Record {len: len, data: data}
	}

	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

struct SPSegment;

/* our newtype struct: create a TID type as an alias to u64 */
struct TID(u64);

impl SPSegment {
	pub fn insert(r: Record) -> TID {
		TID(0_u64)
	}

	pub fn remove(tid: TID) -> bool {
		false
	}

	pub fn lookup(tid: TID) -> Record {
		// TODO
		Record {len: 1, data: vec!(1)}
	}

	pub fn update(tid: TID, r: Record) -> bool {
		false
	}
}

#[test]
fn main() {
}

#[test]
fn create_schema() {
	let name = Column {name: ~"name", datatype: Varchar(128), attributes: vec!(NotNull)};
	let age = Column {name: ~"age", datatype: Integer, attributes: vec!(NotNull)};
	let mut relation = Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	let mut schema = Schema::new();
	schema.add_relation(relation);
	schema.save_to_disk();
	assert!(false);
}
<|endoftext|>


<commit_before>use nom::{alphanumeric, eof, line_ending, space};
use nom::{IResult, Err, ErrorKind, Needed};
use std::str;

use parser::{ConditionTree, fieldlist, unsigned_number};
use caseless_tag::*;

#[derive(Debug, PartialEq)]
pub struct GroupByClause {
    columns: Vec<String>,
    having: String, // XXX(malte): should this be an arbitrary expr?
}

#[derive(Debug, PartialEq)]
pub struct LimitClause {
    limit: u64,
    offset: u64,
}

#[derive(Debug, PartialEq)]
enum OrderType {
    OrderAscending,
    OrderDescending,
}

#[derive(Debug, PartialEq)]
pub struct OrderClause {
    order_type: OrderType,
    order_cols: Vec<String>, // TODO(malte): can this be an arbitrary expr?
}

#[derive(Debug, Default, PartialEq)]
pub struct SelectStatement {
    table: String,
    distinct: bool,
    fields: Vec<String>,
    where_clause: Option<ConditionTree>,
    group_by: Option<GroupByClause>,
    order: Option<OrderClause>,
    limit: Option<LimitClause>,
}

/// Parse LIMIT clause
named!(limit_clause<&[u8], LimitClause>,
    chain!(
        caseless_tag!("limit") ~
        space ~
        limit_val: unsigned_number ~
        space? ~
        offset_val: opt!(chain!(
                caseless_tag!("offset") ~
                space ~
                val: unsigned_number,
                || { val }
            )
        ),
    || {
        LimitClause {
            limit: limit_val,
            offset: match offset_val {
                None => 0,
                Some(v) => v,
            },
        }
    })
);

/// Parse WHERE clause of a selection
named!(where_clause<&[u8], ConditionTree>,
    chain!(
        tag!("where") ~
        space ~
        field: map_res!(alphanumeric, str::from_utf8) ~
        space? ~
        tag!("=") ~
        space? ~
        expr: map_res!(tag_s!(b"?"), str::from_utf8), // XXX(malte): broken!
        || {
            ConditionTree {
                field: String::from(field),
                expr: String::from(expr),
            }
        }
    )
);

/// Parse rule for a SQL selection query.
/// TODO(malte): support nested queries as selection targets
named!(pub selection<&[u8], SelectStatement>,
    chain!(
        caseless_tag!("select") ~
        space ~
        fields: fieldlist ~
        space ~
        caseless_tag!("from") ~
        space ~
        table: map_res!(alphanumeric, str::from_utf8) ~
        space? ~
        cond: opt!(where_clause) ~
        space? ~
        limit: opt!(limit_clause) ~
        space? ~
        alt!(eof | tag!(";") | line_ending),
        || {
            SelectStatement {
                table: String::from(table),
                distinct: false,
                fields: fields.iter().map(|s| String::from(*s)).collect(),
                where_clause: cond,
                group_by: None,
                order: None,
                limit: limit,
            }
        }
    )
);

mod tests {
    use super::*;
    use parser::ConditionTree;

    #[test]
    fn simple_select() {
        let qstring = "SELECT id, name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["id".into(), "name".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn select_all() {
        let qstring = "SELECT * FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["ALL".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn spaces_optional() {
        let qstring = "SELECT id,name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["id".into(), "name".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn case_sensitivity() {
        let qstring_lc = "select id, name from users;";
        let qstring_uc = "SELECT id, name FROM users;";

        assert_eq!(selection(qstring_lc.as_bytes()).unwrap(),
                   selection(qstring_uc.as_bytes()).unwrap());
    }

    #[test]
    fn termination() {
        let qstring_sem = "select id, name from users;";
        let qstring_linebreak = "select id, name from users\n";

        assert_eq!(selection(qstring_sem.as_bytes()).unwrap(),
                   selection(qstring_linebreak.as_bytes()).unwrap());
    }

    #[test]
    fn where_clause() {
        let qstring = "select * from ContactInfo where email=?\n";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("ContactInfo"),
                       fields: vec!["ALL".into()],
                       where_clause: Some(ConditionTree {
                           field: String::from("email"),
                           expr: String::from("?") }),
                       ..Default::default()
                   });
    }

    #[test]
    fn limit_clause() {
        let qstring1 = "select * from users limit 10\n";
        let qstring2 = "select * from users limit 10 offset 10\n";

        let expected_lim1 = LimitClause {
            limit: 10,
            offset: 0,
        };
        let expected_lim2 = LimitClause {
            limit: 10,
            offset: 10,
        };

        let res1 = selection(qstring1.as_bytes());
        let res2 = selection(qstring2.as_bytes());
        assert_eq!(res1.unwrap().1.limit, Some(expected_lim1));
        assert_eq!(res2.unwrap().1.limit, Some(expected_lim2));
    }
}
<commit_msg>Add support for DISTINCT on SELECT.<commit_after>use nom::{alphanumeric, eof, line_ending, space};
use nom::{IResult, Err, ErrorKind, Needed};
use std::str;

use parser::{ConditionTree, fieldlist, unsigned_number};
use caseless_tag::*;

#[derive(Debug, PartialEq)]
pub struct GroupByClause {
    columns: Vec<String>,
    having: String, // XXX(malte): should this be an arbitrary expr?
}

#[derive(Debug, PartialEq)]
pub struct LimitClause {
    limit: u64,
    offset: u64,
}

#[derive(Debug, PartialEq)]
enum OrderType {
    OrderAscending,
    OrderDescending,
}

#[derive(Debug, PartialEq)]
pub struct OrderClause {
    order_type: OrderType,
    order_cols: Vec<String>, // TODO(malte): can this be an arbitrary expr?
}

#[derive(Debug, Default, PartialEq)]
pub struct SelectStatement {
    table: String,
    distinct: bool,
    fields: Vec<String>,
    where_clause: Option<ConditionTree>,
    group_by: Option<GroupByClause>,
    order: Option<OrderClause>,
    limit: Option<LimitClause>,
}

/// Parse LIMIT clause
named!(limit_clause<&[u8], LimitClause>,
    chain!(
        caseless_tag!("limit") ~
        space ~
        limit_val: unsigned_number ~
        space? ~
        offset_val: opt!(chain!(
                caseless_tag!("offset") ~
                space ~
                val: unsigned_number,
                || { val }
            )
        ),
    || {
        LimitClause {
            limit: limit_val,
            offset: match offset_val {
                None => 0,
                Some(v) => v,
            },
        }
    })
);

/// Parse WHERE clause of a selection
named!(where_clause<&[u8], ConditionTree>,
    chain!(
        tag!("where") ~
        space ~
        field: map_res!(alphanumeric, str::from_utf8) ~
        space? ~
        tag!("=") ~
        space? ~
        expr: map_res!(tag_s!(b"?"), str::from_utf8), // XXX(malte): broken!
        || {
            ConditionTree {
                field: String::from(field),
                expr: String::from(expr),
            }
        }
    )
);

/// Parse rule for a SQL selection query.
/// TODO(malte): support nested queries as selection targets
named!(pub selection<&[u8], SelectStatement>,
    chain!(
        caseless_tag!("select") ~
        space ~
        distinct: opt!(caseless_tag!("distinct")) ~
        space? ~
        fields: fieldlist ~
        space ~
        caseless_tag!("from") ~
        space ~
        table: map_res!(alphanumeric, str::from_utf8) ~
        space? ~
        cond: opt!(where_clause) ~
        space? ~
        limit: opt!(limit_clause) ~
        space? ~
        alt!(eof | tag!(";") | line_ending),
        || {
            SelectStatement {
                table: String::from(table),
                distinct: distinct.is_some(),
                fields: fields.iter().map(|s| String::from(*s)).collect(),
                where_clause: cond,
                group_by: None,
                order: None,
                limit: limit,
            }
        }
    )
);

mod tests {
    use super::*;
    use parser::ConditionTree;

    #[test]
    fn simple_select() {
        let qstring = "SELECT id, name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["id".into(), "name".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn select_all() {
        let qstring = "SELECT * FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["ALL".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn spaces_optional() {
        let qstring = "SELECT id,name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["id".into(), "name".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn case_sensitivity() {
        let qstring_lc = "select id, name from users;";
        let qstring_uc = "SELECT id, name FROM users;";

        assert_eq!(selection(qstring_lc.as_bytes()).unwrap(),
                   selection(qstring_uc.as_bytes()).unwrap());
    }

    #[test]
    fn termination() {
        let qstring_sem = "select id, name from users;";
        let qstring_linebreak = "select id, name from users\n";

        assert_eq!(selection(qstring_sem.as_bytes()).unwrap(),
                   selection(qstring_linebreak.as_bytes()).unwrap());
    }

    #[test]
    fn where_clause() {
        let qstring = "select * from ContactInfo where email=?\n";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("ContactInfo"),
                       fields: vec!["ALL".into()],
                       where_clause: Some(ConditionTree {
                           field: String::from("email"),
                           expr: String::from("?") }),
                       ..Default::default()
                   });
    }

    #[test]
    fn limit_clause() {
        let qstring1 = "select * from users limit 10\n";
        let qstring2 = "select * from users limit 10 offset 10\n";

        let expected_lim1 = LimitClause {
            limit: 10,
            offset: 0,
        };
        let expected_lim2 = LimitClause {
            limit: 10,
            offset: 10,
        };

        let res1 = selection(qstring1.as_bytes());
        let res2 = selection(qstring2.as_bytes());
        assert_eq!(res1.unwrap().1.limit, Some(expected_lim1));
        assert_eq!(res2.unwrap().1.limit, Some(expected_lim2));
    }
}
<|endoftext|>


<commit_before>//! Serde integration support.

use std::fmt;
use std::marker::PhantomData;

use serde::*;
use serde::de::{Visitor, Error, Unexpected};

fn i64_to_u64<'d, V: Visitor<'d>, E: Error>(v: V, n: i64) -> Result<V::Value, E> {
	if n >= 0 {
		v.visit_u64(n as u64)
	} else {
		Err(E::invalid_value(Unexpected::Signed(n), &v))
	}
}

/// Ignore deserialization errors and revert to default.
pub fn ignore_errors<'d, T: Deserialize<'d> + Default, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
	let v = Value::deserialize(d)?;
   	Ok(T::deserialize(v).ok().unwrap_or_default())
}

/// Deserialize a maybe-string ID into a u64.
pub fn deserialize_id<'d, D: Deserializer<'d>>(d: D) -> Result<u64, D::Error> {
	struct IdVisitor;
	impl<'d> Visitor<'d> for IdVisitor {
		type Value = u64;

		fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
			write!(fmt, "a u64 or parseable string")
		}

		fn visit_i64<E: Error>(self, v: i64) -> Result<u64, E> {
			i64_to_u64(self, v)
		}

		fn visit_u64<E: Error>(self, v: u64) -> Result<u64, E> {
			Ok(v)
		}

		fn visit_str<E: Error>(self, v: &str) -> Result<u64, E> {
			v.parse::<u64>().map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
		}
	}

	d.deserialize_any(IdVisitor)
}

/// Deserialize a maybe-string discriminator into a u16.
/// Also enforces 0 <= N <= 9999.
#[allow(unused_comparisons)]
pub fn deserialize_discrim<'d, D: Deserializer<'d>>(d: D) -> Result<u16, D::Error> {
	macro_rules! check {
		($self:ident, $v:ident, $wrong:expr) => {
			if $v >= 0 && $v <= 9999 {
				Ok($v as u16)
			} else {
				Err(E::invalid_value($wrong, &$self))
			}
		}
	}

	struct DiscrimVisitor;
	impl<'d> Visitor<'d> for DiscrimVisitor {
		type Value = u16;

		fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
			write!(fmt, "a u16 in [0, 9999] or parseable string")
		}

		fn visit_i64<E: Error>(self, v: i64) -> Result<u16, E> {
			check!(self, v, Unexpected::Signed(v))
		}

		fn visit_u64<E: Error>(self, v: u64) -> Result<u16, E> {
			check!(self, v, Unexpected::Unsigned(v))
		}

		fn visit_str<E: Error>(self, v: &str) -> Result<u16, E> {
			v.parse::<u16>()
				.map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
				.and_then(|v| self.visit_u16(v))
		}
	}

	d.deserialize_u16(DiscrimVisitor)
}

/// Deserialize a single-field struct like a newtype struct.
macro_rules! serial_single_field {
	($typ:ident as $field:ident: $inner:path) => {
		impl ::serde::Serialize for $typ {
			fn serialize<S: ::serde::ser::Serializer>(&self, s: S) -> ::std::result::Result<S::Ok, S::Error> {
				self.$field.serialize(s)
			}
		}

		impl<'d> ::serde::Deserialize<'d> for $typ {
			fn deserialize<D: ::serde::de::Deserializer<'d>>(d: D) -> ::std::result::Result<$typ, D::Error> {
				<$inner as ::serde::de::Deserialize>::deserialize(d).map(|v| $typ { $field: v })
			}
		}
	}
}

/// Special support for the oddly complex `ReactionEmoji`.
pub mod reaction_emoji {
	use super::*;
	use model::{ReactionEmoji, EmojiId};

	#[derive(Serialize)]
	struct EmojiSer<'s> {
		name: &'s str,
		id: Option<EmojiId>
	}

	#[derive(Deserialize)]
	struct EmojiDe {
		name: String,
		id: Option<EmojiId>,
	}

	pub fn serialize<S: Serializer>(v: &ReactionEmoji, s: S) -> Result<S::Ok, S::Error> {
		(match *v {
			ReactionEmoji::Unicode(ref name) => EmojiSer { name: name, id: None },
			ReactionEmoji::Custom { ref name, id } => EmojiSer { name: name, id: Some(id) },
		}).serialize(s)
	}

	pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<ReactionEmoji, D::Error> {
		Ok(match try!(EmojiDe::deserialize(d)) {
			EmojiDe { name, id: None } => ReactionEmoji::Unicode(name),
			EmojiDe { name, id: Some(id) } => ReactionEmoji::Custom { name: name, id: id },
		})
	}
}

/// Support for named enums.
pub mod named {
	use super::*;

	pub trait NamedEnum: Sized {
		fn name(&self) -> &'static str;
		fn from_name(name: &str) -> Option<Self>;
		fn typename() -> &'static str;
	}

	pub fn serialize<T: NamedEnum, S: Serializer>(v: &T, s: S) -> Result<S::Ok, S::Error> {
		v.name().serialize(s)
	}

	pub fn deserialize<'d, T: NamedEnum, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
		struct NameVisitor<T>(PhantomData<T>);
		impl<'d, T: NamedEnum> Visitor<'d> for NameVisitor<T> {
			type Value = T;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid {} name", T::typename())
			}

			fn visit_str<E: Error>(self, v: &str) -> Result<T, E> {
				T::from_name(v).ok_or_else(|| E::invalid_value(Unexpected::Str(v), &self))
			}
		}

		d.deserialize_string(NameVisitor(PhantomData))
	}
}
macro_rules! serial_names {
	($typ:ident; $($entry:ident, $value:expr;)*) => {
		impl $typ {
			pub fn name(&self) -> &'static str {
				match *self {
					$($typ::$entry => $value,)*
				}
			}

			pub fn from_name(name: &str) -> Option<Self> {
				match name {
					$($value => Some($typ::$entry),)*
					_ => None,
				}
			}
		}

		impl ::serial::named::NamedEnum for $typ {
			fn name(&self) -> &'static str {
				self.name()
			}

			fn from_name(name: &str) -> Option<Self> {
				Self::from_name(name)
			}

			fn typename() -> &'static str {
				stringify!($typ)
			}
		}
	}
}

/// Support for numeric enums.
pub mod numeric {
	use super::*;

	pub trait NumericEnum: Sized {
		fn num(&self) -> u64;
		fn from_num(num: u64) -> Option<Self>;
		fn typename() -> &'static str;
	}

	pub fn serialize<T: NumericEnum, S: Serializer>(v: &T, s: S) -> Result<S::Ok, S::Error> {
		v.num().serialize(s)
	}

	pub fn deserialize<'d, T: NumericEnum, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
		struct NumVisitor<T>(PhantomData<T>);
		impl<'d, T: NumericEnum> Visitor<'d> for NumVisitor<T> {
			type Value = T;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid {} number", T::typename())
			}

			fn visit_i64<E: Error>(self, v: i64) -> Result<T, E> {
				i64_to_u64(self, v)
			}

			fn visit_u64<E: Error>(self, v: u64) -> Result<T, E> {
				T::from_num(v).ok_or_else(|| E::invalid_value(Unexpected::Unsigned(v), &self))
			}
		}

		d.deserialize_string(NumVisitor(PhantomData))
	}
}
macro_rules! serial_numbers {
	($typ:ident; $($entry:ident, $value:expr;)*) => {
		impl $typ {
			pub fn num(&self) -> u64 {
				match *self {
					$($typ::$entry => $value,)*
				}
			}

			pub fn from_num(num: u64) -> Option<Self> {
				match num {
					$($value => Some($typ::$entry),)*
					_ => None,
				}
			}
		}
		impl ::serial::numeric::NumericEnum for $typ {
			fn num(&self) -> u64 {
				self.num()
			}

			fn from_num(num: u64) -> Option<Self> {
				Self::from_num(num)
			}

			fn typename() -> &'static str {
				stringify!($typ)
			}
		}
	}
}

/// Support for using "named" or "numeric" as the default ser/de impl.
macro_rules! serial_use_mapping {
	($typ:ident, $which:ident) => {
		impl ::serde::Serialize for $typ {
			#[inline]
			fn serialize<S: ::serde::ser::Serializer>(&self, s: S) -> ::std::result::Result<S::Ok, S::Error> {
				::serial::$which::serialize(self, s)
			}
		}

		impl<'d> ::serde::Deserialize<'d> for $typ {
			#[inline]
			fn deserialize<D: ::serde::de::Deserializer<'d>>(d: D) -> ::std::result::Result<$typ, D::Error> {
				::serial::$which::deserialize(d)
			}
		}
	}
}
<commit_msg>Update serial.rs<commit_after>//! Serde integration support.

use std::fmt;
use std::marker::PhantomData;

use serde::*;
use serde::de::{Visitor, Error, Unexpected};

fn i64_to_u64<'d, V: Visitor<'d>, E: Error>(v: V, n: i64) -> Result<V::Value, E> {
	if n >= 0 {
		v.visit_u64(n as u64)
	} else {
		Err(E::invalid_value(Unexpected::Signed(n), &v))
	}
}

/// Ignore deserialization errors and revert to default.
pub fn ignore_errors<'d, T: Deserialize<'d> + Default, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
	use serde_json::Value;

	let v = Value::deserialize(d)?;
   	Ok(T::deserialize(v).ok().unwrap_or_default())
}

/// Deserialize a maybe-string ID into a u64.
pub fn deserialize_id<'d, D: Deserializer<'d>>(d: D) -> Result<u64, D::Error> {
	struct IdVisitor;
	impl<'d> Visitor<'d> for IdVisitor {
		type Value = u64;

		fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
			write!(fmt, "a u64 or parseable string")
		}

		fn visit_i64<E: Error>(self, v: i64) -> Result<u64, E> {
			i64_to_u64(self, v)
		}

		fn visit_u64<E: Error>(self, v: u64) -> Result<u64, E> {
			Ok(v)
		}

		fn visit_str<E: Error>(self, v: &str) -> Result<u64, E> {
			v.parse::<u64>().map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
		}
	}

	d.deserialize_any(IdVisitor)
}

/// Deserialize a maybe-string discriminator into a u16.
/// Also enforces 0 <= N <= 9999.
#[allow(unused_comparisons)]
pub fn deserialize_discrim<'d, D: Deserializer<'d>>(d: D) -> Result<u16, D::Error> {
	macro_rules! check {
		($self:ident, $v:ident, $wrong:expr) => {
			if $v >= 0 && $v <= 9999 {
				Ok($v as u16)
			} else {
				Err(E::invalid_value($wrong, &$self))
			}
		}
	}

	struct DiscrimVisitor;
	impl<'d> Visitor<'d> for DiscrimVisitor {
		type Value = u16;

		fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
			write!(fmt, "a u16 in [0, 9999] or parseable string")
		}

		fn visit_i64<E: Error>(self, v: i64) -> Result<u16, E> {
			check!(self, v, Unexpected::Signed(v))
		}

		fn visit_u64<E: Error>(self, v: u64) -> Result<u16, E> {
			check!(self, v, Unexpected::Unsigned(v))
		}

		fn visit_str<E: Error>(self, v: &str) -> Result<u16, E> {
			v.parse::<u16>()
				.map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
				.and_then(|v| self.visit_u16(v))
		}
	}

	d.deserialize_u16(DiscrimVisitor)
}

/// Deserialize a single-field struct like a newtype struct.
macro_rules! serial_single_field {
	($typ:ident as $field:ident: $inner:path) => {
		impl ::serde::Serialize for $typ {
			fn serialize<S: ::serde::ser::Serializer>(&self, s: S) -> ::std::result::Result<S::Ok, S::Error> {
				self.$field.serialize(s)
			}
		}

		impl<'d> ::serde::Deserialize<'d> for $typ {
			fn deserialize<D: ::serde::de::Deserializer<'d>>(d: D) -> ::std::result::Result<$typ, D::Error> {
				<$inner as ::serde::de::Deserialize>::deserialize(d).map(|v| $typ { $field: v })
			}
		}
	}
}

/// Special support for the oddly complex `ReactionEmoji`.
pub mod reaction_emoji {
	use super::*;
	use model::{ReactionEmoji, EmojiId};

	#[derive(Serialize)]
	struct EmojiSer<'s> {
		name: &'s str,
		id: Option<EmojiId>
	}

	#[derive(Deserialize)]
	struct EmojiDe {
		name: String,
		id: Option<EmojiId>,
	}

	pub fn serialize<S: Serializer>(v: &ReactionEmoji, s: S) -> Result<S::Ok, S::Error> {
		(match *v {
			ReactionEmoji::Unicode(ref name) => EmojiSer { name: name, id: None },
			ReactionEmoji::Custom { ref name, id } => EmojiSer { name: name, id: Some(id) },
		}).serialize(s)
	}

	pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<ReactionEmoji, D::Error> {
		Ok(match try!(EmojiDe::deserialize(d)) {
			EmojiDe { name, id: None } => ReactionEmoji::Unicode(name),
			EmojiDe { name, id: Some(id) } => ReactionEmoji::Custom { name: name, id: id },
		})
	}
}

/// Support for named enums.
pub mod named {
	use super::*;

	pub trait NamedEnum: Sized {
		fn name(&self) -> &'static str;
		fn from_name(name: &str) -> Option<Self>;
		fn typename() -> &'static str;
	}

	pub fn serialize<T: NamedEnum, S: Serializer>(v: &T, s: S) -> Result<S::Ok, S::Error> {
		v.name().serialize(s)
	}

	pub fn deserialize<'d, T: NamedEnum, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
		struct NameVisitor<T>(PhantomData<T>);
		impl<'d, T: NamedEnum> Visitor<'d> for NameVisitor<T> {
			type Value = T;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid {} name", T::typename())
			}

			fn visit_str<E: Error>(self, v: &str) -> Result<T, E> {
				T::from_name(v).ok_or_else(|| E::invalid_value(Unexpected::Str(v), &self))
			}
		}

		d.deserialize_string(NameVisitor(PhantomData))
	}
}
macro_rules! serial_names {
	($typ:ident; $($entry:ident, $value:expr;)*) => {
		impl $typ {
			pub fn name(&self) -> &'static str {
				match *self {
					$($typ::$entry => $value,)*
				}
			}

			pub fn from_name(name: &str) -> Option<Self> {
				match name {
					$($value => Some($typ::$entry),)*
					_ => None,
				}
			}
		}

		impl ::serial::named::NamedEnum for $typ {
			fn name(&self) -> &'static str {
				self.name()
			}

			fn from_name(name: &str) -> Option<Self> {
				Self::from_name(name)
			}

			fn typename() -> &'static str {
				stringify!($typ)
			}
		}
	}
}

/// Support for numeric enums.
pub mod numeric {
	use super::*;

	pub trait NumericEnum: Sized {
		fn num(&self) -> u64;
		fn from_num(num: u64) -> Option<Self>;
		fn typename() -> &'static str;
	}

	pub fn serialize<T: NumericEnum, S: Serializer>(v: &T, s: S) -> Result<S::Ok, S::Error> {
		v.num().serialize(s)
	}

	pub fn deserialize<'d, T: NumericEnum, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
		struct NumVisitor<T>(PhantomData<T>);
		impl<'d, T: NumericEnum> Visitor<'d> for NumVisitor<T> {
			type Value = T;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid {} number", T::typename())
			}

			fn visit_i64<E: Error>(self, v: i64) -> Result<T, E> {
				i64_to_u64(self, v)
			}

			fn visit_u64<E: Error>(self, v: u64) -> Result<T, E> {
				T::from_num(v).ok_or_else(|| E::invalid_value(Unexpected::Unsigned(v), &self))
			}
		}

		d.deserialize_string(NumVisitor(PhantomData))
	}
}
macro_rules! serial_numbers {
	($typ:ident; $($entry:ident, $value:expr;)*) => {
		impl $typ {
			pub fn num(&self) -> u64 {
				match *self {
					$($typ::$entry => $value,)*
				}
			}

			pub fn from_num(num: u64) -> Option<Self> {
				match num {
					$($value => Some($typ::$entry),)*
					_ => None,
				}
			}
		}
		impl ::serial::numeric::NumericEnum for $typ {
			fn num(&self) -> u64 {
				self.num()
			}

			fn from_num(num: u64) -> Option<Self> {
				Self::from_num(num)
			}

			fn typename() -> &'static str {
				stringify!($typ)
			}
		}
	}
}

/// Support for using "named" or "numeric" as the default ser/de impl.
macro_rules! serial_use_mapping {
	($typ:ident, $which:ident) => {
		impl ::serde::Serialize for $typ {
			#[inline]
			fn serialize<S: ::serde::ser::Serializer>(&self, s: S) -> ::std::result::Result<S::Ok, S::Error> {
				::serial::$which::serialize(self, s)
			}
		}

		impl<'d> ::serde::Deserialize<'d> for $typ {
			#[inline]
			fn deserialize<D: ::serde::de::Deserializer<'d>>(d: D) -> ::std::result::Result<$typ, D::Error> {
				::serial::$which::deserialize(d)
			}
		}
	}
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use conduit::{Handler, Request, Response};
use conduit_router::{RequestParams, RouteBuilder};

use crate::controllers::*;
use crate::util::errors::{std_error, AppError, AppResult, NotFound};
use crate::util::RequestProxy;
use crate::{middleware, App, Env};

pub fn build_router(app: &App) -> R404 {
    let mut api_router = RouteBuilder::new();

    // Route used by both `cargo search` and the frontend
    api_router.get("/crates", C(krate::search::search));

    // Routes used by `cargo`
    api_router.put("/crates/new", C(krate::publish::publish));
    api_router.get("/crates/:crate_id/owners", C(krate::owners::owners));
    api_router.put("/crates/:crate_id/owners", C(krate::owners::add_owners));
    api_router.delete("/crates/:crate_id/owners", C(krate::owners::remove_owners));
    api_router.delete("/crates/:crate_id/:version/yank", C(version::yank::yank));
    api_router.put(
        "/crates/:crate_id/:version/unyank",
        C(version::yank::unyank),
    );
    api_router.get(
        "/crates/:crate_id/:version/download",
        C(version::downloads::download),
    );

    // Routes that appear to be unused
    api_router.get("/versions", C(version::deprecated::index));
    api_router.get("/versions/:version_id", C(version::deprecated::show_by_id));

    // Routes used by the frontend
    api_router.get("/crates/:crate_id", C(krate::metadata::show));
    api_router.get("/crates/:crate_id/:version", C(version::metadata::show));
    api_router.get(
        "/crates/:crate_id/:version/readme",
        C(krate::metadata::readme),
    );
    api_router.get(
        "/crates/:crate_id/:version/dependencies",
        C(version::metadata::dependencies),
    );
    api_router.get(
        "/crates/:crate_id/:version/downloads",
        C(version::downloads::downloads),
    );
    api_router.get(
        "/crates/:crate_id/:version/authors",
        C(version::metadata::authors),
    );
    api_router.get(
        "/crates/:crate_id/downloads",
        C(krate::downloads::downloads),
    );
    api_router.get("/crates/:crate_id/versions", C(krate::metadata::versions));
    api_router.put("/crates/:crate_id/follow", C(krate::follow::follow));
    api_router.delete("/crates/:crate_id/follow", C(krate::follow::unfollow));
    api_router.get("/crates/:crate_id/following", C(krate::follow::following));
    api_router.get("/crates/:crate_id/owner_team", C(krate::owners::owner_team));
    api_router.get("/crates/:crate_id/owner_user", C(krate::owners::owner_user));
    api_router.get(
        "/crates/:crate_id/reverse_dependencies",
        C(krate::metadata::reverse_dependencies),
    );
    api_router.get("/keywords", C(keyword::index));
    api_router.get("/keywords/:keyword_id", C(keyword::show));
    api_router.get("/categories", C(category::index));
    api_router.get("/categories/:category_id", C(category::show));
    api_router.get("/category_slugs", C(category::slugs));
    api_router.get("/users/:user_id", C(user::other::show));
    api_router.put("/users/:user_id", C(user::me::update_user));
    api_router.get("/users/:user_id/stats", C(user::other::stats));
    api_router.get("/teams/:team_id", C(team::show_team));
    api_router.get("/me", C(user::me::me));
    api_router.get("/me/updates", C(user::me::updates));
    api_router.get("/me/tokens", C(token::list));
    api_router.put("/me/tokens", C(token::new));
    api_router.delete("/me/tokens/:id", C(token::revoke));
    api_router.get(
        "/me/crate_owner_invitations",
        C(crate_owner_invitation::list),
    );
    api_router.put(
        "/me/crate_owner_invitations/:crate_id",
        C(crate_owner_invitation::handle_invite),
    );
    api_router.put(
        "/me/crate_owner_invitations/accept/:token",
        C(crate_owner_invitation::handle_invite_with_token),
    );
    api_router.put(
        "/me/email_notifications",
        C(user::me::update_email_notifications),
    );
    api_router.get("/summary", C(krate::metadata::summary));
    api_router.put("/confirm/:email_token", C(user::me::confirm_user_email));
    api_router.put(
        "/users/:user_id/resend",
        C(user::me::regenerate_token_and_send),
    );
    api_router.get("/site_metadata", C(site_metadata::show_deployed_sha));
    let api_router = Arc::new(R404(api_router));

    let mut router = RouteBuilder::new();

    // Mount the router under the /api/v1 path so we're at least somewhat at the
    // liberty to change things in the future!
    router.get("/api/v1/*path", R(Arc::clone(&api_router)));
    router.put("/api/v1/*path", R(Arc::clone(&api_router)));
    router.post("/api/v1/*path", R(Arc::clone(&api_router)));
    router.head("/api/v1/*path", R(Arc::clone(&api_router)));
    router.delete("/api/v1/*path", R(api_router));

    // Session management
    router.get("/api/private/session/begin", C(user::session::begin));
    router.get(
        "/api/private/session/authorize",
        C(user::session::authorize),
    );
    router.delete("/api/private/session", C(user::session::logout));

    // Only serve the local checkout of the git index in development mode.
    // In production, for crates.io, cargo gets the index from
    // https://github.com/rust-lang/crates.io-index directly.
    if app.config.env == Env::Development {
        let s = conduit_git_http_backend::Serve(app.git_repo_checkout.clone());
        let s = Arc::new(s);
        router.get("/git/index/*path", R(Arc::clone(&s)));
        router.post("/git/index/*path", R(s));
    }

    R404(router)
}

struct C(pub fn(&mut dyn Request) -> AppResult<Response>);

impl Handler for C {
    fn call(&self, req: &mut dyn Request) -> middleware::Result<Response> {
        let C(f) = *self;
        match f(req) {
            Ok(resp) => Ok(resp),
            Err(e) => match e.response() {
                Some(response) => Ok(response),
                None => Err(std_error(e)),
            },
        }
    }
}

struct R<H>(pub Arc<H>);

impl<H: Handler> Handler for R<H> {
    fn call(&self, req: &mut dyn Request) -> middleware::Result<Response> {
        let path = req.params()["path"].to_string();
        let R(ref sub_router) = *self;
        sub_router.call(&mut RequestProxy::rewrite_path(req, &path))
    }
}

// Can't derive Debug because of RouteBuilder.
#[allow(missing_debug_implementations)]
pub struct R404(pub RouteBuilder);

impl Handler for R404 {
    fn call(&self, req: &mut dyn Request) -> middleware::Result<Response> {
        let R404(ref router) = *self;
        match router.recognize(&req.method(), req.path()) {
            Ok(m) => {
                req.mut_extensions().insert(m.params.clone());
                m.handler.call(req)
            }
            Err(..) => Ok(NotFound.response().unwrap()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::errors::{bad_request, cargo_err, internal, AppError, NotFound, Unauthorized};

    use conduit_test::MockRequest;
    use diesel::result::Error as DieselError;

    fn err<E: AppError>(err: E) -> AppResult<Response> {
        Err(Box::new(err))
    }

    #[test]
    fn http_error_responses() {
        let mut req = MockRequest::new(::conduit::Method::Get, "/");

        // Types for handling common error status codes
        assert_eq!(
            C(|_| Err(bad_request(""))).call(&mut req).unwrap().status.0,
            400
        );
        assert_eq!(
            C(|_| err(Unauthorized)).call(&mut req).unwrap().status.0,
            403
        );
        assert_eq!(
            C(|_| Err(DieselError::NotFound.into()))
                .call(&mut req)
                .unwrap()
                .status
                .0,
            404
        );
        assert_eq!(C(|_| err(NotFound)).call(&mut req).unwrap().status.0, 404);

        // cargo_err errors are returned as 200 so that cargo displays this nicely on the command line
        assert_eq!(
            C(|_| Err(cargo_err(""))).call(&mut req).unwrap().status.0,
            200
        );

        // All other error types are propogated up the middleware, eventually becoming status 500
        assert!(C(|_| Err(internal(""))).call(&mut req).is_err());
        assert!(C(|_| err(::serde_json::Error::syntax(
            ::serde_json::error::ErrorCode::ExpectedColon,
            0,
            0
        )))
        .call(&mut req)
        .is_err());
        assert!(
            C(|_| err(::std::io::Error::new(::std::io::ErrorKind::Other, "")))
                .call(&mut req)
                .is_err()
        );
    }
}
<commit_msg>Remove use of private items<commit_after>use std::sync::Arc;

use conduit::{Handler, Request, Response};
use conduit_router::{RequestParams, RouteBuilder};

use crate::controllers::*;
use crate::util::errors::{std_error, AppError, AppResult, NotFound};
use crate::util::RequestProxy;
use crate::{middleware, App, Env};

pub fn build_router(app: &App) -> R404 {
    let mut api_router = RouteBuilder::new();

    // Route used by both `cargo search` and the frontend
    api_router.get("/crates", C(krate::search::search));

    // Routes used by `cargo`
    api_router.put("/crates/new", C(krate::publish::publish));
    api_router.get("/crates/:crate_id/owners", C(krate::owners::owners));
    api_router.put("/crates/:crate_id/owners", C(krate::owners::add_owners));
    api_router.delete("/crates/:crate_id/owners", C(krate::owners::remove_owners));
    api_router.delete("/crates/:crate_id/:version/yank", C(version::yank::yank));
    api_router.put(
        "/crates/:crate_id/:version/unyank",
        C(version::yank::unyank),
    );
    api_router.get(
        "/crates/:crate_id/:version/download",
        C(version::downloads::download),
    );

    // Routes that appear to be unused
    api_router.get("/versions", C(version::deprecated::index));
    api_router.get("/versions/:version_id", C(version::deprecated::show_by_id));

    // Routes used by the frontend
    api_router.get("/crates/:crate_id", C(krate::metadata::show));
    api_router.get("/crates/:crate_id/:version", C(version::metadata::show));
    api_router.get(
        "/crates/:crate_id/:version/readme",
        C(krate::metadata::readme),
    );
    api_router.get(
        "/crates/:crate_id/:version/dependencies",
        C(version::metadata::dependencies),
    );
    api_router.get(
        "/crates/:crate_id/:version/downloads",
        C(version::downloads::downloads),
    );
    api_router.get(
        "/crates/:crate_id/:version/authors",
        C(version::metadata::authors),
    );
    api_router.get(
        "/crates/:crate_id/downloads",
        C(krate::downloads::downloads),
    );
    api_router.get("/crates/:crate_id/versions", C(krate::metadata::versions));
    api_router.put("/crates/:crate_id/follow", C(krate::follow::follow));
    api_router.delete("/crates/:crate_id/follow", C(krate::follow::unfollow));
    api_router.get("/crates/:crate_id/following", C(krate::follow::following));
    api_router.get("/crates/:crate_id/owner_team", C(krate::owners::owner_team));
    api_router.get("/crates/:crate_id/owner_user", C(krate::owners::owner_user));
    api_router.get(
        "/crates/:crate_id/reverse_dependencies",
        C(krate::metadata::reverse_dependencies),
    );
    api_router.get("/keywords", C(keyword::index));
    api_router.get("/keywords/:keyword_id", C(keyword::show));
    api_router.get("/categories", C(category::index));
    api_router.get("/categories/:category_id", C(category::show));
    api_router.get("/category_slugs", C(category::slugs));
    api_router.get("/users/:user_id", C(user::other::show));
    api_router.put("/users/:user_id", C(user::me::update_user));
    api_router.get("/users/:user_id/stats", C(user::other::stats));
    api_router.get("/teams/:team_id", C(team::show_team));
    api_router.get("/me", C(user::me::me));
    api_router.get("/me/updates", C(user::me::updates));
    api_router.get("/me/tokens", C(token::list));
    api_router.put("/me/tokens", C(token::new));
    api_router.delete("/me/tokens/:id", C(token::revoke));
    api_router.get(
        "/me/crate_owner_invitations",
        C(crate_owner_invitation::list),
    );
    api_router.put(
        "/me/crate_owner_invitations/:crate_id",
        C(crate_owner_invitation::handle_invite),
    );
    api_router.put(
        "/me/crate_owner_invitations/accept/:token",
        C(crate_owner_invitation::handle_invite_with_token),
    );
    api_router.put(
        "/me/email_notifications",
        C(user::me::update_email_notifications),
    );
    api_router.get("/summary", C(krate::metadata::summary));
    api_router.put("/confirm/:email_token", C(user::me::confirm_user_email));
    api_router.put(
        "/users/:user_id/resend",
        C(user::me::regenerate_token_and_send),
    );
    api_router.get("/site_metadata", C(site_metadata::show_deployed_sha));
    let api_router = Arc::new(R404(api_router));

    let mut router = RouteBuilder::new();

    // Mount the router under the /api/v1 path so we're at least somewhat at the
    // liberty to change things in the future!
    router.get("/api/v1/*path", R(Arc::clone(&api_router)));
    router.put("/api/v1/*path", R(Arc::clone(&api_router)));
    router.post("/api/v1/*path", R(Arc::clone(&api_router)));
    router.head("/api/v1/*path", R(Arc::clone(&api_router)));
    router.delete("/api/v1/*path", R(api_router));

    // Session management
    router.get("/api/private/session/begin", C(user::session::begin));
    router.get(
        "/api/private/session/authorize",
        C(user::session::authorize),
    );
    router.delete("/api/private/session", C(user::session::logout));

    // Only serve the local checkout of the git index in development mode.
    // In production, for crates.io, cargo gets the index from
    // https://github.com/rust-lang/crates.io-index directly.
    if app.config.env == Env::Development {
        let s = conduit_git_http_backend::Serve(app.git_repo_checkout.clone());
        let s = Arc::new(s);
        router.get("/git/index/*path", R(Arc::clone(&s)));
        router.post("/git/index/*path", R(s));
    }

    R404(router)
}

struct C(pub fn(&mut dyn Request) -> AppResult<Response>);

impl Handler for C {
    fn call(&self, req: &mut dyn Request) -> middleware::Result<Response> {
        let C(f) = *self;
        match f(req) {
            Ok(resp) => Ok(resp),
            Err(e) => match e.response() {
                Some(response) => Ok(response),
                None => Err(std_error(e)),
            },
        }
    }
}

struct R<H>(pub Arc<H>);

impl<H: Handler> Handler for R<H> {
    fn call(&self, req: &mut dyn Request) -> middleware::Result<Response> {
        let path = req.params()["path"].to_string();
        let R(ref sub_router) = *self;
        sub_router.call(&mut RequestProxy::rewrite_path(req, &path))
    }
}

// Can't derive Debug because of RouteBuilder.
#[allow(missing_debug_implementations)]
pub struct R404(pub RouteBuilder);

impl Handler for R404 {
    fn call(&self, req: &mut dyn Request) -> middleware::Result<Response> {
        let R404(ref router) = *self;
        match router.recognize(&req.method(), req.path()) {
            Ok(m) => {
                req.mut_extensions().insert(m.params.clone());
                m.handler.call(req)
            }
            Err(..) => Ok(NotFound.response().unwrap()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::errors::{bad_request, cargo_err, internal, AppError, NotFound, Unauthorized};

    use conduit_test::MockRequest;
    use diesel::result::Error as DieselError;

    fn err<E: AppError>(err: E) -> AppResult<Response> {
        Err(Box::new(err))
    }

    #[test]
    fn http_error_responses() {
        let mut req = MockRequest::new(::conduit::Method::Get, "/");

        // Types for handling common error status codes
        assert_eq!(
            C(|_| Err(bad_request(""))).call(&mut req).unwrap().status.0,
            400
        );
        assert_eq!(
            C(|_| err(Unauthorized)).call(&mut req).unwrap().status.0,
            403
        );
        assert_eq!(
            C(|_| Err(DieselError::NotFound.into()))
                .call(&mut req)
                .unwrap()
                .status
                .0,
            404
        );
        assert_eq!(C(|_| err(NotFound)).call(&mut req).unwrap().status.0, 404);

        // cargo_err errors are returned as 200 so that cargo displays this nicely on the command line
        assert_eq!(
            C(|_| Err(cargo_err(""))).call(&mut req).unwrap().status.0,
            200
        );

        // All other error types are propogated up the middleware, eventually becoming status 500
        assert!(C(|_| Err(internal(""))).call(&mut req).is_err());
        assert!(
            C(|_| err::<::serde_json::Error>(::serde::de::Error::custom("ExpectedColon")))
                .call(&mut req)
                .is_err()
        );
        assert!(
            C(|_| err(::std::io::Error::new(::std::io::ErrorKind::Other, "")))
                .call(&mut req)
                .is_err()
        );
    }
}
<|endoftext|>


<commit_before>//!Router asigns handlers to paths and resolves them per request

#[cfg(test)]
use http::method;
use http::method::{ Method, Get, Post, Put, Delete };
use http::server::request::{AbsolutePath};
use regex::Regex;
use std::collections::hashmap::HashMap;
use request::Request;
use response::Response;
use middleware::{ Middleware, Action, Halt, Continue };
use nickel_error::NickelError;

/// A Route is the basic data structure that stores both the path
/// and the handler that gets executed for the route.
/// The path can contain variable pattern such as `user/:userid/invoices`
pub struct Route {
    pub path: String,
    pub method: Method,
    pub handler: fn(request: &Request, response: &mut Response),
    pub variables: HashMap<String, uint>,
    matcher: Regex
}

impl Clone for Route {
    fn clone(&self) -> Route {
        Route {
            path: self.path.clone(),
            method: self.method.clone(),
            handler: self.handler,
            matcher: self.matcher.clone(),
            variables: self.variables.clone()
        }
    }
}

/// A RouteResult is what the router returns when `match_route` is called.
/// It contains the matched `route` and also a `params` property holding
/// a HashMap with the keys being the variable names and the value being the
/// evaluated string
pub struct RouteResult<'a> {
    pub route: &'a Route,
    pub params: HashMap<String, String>
}

/// The PathUtils collects some small helper methods that operate on the path
struct PathUtils;

// matches named variables (e.g. :userid)
static REGEX_VAR_SEQ: Regex                 = regex!(r":([,a-zA-Z0-9_-]*)");
static VAR_SEQ:&'static str                 = "[,a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_SLASH:&'static str      = "[,/a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_CAPTURE:&'static str    = "([,a-zA-Z0-9%_-]*)";
// matches request params (e.g. ?foo=true&bar=false)
static REGEX_PARAM_SEQ:&'static str         = "(\\?[a-zA-Z0-9%_=&-]*)?";
static REGEX_START:&'static str             = "^";
static REGEX_END:&'static str               = "$";

impl PathUtils {
    fn create_regex (route_path: &str) -> Regex {

        let updated_path = route_path.to_string()
                                     // first mark all double wildcards for replacement. We can't directly replace them
                                     // since the replacement does contain the * symbol as well, which would get overwritten
                                     // with the next replace call
                                     .replace("**", "___DOUBLE_WILDCARD___")
                                     // then replace the regular wildcard symbols (*) with the appropriate regex
                                     .replace("*", VAR_SEQ)
                                     // now replace the previously marked double wild cards (**)
                                     .replace("___DOUBLE_WILDCARD___", VAR_SEQ_WITH_SLASH);

        // then replace the variable symbols (:variable) with the appropriate regex
        let result = [REGEX_START,
                      REGEX_VAR_SEQ.replace_all(updated_path.as_slice(),
                                                VAR_SEQ_WITH_CAPTURE)
                                   .as_slice(),
                      REGEX_PARAM_SEQ,
                      REGEX_END].concat();

        match Regex::new(result.as_slice()) {
            Ok(re) => re,
            Err(err) => fail!("{}", err)
        }
    }

    fn get_variable_info (route_path: &str) -> HashMap<String, uint> {
        REGEX_VAR_SEQ.captures_iter(route_path)
             .enumerate()
             .map(|(i, matched)| (matched.at(1).to_string(), i))
             .collect()
    }
}

/// The Router's job is it to hold routes and to resolve them later against
/// concrete URLs. The router is also a regular middleware and needs to be
/// added to the middleware stack with `server.utilize(router)`.

#[deriving(Clone)]
pub struct Router{
    routes: Vec<Route>,
}

impl Router {
    pub fn new () -> Router {
        Router {
            routes: Vec::new()
        }
    }

    /// Registers a handler to be used for a specific GET request.
    /// Handlers are assigned to paths and paths are allowed to contain
    /// variables and wildcards.
    ///
    /// # Example without variables and wildcards
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user");
    /// };
    /// router.get("/user", handler);
    /// ```
    /// # Example with variables
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     let text = format!("This is user: {}", request.params.get(&"userid".to_string()));
    ///     response.send(text.as_slice());
    /// };
    /// router.get("/user/:userid", handler);
    /// ```
    /// # Example with simple wildcard
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user/list/4711 but not /user/extended/list/4711");
    /// };
    /// router.get("/user/*/:userid", handler);
    /// ```
    /// # Example with double wildcard
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user/list/4711 and also /user/extended/list/4711");
    /// };
    /// router.get("/user/**/:userid", handler);
    /// ```
    pub fn get(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Get, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific POST request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a POST request to /a/post/request");
    /// };
    /// router.post("/a/post/request", handler);
    /// ```
    /// Take a look at `get()` for a more detailed description.
    pub fn post(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Post, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific PUT request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a POST request to /a/put/request");
    /// };
    /// router.put("/a/put/request", handler);
    /// ```
    /// Take a look at `get(..)` for a more detailed description.
    pub fn put(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Put, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific DELETE request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a DELETE request to /a/delete/request");
    /// };
    /// router.delete("/a/delete/request", handler);
    /// ```
    /// Take a look at `get(...)` for a more detailed description.
    pub fn delete(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Delete, String::from_str(uri), handler);
    }

    pub fn add_route (&mut self, method: Method, path: String, handler: fn(request: &Request, response: &mut Response)) -> () {
        let matcher = PathUtils::create_regex(path.as_slice());
        let variable_infos = PathUtils::get_variable_info(path.as_slice());
        let route = Route {
            path: path,
            method: method,
            matcher: matcher,
            handler: handler,
            variables: variable_infos
        };
        self.routes.push(route);
    }

    pub fn match_route(&self, method: Method, path: &str) -> Option<RouteResult> {
        self.routes.iter().find(|item| item.method == method && item.matcher.is_match(path))
            .map(|route| {
                let map = match route.matcher.captures(path) {
                    Some(captures) => {
                        route.variables.iter().map(|(name, pos)| {
                            (name.to_string(), captures.at(pos + 1).to_string())
                        }).collect()
                    },
                    None => HashMap::new(),
                };
                RouteResult {
                    route: route,
                    params: map
                }
            })
    }
}

impl Middleware for Router {
    fn invoke (&self, req: &mut Request, res: &mut Response) -> Result<Action, NickelError> {
        match req.origin.request_uri {
            AbsolutePath(ref url) => {
                match self.match_route(req.origin.method.clone(), url.as_slice()) {
                    Some(route_result) => {
                        res.origin.status = ::http::status::Ok;
                        req.params = route_result.params.clone();
                        (route_result.route.handler)(req, res);
                        Ok(Halt)
                    },
                    None => Ok(Continue)
                }
            },
            _ => Ok(Continue)
        }
    }
}

#[test]
fn creates_map_with_var_variable_infos () {
    let map = PathUtils::get_variable_info("foo/:uid/bar/:groupid");

    assert_eq!(map.len(), 2);
    assert_eq!(map.get(&"uid".to_string()), &0);
    assert_eq!(map.get(&"groupid".to_string()), &1);
}

#[test]
fn creates_regex_with_captures () {
    let regex = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let caps = regex.captures("foo/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/*/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/**/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");
}

#[test]
fn creates_valid_regex_for_routes () {
    let regex1 = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let regex2 = PathUtils::create_regex("foo/*/bar");
    let regex3 = PathUtils::create_regex("foo/**/bar");

    assert_eq!(regex1.is_match("foo/4711/bar/5490"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=true&bar=false"), false);
    assert_eq!(regex1.is_match("foo/4711/bar/test%20spacing"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5281?foo=test%20spacing&bar=false"), true);

    assert_eq!(regex2.is_match("foo/4711/bar"), true);
    assert_eq!(regex2.is_match("foo/4711/barr"), false);
    assert_eq!(regex2.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex2.is_match("foo/4711/4712/bar"), false);
    assert_eq!(regex2.is_match("foo/4711/4712/bar?foo=true&bar=false"), false);

    assert_eq!(regex3.is_match("foo/4711/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar?foo=true&bar=false"), true);

    //ensure that this works with commas too
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=1,2,3&bar=false"), false);
}

#[test]
fn can_match_var_routes () {
    let route_store = &mut Router::new();

    fn handler (_request: &Request, response: &mut Response) -> () {
        let _ = response.origin.write("hello from foo".as_bytes());
    };

    route_store.add_route(method::Get, "/foo/:userid".to_string(), handler);
    route_store.add_route(method::Get, "/bar".to_string(), handler);

    let route_result = route_store.match_route(method::Get, "/foo/4711").unwrap();
    let route = route_result.route;

    assert_eq!(route_result.params.get(&"userid".to_string()), &"4711".to_string());

    //assert the route has identified the variable
    assert_eq!(route.variables.len(), 1);
    assert_eq!(route.variables.get(&"userid".to_string()), &0);

    let route_result = route_store.match_route(method::Get, "/bar/4711");
    assert!(route_result.is_none());

    let route_result = route_store.match_route(method::Get, "/foo");
    assert!(route_result.is_none());

    //ensure that this will work with commas too
    let route_result = route_store.match_route(method::Get, "/foo/123,456");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.params.get(&"userid".to_string()), &"123,456".to_string());

    //ensure that this will work with spacing too
    let route_result = route_store.match_route(method::Get, "/foo/John%20Doe");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.params.get(&"userid".to_string()), &"John%20Doe".to_string());
}
<commit_msg>Router::match_route can take &Method.<commit_after>//!Router asigns handlers to paths and resolves them per request

#[cfg(test)]
use http::method;
use http::method::{ Method, Get, Post, Put, Delete };
use http::server::request::{AbsolutePath};
use regex::Regex;
use std::collections::hashmap::HashMap;
use request::Request;
use response::Response;
use middleware::{ Middleware, Action, Halt, Continue };
use nickel_error::NickelError;

/// A Route is the basic data structure that stores both the path
/// and the handler that gets executed for the route.
/// The path can contain variable pattern such as `user/:userid/invoices`
pub struct Route {
    pub path: String,
    pub method: Method,
    pub handler: fn(request: &Request, response: &mut Response),
    pub variables: HashMap<String, uint>,
    matcher: Regex
}

impl Clone for Route {
    fn clone(&self) -> Route {
        Route {
            path: self.path.clone(),
            method: self.method.clone(),
            handler: self.handler,
            matcher: self.matcher.clone(),
            variables: self.variables.clone()
        }
    }
}

/// A RouteResult is what the router returns when `match_route` is called.
/// It contains the matched `route` and also a `params` property holding
/// a HashMap with the keys being the variable names and the value being the
/// evaluated string
pub struct RouteResult<'a> {
    pub route: &'a Route,
    pub params: HashMap<String, String>
}

/// The PathUtils collects some small helper methods that operate on the path
struct PathUtils;

// matches named variables (e.g. :userid)
static REGEX_VAR_SEQ: Regex                 = regex!(r":([,a-zA-Z0-9_-]*)");
static VAR_SEQ:&'static str                 = "[,a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_SLASH:&'static str      = "[,/a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_CAPTURE:&'static str    = "([,a-zA-Z0-9%_-]*)";
// matches request params (e.g. ?foo=true&bar=false)
static REGEX_PARAM_SEQ:&'static str         = "(\\?[a-zA-Z0-9%_=&-]*)?";
static REGEX_START:&'static str             = "^";
static REGEX_END:&'static str               = "$";

impl PathUtils {
    fn create_regex (route_path: &str) -> Regex {

        let updated_path = route_path.to_string()
                                     // first mark all double wildcards for replacement. We can't directly replace them
                                     // since the replacement does contain the * symbol as well, which would get overwritten
                                     // with the next replace call
                                     .replace("**", "___DOUBLE_WILDCARD___")
                                     // then replace the regular wildcard symbols (*) with the appropriate regex
                                     .replace("*", VAR_SEQ)
                                     // now replace the previously marked double wild cards (**)
                                     .replace("___DOUBLE_WILDCARD___", VAR_SEQ_WITH_SLASH);

        // then replace the variable symbols (:variable) with the appropriate regex
        let result = [REGEX_START,
                      REGEX_VAR_SEQ.replace_all(updated_path.as_slice(),
                                                VAR_SEQ_WITH_CAPTURE)
                                   .as_slice(),
                      REGEX_PARAM_SEQ,
                      REGEX_END].concat();

        match Regex::new(result.as_slice()) {
            Ok(re) => re,
            Err(err) => fail!("{}", err)
        }
    }

    fn get_variable_info (route_path: &str) -> HashMap<String, uint> {
        REGEX_VAR_SEQ.captures_iter(route_path)
             .enumerate()
             .map(|(i, matched)| (matched.at(1).to_string(), i))
             .collect()
    }
}

/// The Router's job is it to hold routes and to resolve them later against
/// concrete URLs. The router is also a regular middleware and needs to be
/// added to the middleware stack with `server.utilize(router)`.

#[deriving(Clone)]
pub struct Router{
    routes: Vec<Route>,
}

impl Router {
    pub fn new () -> Router {
        Router {
            routes: Vec::new()
        }
    }

    /// Registers a handler to be used for a specific GET request.
    /// Handlers are assigned to paths and paths are allowed to contain
    /// variables and wildcards.
    ///
    /// # Example without variables and wildcards
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user");
    /// };
    /// router.get("/user", handler);
    /// ```
    /// # Example with variables
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     let text = format!("This is user: {}", request.params.get(&"userid".to_string()));
    ///     response.send(text.as_slice());
    /// };
    /// router.get("/user/:userid", handler);
    /// ```
    /// # Example with simple wildcard
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user/list/4711 but not /user/extended/list/4711");
    /// };
    /// router.get("/user/*/:userid", handler);
    /// ```
    /// # Example with double wildcard
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches /user/list/4711 and also /user/extended/list/4711");
    /// };
    /// router.get("/user/**/:userid", handler);
    /// ```
    pub fn get(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Get, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific POST request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a POST request to /a/post/request");
    /// };
    /// router.post("/a/post/request", handler);
    /// ```
    /// Take a look at `get()` for a more detailed description.
    pub fn post(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Post, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific PUT request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a POST request to /a/put/request");
    /// };
    /// router.put("/a/put/request", handler);
    /// ```
    /// Take a look at `get(..)` for a more detailed description.
    pub fn put(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Put, String::from_str(uri), handler);
    }

    /// Registers a handler to be used for a specific DELETE request.
    ///
    /// # Example
    ///
    /// ```{rust,ignore}
    /// fn handler (request: Request, response: &mut Response) {
    ///     response.send("This matches a DELETE request to /a/delete/request");
    /// };
    /// router.delete("/a/delete/request", handler);
    /// ```
    /// Take a look at `get(...)` for a more detailed description.
    pub fn delete(&mut self, uri: &str, handler: fn(request: &Request, response: &mut Response)){
        self.add_route(Delete, String::from_str(uri), handler);
    }

    pub fn add_route (&mut self, method: Method, path: String, handler: fn(request: &Request, response: &mut Response)) -> () {
        let matcher = PathUtils::create_regex(path.as_slice());
        let variable_infos = PathUtils::get_variable_info(path.as_slice());
        let route = Route {
            path: path,
            method: method,
            matcher: matcher,
            handler: handler,
            variables: variable_infos
        };
        self.routes.push(route);
    }

    pub fn match_route(&self, method: &Method, path: &str) -> Option<RouteResult> {
        self.routes.iter().find(|item| item.method == *method && item.matcher.is_match(path))
            .map(|route| {
                let map = match route.matcher.captures(path) {
                    Some(captures) => {
                        route.variables.iter().map(|(name, pos)| {
                            (name.to_string(), captures.at(pos + 1).to_string())
                        }).collect()
                    },
                    None => HashMap::new(),
                };
                RouteResult {
                    route: route,
                    params: map
                }
            })
    }
}

impl Middleware for Router {
    fn invoke (&self, req: &mut Request, res: &mut Response) -> Result<Action, NickelError> {
        match req.origin.request_uri {
            AbsolutePath(ref url) => {
                match self.match_route(&req.origin.method, url.as_slice()) {
                    Some(route_result) => {
                        res.origin.status = ::http::status::Ok;
                        req.params = route_result.params.clone();
                        (route_result.route.handler)(req, res);
                        Ok(Halt)
                    },
                    None => Ok(Continue)
                }
            },
            _ => Ok(Continue)
        }
    }
}

#[test]
fn creates_map_with_var_variable_infos () {
    let map = PathUtils::get_variable_info("foo/:uid/bar/:groupid");

    assert_eq!(map.len(), 2);
    assert_eq!(map.get(&"uid".to_string()), &0);
    assert_eq!(map.get(&"groupid".to_string()), &1);
}

#[test]
fn creates_regex_with_captures () {
    let regex = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let caps = regex.captures("foo/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/*/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/**/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");
}

#[test]
fn creates_valid_regex_for_routes () {
    let regex1 = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let regex2 = PathUtils::create_regex("foo/*/bar");
    let regex3 = PathUtils::create_regex("foo/**/bar");

    assert_eq!(regex1.is_match("foo/4711/bar/5490"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=true&bar=false"), false);
    assert_eq!(regex1.is_match("foo/4711/bar/test%20spacing"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5281?foo=test%20spacing&bar=false"), true);

    assert_eq!(regex2.is_match("foo/4711/bar"), true);
    assert_eq!(regex2.is_match("foo/4711/barr"), false);
    assert_eq!(regex2.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex2.is_match("foo/4711/4712/bar"), false);
    assert_eq!(regex2.is_match("foo/4711/4712/bar?foo=true&bar=false"), false);

    assert_eq!(regex3.is_match("foo/4711/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar?foo=true&bar=false"), true);

    //ensure that this works with commas too
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=1,2,3&bar=false"), false);
}

#[test]
fn can_match_var_routes () {
    let route_store = &mut Router::new();

    fn handler (_request: &Request, response: &mut Response) -> () {
        let _ = response.origin.write("hello from foo".as_bytes());
    };

    route_store.add_route(method::Get, "/foo/:userid".to_string(), handler);
    route_store.add_route(method::Get, "/bar".to_string(), handler);

    let route_result = route_store.match_route(&method::Get, "/foo/4711").unwrap();
    let route = route_result.route;

    assert_eq!(route_result.params.get(&"userid".to_string()), &"4711".to_string());

    //assert the route has identified the variable
    assert_eq!(route.variables.len(), 1);
    assert_eq!(route.variables.get(&"userid".to_string()), &0);

    let route_result = route_store.match_route(&method::Get, "/bar/4711");
    assert!(route_result.is_none());

    let route_result = route_store.match_route(&method::Get, "/foo");
    assert!(route_result.is_none());

    //ensure that this will work with commas too
    let route_result = route_store.match_route(&method::Get, "/foo/123,456");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.params.get(&"userid".to_string()), &"123,456".to_string());

    //ensure that this will work with spacing too
    let route_result = route_store.match_route(&method::Get, "/foo/John%20Doe");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.params.get(&"userid".to_string()), &"John%20Doe".to_string());
}
<|endoftext|>


<commit_before>use std::iter::Iterator;
use std::collections::HashSet;
use std::hash::Hash;

struct Visited<T> {
    hash_set: HashSet<T>
}

impl<T> Visited<T> where T: Hash + Clone + Eq {
    fn new() -> Visited<T> {
        Visited {
            hash_set: HashSet::new()
        }
    }

    fn insert(&mut self, value: &T) -> bool {
        self.hash_set.insert(value.clone())
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        self == state
    }
}

pub trait SearchSpace {
    type State: Hash + Clone + Eq;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: &Self::State, goal: &G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(start) {
            return Some(vec![]);
        }

        let mut visited = Visited::new();
        let mut stack = vec![(self.expand(start), None)];

        loop {
            let next = match stack.last_mut() {
                None => return None,
                Some(&mut (ref mut iter, _)) => iter.next()
            };
            if let Some((action, state)) = next {
                if !visited.insert(&state) {
                    continue;
                }
                if goal.is_goal(&state) {
                    return Some(
                        stack.into_iter()
                             .filter_map(|(_, a)| a)
                             .chain(Some(action).into_iter())
                             .collect()
                    )
                }
                stack.push((self.expand(&state), Some(action)));
            } else {
                stack.pop();
            }
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use rand::chacha::ChaChaRng;
    use rand::Rng;
    use super::SearchSpace;
    use std::iter::Enumerate;

    struct RandomGraph {
        nodes: Vec<Vec<usize>>
    }

    impl RandomGraph {
        fn new(n_nodes: usize, max_edges: usize) -> RandomGraph {
            let mut rng_e = ChaChaRng::new_unseeded();
            let mut rng_n = ChaChaRng::new_unseeded();

            RandomGraph {
                nodes: rng_e.gen_iter::<usize>().take(n_nodes)
                    .map(|e| rng_n.gen_iter::<usize>().take(e % max_edges)
                        .map(|n| n % n_nodes).collect()).collect()
            }
        }

        #[allow(dead_code)]
        fn to_graphviz(&self) -> String {
            let mut buf = "digraph G {".to_owned();
            for (node_from, nodes) in self.nodes.iter().enumerate() {
                for (action, node_to) in nodes.iter().enumerate() {
                    buf.push_str(format!("\n\t{} -> {} [ label=\"{}\" ];",
                                         node_from, node_to, action).as_ref());
                }
            }
            buf.push_str("\n}");
            buf
        }
    }

    impl SearchSpace for RandomGraph {
        type State = usize;
        type Action = usize;
        type Iterator = Enumerate<IntoIter<usize>>;

        fn expand(&self, state: &usize) -> Self::Iterator {
            if *state < self.nodes.len() {
                self.nodes[*state].clone()
            } else {
                vec![]
            }.into_iter().enumerate()
        }
    }

    #[test]
    pub fn test_dfs_simple() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                match *state {
                    0 => vec![(Dir::Left, 1), (Dir::Right, 2)],
                    1 => vec![(Dir::Left, 3), (Dir::Right, 4)],
                    2 => vec![(Dir::Left, 2)],
                    _ => vec![]
                }.into_iter()
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(&0, &0).unwrap(), vec![]);
        assert_eq!(ts.dfs(&0, &1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(&0, &2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(&0, &3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(&0, &4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(&2, &2).unwrap(), vec![]);
        assert!(ts.dfs(&2, &0).is_none());
        assert!(ts.dfs(&5, &0).is_none());
    }

    #[test]
    pub fn test_dfs_random() {
        const N_NODES: usize = 24;
        const MAX_EDGES: usize = 6;

        let g = RandomGraph::new(N_NODES, MAX_EDGES);

        assert!(g.dfs(&N_NODES, &0).is_none());
        assert!(g.dfs(&0, &N_NODES).is_none());

        for start in (0..N_NODES) {
            for goal in (0..N_NODES) {
                if let Some(path) = g.dfs(&start, &goal) {
                    let mut state = start;
                    for action in path {
                        state = g.expand(&state).skip(action).next().unwrap().1;
                    }
                    assert_eq!(state, goal);
                }
            }
        }
    }
}
<commit_msg>Implement state observer, verify correctness of dfs() returning None<commit_after>use std::iter::Iterator;
use std::collections::HashSet;
use std::hash::Hash;

struct Visited<T> {
    hash_set: HashSet<T>
}

impl<T> Visited<T> where T: Hash + Clone + Eq {
    fn new() -> Visited<T> {
        Visited {
            hash_set: HashSet::new()
        }
    }

    fn insert(&mut self, value: &T) -> bool {
        self.hash_set.insert(value.clone())
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        self == state
    }
}

pub trait SearchSpace {
    type State: Hash + Clone + Eq;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: &Self::State, goal: &G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(start) {
            return Some(vec![]);
        }

        let mut visited = Visited::new();
        let mut stack = vec![(self.expand(start), None)];

        loop {
            let next = match stack.last_mut() {
                None => return None,
                Some(&mut (ref mut iter, _)) => iter.next()
            };
            if let Some((action, state)) = next {
                if !visited.insert(&state) {
                    continue;
                }
                if goal.is_goal(&state) {
                    return Some(
                        stack.into_iter()
                             .filter_map(|(_, a)| a)
                             .chain(Some(action).into_iter())
                             .collect()
                    )
                }
                stack.push((self.expand(&state), Some(action)));
            } else {
                stack.pop();
            }
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::{SearchSpace, SearchGoal};
    use std::vec::IntoIter;
    use rand::chacha::ChaChaRng;
    use rand::Rng;
    use std::iter::Enumerate;
    use std::cell::RefCell;
    use std::collections::HashSet;

    struct RandomGraph {
        nodes: Vec<Vec<usize>>
    }

    impl RandomGraph {
        fn new(n_nodes: usize, max_edges: usize) -> RandomGraph {
            let mut rng_e = ChaChaRng::new_unseeded();
            let mut rng_n = ChaChaRng::new_unseeded();

            RandomGraph {
                nodes: rng_e.gen_iter::<usize>().take(n_nodes)
                    .map(|e| rng_n.gen_iter::<usize>().take(e % max_edges)
                        .map(|n| n % n_nodes).collect()).collect()
            }
        }

        #[allow(dead_code)]
        fn to_graphviz(&self) -> String {
            let mut buf = "digraph G {".to_owned();
            for (node_from, nodes) in self.nodes.iter().enumerate() {
                for (action, node_to) in nodes.iter().enumerate() {
                    buf.push_str(format!("\n\t{} -> {} [ label=\"{}\" ];",
                                         node_from, node_to, action).as_ref());
                }
            }
            buf.push_str("\n}");
            buf
        }
    }

    impl SearchSpace for RandomGraph {
        type State = usize;
        type Action = usize;
        type Iterator = Enumerate<IntoIter<usize>>;

        fn expand(&self, state: &usize) -> Self::Iterator {
            if *state < self.nodes.len() {
                self.nodes[*state].clone()
            } else {
                vec![]
            }.into_iter().enumerate()
        }
    }

    struct Observer<T> {
        goal: T,
        visited: RefCell<Vec<T>>,
    }

    impl<T> Observer<T> where T: Clone {
        pub fn new(goal: T) -> Observer<T> {
            Observer {
                goal: goal,
                visited: RefCell::new(vec![])
            }
        }

        pub fn visited(&self) -> Vec<T> {
            self.visited.borrow().clone()
        }
    }

    impl<T> SearchGoal<T> for Observer<T> where T: PartialEq + Clone {
        fn is_goal(&self, state: &T) -> bool {
            self.visited.borrow_mut().push(state.clone());
            self.goal == *state
        }
    }

    #[test]
    pub fn test_dfs_simple() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                match *state {
                    0 => vec![(Dir::Left, 1), (Dir::Right, 2)],
                    1 => vec![(Dir::Left, 3), (Dir::Right, 4)],
                    2 => vec![(Dir::Left, 2)],
                    _ => vec![]
                }.into_iter()
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(&0, &0).unwrap(), vec![]);
        assert_eq!(ts.dfs(&0, &1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(&0, &2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(&0, &3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(&0, &4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(&2, &2).unwrap(), vec![]);
        assert!(ts.dfs(&2, &0).is_none());
        assert!(ts.dfs(&5, &0).is_none());
    }

    #[test]
    pub fn test_dfs_random() {
        const N_NODES: usize = 24;
        const MAX_EDGES: usize = 6;

        let g = RandomGraph::new(N_NODES, MAX_EDGES);

        assert!(g.dfs(&N_NODES, &0).is_none());
        assert!(g.dfs(&0, &N_NODES).is_none());

        for start in (0..N_NODES) {
            for goal in (0..N_NODES) {
                let observer = Observer::new(goal);
                if let Some(path) = g.dfs(&start, &observer) {
                    let mut state = start;
                    for action in path {
                        state = g.expand(&state).skip(action).next().unwrap().1;
                    }
                    assert_eq!(state, goal);
                } else {
                    let visited: HashSet<_> = observer.visited().iter().cloned().collect();
                    for state in observer.visited() {
                        assert!(!observer.is_goal(&state));
                        for (_, next_state) in g.expand(&state) {
                            assert!(!observer.is_goal(&next_state));
                            assert!(visited.contains(&next_state));
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>