<commit_before>//! A lightweight, self-contained s-expression parser and data format.
//! Use `parse` to get an s-expression from its string representation, and the
//! `Display` trait to serialize it, potentially by doing `sexp.to_string()`.

// Needed for `is_char_boundary` and `char_range_at`. I'd love to remove this
// so that this library works on stable, but it involved a LOT of copy-paste
// from the standard library.
#![feature(str_char)]

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

use std::fmt;
use std::str::{self, FromStr};

/// A single data element in an s-expression. Floats are excluded to ensure
/// atoms may be used as keys in ordered and hashed data structures.
///
/// All strings must be valid utf-8.
#[derive(Eq, Ord, PartialEq, Clone, PartialOrd, Debug, Hash)]
#[allow(missing_docs)]
pub enum Atom {
  S(String),
  I(i64),
}

/// An s-expression is either an atom or a list of s-expressions. This is
/// similar to the data format used by lisp.
#[derive(Eq, Ord, PartialEq, Clone, PartialOrd, Debug, Hash)]
#[allow(missing_docs)]
pub enum Sexp {
  Atom(Atom),
  List(Vec<Sexp>),
}

#[test]
fn sexp_size() {
  // I just want to see when this changes, in the diff.
  use std::mem;
  assert_eq!(mem::size_of::<Sexp>(), mem::size_of::<isize>()*5);
}

/// Helps clean up type signatures, but shouldn't be exposed to the outside
/// world.
type ERes<T> = Result<T, ()>;

/// A helpful utility to trace the execution of a parser while testing.  It will
/// be compiled out in release builds.
#[allow(unused_variables)]
fn dbg(msg: &str, pos: &usize) {
  //println!("{} @ {}", msg, pos)
}

fn atom_of_string(s: String) -> Atom {
  match FromStr::from_str(&s) {
    Ok(i)  => return Atom::I(i),
    Err(_) => {},
  };

  Atom::S(s)
}

// returns the char it found, and the new size if you wish to consume that char
fn peek(s: &str, pos: &usize) -> ERes<(char, usize)> {
  dbg("peek", pos);
  if *pos == s.len() { return Err(()) }
  if s.is_char_boundary(*pos) {
    let str::CharRange { ch, next } = s.char_range_at(*pos);
    Ok((ch, next))
  } else {
    Err(())
  }
}

fn expect(s: &str, pos: &mut usize, c: char) -> ERes<()> {
  dbg("expect", pos);
  let (ch, next) = try!(peek(s, pos));
  *pos = next;
  if ch == c { Ok(()) } else { Err(()) }
}

fn consume_until_newline(s: &str, pos: &mut usize) -> ERes<()> {
  loop {
    if *pos == s.len() { return Ok(()) }
    let (ch, next) = try!(peek(s, pos));
    *pos = next;
    if ch == '\n' { return Ok(()) }
  }
}

// zero or more spaces
fn zspace(s: &str, pos: &mut usize) -> ERes<()> {
  dbg("zspace", pos);
  loop {
    if *pos == s.len() { return Ok(()) }
    let (ch, next) = try!(peek(s, pos));

    if ch == ';'               { try!(consume_until_newline(s, pos)) }
    else if ch.is_whitespace() { *pos = next; }
    else                       { return Ok(()) }
  }
}

fn parse_quoted_atom(s: &str, pos: &mut usize) -> ERes<Atom> {
  dbg("parse_quoted_atom", pos);
  let mut cs: String = String::new();

  try!(expect(s, pos, '"'));

  loop {
    let (ch, next) = try!(peek(s, pos));
    if ch == '"' {
      *pos = next;
      break;
    } else if ch == '\\' {
      let (postslash, nextnext) = try!(peek(s, &next));
      if postslash == '"' || postslash == '\\' {
        cs.push(postslash);
      } else {
        cs.push(ch);
        cs.push(postslash);
      }
      *pos = nextnext;
    } else {
      cs.push(ch);
      *pos = next;
    }
  }

  Ok(atom_of_string(cs))
}

fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes<Atom> {
  dbg("parse_unquoted_atom", pos);
  let mut cs: String = String::new();

  loop {
    if *pos == s.len() { break }
    let (c, next) = try!(peek(s, pos));

    if c == ';' { try!(consume_until_newline(s, pos)); break }
    if c.is_whitespace() || c == ')' { break }
    cs.push(c);
    *pos = next;
  }

  Ok(atom_of_string(cs))
}

fn parse_atom(s: &str, pos: &mut usize) -> ERes<Atom> {
  dbg("parse_atom", pos);
  let (ch, _) = try!(peek(s, pos));

  if ch == '"' { parse_quoted_atom  (s, pos) }
  else         { parse_unquoted_atom(s, pos) }
}

fn parse_list(s: &str, pos: &mut usize) -> ERes<Vec<Sexp>> {
  dbg("parse_list", pos);
  try!(zspace(s, pos));
  try!(expect(s, pos, '('));

  let mut sexps: Vec<Sexp> = Vec::new();

  loop {
    try!(zspace(s, pos));
    let (c, next) = try!(peek(s, pos));
    if c == ')' {
      *pos = next;
      break;
    }
    sexps.push(try!(parse_sexp(s, pos)));
  }

  try!(zspace(s, pos));

  Ok(sexps)
}

fn parse_sexp(s: &str, pos: &mut usize) -> ERes<Sexp> {
  dbg("parse_sexp", pos);
  try!(zspace(s, pos));
  let (c, _) = try!(peek(s, pos));
  let r =
    if c == '(' { Ok(Sexp::List(try!(parse_list(s, pos)))) }
    else        { Ok(Sexp::Atom(try!(parse_atom(s, pos)))) };
  try!(zspace(s, pos));
  r
}

/// Constructs an atomic s-expression from a string.
pub fn atom_s(s: &str) -> Sexp {
  Sexp::Atom(Atom::S(s.to_owned()))
}

/// Constructs an atomic s-expression from an int.
pub fn atom_i(i: i64) -> Sexp {
  Sexp::Atom(Atom::I(i))
}

/// Constructs a list s-expression given a slice of s-expressions.
pub fn list(xs: &[Sexp]) -> Sexp {
  Sexp::List(xs.to_owned())
}

/// Reads an s-expression out of a `&str`. Returns `Err(())` on parse error.
pub fn parse(s: &str) -> Result<Sexp, ()> {
  let mut pos = 0;
  let ret = try!(parse_sexp(s, &mut pos));
  if pos == s.len() { Ok(ret) } else { Err(()) }
}

// TODO: Pretty print in lisp convention, instead of all on the same line,
// packed as tightly as possible. It's kinda ugly.

impl fmt::Display for Atom {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    match *self {
      Atom::S(ref s) => write!(f, "{}", s),
      Atom::I(i)     => write!(f, "{}", i),
    }
  }
}

impl fmt::Display for Sexp {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    match *self {
      Sexp::Atom(ref a) => write!(f, "{}", a),
      Sexp::List(ref xs) => {
        try!(write!(f, "("));
        for (i, x) in xs.iter().enumerate() {
          let s = if i == 0 { "" } else { " " };
          try!(write!(f, "{}{}", s, x));
        }
        write!(f, ")")
      },
    }
  }
}

#[test]
fn test_hello_world() {
  assert_eq!(
    parse("(hello -42\n\t  \"world\") ; comment"),
    Ok(list(&[ atom_s("hello"), atom_i(-42), atom_s("world") ])));
}

#[test]
fn test_escaping() {
  assert_eq!(
    parse("(\"\\\"\\q\")"),
    Ok(list(&[ atom_s("\"\\q") ])));
}

#[test]
fn test_pp() {
  let s = "(hello world (what is (up) (4 you)))";
  let sexp = parse(s).unwrap();
  assert_eq!(s, sexp.to_string());
}
<commit_msg>more correct pretty printing<commit_after>//! A lightweight, self-contained s-expression parser and data format.
//! Use `parse` to get an s-expression from its string representation, and the
//! `Display` trait to serialize it, potentially by doing `sexp.to_string()`.

// Needed for `is_char_boundary` and `char_range_at`. I'd love to remove this
// so that this library works on stable, but it involved a LOT of copy-paste
// from the standard library.
#![feature(str_char)]

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

use std::borrow::Cow;
use std::fmt;
use std::str::{self, FromStr};

/// A single data element in an s-expression. Floats are excluded to ensure
/// atoms may be used as keys in ordered and hashed data structures.
///
/// All strings must be valid utf-8.
#[derive(Eq, Ord, PartialEq, Clone, PartialOrd, Debug, Hash)]
#[allow(missing_docs)]
pub enum Atom {
  S(String),
  I(i64),
}

/// An s-expression is either an atom or a list of s-expressions. This is
/// similar to the data format used by lisp.
#[derive(Eq, Ord, PartialEq, Clone, PartialOrd, Debug, Hash)]
#[allow(missing_docs)]
pub enum Sexp {
  Atom(Atom),
  List(Vec<Sexp>),
}

#[test]
fn sexp_size() {
  // I just want to see when this changes, in the diff.
  use std::mem;
  assert_eq!(mem::size_of::<Sexp>(), mem::size_of::<isize>()*5);
}

/// Helps clean up type signatures, but shouldn't be exposed to the outside
/// world.
type ERes<T> = Result<T, ()>;

/// A helpful utility to trace the execution of a parser while testing.  It will
/// be compiled out in release builds.
#[allow(unused_variables)]
fn dbg(msg: &str, pos: &usize) {
  //println!("{} @ {}", msg, pos)
}

fn atom_of_string(s: String) -> Atom {
  match FromStr::from_str(&s) {
    Ok(i)  => return Atom::I(i),
    Err(_) => {},
  };

  Atom::S(s)
}

// returns the char it found, and the new size if you wish to consume that char
fn peek(s: &str, pos: &usize) -> ERes<(char, usize)> {
  dbg("peek", pos);
  if *pos == s.len() { return Err(()) }
  if s.is_char_boundary(*pos) {
    let str::CharRange { ch, next } = s.char_range_at(*pos);
    Ok((ch, next))
  } else {
    Err(())
  }
}

fn expect(s: &str, pos: &mut usize, c: char) -> ERes<()> {
  dbg("expect", pos);
  let (ch, next) = try!(peek(s, pos));
  *pos = next;
  if ch == c { Ok(()) } else { Err(()) }
}

fn consume_until_newline(s: &str, pos: &mut usize) -> ERes<()> {
  loop {
    if *pos == s.len() { return Ok(()) }
    let (ch, next) = try!(peek(s, pos));
    *pos = next;
    if ch == '\n' { return Ok(()) }
  }
}

// zero or more spaces
fn zspace(s: &str, pos: &mut usize) -> ERes<()> {
  dbg("zspace", pos);
  loop {
    if *pos == s.len() { return Ok(()) }
    let (ch, next) = try!(peek(s, pos));

    if ch == ';'               { try!(consume_until_newline(s, pos)) }
    else if ch.is_whitespace() { *pos = next; }
    else                       { return Ok(()) }
  }
}

fn parse_quoted_atom(s: &str, pos: &mut usize) -> ERes<Atom> {
  dbg("parse_quoted_atom", pos);
  let mut cs: String = String::new();

  try!(expect(s, pos, '"'));

  loop {
    let (ch, next) = try!(peek(s, pos));
    if ch == '"' {
      *pos = next;
      break;
    } else if ch == '\\' {
      let (postslash, nextnext) = try!(peek(s, &next));
      if postslash == '"' || postslash == '\\' {
        cs.push(postslash);
      } else {
        cs.push(ch);
        cs.push(postslash);
      }
      *pos = nextnext;
    } else {
      cs.push(ch);
      *pos = next;
    }
  }

  Ok(atom_of_string(cs))
}

fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes<Atom> {
  dbg("parse_unquoted_atom", pos);
  let mut cs: String = String::new();

  loop {
    if *pos == s.len() { break }
    let (c, next) = try!(peek(s, pos));

    if c == ';' { try!(consume_until_newline(s, pos)); break }
    if c.is_whitespace() || c == ')' { break }
    cs.push(c);
    *pos = next;
  }

  Ok(atom_of_string(cs))
}

fn parse_atom(s: &str, pos: &mut usize) -> ERes<Atom> {
  dbg("parse_atom", pos);
  let (ch, _) = try!(peek(s, pos));

  if ch == '"' { parse_quoted_atom  (s, pos) }
  else         { parse_unquoted_atom(s, pos) }
}

fn parse_list(s: &str, pos: &mut usize) -> ERes<Vec<Sexp>> {
  dbg("parse_list", pos);
  try!(zspace(s, pos));
  try!(expect(s, pos, '('));

  let mut sexps: Vec<Sexp> = Vec::new();

  loop {
    try!(zspace(s, pos));
    let (c, next) = try!(peek(s, pos));
    if c == ')' {
      *pos = next;
      break;
    }
    sexps.push(try!(parse_sexp(s, pos)));
  }

  try!(zspace(s, pos));

  Ok(sexps)
}

fn parse_sexp(s: &str, pos: &mut usize) -> ERes<Sexp> {
  dbg("parse_sexp", pos);
  try!(zspace(s, pos));
  let (c, _) = try!(peek(s, pos));
  let r =
    if c == '(' { Ok(Sexp::List(try!(parse_list(s, pos)))) }
    else        { Ok(Sexp::Atom(try!(parse_atom(s, pos)))) };
  try!(zspace(s, pos));
  r
}

/// Constructs an atomic s-expression from a string.
pub fn atom_s(s: &str) -> Sexp {
  Sexp::Atom(Atom::S(s.to_owned()))
}

/// Constructs an atomic s-expression from an int.
pub fn atom_i(i: i64) -> Sexp {
  Sexp::Atom(Atom::I(i))
}

/// Constructs a list s-expression given a slice of s-expressions.
pub fn list(xs: &[Sexp]) -> Sexp {
  Sexp::List(xs.to_owned())
}

/// Reads an s-expression out of a `&str`. Returns `Err(())` on parse error.
pub fn parse(s: &str) -> Result<Sexp, ()> {
  let mut pos = 0;
  let ret = try!(parse_sexp(s, &mut pos));
  if pos == s.len() { Ok(ret) } else { Err(()) }
}

// TODO: Pretty print in lisp convention, instead of all on the same line,
// packed as tightly as possible. It's kinda ugly.

fn quote(s: &str) -> Cow<str> {
  if !s.contains("\"") {
    Cow::Borrowed(s)
  } else {
    let mut r: String = "\"".to_string();
    r.push_str(&s.replace("\\", "\\\\").replace("\"", "\\\""));
    r.push_str("\"");
    Cow::Owned(r)
  }
}

impl fmt::Display for Atom {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    match *self {
      Atom::S(ref s) => write!(f, "{}", quote(s)),
      Atom::I(i)     => write!(f, "{}", i),
    }
  }
}

impl fmt::Display for Sexp {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    match *self {
      Sexp::Atom(ref a) => write!(f, "{}", a),
      Sexp::List(ref xs) => {
        try!(write!(f, "("));
        for (i, x) in xs.iter().enumerate() {
          let s = if i == 0 { "" } else { " " };
          try!(write!(f, "{}{}", s, x));
        }
        write!(f, ")")
      },
    }
  }
}

#[test]
fn test_hello_world() {
  assert_eq!(
    parse("(hello -42\n\t  \"world\") ; comment"),
    Ok(list(&[ atom_s("hello"), atom_i(-42), atom_s("world") ])));
}

#[test]
fn test_escaping() {
  assert_eq!(
    parse("(\"\\\"\\q\")"),
    Ok(list(&[ atom_s("\"\\q") ])));
}

#[test]
fn test_pp() {
  let s = "(hello world (what is (up) (4 you \"123\\\\ \\\"\")))";
  let sexp = parse(s).unwrap();
  assert_eq!(s, sexp.to_string());
}
<|endoftext|>


<commit_before>#![feature(associated_consts)]
extern crate lmdb_rs as lmdb;

use lmdb::{FromMdbValue, ToMdbValue, MdbValue, DbFlags, DbHandle, Database, Environment, Transaction, ReadonlyTransaction, Cursor, MDB_val};
use lmdb::core::MdbResult;
use std::marker::PhantomData;

#[macro_export]
macro_rules! table_def {
    (
        $structname:ident, $tableclass:ty, $str:expr
    ) => {
        #[derive(Clone)]
        struct $structname;
        impl TableDef for $structname {
            type C = $tableclass;
            fn table_name() -> &'static str { $str }
        }
    };
}

/// A TableClass describes the types of key/value and the behaviour (sort function) of a Table.
pub trait TableClass: Sized {
    type Key: ToMdbValue + FromMdbValue;
    type Value: ToMdbValue + FromMdbValue;

    /// Flags used for creation/open
    fn dbflags() -> DbFlags;

    /// Is called before accessing a database. Used to setup custom sort functions etc.
    fn prepare_database(db: &Database);
}

/// A TableDef is a concrete instance of a TableClass with a given table name.
pub trait TableDef: Sized {
    type C: TableClass;
    fn table_name() -> &'static str;

    fn create_table(env: &Environment) -> MdbResult<TableHandle<Self>> {
        env.create_db(Self::table_name(), Self::C::dbflags()).map(|dbh| {
            TableHandle {
                dbh: dbh,
                tabledef: PhantomData,
            }
        })
    }

    fn open_table(env: &Environment) -> MdbResult<TableHandle<Self>> {
        env.get_db(Self::table_name(), Self::C::dbflags()).map(|dbh| {
            TableHandle {
                dbh: dbh,
                tabledef: PhantomData,
            }
        })
    }

}

/// Wraps a lmdb::DbHandle, keeping the TableDef in the type signature.
pub struct TableHandle<D: TableDef> {
    dbh: DbHandle,
    tabledef: PhantomData<D>,
}

impl<D: TableDef> TableHandle<D> {
    /// Bind a TableHandle towards a transaction. Returns a Table which you can use to
    /// modify the database.
    #[inline(always)]
    pub fn bind_transaction<'txn>(&self, txn: &'txn Transaction) -> Table<'txn, D> {
        let db = txn.bind(&self.dbh);
        D::C::prepare_database(&db);
        Table {
            db: db,
            tabledef: PhantomData,
        }
    }

    /// Bind a TableHandle towards a readonly transaction.
    /// XXX: This should return a ReadonlyTable?
    #[inline(always)]
    pub fn bind_reader<'txn>(&self, txn: &'txn ReadonlyTransaction) -> Table<'txn, D> {
        let db = txn.bind(&self.dbh);
        D::C::prepare_database(&db);
        Table {
            db: db,
            tabledef: PhantomData,
        }
    }

}

/// Wraps a lmdb::Database, keeping the TableDef in the type signature.
pub struct Table<'db, D: TableDef> {
    db: Database<'db>,
    tabledef: PhantomData<D>,
}

impl<'db, D: TableDef> Table<'db, D> {
    #[inline(always)]
    pub fn set(&self, key: &<<D as TableDef>::C as TableClass>::Key, value: &<<D as TableDef>::C as TableClass>::Value) -> MdbResult<()> {
        self.db.set(key, value)
    }

    #[inline(always)]
    pub fn new_cursor<'table>(&'table self) -> MdbResult<TypedCursor<'table, D>> {
        self.db.new_cursor().map(|c| TypedCursor {cursor: c, tabledef: PhantomData})
    }
}

/// Is a typed version of lmdb::Cursor.
pub struct TypedCursor<'table, D: TableDef> {
    cursor: Cursor<'table>,
    tabledef: PhantomData<D>,
}

impl<'table, D: TableDef> TypedCursor<'table, D> {
    #[inline(always)]
    pub fn to_item(&mut self, key: &<<D as TableDef>::C as TableClass>::Key, value: &<<D as TableDef>::C as TableClass>::Value) -> MdbResult<()> {
        self.cursor.to_item(key, value)
    }

    #[inline(always)]
    pub fn to_next_item(&mut self) -> MdbResult<()> {
        self.cursor.to_next_item()
    }

    #[inline(always)]
    pub fn get<'a>(&'a mut self) -> MdbResult<(<<D as TableDef>::C as TableClass>::Key, <<D as TableDef>::C as TableClass>::Value)> /*where D::C::Key: 'a, D::C::Value: 'a*/ {
        self.cursor.get()
    }
}

extern "C" fn sort<T:FromMdbValue+Ord>(lhs_val: *const MDB_val, rhs_val: *const MDB_val) -> lmdb::c_int {
    let lhs = T::from_mdb_value(&unsafe{MdbValue::from_raw(lhs_val)});
    let rhs = T::from_mdb_value(&unsafe{MdbValue::from_raw(rhs_val)});

    let order: i8 = unsafe { std::mem::transmute(lhs.cmp(&rhs)) };
    order as lmdb::c_int
}

extern "C" fn sort_reverse<T:FromMdbValue+Ord>(lhs_val: *const MDB_val, rhs_val: *const MDB_val) -> lmdb::c_int {
    let lhs = T::from_mdb_value(&unsafe{MdbValue::from_raw(lhs_val)});
    let rhs = T::from_mdb_value(&unsafe{MdbValue::from_raw(rhs_val)});
    let order: i8 = unsafe { std::mem::transmute(lhs.cmp(&rhs).reverse()) };
    order as lmdb::c_int
}

#[test]
fn test_simple_table() {
    use std::path::Path;
    use lmdb::MdbError;

    let env = lmdb::EnvBuilder::new().max_dbs(2).autocreate_dir(true).open(&Path::new("./test/db1"), 0o777).unwrap();

    //def_tablename!(MyfirstTable_TableName as "myfirst_table");

    type Id64 = u64;
    struct SortedSet_Id64_Id64Rev;
    impl TableClass for SortedSet_Id64_Id64Rev {
        type Key = Id64;
        type Value = Id64;

        fn dbflags() -> lmdb::DbFlags {
            lmdb::core::DbIntKey | lmdb::core::DbAllowDups | lmdb::core::DbAllowIntDups | lmdb::core::DbDupFixed
        }
        // XXX: pass MdbResult back
        fn prepare_database(db: &lmdb::Database) {
            db.set_dupsort(sort_reverse::<Id64>).unwrap()
        }
    }

    table_def!(MyFirstTable, SortedSet_Id64_Id64Rev, "my_first_table");

/*
    struct MyFirstTable;
    impl TableDef for MyFirstTable {
        // The TableClass
        type C = SortedSet_Id64_Id64Rev;

        fn table_name() -> &'static str { "my_first_table" }
    }
*/

    // A Unique(Id64, Id64 DESC) table
    let table_handle = MyFirstTable::create_table(&env).unwrap();

    // prepare database
    {
        let txn = env.new_transaction().unwrap();
        {
            let table = table_handle.bind_transaction(&txn);
            let key = 1u64;
            table.set(&key, &200u64).unwrap();
            table.set(&key, &100u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &1u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &3u64).unwrap();
            table.set(&key, &100u64).unwrap();
        }
        txn.commit().unwrap();
    }
    // read
    {
        let rdr = env.get_reader().unwrap();
        let table = table_handle.bind_reader(&rdr);

        let key = 1u64;

/*
        let mut iter = db.item_iter(&key).unwrap();

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 200u64)), kv);

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 100u64)), kv);

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 3u64)), kv);

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 2u64)), kv);

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 1u64)), kv);

        let kv: Option<(u64,u64)> = iter.next().map(|c| c.get());
        assert_eq!(None, kv);
*/

        let mut cursor = table.new_cursor().unwrap();
        {
            cursor.to_item(&1u64, &3u64).unwrap();
            assert_eq!((1u64, 3u64), cursor.get().unwrap());

            cursor.to_next_item().unwrap();
            assert_eq!((1u64, 2u64), cursor.get().unwrap());

            cursor.to_next_item().unwrap();
            assert_eq!((1u64, 1u64), cursor.get().unwrap());

            let err = cursor.to_next_item();
            match err {
                Err(MdbError::NotFound) => assert!(true),
                _ => assert!(false),
            }
        }
    }
}

<commit_msg>Add more functions<commit_after>#![feature(associated_consts)]
extern crate lmdb_rs as lmdb;

use lmdb::{FromMdbValue, ToMdbValue, MdbValue, DbFlags, DbHandle, Database, Environment, Transaction, ReadonlyTransaction, Cursor, MDB_val};
use lmdb::core::MdbResult;
use std::marker::PhantomData;

#[macro_export]
macro_rules! table_def {
    (
        $structname:ident, $tableclass:ty, $str:expr
    ) => {
        #[derive(Clone)]
        struct $structname;
        impl TableDef for $structname {
            type C = $tableclass;
            fn table_name() -> &'static str { $str }
        }
    };
}

/// A TableClass describes the types of key/value and the behaviour (sort function) of a Table.
pub trait TableClass: Sized {
    type Key: ToMdbValue + FromMdbValue;
    type Value: ToMdbValue + FromMdbValue;

    /// Flags used for creation/open
    fn dbflags() -> DbFlags;

    /// Is called before accessing a database. Used to setup custom sort functions etc.
    fn prepare_database(db: &Database);
}

/// A TableDef is a concrete instance of a TableClass with a given table name.
pub trait TableDef: Sized {
    type C: TableClass;
    fn table_name() -> &'static str;

    fn create_table(env: &Environment) -> MdbResult<TableHandle<Self>> {
        env.create_db(Self::table_name(), Self::C::dbflags()).map(|dbh| {
            TableHandle {
                dbh: dbh,
                tabledef: PhantomData,
            }
        })
    }

    fn open_table(env: &Environment) -> MdbResult<TableHandle<Self>> {
        env.get_db(Self::table_name(), Self::C::dbflags()).map(|dbh| {
            TableHandle {
                dbh: dbh,
                tabledef: PhantomData,
            }
        })
    }

}

/// Wraps a lmdb::DbHandle, keeping the TableDef in the type signature.
pub struct TableHandle<D: TableDef> {
    dbh: DbHandle,
    tabledef: PhantomData<D>,
}

impl<D: TableDef> TableHandle<D> {
    /// Bind a TableHandle towards a transaction. Returns a Table which you can use to
    /// modify the database.
    #[inline(always)]
    pub fn bind_transaction<'txn>(&self, txn: &'txn Transaction) -> Table<'txn, D> {
        let db = txn.bind(&self.dbh);
        D::C::prepare_database(&db);
        Table {
            db: db,
            tabledef: PhantomData,
        }
    }

    /// Bind a TableHandle towards a readonly transaction.
    /// XXX: This should return a ReadonlyTable?
    #[inline(always)]
    pub fn bind_reader<'txn>(&self, txn: &'txn ReadonlyTransaction) -> Table<'txn, D> {
        let db = txn.bind(&self.dbh);
        D::C::prepare_database(&db);
        Table {
            db: db,
            tabledef: PhantomData,
        }
    }

}

/// Wraps a lmdb::Database, keeping the TableDef in the type signature.
pub struct Table<'db, D: TableDef> {
    db: Database<'db>,
    tabledef: PhantomData<D>,
}

impl<'db, D: TableDef> Table<'db, D> {
    #[inline(always)]
    pub fn set(&self, key: &<<D as TableDef>::C as TableClass>::Key, value: &<<D as TableDef>::C as TableClass>::Value) -> MdbResult<()> {
        self.db.set(key, value)
    }

    #[inline(always)]
    pub fn insert(&self, key: &<<D as TableDef>::C as TableClass>::Key, value: &<<D as TableDef>::C as TableClass>::Value) -> MdbResult<()> {
        self.db.insert(key, value)
    }

    #[inline(always)]
    pub fn get(&self, key: &<<D as TableDef>::C as TableClass>::Key) -> MdbResult<<<D as TableDef>::C as TableClass>::Value> {
        self.db.get(key)
    }

    #[inline(always)]
    pub fn del(&self, key: &<<D as TableDef>::C as TableClass>::Key) -> MdbResult<()> {
        self.db.del(key)
    }

    #[inline(always)]
    pub fn del_item(&self, key: &<<D as TableDef>::C as TableClass>::Key, value: &<<D as TableDef>::C as TableClass>::Value) -> MdbResult<()> {
        self.db.del_item(key, value)
    }

    #[inline(always)]
    pub fn new_cursor<'table>(&'table self) -> MdbResult<TypedCursor<'table, D>> {
        self.db.new_cursor().map(|c| TypedCursor {cursor: c, tabledef: PhantomData})
    }
}

/// Is a typed version of lmdb::Cursor.
pub struct TypedCursor<'table, D: TableDef> {
    cursor: Cursor<'table>,
    tabledef: PhantomData<D>,
}

impl<'table, D: TableDef> TypedCursor<'table, D> {
    #[inline(always)]
    pub fn to_first(&mut self) -> MdbResult<()> {
        self.cursor.to_first()
    }

    #[inline(always)]
    pub fn to_last(&mut self) -> MdbResult<()> {
        self.cursor.to_last()
    }

    #[inline(always)]
    pub fn to_item(&mut self, key: &<<D as TableDef>::C as TableClass>::Key, value: &<<D as TableDef>::C as TableClass>::Value) -> MdbResult<()> {
        self.cursor.to_item(key, value)
    }

    #[inline(always)]
    pub fn to_first_item(&mut self) -> MdbResult<()> {
        self.cursor.to_first_item()
    }

    #[inline(always)]
    pub fn to_last_item(&mut self) -> MdbResult<()> {
        self.cursor.to_last_item()
    }

    #[inline(always)]
    pub fn to_next_item(&mut self) -> MdbResult<()> {
        self.cursor.to_next_item()
    }

    #[inline(always)]
    pub fn to_prev_item(&mut self) -> MdbResult<()> {
        self.cursor.to_prev_item()
    }

    #[inline(always)]
    pub fn item_count(&mut self) -> MdbResult<u64> {
        self.cursor.item_count()
    }

    #[inline(always)]
    pub fn get<'a>(&'a mut self) -> MdbResult<(<<D as TableDef>::C as TableClass>::Key, <<D as TableDef>::C as TableClass>::Value)> /*where D::C::Key: 'a, D::C::Value: 'a*/ {
        self.cursor.get()
    }

    #[inline(always)]
    pub fn get_value<'a>(&'a mut self) -> MdbResult<<<D as TableDef>::C as TableClass>::Value> {
        self.cursor.get_value()
    }

    #[inline(always)]
    pub fn get_key<'a>(&'a mut self) -> MdbResult<<<D as TableDef>::C as TableClass>::Key> {
        self.cursor.get_key()
    }

    #[inline(always)]
    pub fn replace(&mut self, value: &<<D as TableDef>::C as TableClass>::Value) -> MdbResult<()> {
        self.cursor.replace(value)
    }

}

extern "C" fn sort<T:FromMdbValue+Ord>(lhs_val: *const MDB_val, rhs_val: *const MDB_val) -> lmdb::c_int {
    let lhs = T::from_mdb_value(&unsafe{MdbValue::from_raw(lhs_val)});
    let rhs = T::from_mdb_value(&unsafe{MdbValue::from_raw(rhs_val)});

    let order: i8 = unsafe { std::mem::transmute(lhs.cmp(&rhs)) };
    order as lmdb::c_int
}

extern "C" fn sort_reverse<T:FromMdbValue+Ord>(lhs_val: *const MDB_val, rhs_val: *const MDB_val) -> lmdb::c_int {
    let lhs = T::from_mdb_value(&unsafe{MdbValue::from_raw(lhs_val)});
    let rhs = T::from_mdb_value(&unsafe{MdbValue::from_raw(rhs_val)});
    let order: i8 = unsafe { std::mem::transmute(lhs.cmp(&rhs).reverse()) };
    order as lmdb::c_int
}

#[test]
fn test_simple_table() {
    use std::path::Path;
    use lmdb::MdbError;

    let env = lmdb::EnvBuilder::new().max_dbs(2).autocreate_dir(true).open(&Path::new("./test/db1"), 0o777).unwrap();

    type Id64 = u64;
    struct SortedSet_Id64_Id64Rev;
    impl TableClass for SortedSet_Id64_Id64Rev {
        type Key = Id64;
        type Value = Id64;

        fn dbflags() -> lmdb::DbFlags {
            lmdb::core::DbIntKey | lmdb::core::DbAllowDups | lmdb::core::DbAllowIntDups | lmdb::core::DbDupFixed
        }
        // XXX: pass MdbResult back
        fn prepare_database(db: &lmdb::Database) {
            db.set_dupsort(sort_reverse::<Id64>).unwrap()
        }
    }

    table_def!(MyFirstTable, SortedSet_Id64_Id64Rev, "my_first_table");

    // A Unique(Id64, Id64 DESC) table
    let table_handle = MyFirstTable::create_table(&env).unwrap();

    // prepare database
    {
        let txn = env.new_transaction().unwrap();
        {
            let table = table_handle.bind_transaction(&txn);
            let key = 1u64;
            table.set(&key, &200u64).unwrap();
            table.set(&key, &100u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &1u64).unwrap();
            table.set(&key, &2u64).unwrap();
            table.set(&key, &3u64).unwrap();
            table.set(&key, &100u64).unwrap();
        }
        txn.commit().unwrap();
    }
    // read
    {
        let rdr = env.get_reader().unwrap();
        let table = table_handle.bind_reader(&rdr);

        let key = 1u64;

/*
        let mut iter = db.item_iter(&key).unwrap();

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 200u64)), kv);

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 100u64)), kv);

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 3u64)), kv);

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 2u64)), kv);

        let kv = iter.next().map(|c| c.get());
        assert_eq!(Some((1u64, 1u64)), kv);

        let kv: Option<(u64,u64)> = iter.next().map(|c| c.get());
        assert_eq!(None, kv);
*/

        let mut cursor = table.new_cursor().unwrap();
        {
            let err = cursor.to_item(&2u64, &3u64);
            match err {
                Err(MdbError::NotFound) => assert!(true),
                _ => assert!(false),
            }

            cursor.to_item(&1u64, &3u64).unwrap();
            assert_eq!((1u64, 3u64), cursor.get().unwrap());

            cursor.to_next_item().unwrap();
            assert_eq!((1u64, 2u64), cursor.get().unwrap());

            cursor.to_next_item().unwrap();
            assert_eq!((1u64, 1u64), cursor.get().unwrap());

            let err = cursor.to_next_item();
            match err {
                Err(MdbError::NotFound) => assert!(true),
                _ => assert!(false),
            }
        }
    }
}

<|endoftext|>


<commit_before>/// Implementation of Stochastic universal sampling
/// https://en.wikipedia.org/wiki/Stochastic_universal_sampling
/// Runtime: O(n)
/// Memory Usage: O(n) or O(1) (in place variant)

extern crate rand;

use self::rand::{thread_rng, Rng};

pub struct RandomChoice;

impl RandomChoice {
    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    ///
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    /// @param n Number of randomly chosen samples by weight.
    /// @return randomly selected samples by their weights
    pub fn random_choice<'a, T>(samples: &'a [T], weights: &[f64], n: usize) -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 { return Vec::new(); }

        let sum:f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = thread_rng().next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        for _ in 0 .. n {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i];
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        choices
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    /// The result gets saved directly in the samples argument.
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    pub fn random_choice_in_place<T: Clone>(samples: &mut [T], weights: &[f64]) {
        if weights.len() < 2 { return; }

        let sum:f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = thread_rng().next_f64() * spoke_gap;

        let mut j: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut current_spoke: f64 = spin;

        for i in 0 .. n {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j];
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_random_choice() {
        let test_vec: Vec<f64> = vec![3.1, 1.2, 1.3, 2.0];
        //let sum:f64 = test_vec.iter().fold(0.0, |acc, &i| acc + i);

        //assert_eq!(6.0, sum);

        let choices = super::RandomChoice::random_choice(&test_vec, &test_vec, 4 as usize);

        for choice in choices {
            print!("{}, ", choice);
        }
    }

    #[test]
    fn test_random_choice_in_place() {
        let mut samples = vec!["hi", "this", "is", "a", "test!"];
        let weights: Vec<f64> = vec![1.0, 1.0, 1.0, 1.0, 1.0];

        super::RandomChoice::random_choice_in_place(&mut samples, &weights);

        for sample in samples {
            print!("{}, ", sample);
        }
    }

    #[test]
    fn test_random_choice_in_place_2() {
        struct Foo {j: i32, };
        let test1 = Foo {j: 1};
        let test2 = Foo {j: 2};
        let test3 = Foo {j: 3};
        let mut samples: Vec<&Foo> = Vec::new();

        samples.push(&test1);
        samples.push(&test2);
        samples.push(&test3);

        let weights: Vec<f64> = vec![1.0, 1.0, 1.0];

        super::RandomChoice::random_choice_in_place(&mut samples, &weights);

        for sample in samples {
            print!("foo{}, ", sample.j);
        }
    }
}<commit_msg>renaming<commit_after>/// Implementation of Stochastic universal sampling
/// https://en.wikipedia.org/wiki/Stochastic_universal_sampling
/// Runtime: O(n)
/// Memory Usage: O(n) or O(1) (in place variant)

extern crate rand;

use self::rand::{thread_rng, Rng};

pub struct RandomChoice;

impl RandomChoice {
    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    ///
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    /// @param n Number of randomly chosen samples by weight.
    /// @return randomly selected samples by their weights
    pub fn random_choice_f64<'a, T>(samples: &'a [T], weights: &[f64], n: usize) -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 { return Vec::new(); }

        let sum:f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = thread_rng().next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        for _ in 0 .. n {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i];
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        choices
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    /// The result gets saved directly in the samples argument.
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    pub fn random_choice_in_place_f64<T: Clone>(samples: &mut [T], weights: &[f64]) {
        if weights.len() < 2 { return; }

        let sum:f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = thread_rng().next_f64() * spoke_gap;

        let mut j: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut current_spoke: f64 = spin;

        for i in 0 .. n {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j];
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_random_choice() {
        let test_vec: Vec<f64> = vec![3.1, 1.2, 1.3, 2.0];
        //let sum:f64 = test_vec.iter().fold(0.0, |acc, &i| acc + i);

        //assert_eq!(6.0, sum);

        let choices = super::RandomChoice::random_choice_f64(&test_vec, &test_vec, 4 as usize);

        for choice in choices {
            print!("{}, ", choice);
        }
    }

    #[test]
    fn test_random_choice_in_place() {
        let mut samples = vec!["hi", "this", "is", "a", "test!"];
        let weights: Vec<f64> = vec![1.0, 1.0, 1.0, 1.0, 1.0];

        super::RandomChoice::random_choice_in_place_f64(&mut samples, &weights);

        for sample in samples {
            print!("{}, ", sample);
        }
    }

    #[test]
    fn test_random_choice_in_place_2() {
        struct Foo {j: i32, };
        let test1 = Foo {j: 1};
        let test2 = Foo {j: 2};
        let test3 = Foo {j: 3};
        let mut samples: Vec<&Foo> = Vec::new();

        samples.push(&test1);
        samples.push(&test2);
        samples.push(&test3);

        let weights: Vec<f64> = vec![1.0, 1.0, 1.0];

        super::RandomChoice::random_choice_in_place_f64(&mut samples, &weights);

        for sample in samples {
            print!("foo{}, ", sample.j);
        }
    }
}<|endoftext|>


<commit_before>//! Furry Fiesta is a library for poker.
//! It's mostly meant for Holdem games, however the core functionality
//! should work for all game types.
//!
//! # Implemented:
//! Currently Furry Fiesta supports:
//!
//! * Hand Iteration.
//! * Hand Ranking.
//! * Hand Range parsing.
//! * Hand Range generation.
//!
//! # Planned:
//! * Holdem Game State.
//! * Multi-threading
#![feature(box_syntax, box_patterns)]

/// Allow all the core poker functionality to be used
/// externally. Everything in core should be agnostic
/// to poker style.
pub mod core;
/// The holdem specific code. This contains range
/// parsing, game state, and starting hand code.
pub mod holdem;
<commit_msg>Remove box syntax feature since it's not used<commit_after>//! Furry Fiesta is a library for poker.
//! It's mostly meant for Holdem games, however the core functionality
//! should work for all game types.
//!
//! # Implemented:
//! Currently Furry Fiesta supports:
//!
//! * Hand Iteration.
//! * Hand Ranking.
//! * Hand Range parsing.
//! * Hand Range generation.
//!
//! # Planned:
//! * Holdem Game State.
//! * Multi-threading

/// Allow all the core poker functionality to be used
/// externally. Everything in core should be agnostic
/// to poker style.
pub mod core;
/// The holdem specific code. This contains range
/// parsing, game state, and starting hand code.
pub mod holdem;
<|endoftext|>


<commit_before>//! Abomonation (spelling intentional) is a fast serialization / deserialization crate.
//!
//! Abomonation takes vectors of types and simply writes their contents as binary.
//! It then gives each typed element of the vector the opportunity to serialize more data, which is
//! useful for types with owned memory such as String and Vec.
//! The result is effectively a copy of reachable memory, where pointers are zero-ed out and vector
//! capacities are set to the vector length. The intended locations of pointers can be recovered on
//! deserialization by consuming binary data in the same way it was produced.
//!
//! Abomonation does several unsafe things, and should ideally be used only through the methods
//! `encode` and `decode` on types implementing the `Abomonation` trait.
//! Implementing the `Abomonation` trait is highly discouraged, unless you are just doing a
//! copy/paste of the tuple code for your struct.
//!
//! **Very important**: Abomonation reproduces the memory as laid out by the serializer, and this means
//! architectural variations are revealed. Data encoded on a 32bit big-endian machine will not
//! decode properly on a 64bit little-endian machine. Ideally it won't eat your laundry, but rather panic.
//!
//!
//! #Examples
//! ```
//! use abomonation::{encode, decode};
//!
//! // create some test data out of abomonation-approved types
//! let vector = (0..256u64).map(|i| (i, format!("{}", i))).collect();
//!
//! // encode vector into a Vec<u8>
//! let mut bytes = Vec::new();
//! encode(&vector, &mut bytes);
//!
//! // decode a &Vec<(u64, String)> from binary data
//! if let Ok(result) = decode::<(u64, String)>(&mut bytes) {
//!     assert!(result == &vector);
//! }
//! ```

use std::mem;       // yup, used pretty much everywhere.
use std::io::Write; // for bytes.write_all; push_all is unstable and extend is slow.

/// Encodes a vector of typed data into a binary buffer.
///
/// `encode` will transmute `typed` to binary and write its contents to `bytes`. After doing this,
/// it will offer each element of typed the opportunity to serialize more data. Having done that,
/// it offers each element the opportunity to "tidy up", in which the elements can erasing things
/// like memory addresses that it is impolite to share.
///
/// #Examples
/// ```
/// use abomonation::{encode, decode};
///
/// // create some test data out of abomonation-approved types
/// let vector = (0..256u64).map(|i| (i, format!("{}", i))).collect();
///
/// // encode vector into a Vec<u8>
/// let mut bytes = Vec::new();
/// encode(&vector, &mut bytes);
///
/// // decode a &Vec<(u64, String)> from binary data
/// if let Ok(result) = decode::<(u64, String)>(&mut bytes) {
///     assert!(result == &vector);
/// }
/// ```
///
pub fn encode<T: Abomonation>(typed: &Vec<T>, bytes: &mut Vec<u8>) {
    let slice = unsafe { std::slice::from_raw_parts(mem::transmute(typed), mem::size_of::<Vec<T>>()) };
    bytes.write_all(slice).unwrap();    // write to a Vec<u8> is claimed to never fail.
    unsafe { typed.entomb(bytes); }

    let result: &mut Vec<T> = unsafe { mem::transmute(bytes.get_unchecked_mut(0)) };
    unsafe { result.tidyup(); }
}

/// Decodes a binary buffer into a reference to a typed vector.
///
/// `decode` will read a `Vec<T>` amount of data from the head of `bytes`, and then use the length
/// there to read enough additional data from `bytes` to back its memory. It then proceeds to `exhume`
/// each element, offering them the ability to consume prefixes of `bytes` to back further owned data.
///
/// #Examples
/// ```
/// use abomonation::{encode, decode};
///
/// // create some test data out of abomonation-approved types
/// let vector = (0..256u64).map(|i| (i, format!("{}", i))).collect();
///
/// // encode vector into a Vec<u8>
/// let mut bytes = Vec::new();
/// encode(&vector, &mut bytes);
///
/// // decode a &Vec<(u64, String)> from binary data
/// if let Ok(result) = decode::<(u64, String)>(&mut bytes) {
///     assert!(result == &vector);
/// }
/// ```
pub fn decode<T: Abomonation>(bytes: &mut [u8]) -> Result<&Vec<T>,()> {
    let (split1, split2) = bytes.split_at_mut(mem::size_of::<Vec<T>>());

    let result: &mut Vec<T> = unsafe { mem::transmute(split1.get_unchecked_mut(0)) };
    unsafe { try!(result.exhume(&mut &*split2)); }
    Ok(result)
}

/// Abomonation provides methods to serialize any heap data the implementor owns.
///
/// The default implementations for Abomonation's methods are all empty. The majority of types have
/// no owned data to transcribe. Some do, however, and need to carefully implement these unsafe methods.
///
/// #Safety
///
/// Abomonation has no safe methods. Please do not call them. They are intended to be called only by
/// `encode` and `decode`, each of which impose restrictions on ownership and lifetime of the data
/// they take as input and return as output.
pub trait Abomonation {

    /// Write any additional information about `&self` beyond its binary representation.
    ///
    /// Most commonly this is owned data on the other end of pointers in `&self`.
    unsafe fn entomb(&self, _writer: &mut Vec<u8>) { }

    /// Perform any final edits before committing `&mut self`.
    ///
    /// Most commonly this overwrites pointers whose values should not be recorded.
    unsafe fn tidyup(&mut self) { }

    /// Recover any information for `&mut self` not evident from its binary representation.
    ///
    /// Most commonly this populates pointers with valid references into `_bytes`.
    unsafe fn exhume(&mut self, _bytes: &mut &[u8]) -> Result<(),()> { Ok(()) }
}

impl Abomonation for u8 { }
impl Abomonation for u64 { }
impl<T1: Abomonation, T2: Abomonation> Abomonation for (T1, T2) {
    unsafe fn entomb(&self, bytes: &mut Vec<u8>) { self.0.entomb(bytes); self.1.entomb(bytes); }
    unsafe fn tidyup(&mut self) { self.0.tidyup(); self.1.tidyup(); }
    unsafe fn exhume(&mut self, bytes: &mut &[u8]) -> Result<(), ()> { try!(self.0.exhume(bytes)); try!(self.1.exhume(bytes)); Ok(()) }
}

impl Abomonation for String {
    unsafe fn entomb(&self, bytes: &mut Vec<u8>) {
        bytes.write_all(self.as_bytes()).unwrap();
    }
    unsafe fn tidyup(&mut self) {
        std::ptr::write(self, String::from_raw_parts(0 as *mut u8, self.len(), self.len()));
    }
    unsafe fn exhume(&mut self, bytes: &mut &[u8]) -> Result<(), ()> {
        if self.len() > bytes.len() { Err(()) }
        else {
            let mine = &bytes[..self.len()];
            *bytes = &bytes[self.len()..];
            std::ptr::write(self, String::from_raw_parts(mem::transmute(mine.as_ptr()), self.len(), self.len()));
            Ok(())
        }
    }
}

impl<T: Abomonation> Abomonation for Vec<T> {
    unsafe fn entomb(&self, bytes: &mut Vec<u8>) {
        let position = bytes.len();
        bytes.write_all(typed_to_bytes(&self[..])).unwrap();
        for element in bytes_to_typed::<T>(&mut bytes[position..]) { element.tidyup(); }
        for element in self.iter() { element.entomb(bytes); }
    }
    unsafe fn tidyup(&mut self) {
        std::ptr::write(self, Vec::from_raw_parts(0 as *mut T, self.len(), self.len()));
    }
    unsafe fn exhume(&mut self, bytes: &mut &[u8]) -> Result<(),()> {

        // extract memory from bytes to back our vector
        let binary_len = self.len() * mem::size_of::<T>();
        if binary_len > bytes.len() { Err(()) }
        else {
            let back = &bytes[..binary_len];
            *bytes = &bytes[binary_len..];

            // transmute back to &mut [u8], and then transmute to &mut [T].
            let slice: &mut [T] = bytes_to_typed(mem::transmute(back));

            // build a new vector using this memory
            let vector = Vec::from_raw_parts(slice.as_mut_ptr(), self.len(), self.len());

            // overwrite *self w/o dropping it.
            std::ptr::write(self, vector);

            // pretend everything is normal; call exhume on each element
            for element in self.iter_mut() { try!(element.exhume(bytes)); }
            Ok(())
        }
    }
}

unsafe fn typed_to_bytes<T>(slice: &[T]) -> &[u8] {
    std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}

unsafe fn bytes_to_typed<T>(slice: &mut [u8]) -> &mut [T] {
    std::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut T, slice.len() / mem::size_of::<T>())
}
<commit_msg>update a bit of lib<commit_after>//! Abomonation (spelling intentional) is a fast serialization / deserialization crate.
//!
//! Abomonation takes vectors of types and simply writes their contents as binary.
//! It then gives each typed element of the vector the opportunity to serialize more data, which is
//! useful for types with owned memory such as String and Vec.
//! The result is effectively a copy of reachable memory, where pointers are zero-ed out and vector
//! capacities are set to the vector length. The intended locations of pointers can be recovered on
//! deserialization by consuming binary data in the same way it was produced.
//!
//! Abomonation does several unsafe things, and should ideally be used only through the methods
//! `encode` and `decode` on types implementing the `Abomonation` trait.
//! Implementing the `Abomonation` trait is highly discouraged, unless you are just doing a
//! copy/paste of the tuple code for your struct.
//!
//! **Very important**: Abomonation reproduces the memory as laid out by the serializer, and this means
//! architectural variations are revealed. Data encoded on a 32bit big-endian machine will not
//! decode properly on a 64bit little-endian machine. Ideally it won't eat your laundry, but rather panic.
//!
//!
//! #Examples
//! ```
//! use abomonation::{encode, decode};
//!
//! // create some test data out of abomonation-approved types
//! let vector = (0..256u64).map(|i| (i, format!("{}", i))).collect();
//!
//! // encode vector into a Vec<u8>
//! let mut bytes = Vec::new();
//! encode(&vector, &mut bytes);
//!
//! // decode a &Vec<(u64, String)> from binary data
//! if let Ok(result) = decode::<(u64, String)>(&mut bytes) {
//!     assert!(result == &vector);
//! }
//! ```

// extern crate num;

use std::mem;       // yup, used pretty much everywhere.
use std::io::Write; // for bytes.write_all; push_all is unstable and extend is slow.
// use std::traits::PrimInt;

/// Encodes a vector of typed data into a binary buffer.
///
/// `encode` will transmute `typed` to binary and write its contents to `bytes`. After doing this,
/// it will offer each element of typed the opportunity to serialize more data. Having done that,
/// it offers each element the opportunity to "tidy up", in which the elements can erasing things
/// like memory addresses that it is impolite to share.
///
/// #Examples
/// ```
/// use abomonation::{encode, decode};
///
/// // create some test data out of abomonation-approved types
/// let vector = (0..256u64).map(|i| (i, format!("{}", i))).collect();
///
/// // encode vector into a Vec<u8>
/// let mut bytes = Vec::new();
/// encode(&vector, &mut bytes);
///
/// // decode a &Vec<(u64, String)> from binary data
/// if let Ok(result) = decode::<(u64, String)>(&mut bytes) {
///     assert!(result == &vector);
/// }
/// ```
///
pub fn encode<T: Abomonation>(typed: &Vec<T>, bytes: &mut Vec<u8>) {
    let slice = unsafe { std::slice::from_raw_parts(mem::transmute(typed), mem::size_of::<Vec<T>>()) };
    bytes.write_all(slice).unwrap();    // write to a Vec<u8> is claimed to never fail.
    let result: &mut Vec<T> = unsafe { mem::transmute(bytes.get_unchecked_mut(0)) };
    unsafe { result.embalm(); }
    unsafe { typed.entomb(bytes); }
}

/// Decodes a binary buffer into a reference to a typed vector.
///
/// `decode` will read a `Vec<T>` amount of data from the head of `bytes`, and then use the length
/// there to read enough additional data from `bytes` to back its memory. It then proceeds to `exhume`
/// each element, offering them the ability to consume prefixes of `bytes` to back further owned data.
///
/// #Examples
/// ```
/// use abomonation::{encode, decode};
///
/// // create some test data out of abomonation-approved types
/// let vector = (0..256u64).map(|i| (i, format!("{}", i))).collect();
///
/// // encode vector into a Vec<u8>
/// let mut bytes = Vec::new();
/// encode(&vector, &mut bytes);
///
/// // decode a &Vec<(u64, String)> from binary data
/// if let Ok(result) = decode::<(u64, String)>(&mut bytes) {
///     assert!(result == &vector);
/// }
/// ```
pub fn decode<T: Abomonation>(bytes: &mut [u8]) -> Result<&Vec<T>,()> {
    let (split1, split2) = bytes.split_at_mut(mem::size_of::<Vec<T>>());

    let result: &mut Vec<T> = unsafe { mem::transmute(split1.get_unchecked_mut(0)) };
    unsafe { try!(result.exhume(&mut &*split2)); }
    Ok(result)
}

/// Abomonation provides methods to serialize any heap data the implementor owns.
///
/// The default implementations for Abomonation's methods are all empty. The majority of types have
/// no owned data to transcribe. Some do, however, and need to carefully implement these unsafe methods.
///
/// #Safety
///
/// Abomonation has no safe methods. Please do not call them. They are intended to be called only by
/// `encode` and `decode`, each of which impose restrictions on ownership and lifetime of the data
/// they take as input and return as output.
pub trait Abomonation {

    /// Write any additional information about `&self` beyond its binary representation.
    ///
    /// Most commonly this is owned data on the other end of pointers in `&self`.
    unsafe fn entomb(&self, _writer: &mut Vec<u8>) { }

    /// Perform any final edits before committing `&mut self`.
    ///
    /// Most commonly this overwrites pointers whose values should not be recorded.
    unsafe fn embalm(&mut self) { }

    /// Recover any information for `&mut self` not evident from its binary representation.
    ///
    /// Most commonly this populates pointers with valid references into `_bytes`.
    unsafe fn exhume(&mut self, _bytes: &mut &[u8]) -> Result<(),()> { Ok(()) }
}

impl Abomonation for u8 { }
impl Abomonation for u64 {
    // TODO : if these were optimized out, hooray! they aren't.
    // unsafe fn embalm(&mut self) { *self = (*self).to_le(); }
    // unsafe fn exhume(&mut self, bytes: &mut &[u8]) -> Result<(), ()> { *self = u64::from_le(*self); Ok(()) }
}
impl<T1: Abomonation, T2: Abomonation> Abomonation for (T1, T2) {
    unsafe fn embalm(&mut self) { self.0.embalm(); self.1.embalm(); }
    unsafe fn entomb(&self, bytes: &mut Vec<u8>) { self.0.entomb(bytes); self.1.entomb(bytes); }
    unsafe fn exhume(&mut self, bytes: &mut &[u8]) -> Result<(), ()> { try!(self.0.exhume(bytes)); try!(self.1.exhume(bytes)); Ok(()) }
}

impl Abomonation for String {
    unsafe fn embalm(&mut self) {
        std::ptr::write(self, String::from_raw_parts(0 as *mut u8, self.len(), self.len()));
    }
    unsafe fn entomb(&self, bytes: &mut Vec<u8>) {
        bytes.write_all(self.as_bytes()).unwrap();
    }
    unsafe fn exhume(&mut self, bytes: &mut &[u8]) -> Result<(), ()> {
        if self.len() > bytes.len() { Err(()) }
        else {
            let mine = &bytes[..self.len()];
            *bytes = &bytes[self.len()..];
            std::ptr::write(self, String::from_raw_parts(mem::transmute(mine.as_ptr()), self.len(), self.len()));
            Ok(())
        }
    }
}

impl<T: Abomonation> Abomonation for Vec<T> {
    unsafe fn embalm(&mut self) {
        std::ptr::write(self, Vec::from_raw_parts(0 as *mut T, self.len(), self.len()));
    }
    unsafe fn entomb(&self, bytes: &mut Vec<u8>) {
        let position = bytes.len();
        bytes.write_all(typed_to_bytes(&self[..])).unwrap();
        for element in bytes_to_typed::<T>(&mut bytes[position..]) { element.embalm(); }
        for element in self.iter() { element.entomb(bytes); }
    }
    unsafe fn exhume(&mut self, bytes: &mut &[u8]) -> Result<(),()> {

        // extract memory from bytes to back our vector
        let binary_len = self.len() * mem::size_of::<T>();
        if binary_len > bytes.len() { Err(()) }
        else {
            let back = &bytes[..binary_len];
            *bytes = &bytes[binary_len..];

            // transmute back to &mut [u8], and then transmute to &mut [T].
            let slice: &mut [T] = bytes_to_typed(mem::transmute(back));

            // build a new vector using this memory
            let vector = Vec::from_raw_parts(slice.as_mut_ptr(), self.len(), self.len());

            // overwrite *self w/o dropping it.
            std::ptr::write(self, vector);

            // pretend everything is normal; call exhume on each element
            for element in self.iter_mut() { try!(element.exhume(bytes)); }
            Ok(())
        }
    }
}

unsafe fn typed_to_bytes<T>(slice: &[T]) -> &[u8] {
    std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}

unsafe fn bytes_to_typed<T>(slice: &mut [u8]) -> &mut [T] {
    std::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut T, slice.len() / mem::size_of::<T>())
}
<|endoftext|>


<commit_before>//! rdb - Parse, analyze and dump RDB files
//!
//! A RDB file is a binary representation of the in-memory data of Redis.
//! This binary file is sufficient to completely restore Redis’ state.
//!
//! This library provides the methods to parse and analyze a RDB file
//! and to reformat and dump it in another format such as JSON or
//! RESP, the Redis Serialization.
//!
//! You can depend on this library via Cargo:
//!
//! ```ini
//! [dependencies]
//! rdb = "*"
//! ```
//!
//! # Basic operation
//!
//! rdb-rs exposes just one important method: `parse`.
//! This methods takes care of reading the RDB from a stream,
//! parsing the containted data and calling the provided formatter with already-parsed values.
//!
//! ```rust,no_run
//! # #![allow(unstable)]
//! # use std::io::BufReader;
//! # use std::fs::File;
//! # use std::path::Path;
//! let file = File::open(&Path::new("dump.rdb")).unwrap();
//! let reader = BufReader::new(file);
//! rdb::parse(reader, rdb::formatter::JSON::new(), rdb::filter::Simple::new());
//! ```
//!
//! # Formatter
//!
//! rdb-rs brings 4 pre-defined formatters, which can be used:
//!
//! * `PlainFormatter`: Just plain output for testing
//! * `JSONFormatter`: JSON-encoded output
//! * `NilFormatter`: Surpresses all output
//! * `ProtocolFormatter`: Formats the data in [RESP](http://redis.io/topics/protocol),
//! the Redis Serialization Protocol
//!
//! These formatters adhere to the `RdbParseFormatter` trait
//! and supply a method for each possible datatype or opcode.
//! Its up to the formatter to correctly handle all provided data such as
//! lists, sets, hashes, expires and metadata.
//!
//! # Command-line
//!
//! rdb-rs brings a Command Line application as well.
//!
//! This application will take a RDB file as input and format it in the specified format (JSON by
//! default).
//!
//! Example:
//!
//! ```shell,no_compile
//! $ rdb --format json dump.rdb
//! [{"key":"value"}]
//! $ rdb --format protocol dump.rdb
//! *2
//! $6
//! SELECT
//! $1
//! 0
//! *3
//! $3
//! SET
//! $3
//! key
//! $5
//! value
//! ```

#![feature(io)]
#![feature(core)]

extern crate lzf;
extern crate "rustc-serialize" as serialize;
extern crate regex;
extern crate byteorder;

use std::io::Read;

#[doc(hidden)]
pub use types::{
    ZiplistEntry,
    Type,

    /* error and result types */
    RdbError,
    RdbResult,
    RdbOk,
};

pub use parser::RdbParser;

use formatter::Formatter;
use filter::Filter;

mod macros;
mod constants;
mod helper;

pub mod types;
pub mod parser;
pub mod formatter;
pub mod filter;

pub fn parse<R: Read, F: Formatter, T: Filter>(input: R, formatter: F, filter: T) -> RdbOk {
    let mut parser = RdbParser::new(input, formatter, filter);
    parser.parse()
}
<commit_msg>Use updated crate name<commit_after>//! rdb - Parse, analyze and dump RDB files
//!
//! A RDB file is a binary representation of the in-memory data of Redis.
//! This binary file is sufficient to completely restore Redis’ state.
//!
//! This library provides the methods to parse and analyze a RDB file
//! and to reformat and dump it in another format such as JSON or
//! RESP, the Redis Serialization.
//!
//! You can depend on this library via Cargo:
//!
//! ```ini
//! [dependencies]
//! rdb = "*"
//! ```
//!
//! # Basic operation
//!
//! rdb-rs exposes just one important method: `parse`.
//! This methods takes care of reading the RDB from a stream,
//! parsing the containted data and calling the provided formatter with already-parsed values.
//!
//! ```rust,no_run
//! # #![allow(unstable)]
//! # use std::io::BufReader;
//! # use std::fs::File;
//! # use std::path::Path;
//! let file = File::open(&Path::new("dump.rdb")).unwrap();
//! let reader = BufReader::new(file);
//! rdb::parse(reader, rdb::formatter::JSON::new(), rdb::filter::Simple::new());
//! ```
//!
//! # Formatter
//!
//! rdb-rs brings 4 pre-defined formatters, which can be used:
//!
//! * `PlainFormatter`: Just plain output for testing
//! * `JSONFormatter`: JSON-encoded output
//! * `NilFormatter`: Surpresses all output
//! * `ProtocolFormatter`: Formats the data in [RESP](http://redis.io/topics/protocol),
//! the Redis Serialization Protocol
//!
//! These formatters adhere to the `RdbParseFormatter` trait
//! and supply a method for each possible datatype or opcode.
//! Its up to the formatter to correctly handle all provided data such as
//! lists, sets, hashes, expires and metadata.
//!
//! # Command-line
//!
//! rdb-rs brings a Command Line application as well.
//!
//! This application will take a RDB file as input and format it in the specified format (JSON by
//! default).
//!
//! Example:
//!
//! ```shell,no_compile
//! $ rdb --format json dump.rdb
//! [{"key":"value"}]
//! $ rdb --format protocol dump.rdb
//! *2
//! $6
//! SELECT
//! $1
//! 0
//! *3
//! $3
//! SET
//! $3
//! key
//! $5
//! value
//! ```

#![feature(io)]
#![feature(core)]

extern crate lzf;
extern crate rustc_serialize as serialize;
extern crate regex;
extern crate byteorder;

use std::io::Read;

#[doc(hidden)]
pub use types::{
    ZiplistEntry,
    Type,

    /* error and result types */
    RdbError,
    RdbResult,
    RdbOk,
};

pub use parser::RdbParser;

use formatter::Formatter;
use filter::Filter;

mod macros;
mod constants;
mod helper;

pub mod types;
pub mod parser;
pub mod formatter;
pub mod filter;

pub fn parse<R: Read, F: Formatter, T: Filter>(input: R, formatter: F, filter: T) -> RdbOk {
    let mut parser = RdbParser::new(input, formatter, filter);
    parser.parse()
}
<|endoftext|>


<commit_before>#![cfg_attr(test, deny(warnings))]

extern crate time;
extern crate conduit;
extern crate conduit_middleware as middleware;

use conduit::{Request, Method};
use middleware::Middleware;
use std::borrow::Cow;
use std::error::Error;
use std::io;
use time::{Tm, strptime, ParseError};

type Response = Result<conduit::Response, Box<Error+Send>>;

#[allow(missing_copy_implementations)]
pub struct ConditionalGet;

impl Middleware for ConditionalGet {
    fn after(&self, req: &mut Request, res: Response) -> Response {
        let mut res = try!(res);

        match req.method() {
            Method::Get | Method::Head => {
                if is_ok(&res) && is_fresh(req, &res) {
                    res.status = (304, "Not Modified");
                    res.headers.remove("Content-Type");
                    res.headers.remove("Content-Length");
                    res.body = Box::new(io::empty());
                }
            },
            _ => ()
        }

        Ok(res)
    }
}

fn is_ok(response: &conduit::Response) -> bool {
    match *response {
        conduit::Response { status: (200, _), .. } => true,
        _ => false
    }
}

fn is_fresh(req: &Request, res: &conduit::Response) -> bool {
    let modified_since = req.headers().find("If-Modified-Since").map(header_val);
    let none_match     = req.headers().find("If-None-Match").map(header_val);

    if modified_since.as_ref().or_else(|| none_match.as_ref()).is_none() {
        return false;
    }

    let mut success = true;

    modified_since.and_then(|modified_since| {
        parse_http_date(&modified_since).map_err(|_| { success = false; }).ok()
    }).map(|parsed| {
        success = success && is_modified_since(parsed, res);
    });

    none_match.map(|none_match| {
        success = success && etag_matches(&none_match, res);
    });

    success
}

fn etag_matches(none_match: &str, res: &conduit::Response) -> bool {
    res.headers.get("ETag").map(|etag| {
        res_header_val(etag) == none_match
    }).unwrap_or(false)
}

fn is_modified_since(modified_since: Tm, res: &conduit::Response) -> bool {
    res.headers.get("Last-Modified").and_then(|last_modified| {
        parse_http_date(&res_header_val(last_modified)).ok()
    }).map(|last_modified| {
        modified_since.to_timespec() >= last_modified.to_timespec()
    }).unwrap_or(false)
}

fn header_val<'a>(header: Vec<&'a str>) -> Cow<'a, str> {
    if header.len() == 1 {
        Cow::Borrowed(header[0])
    } else {
        Cow::Owned(header.concat())
    }
}

fn res_header_val<'a>(header: &'a Vec<String>) -> Cow<'a, str> {
    if header.len() == 1 {
        Cow::Borrowed(&header[0])
    } else {
        Cow::Owned(header.concat())
    }
}

fn parse_http_date(string: &str) -> Result<Tm, ()> {
    parse_rfc1123(string)
        .or_else(|_| parse_rfc850(string))
        .or_else(|_| parse_asctime(string))
        .map_err(|_| ())
}

fn parse_rfc1123(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a, %d %b %Y %T GMT")
}

fn parse_rfc850(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a, %d-%m-%y %T GMT")
}

fn parse_asctime(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a %m%t%d %T %Y")
}

#[cfg(test)]
mod tests {
    extern crate conduit_test as test;

    use std::collections::HashMap;
    use std::error::Error;
    use std::io::Cursor;
    use time;
    use time::Tm;
    use conduit::{Request, Response, Handler, Method};
    use middleware::MiddlewareBuilder;

    use super::ConditionalGet;

    macro_rules! returning {
        ($code:expr, $($header:expr => $value:expr),+) => ({
            let mut headers = HashMap::new();
            $(headers.insert($header.to_string(), vec!($value.to_string()));)+
            let handler = SimpleHandler::new(headers, $code, "hello");
            let mut stack = MiddlewareBuilder::new(handler);
            stack.add(ConditionalGet);
            stack
        });
        ($($header:expr => $value:expr),+) => ({
            returning!((200, "OK"), $($header => $value),+)
        })
    }

    macro_rules! request {
        ($($header:expr => $value:expr),+) => ({
            let mut req = test::MockRequest::new(Method::Get, "/");
            $(req.header($header, &$value.to_string());)+
            req
        })
    }

    #[test]
    fn test_sends_304() {
        let handler = returning!("Last-Modified" => httpdate(time::now()));
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        )));
    }

    #[test]
    fn test_sends_304_if_older_than_now() {
        let handler = returning!("Last-Modified" => before_now());
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        )));
    }

    #[test]
    fn test_sends_304_with_etag() {
        let handler = returning!("ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_time_but_not_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "4321"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_etag_but_not_time() {
        let handler = returning!("Last-Modified" => now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_etag() {
        let handler = returning!("ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-None-Match" => "4321"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_time() {
        let handler = returning!("Last-Modified" => now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now()
        )));
    }

    #[test]
    fn test_sends_304_with_fresh_time_and_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_does_not_affect_non_200() {
        let code = (302, "Found");
        let handler = returning!(code, "Last-Modified" => before_now(), "ETag" => "1234");
        expect(code, handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_does_not_affect_malformed_timestamp() {
        let bad_stamp = time::now().strftime("%Y-%m-%d %H:%M:%S %z").unwrap().to_string();
        let handler = returning!("Last-Modified" => before_now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => bad_stamp
        )));
    }

    fn expect_304(response: Result<Response, Box<Error+Send>>) {
        let mut response = response.ok().expect("No response");
        let mut body = String::new();
        response.body.read_to_string(&mut body).ok().expect("No body");

        assert_eq!(response.status, (304, "Not Modified"));
        assert_eq!(body, "");
    }

    fn expect_200(response: Result<Response, Box<Error+Send>>) {
        expect((200, "OK"), response);
    }

    fn expect(status: (u32, &'static str), response: Result<Response, Box<Error+Send>>) {
        let mut response = response.ok().expect("No response");
        let mut body = String::new();
        response.body.read_to_string(&mut body).ok().expect("No body");

        assert_eq!(response.status, status);
        assert_eq!(body, "hello");
    }

    struct SimpleHandler {
        map: HashMap<String, Vec<String>>,
        status: Status,
        body: &'static str
    }

    type Status = (u32, &'static str);

    impl SimpleHandler {
        fn new(map: HashMap<String, Vec<String>>, status: Status, body: &'static str) -> SimpleHandler {
            SimpleHandler { map: map, status: status, body: body }
        }
    }

    impl Handler for SimpleHandler {
        fn call(&self, _: &mut Request) -> Result<Response, Box<Error+Send>> {
            Ok(Response {
                status: self.status,
                headers: self.map.clone(),
                body: Box::new(Cursor::new(self.body.to_string().into_bytes())),
            })
        }
    }

    fn before_now() -> String {
        let mut now = time::now();
        now.tm_year -= 1;
        httpdate(now)
    }

    fn now() -> String {
        httpdate(time::now())
    }

    fn httpdate(time: Tm) -> String {
        time.strftime("%a, %d-%m-%y %T GMT").unwrap().to_string()
    }
}
<commit_msg>Fix build on nightly<commit_after>#![cfg_attr(test, deny(warnings))]

extern crate time;
extern crate conduit;
extern crate conduit_middleware as middleware;

use conduit::{Request, Method};
use middleware::Middleware;
use std::borrow::Cow;
use std::error::Error;
use std::io;
use time::{Tm, ParseError};

pub type Response = Result<conduit::Response, Box<Error+Send>>;

#[allow(missing_copy_implementations)]
pub struct ConditionalGet;

impl Middleware for ConditionalGet {
    fn after(&self, req: &mut Request, res: Response) -> Response {
        let mut res = try!(res);

        match req.method() {
            Method::Get | Method::Head => {
                if is_ok(&res) && is_fresh(req, &res) {
                    res.status = (304, "Not Modified");
                    res.headers.remove("Content-Type");
                    res.headers.remove("Content-Length");
                    res.body = Box::new(io::empty());
                }
            },
            _ => ()
        }

        Ok(res)
    }
}

fn is_ok(response: &conduit::Response) -> bool {
    match *response {
        conduit::Response { status: (200, _), .. } => true,
        _ => false
    }
}

fn is_fresh(req: &Request, res: &conduit::Response) -> bool {
    let modified_since = req.headers().find("If-Modified-Since").map(header_val);
    let none_match     = req.headers().find("If-None-Match").map(header_val);

    if modified_since.as_ref().or_else(|| none_match.as_ref()).is_none() {
        return false;
    }

    let mut success = true;

    modified_since.and_then(|modified_since| {
        parse_http_date(&modified_since).map_err(|_| { success = false; }).ok()
    }).map(|parsed| {
        success = success && is_modified_since(parsed, res);
    });

    none_match.map(|none_match| {
        success = success && etag_matches(&none_match, res);
    });

    success
}

fn etag_matches(none_match: &str, res: &conduit::Response) -> bool {
    res.headers.get("ETag").map(|etag| {
        res_header_val(etag) == none_match
    }).unwrap_or(false)
}

fn is_modified_since(modified_since: Tm, res: &conduit::Response) -> bool {
    res.headers.get("Last-Modified").and_then(|last_modified| {
        parse_http_date(&res_header_val(last_modified)).ok()
    }).map(|last_modified| {
        modified_since.to_timespec() >= last_modified.to_timespec()
    }).unwrap_or(false)
}

fn header_val<'a>(header: Vec<&'a str>) -> Cow<'a, str> {
    if header.len() == 1 {
        Cow::Borrowed(header[0])
    } else {
        Cow::Owned(header.concat())
    }
}

fn res_header_val<'a>(header: &'a Vec<String>) -> Cow<'a, str> {
    if header.len() == 1 {
        Cow::Borrowed(&header[0])
    } else {
        Cow::Owned(header.concat())
    }
}

fn parse_http_date(string: &str) -> Result<Tm, ()> {
    parse_rfc1123(string)
        .or_else(|_| parse_rfc850(string))
        .or_else(|_| parse_asctime(string))
        .map_err(|_| ())
}

fn parse_rfc1123(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a, %d %b %Y %T GMT")
}

fn parse_rfc850(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a, %d-%m-%y %T GMT")
}

fn parse_asctime(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a %m%t%d %T %Y")
}

#[cfg(test)]
mod tests {
    extern crate conduit_test as test;

    use std::collections::HashMap;
    use std::error::Error;
    use std::io::Cursor;
    use time;
    use time::Tm;
    use conduit::{Request, Response, Handler, Method};
    use middleware::MiddlewareBuilder;

    use super::ConditionalGet;

    macro_rules! returning {
        ($code:expr, $($header:expr => $value:expr),+) => ({
            let mut headers = HashMap::new();
            $(headers.insert($header.to_string(), vec!($value.to_string()));)+
            let handler = SimpleHandler::new(headers, $code, "hello");
            let mut stack = MiddlewareBuilder::new(handler);
            stack.add(ConditionalGet);
            stack
        });
        ($($header:expr => $value:expr),+) => ({
            returning!((200, "OK"), $($header => $value),+)
        })
    }

    macro_rules! request {
        ($($header:expr => $value:expr),+) => ({
            let mut req = test::MockRequest::new(Method::Get, "/");
            $(req.header($header, &$value.to_string());)+
            req
        })
    }

    #[test]
    fn test_sends_304() {
        let handler = returning!("Last-Modified" => httpdate(time::now()));
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        )));
    }

    #[test]
    fn test_sends_304_if_older_than_now() {
        let handler = returning!("Last-Modified" => before_now());
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        )));
    }

    #[test]
    fn test_sends_304_with_etag() {
        let handler = returning!("ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_time_but_not_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "4321"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_etag_but_not_time() {
        let handler = returning!("Last-Modified" => now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_etag() {
        let handler = returning!("ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-None-Match" => "4321"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_time() {
        let handler = returning!("Last-Modified" => now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now()
        )));
    }

    #[test]
    fn test_sends_304_with_fresh_time_and_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_does_not_affect_non_200() {
        let code = (302, "Found");
        let handler = returning!(code, "Last-Modified" => before_now(), "ETag" => "1234");
        expect(code, handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_does_not_affect_malformed_timestamp() {
        let bad_stamp = time::now().strftime("%Y-%m-%d %H:%M:%S %z").unwrap().to_string();
        let handler = returning!("Last-Modified" => before_now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => bad_stamp
        )));
    }

    fn expect_304(response: Result<Response, Box<Error+Send>>) {
        let mut response = response.ok().expect("No response");
        let mut body = String::new();
        response.body.read_to_string(&mut body).ok().expect("No body");

        assert_eq!(response.status, (304, "Not Modified"));
        assert_eq!(body, "");
    }

    fn expect_200(response: Result<Response, Box<Error+Send>>) {
        expect((200, "OK"), response);
    }

    fn expect(status: (u32, &'static str), response: Result<Response, Box<Error+Send>>) {
        let mut response = response.ok().expect("No response");
        let mut body = String::new();
        response.body.read_to_string(&mut body).ok().expect("No body");

        assert_eq!(response.status, status);
        assert_eq!(body, "hello");
    }

    struct SimpleHandler {
        map: HashMap<String, Vec<String>>,
        status: Status,
        body: &'static str
    }

    type Status = (u32, &'static str);

    impl SimpleHandler {
        fn new(map: HashMap<String, Vec<String>>, status: Status, body: &'static str) -> SimpleHandler {
            SimpleHandler { map: map, status: status, body: body }
        }
    }

    impl Handler for SimpleHandler {
        fn call(&self, _: &mut Request) -> Result<Response, Box<Error+Send>> {
            Ok(Response {
                status: self.status,
                headers: self.map.clone(),
                body: Box::new(Cursor::new(self.body.to_string().into_bytes())),
            })
        }
    }

    fn before_now() -> String {
        let mut now = time::now();
        now.tm_year -= 1;
        httpdate(now)
    }

    fn now() -> String {
        httpdate(time::now())
    }

    fn httpdate(time: Tm) -> String {
        time.strftime("%a, %d-%m-%y %T GMT").unwrap().to_string()
    }
}
<|endoftext|>


<commit_before>#![feature(plugin_registrar)]
#![feature(quote)]
#![feature(rustc_private)]
#![feature(slice_patterns)]

extern crate rustc_plugin;
extern crate syntax;

use rustc_plugin::Registry;
use syntax::abi::Abi;
use syntax::ast::{
    Constness, Expr, ExprKind, FnDecl, FunctionRetTy, Generics, Ident, ItemKind, Lifetime, Path,
    Unsafety
};
use syntax::codemap::{DUMMY_SP, Span, Spanned};
use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::parse::PResult;
use syntax::parse::common::SeqSep;
use syntax::parse::parser::{Parser, PathStyle};
use syntax::parse::token::keywords;
use syntax::parse::token::{DelimToken, Token};
use syntax::ptr::P;
use syntax::tokenstream::TokenTree;
use syntax::util::small_vector::SmallVector;

type ItemInfo = (Ident, ItemKind);

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("plague", plague_macro);
}

pub fn plague_macro<'cx>(cx: &'cx mut ExtCtxt, span: Span, tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let mut parser = cx.new_parser_from_tts(tts);

    match parse_plague(&mut parser) {
        Ok((params, fn_, should_panic)) => {
            match make_plague(cx, &mut parser, params, fn_, should_panic) {
                Ok(r) => r,
                Err(mut err) => {
                    err.emit();
                    DummyResult::any(span)
                }
            }
        }
        Err(mut err) => {
            err.emit();
            DummyResult::any(span)
        }
    }
}

enum FnKind {
    Decl {
        name: Ident,
        fn_: ItemKind,
        span: Span,
    },
    Path(Path)
}

fn parse_plague<'a>(parser: &mut Parser<'a>) -> PResult<'a, (Spanned<Vec<Param>>, FnKind, bool)> {
    try!(parser.expect_keyword(keywords::For));

    let params = try!(parser.parse_seq(
        &Token::OpenDelim(DelimToken::Bracket),
        &Token::CloseDelim(DelimToken::Bracket),
        SeqSep::trailing_allowed(Token::Comma),
        parse_param
    ));

    try!(expect_keyword(parser, "test"));

    let should_panic = parser.eat(&Token::Not);

    let result = if parser.look_ahead(1, Token::is_ident) {
        try!(parse_fn_decl(parser))
    }
    else {
        try!(parse_fn_use(parser))
    };

    if !parser.eat(&Token::Eof) {
        parser.span_err(parser.span, &format!("expected end of macro, got `{}`", parser. this_token_to_string()));
    }

    Ok((params, result, should_panic))
}

type Param = (Option<Lifetime>, P<Expr>, Option<P<Expr>>);

fn parse_param<'a>(parser: &mut Parser<'a>) -> PResult<'a, Param> {
    let name = try!(parser.parse_opt_lifetime());
    let expr = try!(parser.parse_expr());

    let ret = if parser.eat(&Token::RArrow) {
        Some(try!(parser.parse_expr()))
    }
    else {
        None
    };

    Ok((name, expr, ret))
}

fn parse_fn_use<'a>(parser: &mut Parser<'a>) -> PResult<'a, FnKind> {
    let path = try!(parser.parse_path(PathStyle::Expr));

    Ok(FnKind::Path(path))
}

fn parse_fn_decl<'a>(parser: &mut Parser<'a>) -> PResult<'a, FnKind> {
    let mut span = parser.span;
    let (constness, unsafety, abi) = try!(parser.parse_fn_front_matter());

    // Yep, copy-pasted pieces from libsyntax
    let ident = try!(parser.parse_ident());
    let mut generics = try!(parser.parse_generics());
    let decl = try!(parser.parse_fn_decl(false));
    generics.where_clause = try!(parser.parse_where_clause());
    let body = try!(parser.parse_block());
    let fn_ = ItemKind::Fn(decl, unsafety, constness, abi, generics, body);

    span.hi = parser.last_span.hi;

    Ok(FnKind::Decl{ name: ident, fn_: fn_, span: span })
}

fn expect_keyword<'a>(parser: &mut Parser<'a>, kw: &str) -> PResult<'a, ()> {
    match parser.parse_ident() {
        Ok(test_ident) if test_ident.name.as_str() == kw => Ok(()),
        Ok(ident) => {
            Err(parser.fatal(&format!("expected `test`, found `{}`", ident)))
        }
        Err(mut err) => {
            err.cancel();
            Err(parser.fatal(&format!("expected `test`, found `{}`", parser.this_token_to_string())))
        }
    }
}

fn make_plague<'cx, 'a>(
    cx: &'cx mut ExtCtxt,
    parser: &mut Parser<'a>,
    params: Spanned<Vec<Param>>,
    fn_: FnKind,
    should_panic: bool
) -> PResult<'a, Box<MacResult + 'cx>> {
    if params.node.is_empty() {
        cx.span_err(params.span, "empty parametrized tests are useless");
    }

    let mut fns = Vec::with_capacity(params.node.len());

    let (ident, fn_) = match fn_ {
        FnKind::Decl { name, fn_, span } => {
            let unused = quote_meta_item!(cx, allow(unused));
            let unused = cx.attribute(span, unused);
            fns.push(cx.item(span, name, vec![unused], fn_));

            (name, cx.expr_ident(span, name))
        }
        FnKind::Path(path) => {
            let name = path.segments.iter().last().unwrap().identifier;
            (name, cx.expr_path(path))
        }
    };

    let attributes = if should_panic {
        vec![
            cx.attribute(DUMMY_SP, quote_meta_item!(cx, test)),
            cx.attribute(DUMMY_SP, quote_meta_item!(cx, should_panic)),
        ]
    }
    else {
        vec![cx.attribute(DUMMY_SP, quote_meta_item!(cx, test))]
    };

    let span = params.span;
    for (i, param) in params.node.iter().enumerate() {
        let params = try!(make_params(parser, &param.1));
        let fn_ = make_test_fn(cx, span, fn_.clone(), params, &param.2);

        let name = if let Some(name) = param.0 {
            format!("{}{}", ident.name, name.name)
        }
        else {
            format!("{}#{}", ident.name, i)
        };

        fns.push(cx.item(
            span,
            //cx.ident_of(&name),
            Ident {
                name: cx.name_of(&name),
                ctxt: ident.ctxt,
            },
            attributes.clone(),
            fn_
        ));
    }

    Ok(MacEager::items(SmallVector::many(fns)))
}

fn make_test_fn<'cx>(
    cx: &'cx mut ExtCtxt,
    span: Span,
    fn_: P<Expr>,
    params: Vec<P<Expr>>,
    ret: &Option<P<Expr>>,
) -> ItemKind {
    let call = cx.expr_call(span, fn_, params);
    let call = if let Some(ref exp_ret) = *ret {
        quote_expr!(cx, {
            let exp = $exp_ret;
            let got = $call;

            if !(got == exp) {
                panic!("test failed: got `{:?}`, expected `{:?}`", got, exp);
            }
        })
    }
    else {
        call
    };

    let block = cx.block_expr(call);

    ItemKind::Fn(
        P(FnDecl { inputs: vec![], output: FunctionRetTy::Default(DUMMY_SP), variadic: false }),
        Unsafety::Normal,
        Constness::NotConst,
        Abi::Rust,
        Generics::default(),
        block
    )
}

fn make_params<'a>(parser: &mut Parser<'a>, params: &P<Expr>) -> PResult<'a, Vec<P<Expr>>> {
    if let ExprKind::Tup(ref params) = params.node {
        Ok(params.clone())
    }
    else {
        Err(parser.span_fatal(params.span, "expected tuple literal"))
    }
}
<commit_msg>Rustup to *rustc 1.13.0-nightly (1265cbf4e 2016-09-15)*<commit_after>#![feature(plugin_registrar)]
#![feature(quote)]
#![feature(rustc_private)]
#![feature(slice_patterns)]

extern crate rustc_plugin;
extern crate syntax;

use rustc_plugin::Registry;
use syntax::abi::Abi;
use syntax::ast::{
    Constness, Expr, ExprKind, FnDecl, FunctionRetTy, Generics, Ident, ItemKind, Lifetime, Path,
    Unsafety
};
use syntax::codemap::{self, DUMMY_SP, Span, Spanned};
use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::parse::PResult;
use syntax::parse::common::SeqSep;
use syntax::parse::parser::{Parser, PathStyle};
use syntax::parse::token::keywords;
use syntax::parse::token::{DelimToken, Token};
use syntax::ptr::P;
use syntax::tokenstream::TokenTree;
use syntax::util::small_vector::SmallVector;

type ItemInfo = (Ident, ItemKind);

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("plague", plague_macro);
}

pub fn plague_macro<'cx>(cx: &'cx mut ExtCtxt, span: Span, tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let mut parser = cx.new_parser_from_tts(tts);

    match parse_plague(&mut parser) {
        Ok((params, fn_, should_panic)) => {
            match make_plague(cx, &mut parser, params, fn_, should_panic) {
                Ok(r) => r,
                Err(mut err) => {
                    err.emit();
                    DummyResult::any(span)
                }
            }
        }
        Err(mut err) => {
            err.emit();
            DummyResult::any(span)
        }
    }
}

enum FnKind {
    Decl {
        name: Ident,
        fn_: ItemKind,
        span: Span,
    },
    Path(Path)
}

fn parse_plague<'a>(parser: &mut Parser<'a>) -> PResult<'a, (Spanned<Vec<Param>>, FnKind, bool)> {
    try!(parser.expect_keyword(keywords::For));

    let params = try!(parser.parse_seq(
        &Token::OpenDelim(DelimToken::Bracket),
        &Token::CloseDelim(DelimToken::Bracket),
        SeqSep::trailing_allowed(Token::Comma),
        parse_param
    ));

    try!(expect_keyword(parser, "test"));

    let should_panic = parser.eat(&Token::Not);

    let result = if parser.look_ahead(1, Token::is_ident) {
        try!(parse_fn_decl(parser))
    }
    else {
        try!(parse_fn_use(parser))
    };

    if !parser.eat(&Token::Eof) {
        parser.span_err(parser.span, &format!("expected end of macro, got `{}`", parser. this_token_to_string()));
    }

    Ok((params, result, should_panic))
}

type Param = (Option<Lifetime>, P<Expr>, Option<P<Expr>>);

fn parse_param<'a>(parser: &mut Parser<'a>) -> PResult<'a, Param> {
    let name = try!(parser.parse_opt_lifetime());
    let expr = try!(parser.parse_expr());

    let ret = if parser.eat(&Token::RArrow) {
        Some(try!(parser.parse_expr()))
    }
    else {
        None
    };

    Ok((name, expr, ret))
}

fn parse_fn_use<'a>(parser: &mut Parser<'a>) -> PResult<'a, FnKind> {
    let path = try!(parser.parse_path(PathStyle::Expr));

    Ok(FnKind::Path(path))
}

fn parse_fn_decl<'a>(parser: &mut Parser<'a>) -> PResult<'a, FnKind> {
    let mut span = parser.span;
    let (constness, unsafety, abi) = try!(parser.parse_fn_front_matter());

    // Yep, copy-pasted pieces from libsyntax
    let ident = try!(parser.parse_ident());
    let mut generics = try!(parser.parse_generics());
    let decl = try!(parser.parse_fn_decl(false));
    generics.where_clause = try!(parser.parse_where_clause());
    let body = try!(parser.parse_block());
    let fn_ = ItemKind::Fn(decl, unsafety, constness, abi, generics, body);

    span.hi = parser.last_span.hi;

    Ok(FnKind::Decl{ name: ident, fn_: fn_, span: span })
}

fn expect_keyword<'a>(parser: &mut Parser<'a>, kw: &str) -> PResult<'a, ()> {
    match parser.parse_ident() {
        Ok(test_ident) if test_ident.name.as_str() == kw => Ok(()),
        Ok(ident) => {
            Err(parser.fatal(&format!("expected `test`, found `{}`", ident)))
        }
        Err(mut err) => {
            err.cancel();
            Err(parser.fatal(&format!("expected `test`, found `{}`", parser.this_token_to_string())))
        }
    }
}

fn make_plague<'cx, 'a>(
    cx: &'cx mut ExtCtxt,
    parser: &mut Parser<'a>,
    params: Spanned<Vec<Param>>,
    fn_: FnKind,
    should_panic: bool
) -> PResult<'a, Box<MacResult + 'cx>> {
    if params.node.is_empty() {
        cx.span_err(params.span, "empty parametrized tests are useless");
    }

    let mut fns = Vec::with_capacity(params.node.len());

    let (ident, fn_) = match fn_ {
        FnKind::Decl { name, fn_, span } => {
            let unused = quote_meta_item!(cx, allow(unused));
            let unused = cx.attribute(span, unused);
            fns.push(cx.item(span, name, vec![unused], fn_));

            (name, cx.expr_ident(span, name))
        }
        FnKind::Path(path) => {
            let name = path.segments.iter().last().unwrap().identifier;
            (name, cx.expr_path(path))
        }
    };

    let attributes = if should_panic {
        vec![
            cx.attribute(DUMMY_SP, quote_meta_item!(cx, test)),
            cx.attribute(DUMMY_SP, quote_meta_item!(cx, should_panic)),
        ]
    }
    else {
        vec![cx.attribute(DUMMY_SP, quote_meta_item!(cx, test))]
    };

    let span = params.span;
    for (i, param) in params.node.iter().enumerate() {
        let params = try!(make_params(parser, &param.1));
        let fn_ = make_test_fn(cx, span, fn_.clone(), params, &param.2);

        let name = if let Some(name) = param.0 {
            format!("{}{}", ident.name, name.name)
        }
        else {
            format!("{}#{}", ident.name, i)
        };

        fns.push(cx.item(
            span,
            //cx.ident_of(&name),
            Ident {
                name: cx.name_of(&name),
                ctxt: ident.ctxt,
            },
            attributes.clone(),
            fn_
        ));
    }

    Ok(MacEager::items(SmallVector::many(fns)))
}

fn make_test_fn<'cx>(
    cx: &'cx mut ExtCtxt,
    span: Span,
    fn_: P<Expr>,
    params: Vec<P<Expr>>,
    ret: &Option<P<Expr>>,
) -> ItemKind {
    let call = cx.expr_call(span, fn_, params);
    let call = if let Some(ref exp_ret) = *ret {
        quote_expr!(cx, {
            let exp = $exp_ret;
            let got = $call;

            if !(got == exp) {
                panic!("test failed: got `{:?}`, expected `{:?}`", got, exp);
            }
        })
    }
    else {
        call
    };

    let block = cx.block_expr(call);

    ItemKind::Fn(
        P(FnDecl { inputs: vec![], output: FunctionRetTy::Default(DUMMY_SP), variadic: false }),
        Unsafety::Normal,
        codemap::respan(span, Constness::NotConst),
        Abi::Rust,
        Generics::default(),
        block
    )
}

fn make_params<'a>(parser: &mut Parser<'a>, params: &P<Expr>) -> PResult<'a, Vec<P<Expr>>> {
    if let ExprKind::Tup(ref params) = params.node {
        Ok(params.clone())
    }
    else {
        Err(parser.span_fatal(params.span, "expected tuple literal"))
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>Kick doc<commit_after>//! Temporary doc
<|endoftext|>