<commit_before>//! `rust-sqlite3` is a rustic binding to the [sqlite3 API][].
//!
//! [sqlite3 API]: http://www.sqlite.org/c3ref/intro.html
//!
//! Three layers of API are provided:
//!
//!  - `mod ffi` provides exhaustive, though unsafe, [bindgen] bindings for `libsqlite.h`.
//!  - `mod core` provides a minimal safe interface to the basic sqlite3 API.
//!  - `mod types` provides `ToSql`/`FromSql` traits, and the library provides
//!     convenient `query()` and `update()` APIs.
//!
//! [bindgen]: https://github.com/crabtw/rust-bindgen
//!
//! The following example demonstrates opening a database, executing
//! DDL, and using the high-level `query()` and `update()` API. Note the
//! use of `Result` and `try!()` for error handling.
//!
//! ```rust
//! extern crate time;
//! extern crate sqlite3;
//!
//! use time::Timespec;
//!
//!
//! use sqlite3::{DatabaseConnection, DatabaseUpdate,
//!               Query, ResultRowAccess,
//!               SqliteResult, SqliteError};
//!
//! #[deriving(Show)]
//! struct Person {
//!     id: i32,
//!     name: String,
//!     time_created: Timespec,
//!     // TODO: data: Option<Vec<u8>>
//! }
//!
//! pub fn main() {
//!     match io() {
//!         Ok(ppl) => println!("Found people: {}", ppl),
//!         Err(oops) => panic!(oops)
//!     }
//! }
//!
//! fn io() -> Result<Vec<Person>, (SqliteError, String)> {
//!     let mut conn = try!(DatabaseConnection::in_memory());
//!     with_conn(&mut conn).map_err(|code| (code, conn.errmsg()))
//! }
//!
//! fn with_conn(conn: &mut DatabaseConnection) -> SqliteResult<Vec<Person>> {
//!     try!(conn.exec("CREATE TABLE person (
//!                  id              SERIAL PRIMARY KEY,
//!                  name            VARCHAR NOT NULL,
//!                  time_created    TIMESTAMP NOT NULL
//!                )"));
//!
//!     let me = Person {
//!         id: 0,
//!         name: "Dan".to_string(),
//!         time_created: time::get_time(),
//!     };
//!     {
//!         let mut tx = try!(conn.prepare("INSERT INTO person (name, time_created)
//!                            VALUES ($1, $2)"));
//!         let changes = try!(conn.update(&mut tx, &[&me.name, &me.time_created]));
//!         assert_eq!(changes, 1);
//!     }
//!
//!     let mut stmt = try!(conn.prepare("SELECT id, name, time_created FROM person"));
//!
//!     let mut ppl = vec!();
//!     try!(stmt.query(
//!         [], |row| {
//!             ppl.push(Person {
//!                 id: row.get("id"),
//!                 name: row.get("name"),
//!                 time_created: row.get(2u)
//!             });
//!             Ok(())
//!         }));
//!     Ok(ppl)
//! }
//! ```

#![crate_name = "sqlite3"]
#![crate_type = "lib"]
#![feature(unsafe_destructor)]
#![warn(missing_docs)]

extern crate libc;
extern crate time;

use std::fmt::Show;

pub use core::Access;
pub use core::{DatabaseConnection, PreparedStatement, ResultSet, ResultRow};
pub use types::{FromSql, ToSql};

pub mod core;
pub mod types;

/// bindgen-bindings to libsqlite3
#[allow(non_camel_case_types, non_snake_case)]
#[allow(dead_code)]
#[allow(missing_docs)]
pub mod ffi;

pub mod access;

/// Mix in `update()` convenience function.
pub trait DatabaseUpdate {
    /// Execute a statement after binding any parameters.
    fn update<'db, 's>(&'db mut self,
                       stmt: &'s mut PreparedStatement<'s>,
                       values: &[&ToSql]) -> SqliteResult<uint>;
}

impl DatabaseUpdate for core::DatabaseConnection {
    /// Execute a statement after binding any parameters.
    ///
    /// When the statement is done, The [number of rows
    /// modified][changes] is reported.
    ///
    /// Fail with `Err(SQLITE_MISUSE)` in case the statement results
    /// in any any rows (e.g. a `SELECT` rather than `INSERT` or
    /// `UPDATE`).
    ///
    /// [changes]: http://www.sqlite.org/c3ref/changes.html
    fn update<'db, 's>(&'db mut self,
                       stmt: &'s mut PreparedStatement<'s>,
                       values: &[&ToSql]) -> SqliteResult<uint> {
        let check = {
            try!(bind_values(stmt, values));
            let mut results = stmt.execute();
            match results.step() {
                None => Ok(()),
                Some(Ok(_row)) => Err(SQLITE_MISUSE),
                Some(Err(e)) => Err(e)
            }
        };
        check.map(|_ok| self.changes())
    }
}

/// Mix in `query()` convenience function.
pub trait Query<'s> {
    /// Process rows from a query after binding parameters.
    fn query(&'s mut self,
             values: &[&ToSql],
             each_row: |&mut ResultRow|: 's -> SqliteResult<()>
             ) -> SqliteResult<()>;
}

impl<'s> Query<'s> for core::PreparedStatement<'s> {
    /// Process rows from a query after binding parameters.
    ///
    /// For call `each_row(row)` for each resulting step,
    /// exiting on `Err`.
    fn query(&'s mut self,
             values: &[&ToSql],
             each_row: |&mut ResultRow|: 's -> SqliteResult<()>
             ) -> SqliteResult<()> {
        try!(bind_values(self, values));
        let mut results = self.execute();
        loop {
            match results.step() {
                None => break,
                Some(Ok(ref mut row)) => try!(each_row(row)),
                Some(Err(e)) => return Err(e)
            }
        }
        Ok(())
    }
}

fn bind_values<'db>(s: &'db mut PreparedStatement, values: &[&ToSql]) -> SqliteResult<()> {
    for (ix, v) in values.iter().enumerate() {
        try!(v.to_sql(s, ix + 1));
    }
    Ok(())
}

/// Access result columns of a row by name or numeric index.
pub trait ResultRowAccess {
    /// Get `T` type result value from `idx`th column of a row.
    ///
    /// # Failure
    ///
    /// Fails if there is no such column or value.
    fn get<I: RowIndex + Show + Clone, T: FromSql>(&mut self, idx: I) -> T;

    /// Try to get `T` type result value from `idx`th column of a row.
    fn get_opt<I: RowIndex, T: FromSql>(&mut self, idx: I) -> SqliteResult<T>;
}

impl<'s, 'r> ResultRowAccess for core::ResultRow<'s, 'r> {
    fn get<I: RowIndex + Show + Clone, T: FromSql>(&mut self, idx: I) -> T {
        match self.get_opt(idx.clone()) {
            Ok(ok) => ok,
            Err(err) => panic!("retrieving column {}: {}", idx, err)
        }
    }

    fn get_opt<I: RowIndex, T: FromSql>(&mut self, idx: I) -> SqliteResult<T> {
        match idx.idx(self) {
            Some(idx) => FromSql::from_sql(self, idx),
            None => Err(SQLITE_MISUSE)
        }
    }

}

/// A trait implemented by types that can index into columns of a row.
///
/// *inspired by sfackler's [RowIndex][]*
/// [RowIndex]: http://www.rust-ci.org/sfackler/rust-postgres/doc/postgres/trait.RowIndex.html
pub trait RowIndex {
    /// Try to convert `self` to an index into a row.
    fn idx(&self, row: &mut ResultRow) -> Option<uint>;
}

impl RowIndex for uint {
    /// Index into a row directly by uint.
    fn idx(&self, _row: &mut ResultRow) -> Option<uint> { Some(*self) }
}

impl RowIndex for &'static str {
    /// Index into a row by column name.
    ///
    /// *TODO: figure out how to use lifetime of row rather than
    /// `static`.*
    fn idx(&self, row: &mut ResultRow) -> Option<uint> {
        let mut ixs = range(0, row.column_count());
        ixs.find(|ix| row.with_column_name(*ix, false, |name| name == *self))
    }
}

/// The type used for returning and propagating sqlite3 errors.
#[must_use]
pub type SqliteResult<T> = Result<T, SqliteError>;

/// Result codes for errors.
///
/// cf. [sqlite3 result codes][codes].
///
/// Note `SQLITE_OK` is not included; we use `Ok(...)` instead.
///
/// Likewise, in place of `SQLITE_ROW` and `SQLITE_DONE`, we return
/// `Some(...)` or `None` from `ResultSet::next()`.
///
/// [codes]: http://www.sqlite.org/c3ref/c_abort.html
#[deriving(Show, PartialEq, Eq, FromPrimitive)]
#[allow(non_camel_case_types)]
#[allow(missing_docs)]
pub enum SqliteError {
    SQLITE_ERROR     =  1,
    SQLITE_INTERNAL  =  2,
    SQLITE_PERM      =  3,
    SQLITE_ABORT     =  4,
    SQLITE_BUSY      =  5,
    SQLITE_LOCKED    =  6,
    SQLITE_NOMEM     =  7,
    SQLITE_READONLY  =  8,
    SQLITE_INTERRUPT =  9,
    SQLITE_IOERR     = 10,
    SQLITE_CORRUPT   = 11,
    SQLITE_NOTFOUND  = 12,
    SQLITE_FULL      = 13,
    SQLITE_CANTOPEN  = 14,
    SQLITE_PROTOCOL  = 15,
    SQLITE_EMPTY     = 16,
    SQLITE_SCHEMA    = 17,
    SQLITE_TOOBIG    = 18,
    SQLITE_CONSTRAINT= 19,
    SQLITE_MISMATCH  = 20,
    SQLITE_MISUSE    = 21,
    SQLITE_NOLFS     = 22,
    SQLITE_AUTH      = 23,
    SQLITE_FORMAT    = 24,
    SQLITE_RANGE     = 25,
    SQLITE_NOTADB    = 26
}

/// Fundamental Datatypes
#[deriving(Show, PartialEq, Eq, FromPrimitive)]
#[allow(non_camel_case_types)]
#[allow(missing_docs)]
pub enum ColumnType {
    SQLITE_INTEGER = 1,
    SQLITE_FLOAT   = 2,
    SQLITE_TEXT    = 3,
    SQLITE_BLOB    = 4,
    SQLITE_NULL    = 5
}

#[cfg(test)]
mod bind_tests {
    use super::{DatabaseConnection, ResultSet};
    use super::{ResultRowAccess};
    use super::{SqliteResult};

    #[test]
    fn bind_fun() {
        fn go() -> SqliteResult<()> {
            let mut database = try!(DatabaseConnection::in_memory()
                                    .map_err(|(code, _msg)| code));

            try!(database.exec(
                "BEGIN;
                CREATE TABLE test (id int, name text, address text);
                INSERT INTO test (id, name, address) VALUES (1, 'John Doe', '123 w Pine');
                COMMIT;"));

            {
                let mut tx = try!(database.prepare(
                    "INSERT INTO test (id, name, address) VALUES (?, ?, ?)"));
                try!(tx.bind_int(1, 2));
                try!(tx.bind_text(2, "Jane Doe"));
                try!(tx.bind_text(3, "345 e Walnut"));
                let mut results = tx.execute();
                assert!(results.step().is_none());
            }
            assert_eq!(database.changes(), 1);

            let mut q = try!(database.prepare("select * from test order by id"));
            let mut rows = q.execute();
            match rows.step() {
                Some(Ok(ref mut row)) => {
                    assert_eq!(row.get::<uint, i32>(0), 1);
                    // TODO let name = q.get_text(1);
                    // assert_eq!(name.as_slice(), "John Doe");
                },
                _ => panic!()
            }

            match rows.step() {
                Some(Ok(ref mut row)) => {
                    assert_eq!(row.get::<uint, i32>(0), 2);
                    //TODO let addr = q.get_text(2);
                    // assert_eq!(addr.as_slice(), "345 e Walnut");
                },
                _ => panic!()
            }
            Ok(())
        }
        match go() {
            Ok(_) => (),
            Err(e) => panic!("oops! {}", e)
        }
    }

    fn with_query<T>(sql: &str, f: |rows: &mut ResultSet| -> T) -> SqliteResult<T> {
        let mut db = try!(DatabaseConnection::in_memory()
                          .map_err(|(code, _msg)| code));
        let mut s = try!(db.prepare(sql));
        let mut rows = s.execute();
        Ok(f(&mut rows))
    }

    #[test]
    fn named_rowindex() {
        fn go() -> SqliteResult<(uint, i32)> {
            let mut count = 0;
            let mut sum = 0;

            with_query("select 1 as col1
                       union all
                       select 2", |rows| {
                loop {
                    match rows.step() {
                        Some(Ok(ref mut row)) => {
                            count += 1;
                            sum += row.get("col1")
                        },
                        _ => break
                    }
                }
                (count, sum)
            })
        }
        assert_eq!(go(), Ok((2, 3)))
    }
}
<commit_msg>change Failure to Panic in docstring<commit_after>//! `rust-sqlite3` is a rustic binding to the [sqlite3 API][].
//!
//! [sqlite3 API]: http://www.sqlite.org/c3ref/intro.html
//!
//! Three layers of API are provided:
//!
//!  - `mod ffi` provides exhaustive, though unsafe, [bindgen] bindings for `libsqlite.h`.
//!  - `mod core` provides a minimal safe interface to the basic sqlite3 API.
//!  - `mod types` provides `ToSql`/`FromSql` traits, and the library provides
//!     convenient `query()` and `update()` APIs.
//!
//! [bindgen]: https://github.com/crabtw/rust-bindgen
//!
//! The following example demonstrates opening a database, executing
//! DDL, and using the high-level `query()` and `update()` API. Note the
//! use of `Result` and `try!()` for error handling.
//!
//! ```rust
//! extern crate time;
//! extern crate sqlite3;
//!
//! use time::Timespec;
//!
//!
//! use sqlite3::{DatabaseConnection, DatabaseUpdate,
//!               Query, ResultRowAccess,
//!               SqliteResult, SqliteError};
//!
//! #[deriving(Show)]
//! struct Person {
//!     id: i32,
//!     name: String,
//!     time_created: Timespec,
//!     // TODO: data: Option<Vec<u8>>
//! }
//!
//! pub fn main() {
//!     match io() {
//!         Ok(ppl) => println!("Found people: {}", ppl),
//!         Err(oops) => panic!(oops)
//!     }
//! }
//!
//! fn io() -> Result<Vec<Person>, (SqliteError, String)> {
//!     let mut conn = try!(DatabaseConnection::in_memory());
//!     with_conn(&mut conn).map_err(|code| (code, conn.errmsg()))
//! }
//!
//! fn with_conn(conn: &mut DatabaseConnection) -> SqliteResult<Vec<Person>> {
//!     try!(conn.exec("CREATE TABLE person (
//!                  id              SERIAL PRIMARY KEY,
//!                  name            VARCHAR NOT NULL,
//!                  time_created    TIMESTAMP NOT NULL
//!                )"));
//!
//!     let me = Person {
//!         id: 0,
//!         name: "Dan".to_string(),
//!         time_created: time::get_time(),
//!     };
//!     {
//!         let mut tx = try!(conn.prepare("INSERT INTO person (name, time_created)
//!                            VALUES ($1, $2)"));
//!         let changes = try!(conn.update(&mut tx, &[&me.name, &me.time_created]));
//!         assert_eq!(changes, 1);
//!     }
//!
//!     let mut stmt = try!(conn.prepare("SELECT id, name, time_created FROM person"));
//!
//!     let mut ppl = vec!();
//!     try!(stmt.query(
//!         [], |row| {
//!             ppl.push(Person {
//!                 id: row.get("id"),
//!                 name: row.get("name"),
//!                 time_created: row.get(2u)
//!             });
//!             Ok(())
//!         }));
//!     Ok(ppl)
//! }
//! ```

#![crate_name = "sqlite3"]
#![crate_type = "lib"]
#![feature(unsafe_destructor)]
#![warn(missing_docs)]

extern crate libc;
extern crate time;

use std::fmt::Show;

pub use core::Access;
pub use core::{DatabaseConnection, PreparedStatement, ResultSet, ResultRow};
pub use types::{FromSql, ToSql};

pub mod core;
pub mod types;

/// bindgen-bindings to libsqlite3
#[allow(non_camel_case_types, non_snake_case)]
#[allow(dead_code)]
#[allow(missing_docs)]
pub mod ffi;

pub mod access;

/// Mix in `update()` convenience function.
pub trait DatabaseUpdate {
    /// Execute a statement after binding any parameters.
    fn update<'db, 's>(&'db mut self,
                       stmt: &'s mut PreparedStatement<'s>,
                       values: &[&ToSql]) -> SqliteResult<uint>;
}

impl DatabaseUpdate for core::DatabaseConnection {
    /// Execute a statement after binding any parameters.
    ///
    /// When the statement is done, The [number of rows
    /// modified][changes] is reported.
    ///
    /// Fail with `Err(SQLITE_MISUSE)` in case the statement results
    /// in any any rows (e.g. a `SELECT` rather than `INSERT` or
    /// `UPDATE`).
    ///
    /// [changes]: http://www.sqlite.org/c3ref/changes.html
    fn update<'db, 's>(&'db mut self,
                       stmt: &'s mut PreparedStatement<'s>,
                       values: &[&ToSql]) -> SqliteResult<uint> {
        let check = {
            try!(bind_values(stmt, values));
            let mut results = stmt.execute();
            match results.step() {
                None => Ok(()),
                Some(Ok(_row)) => Err(SQLITE_MISUSE),
                Some(Err(e)) => Err(e)
            }
        };
        check.map(|_ok| self.changes())
    }
}

/// Mix in `query()` convenience function.
pub trait Query<'s> {
    /// Process rows from a query after binding parameters.
    fn query(&'s mut self,
             values: &[&ToSql],
             each_row: |&mut ResultRow|: 's -> SqliteResult<()>
             ) -> SqliteResult<()>;
}

impl<'s> Query<'s> for core::PreparedStatement<'s> {
    /// Process rows from a query after binding parameters.
    ///
    /// For call `each_row(row)` for each resulting step,
    /// exiting on `Err`.
    fn query(&'s mut self,
             values: &[&ToSql],
             each_row: |&mut ResultRow|: 's -> SqliteResult<()>
             ) -> SqliteResult<()> {
        try!(bind_values(self, values));
        let mut results = self.execute();
        loop {
            match results.step() {
                None => break,
                Some(Ok(ref mut row)) => try!(each_row(row)),
                Some(Err(e)) => return Err(e)
            }
        }
        Ok(())
    }
}

fn bind_values<'db>(s: &'db mut PreparedStatement, values: &[&ToSql]) -> SqliteResult<()> {
    for (ix, v) in values.iter().enumerate() {
        try!(v.to_sql(s, ix + 1));
    }
    Ok(())
}

/// Access result columns of a row by name or numeric index.
pub trait ResultRowAccess {
    /// Get `T` type result value from `idx`th column of a row.
    ///
    /// # Panic
    ///
    /// Panics if there is no such column or value.
    fn get<I: RowIndex + Show + Clone, T: FromSql>(&mut self, idx: I) -> T;

    /// Try to get `T` type result value from `idx`th column of a row.
    fn get_opt<I: RowIndex, T: FromSql>(&mut self, idx: I) -> SqliteResult<T>;
}

impl<'s, 'r> ResultRowAccess for core::ResultRow<'s, 'r> {
    fn get<I: RowIndex + Show + Clone, T: FromSql>(&mut self, idx: I) -> T {
        match self.get_opt(idx.clone()) {
            Ok(ok) => ok,
            Err(err) => panic!("retrieving column {}: {}", idx, err)
        }
    }

    fn get_opt<I: RowIndex, T: FromSql>(&mut self, idx: I) -> SqliteResult<T> {
        match idx.idx(self) {
            Some(idx) => FromSql::from_sql(self, idx),
            None => Err(SQLITE_MISUSE)
        }
    }

}

/// A trait implemented by types that can index into columns of a row.
///
/// *inspired by sfackler's [RowIndex][]*
/// [RowIndex]: http://www.rust-ci.org/sfackler/rust-postgres/doc/postgres/trait.RowIndex.html
pub trait RowIndex {
    /// Try to convert `self` to an index into a row.
    fn idx(&self, row: &mut ResultRow) -> Option<uint>;
}

impl RowIndex for uint {
    /// Index into a row directly by uint.
    fn idx(&self, _row: &mut ResultRow) -> Option<uint> { Some(*self) }
}

impl RowIndex for &'static str {
    /// Index into a row by column name.
    ///
    /// *TODO: figure out how to use lifetime of row rather than
    /// `static`.*
    fn idx(&self, row: &mut ResultRow) -> Option<uint> {
        let mut ixs = range(0, row.column_count());
        ixs.find(|ix| row.with_column_name(*ix, false, |name| name == *self))
    }
}

/// The type used for returning and propagating sqlite3 errors.
#[must_use]
pub type SqliteResult<T> = Result<T, SqliteError>;

/// Result codes for errors.
///
/// cf. [sqlite3 result codes][codes].
///
/// Note `SQLITE_OK` is not included; we use `Ok(...)` instead.
///
/// Likewise, in place of `SQLITE_ROW` and `SQLITE_DONE`, we return
/// `Some(...)` or `None` from `ResultSet::next()`.
///
/// [codes]: http://www.sqlite.org/c3ref/c_abort.html
#[deriving(Show, PartialEq, Eq, FromPrimitive)]
#[allow(non_camel_case_types)]
#[allow(missing_docs)]
pub enum SqliteError {
    SQLITE_ERROR     =  1,
    SQLITE_INTERNAL  =  2,
    SQLITE_PERM      =  3,
    SQLITE_ABORT     =  4,
    SQLITE_BUSY      =  5,
    SQLITE_LOCKED    =  6,
    SQLITE_NOMEM     =  7,
    SQLITE_READONLY  =  8,
    SQLITE_INTERRUPT =  9,
    SQLITE_IOERR     = 10,
    SQLITE_CORRUPT   = 11,
    SQLITE_NOTFOUND  = 12,
    SQLITE_FULL      = 13,
    SQLITE_CANTOPEN  = 14,
    SQLITE_PROTOCOL  = 15,
    SQLITE_EMPTY     = 16,
    SQLITE_SCHEMA    = 17,
    SQLITE_TOOBIG    = 18,
    SQLITE_CONSTRAINT= 19,
    SQLITE_MISMATCH  = 20,
    SQLITE_MISUSE    = 21,
    SQLITE_NOLFS     = 22,
    SQLITE_AUTH      = 23,
    SQLITE_FORMAT    = 24,
    SQLITE_RANGE     = 25,
    SQLITE_NOTADB    = 26
}

/// Fundamental Datatypes
#[deriving(Show, PartialEq, Eq, FromPrimitive)]
#[allow(non_camel_case_types)]
#[allow(missing_docs)]
pub enum ColumnType {
    SQLITE_INTEGER = 1,
    SQLITE_FLOAT   = 2,
    SQLITE_TEXT    = 3,
    SQLITE_BLOB    = 4,
    SQLITE_NULL    = 5
}

#[cfg(test)]
mod bind_tests {
    use super::{DatabaseConnection, ResultSet};
    use super::{ResultRowAccess};
    use super::{SqliteResult};

    #[test]
    fn bind_fun() {
        fn go() -> SqliteResult<()> {
            let mut database = try!(DatabaseConnection::in_memory()
                                    .map_err(|(code, _msg)| code));

            try!(database.exec(
                "BEGIN;
                CREATE TABLE test (id int, name text, address text);
                INSERT INTO test (id, name, address) VALUES (1, 'John Doe', '123 w Pine');
                COMMIT;"));

            {
                let mut tx = try!(database.prepare(
                    "INSERT INTO test (id, name, address) VALUES (?, ?, ?)"));
                try!(tx.bind_int(1, 2));
                try!(tx.bind_text(2, "Jane Doe"));
                try!(tx.bind_text(3, "345 e Walnut"));
                let mut results = tx.execute();
                assert!(results.step().is_none());
            }
            assert_eq!(database.changes(), 1);

            let mut q = try!(database.prepare("select * from test order by id"));
            let mut rows = q.execute();
            match rows.step() {
                Some(Ok(ref mut row)) => {
                    assert_eq!(row.get::<uint, i32>(0), 1);
                    // TODO let name = q.get_text(1);
                    // assert_eq!(name.as_slice(), "John Doe");
                },
                _ => panic!()
            }

            match rows.step() {
                Some(Ok(ref mut row)) => {
                    assert_eq!(row.get::<uint, i32>(0), 2);
                    //TODO let addr = q.get_text(2);
                    // assert_eq!(addr.as_slice(), "345 e Walnut");
                },
                _ => panic!()
            }
            Ok(())
        }
        match go() {
            Ok(_) => (),
            Err(e) => panic!("oops! {}", e)
        }
    }

    fn with_query<T>(sql: &str, f: |rows: &mut ResultSet| -> T) -> SqliteResult<T> {
        let mut db = try!(DatabaseConnection::in_memory()
                          .map_err(|(code, _msg)| code));
        let mut s = try!(db.prepare(sql));
        let mut rows = s.execute();
        Ok(f(&mut rows))
    }

    #[test]
    fn named_rowindex() {
        fn go() -> SqliteResult<(uint, i32)> {
            let mut count = 0;
            let mut sum = 0;

            with_query("select 1 as col1
                       union all
                       select 2", |rows| {
                loop {
                    match rows.step() {
                        Some(Ok(ref mut row)) => {
                            count += 1;
                            sum += row.get("col1")
                        },
                        _ => break
                    }
                }
                (count, sum)
            })
        }
        assert_eq!(go(), Ok((2, 3)))
    }
}
<|endoftext|>


<commit_before>//! A simple, connection pool library.
#![crate_name = "netutils"]
#![crate_type = "lib"]
#![unstable]
#![warn(missing_docs)]
#![feature(slicing_syntax,phase)]
#[cfg(feature = "ssl")] extern crate openssl;
#[phase(plugin, link)]extern crate log;

pub mod net;

#[cfg(test)]
pub mod test {
	use std::default::Default;
	use net::config;
	use net::poolmgr;
#[test]
fn test_lib() {
let mut cfg : config::Config = Default::default();
    //set port to 80
    cfg.port= Some(80);
    //set host to
    cfg.server = Some("google.com".to_string());
    let mut pool = poolmgr::ConnectionPool::new(2, 20, true, &cfg);
   //get the connection
    let mut conn = pool.aquire().unwrap();
    conn.writer.write_str("GET google.com\r\n").unwrap();
    conn.writer.flush().unwrap();
    let r = conn.reader.read_line();
    println!("Received {}", r);
    pool.release(conn);
}
}

<commit_msg>Update lib.rs<commit_after>//! A simple, connection pool library.
#![crate_name = "net-utils"]
#![crate_type = "lib"]
#![unstable]
#![warn(missing_docs)]
#![feature(slicing_syntax,phase)]
#[cfg(feature = "ssl")] extern crate openssl;
#[phase(plugin, link)]extern crate log;

pub mod net;

#[cfg(test)]
pub mod test {
	use std::default::Default;
	use net::config;
	use net::poolmgr;
#[test]
fn test_lib() {
let mut cfg : config::Config = Default::default();
    //set port to 80
    cfg.port= Some(80);
    //set host to
    cfg.server = Some("google.com".to_string());
    let mut pool = poolmgr::ConnectionPool::new(2, 20, true, &cfg);
   //get the connection
    let mut conn = pool.aquire().unwrap();
    conn.writer.write_str("GET google.com\r\n").unwrap();
    conn.writer.flush().unwrap();
    let r = conn.reader.read_line();
    println!("Received {}", r);
    pool.release(conn);
}
}

<|endoftext|>


<commit_before>//! Perlin is a free and open source information retrieval library.
//!
//! Version 0.0.0 (not released)
//!
//! # Basic Usage
//!
//! Add Perlin to Cargo.toml
//!
//! ```toml
//! [dependencies]
//! perlin = "0.0"
//! ```
//!
//! and import it in your crate root:
//!
//! ```rust
//! extern crate perlin;
//! ```
//!
//!
//! ## Build index and run queries
//! ```rust
//! use perlin::index::Index;
//! use perlin::storage::RamStorage;
//! use perlin::index::boolean_index::{QueryBuilder, IndexBuilder};
//!
//! //Use `IndexBuilder` to construct a new Index and add documents to it.
//! //Perlin does not dictate what "documents" are.
//! //Rather it expects iterators over iterators over terms.
//! //In our case a "document" consists of a series of `usize` values:
//! let index = IndexBuilder::<_, RamStorage<_>>::new().create(vec![(0..10),
//! (0..15), (10..34)].into_iter()).unwrap();
//!
//! //Now use the `QueryBuilder` to construct a query
//! //Simple query for the number 4
//! let simple_query = QueryBuilder::atom(4).build();
//!
//! //When executing queries the index does not evaluate return all results at once.
//! //Rather it runs lazily and returns an iterator over the resulting document ids.
//! for id in index.execute_query(&simple_query) {
//!    println!("{}", id); //Will print 0 and 1
//! }
//!
//! ```
//!
#![doc(html_root_url = "https://doc.perlin-ir.org/")]
#![deny(missing_docs, warnings)]

#[macro_use]
mod utils;
pub mod language;
pub mod index;
pub mod storage;
<commit_msg>Fix docs<commit_after>//! Perlin is a free and open source information retrieval library.
//!
//! Version 0.0.0 (not released)
//!
//! # Basic Usage
//!
//! Add Perlin to Cargo.toml
//!
//! ```toml
//! [dependencies]
//! perlin = "0.0"
//! ```
//!
//! and import it in your crate root:
//!
//! ```rust
//! extern crate perlin;
//! ```
//!
//!
//! ## Build index and run queries
//! ```rust
//! use perlin::index::Index;
//! use perlin::storage::RamStorage;
//! use perlin::index::boolean_index::{QueryBuilder, IndexBuilder};
//!
//! //Use `IndexBuilder` to construct a new Index and add documents to it.
//! //Perlin does not dictate what "documents" are.
//! //Rather it expects iterators over iterators over terms.
//! //In our case a "document" consists of a series of `usize` values:
//! let index = IndexBuilder::<_, RamStorage<_>>::new().create(vec![(0..10),
//! (0..15), (10..34)].into_iter()).unwrap();
//!
//! //Now use the `QueryBuilder` to construct a query
//! //Simple query for the number 4
//! let simple_query = QueryBuilder::atom(4).build();
//!
//! //When executing queries the index does not evaluate return all results at once.
//! //Rather it runs lazily and returns an iterator over the resulting document ids.
//! for id in index.execute_query(&simple_query) {
//!    println!("{}", id); //Will print 0 and 1
//! }
//!
//! ```
//!
#![deny(missing_docs, warnings)]

#[macro_use]
mod utils;
pub mod language;
pub mod index;
pub mod storage;
<|endoftext|>


<commit_before>// Copyright 2016 Doug Goldstein
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A simple logger to provide symantics similar to what is expected
//! of most UNIX utilities by logging to stderr and the higher the
//! verbosity the higher the log level. Additionally it supports the
//! ability to provide timestamps at different granularities.
//!
//! ## Simple Use Case
//!
//! ```rust
//! #[macro_use]
//! extern crate log;
//! extern crate stderrlog;
//!
//! fn main() {
//!     stderrlog::new().module(module_path!()).init().unwrap();
//!
//!     info!("starting up");
//!
//!     // ...
//! }
//! ```
//!
//! ## docopt Example
//!
//! ```rust
//! extern crate docopt;
//! #[macro_use]
//! extern crate log;
//! extern crate rustc_serialize;
//! extern crate stderrlog;
//!
//! use docopt::Docopt;
//!
//! const USAGE: &'static str = "
//! Usage: program [-q] [-v...]
//! ";
//!
//! #[derive(Debug, RustcDecodable)]
//! struct Args {
//!     flag_q: bool,
//!     flag_v: usize,
//! }
//!
//! fn main() {
//!     let args: Args = Docopt::new(USAGE)
//!                             .and_then(|d| d.decode())
//!                             .unwrap_or_else(|e| e.exit());
//!
//!     stderrlog::new()
//!             .module(module_path!())
//!             .quiet(args.flag_q)
//!             .timestamp(stderrlog::Timestamp::Second)
//!             .verbosity(args.flag_v)
//!             .init()
//!             .unwrap();
//!     trace!("trace message");
//!     debug!("debug message");
//!     info!("info message");
//!     warn!("warn message");
//!     error!("error message");
//!
//!     // ...
//! }
//! ```
//!
//! # clap Example
//!
//! ```
//! #[macro_use]
//! extern crate clap;
//! #[macro_use]
//! extern crate log;
//! extern crate stderrlog;
//!
//! use clap::{Arg, App};
//!
//! fn main() {
//!     let m = App::new("stderrlog example")
//!         .version(crate_version!())
//!         .arg(Arg::with_name("verbosity")
//!              .short("v")
//!              .multiple(true)
//!              .help("Increase message verbosity"))
//!         .arg(Arg::with_name("quiet")
//!              .short("q")
//!              .help("Silence all output"))
//!         .arg(Arg::with_name("timestamp")
//!              .short("t")
//!              .help("prepend log lines with a timestamp")
//!              .takes_value(true)
//!              .possible_values(&["none", "sec", "ms", "ns"]))
//!         .get_matches();
//!
//!     let verbose = m.occurrences_of("verbosity") as usize;
//!     let quiet = m.is_present("quiet");
//!     let ts = match m.value_of("timestamp") {
//!         Some("ns") => stderrlog::Timestamp::Nanosecond,
//!         Some("ms") => stderrlog::Timestamp::Microsecond,
//!         Some("sec") => stderrlog::Timestamp::Second,
//!         Some("none") | None => stderrlog::Timestamp::Off,
//!         Some(_) => clap::Error {
//!             message: "invalid value for 'timestamp'".into(),
//!             kind: clap::ErrorKind::InvalidValue,
//!             info: None,
//!         }.exit(),
//!     };
//!
//!     stderrlog::new()
//!         .module(module_path!())
//!         .quiet(quiet)
//!         .verbosity(verbose)
//!         .timestamp(ts)
//!         .init()
//!         .unwrap();
//!     trace!("trace message");
//!     debug!("debug message");
//!     info!("info message");
//!     warn!("warn message");
//!     error!("error message");
//! }
//! ```

extern crate chrono;
extern crate log;
extern crate termcolor;
extern crate thread_local;

use chrono::Local;
use log::{LogLevel, LogLevelFilter, LogMetadata};
use std::cell::RefCell;
use std::io::{self, Write};
use std::fmt;
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
use thread_local::CachedThreadLocal;

pub use termcolor::ColorChoice;

/// State of the timestampping in the logger.
#[derive(Clone, Copy, Debug)]
pub enum Timestamp {
    /// Disable timestamping of log messages
    Off,
    /// Timestamp with second granularity
    Second,
    /// Timestamp with microsecond granularity
    Microsecond,
    /// Timestamp with nanosecond granularity
    Nanosecond,
}

/// Data specific to this logger
pub struct StdErrLog {
    verbosity: LogLevelFilter,
    quiet: bool,
    timestamp: Timestamp,
    modules: Vec<String>,
    writer: CachedThreadLocal<RefCell<io::LineWriter<StandardStream>>>,
    color_choice: ColorChoice,
}

impl fmt::Debug for StdErrLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("StdErrLog")
            .field("verbosity", &self.verbosity)
            .field("quiet", &self.quiet)
            .field("timestamp", &self.timestamp)
            .field("modules", &self.modules)
            .field("writer", &"stderr")
            .field("color_choice", &self.color_choice)
            .finish()
    }
}

impl Clone for StdErrLog {
    fn clone(&self) -> StdErrLog {
        StdErrLog {
            modules: self.modules.clone(),
            writer: CachedThreadLocal::new(),
            .. *self
        }
    }
}

impl log::Log for StdErrLog {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.log_level_filter() && self.includes_module(metadata.target())
    }

    fn log(&self, record: &log::LogRecord) {

        // if logging isn't enabled for this level do a quick out
        if !self.enabled(record.metadata()) {
            return;
        }

        // this logger only logs the requested modules unless the
        // vector of modules is empty
        // modules will have module::file in the module_path
        let writer =
            self.writer.get_or(|| Box::new(RefCell::new(io::LineWriter::new(StandardStream::stderr(self.color_choice)))));
        let mut writer = writer.borrow_mut();
        let color = match record.metadata().level() {
            LogLevel::Error => Color::Red,
            LogLevel::Warn => Color::Magenta,
            LogLevel::Info => Color::Yellow,
            LogLevel::Debug => Color::Cyan,
            LogLevel::Trace => Color::Blue,
        };
        {
            writer.get_mut().set_color(ColorSpec::new().set_fg(Some(color))).expect("failed to set color");
        }
        match self.timestamp {
            Timestamp::Second => {
                let fmt = "%Y-%m-%dT%H:%M:%S%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Microsecond => {
                let fmt = "%Y-%m-%dT%H:%M:%S%.6f%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Nanosecond => {
                let fmt = "%Y-%m-%dT%H:%M:%S%.9f%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Off => {},
        }
        let _ = writeln!(writer, "{} - {}", record.level(), record.args());
        {
            writer.get_mut().reset().expect("failed to reset the color");
        }
    }
}

impl StdErrLog {
    /// creates a new stderr logger
    pub fn new() -> StdErrLog {
        StdErrLog {
            verbosity: LogLevelFilter::Error,
            quiet: false,
            timestamp: Timestamp::Off,
            modules: Vec::new(),
            writer: CachedThreadLocal::new(),
            color_choice: ColorChoice::Auto,
        }
    }

    /// Sets the verbosity level of messages that will be displayed
    pub fn verbosity(&mut self, verbosity: usize) -> &mut StdErrLog {
        let log_lvl = match verbosity {
            0 => LogLevelFilter::Error,
            1 => LogLevelFilter::Warn,
            2 => LogLevelFilter::Info,
            3 => LogLevelFilter::Debug,
            _ => LogLevelFilter::Trace,
        };

        self.verbosity = log_lvl;
        self
    }

    /// silence all output, no matter the value of verbosity
    pub fn quiet(&mut self, quiet: bool) -> &mut StdErrLog {
        self.quiet = quiet;
        self
    }

    /// Enables or disables the use of timestamps in log messages
    pub fn timestamp(&mut self, timestamp: Timestamp) -> &mut StdErrLog {
        self.timestamp = timestamp;
        self
    }

    /// Enables or disables the use of color in log messages
    pub fn color(&mut self, choice: ColorChoice) -> &mut StdErrLog {
        self.color_choice = choice;
        self
    }

    /// specify a module to allow to log to stderr
    pub fn module<T: Into<String>>(&mut self, module: T) -> &mut StdErrLog {
        let to_insert = module.into();
        // If Ok, the module was already found
        if let Err(i) = self.modules.binary_search(&to_insert) {
            // If a super-module of the current module already exists, don't insert this module
            if i == 0 || !is_submodule(&self.modules[i - 1], &to_insert) {
                // Remove any submodules of the module we're inserting
                let submodule_count = self.modules[i..]
                    .iter()
                    .take_while(|possible_submodule|
                        is_submodule(&to_insert, possible_submodule)
                    )
                    .count();
                self.modules.drain(i..i+submodule_count);
                self.modules.insert(i, to_insert);
            }
        }
        self
    }

    /// specifiy modules to allow to log to stderr
    pub fn modules<T: Into<String>, I: IntoIterator<Item = T>>(&mut self,
                                                               modules: I)
                                                               -> &mut StdErrLog {
        for module in modules {
            self.module(module);
        }
        self
    }

    fn log_level_filter(&self) -> LogLevelFilter {
        if self.quiet {
            LogLevelFilter::Off
        } else {
            self.verbosity
        }
    }

    fn includes_module(&self, module_path: &str) -> bool {
        // If modules is empty, include all module paths
        if self.modules.is_empty() {
            return true;
        }
        // if a prefix of module_path is in `self.modules`, it must
        // be located at the first location before
        // where module_path would be.
        match self.modules.binary_search_by(|module| module.as_str().cmp(&module_path)) {
            Ok(_) => {
                // Found exact module: return true
                true
            }
            Err(0) => {
                // if there's no item which would be located before module_path, no prefix is there
                false
            }
            Err(i) => {
                is_submodule(&self.modules[i - 1], module_path)
            }
        }
    }

    /// sets the the logger as active
    pub fn init(&self) -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
                            max_log_level.set(self.log_level_filter());

                            Box::new(self.clone())
                        })
    }
}

impl Default for StdErrLog {
    fn default() -> Self {
        StdErrLog::new()
    }
}

/// creates a new stderr logger
pub fn new() -> StdErrLog {
    StdErrLog::new()
}

fn is_submodule(parent: &str, possible_child: &str) -> bool {
    parent.split("::").zip(possible_child.split("::")).all(|(a, b)| a == b)
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_default_level() {
        extern crate log;

        super::new().module(module_path!()).init().unwrap();

        assert_eq!(log::LogLevel::Error, log::max_log_level())
    }
}
<commit_msg>Reduce monomorphization cost<commit_after>// Copyright 2016 Doug Goldstein
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A simple logger to provide symantics similar to what is expected
//! of most UNIX utilities by logging to stderr and the higher the
//! verbosity the higher the log level. Additionally it supports the
//! ability to provide timestamps at different granularities.
//!
//! ## Simple Use Case
//!
//! ```rust
//! #[macro_use]
//! extern crate log;
//! extern crate stderrlog;
//!
//! fn main() {
//!     stderrlog::new().module(module_path!()).init().unwrap();
//!
//!     info!("starting up");
//!
//!     // ...
//! }
//! ```
//!
//! ## docopt Example
//!
//! ```rust
//! extern crate docopt;
//! #[macro_use]
//! extern crate log;
//! extern crate rustc_serialize;
//! extern crate stderrlog;
//!
//! use docopt::Docopt;
//!
//! const USAGE: &'static str = "
//! Usage: program [-q] [-v...]
//! ";
//!
//! #[derive(Debug, RustcDecodable)]
//! struct Args {
//!     flag_q: bool,
//!     flag_v: usize,
//! }
//!
//! fn main() {
//!     let args: Args = Docopt::new(USAGE)
//!                             .and_then(|d| d.decode())
//!                             .unwrap_or_else(|e| e.exit());
//!
//!     stderrlog::new()
//!             .module(module_path!())
//!             .quiet(args.flag_q)
//!             .timestamp(stderrlog::Timestamp::Second)
//!             .verbosity(args.flag_v)
//!             .init()
//!             .unwrap();
//!     trace!("trace message");
//!     debug!("debug message");
//!     info!("info message");
//!     warn!("warn message");
//!     error!("error message");
//!
//!     // ...
//! }
//! ```
//!
//! # clap Example
//!
//! ```
//! #[macro_use]
//! extern crate clap;
//! #[macro_use]
//! extern crate log;
//! extern crate stderrlog;
//!
//! use clap::{Arg, App};
//!
//! fn main() {
//!     let m = App::new("stderrlog example")
//!         .version(crate_version!())
//!         .arg(Arg::with_name("verbosity")
//!              .short("v")
//!              .multiple(true)
//!              .help("Increase message verbosity"))
//!         .arg(Arg::with_name("quiet")
//!              .short("q")
//!              .help("Silence all output"))
//!         .arg(Arg::with_name("timestamp")
//!              .short("t")
//!              .help("prepend log lines with a timestamp")
//!              .takes_value(true)
//!              .possible_values(&["none", "sec", "ms", "ns"]))
//!         .get_matches();
//!
//!     let verbose = m.occurrences_of("verbosity") as usize;
//!     let quiet = m.is_present("quiet");
//!     let ts = match m.value_of("timestamp") {
//!         Some("ns") => stderrlog::Timestamp::Nanosecond,
//!         Some("ms") => stderrlog::Timestamp::Microsecond,
//!         Some("sec") => stderrlog::Timestamp::Second,
//!         Some("none") | None => stderrlog::Timestamp::Off,
//!         Some(_) => clap::Error {
//!             message: "invalid value for 'timestamp'".into(),
//!             kind: clap::ErrorKind::InvalidValue,
//!             info: None,
//!         }.exit(),
//!     };
//!
//!     stderrlog::new()
//!         .module(module_path!())
//!         .quiet(quiet)
//!         .verbosity(verbose)
//!         .timestamp(ts)
//!         .init()
//!         .unwrap();
//!     trace!("trace message");
//!     debug!("debug message");
//!     info!("info message");
//!     warn!("warn message");
//!     error!("error message");
//! }
//! ```

extern crate chrono;
extern crate log;
extern crate termcolor;
extern crate thread_local;

use chrono::Local;
use log::{LogLevel, LogLevelFilter, LogMetadata};
use std::cell::RefCell;
use std::io::{self, Write};
use std::fmt;
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
use thread_local::CachedThreadLocal;

pub use termcolor::ColorChoice;

/// State of the timestampping in the logger.
#[derive(Clone, Copy, Debug)]
pub enum Timestamp {
    /// Disable timestamping of log messages
    Off,
    /// Timestamp with second granularity
    Second,
    /// Timestamp with microsecond granularity
    Microsecond,
    /// Timestamp with nanosecond granularity
    Nanosecond,
}

/// Data specific to this logger
pub struct StdErrLog {
    verbosity: LogLevelFilter,
    quiet: bool,
    timestamp: Timestamp,
    modules: Vec<String>,
    writer: CachedThreadLocal<RefCell<io::LineWriter<StandardStream>>>,
    color_choice: ColorChoice,
}

impl fmt::Debug for StdErrLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("StdErrLog")
            .field("verbosity", &self.verbosity)
            .field("quiet", &self.quiet)
            .field("timestamp", &self.timestamp)
            .field("modules", &self.modules)
            .field("writer", &"stderr")
            .field("color_choice", &self.color_choice)
            .finish()
    }
}

impl Clone for StdErrLog {
    fn clone(&self) -> StdErrLog {
        StdErrLog {
            modules: self.modules.clone(),
            writer: CachedThreadLocal::new(),
            .. *self
        }
    }
}

impl log::Log for StdErrLog {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.log_level_filter() && self.includes_module(metadata.target())
    }

    fn log(&self, record: &log::LogRecord) {

        // if logging isn't enabled for this level do a quick out
        if !self.enabled(record.metadata()) {
            return;
        }

        let writer =
            self.writer.get_or(|| Box::new(RefCell::new(io::LineWriter::new(StandardStream::stderr(self.color_choice)))));
        let mut writer = writer.borrow_mut();
        let color = match record.metadata().level() {
            LogLevel::Error => Color::Red,
            LogLevel::Warn => Color::Magenta,
            LogLevel::Info => Color::Yellow,
            LogLevel::Debug => Color::Cyan,
            LogLevel::Trace => Color::Blue,
        };
        {
            writer.get_mut().set_color(ColorSpec::new().set_fg(Some(color))).expect("failed to set color");
        }
        match self.timestamp {
            Timestamp::Second => {
                let fmt = "%Y-%m-%dT%H:%M:%S%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Microsecond => {
                let fmt = "%Y-%m-%dT%H:%M:%S%.6f%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Nanosecond => {
                let fmt = "%Y-%m-%dT%H:%M:%S%.9f%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Off => {},
        }
        let _ = writeln!(writer, "{} - {}", record.level(), record.args());
        {
            writer.get_mut().reset().expect("failed to reset the color");
        }
    }
}

impl StdErrLog {
    /// creates a new stderr logger
    pub fn new() -> StdErrLog {
        StdErrLog {
            verbosity: LogLevelFilter::Error,
            quiet: false,
            timestamp: Timestamp::Off,
            modules: Vec::new(),
            writer: CachedThreadLocal::new(),
            color_choice: ColorChoice::Auto,
        }
    }

    /// Sets the verbosity level of messages that will be displayed
    pub fn verbosity(&mut self, verbosity: usize) -> &mut StdErrLog {
        let log_lvl = match verbosity {
            0 => LogLevelFilter::Error,
            1 => LogLevelFilter::Warn,
            2 => LogLevelFilter::Info,
            3 => LogLevelFilter::Debug,
            _ => LogLevelFilter::Trace,
        };

        self.verbosity = log_lvl;
        self
    }

    /// silence all output, no matter the value of verbosity
    pub fn quiet(&mut self, quiet: bool) -> &mut StdErrLog {
        self.quiet = quiet;
        self
    }

    /// Enables or disables the use of timestamps in log messages
    pub fn timestamp(&mut self, timestamp: Timestamp) -> &mut StdErrLog {
        self.timestamp = timestamp;
        self
    }

    /// Enables or disables the use of color in log messages
    pub fn color(&mut self, choice: ColorChoice) -> &mut StdErrLog {
        self.color_choice = choice;
        self
    }

    /// specify a module to allow to log to stderr
    pub fn module<T: Into<String>>(&mut self, module: T) -> &mut StdErrLog {
        self._module(module.into())
    }

    fn _module(&mut self, module: String) -> &mut StdErrLog {
        // If Ok, the module was already found
        if let Err(i) = self.modules.binary_search(&module) {
            // If a super-module of the current module already exists, don't insert this module
            if i == 0 || !is_submodule(&self.modules[i - 1], &module) {
                // Remove any submodules of the module we're inserting
                let submodule_count = self.modules[i..]
                    .iter()
                    .take_while(|possible_submodule|
                        is_submodule(&module, possible_submodule)
                    )
                    .count();
                self.modules.drain(i..i+submodule_count);
                self.modules.insert(i, module);
            }
        }
        self
    }

    /// specifiy modules to allow to log to stderr
    pub fn modules<T: Into<String>, I: IntoIterator<Item = T>>(&mut self,
                                                               modules: I)
                                                               -> &mut StdErrLog {
        for module in modules {
            self.module(module);
        }
        self
    }

    fn log_level_filter(&self) -> LogLevelFilter {
        if self.quiet {
            LogLevelFilter::Off
        } else {
            self.verbosity
        }
    }

    fn includes_module(&self, module_path: &str) -> bool {
        // If modules is empty, include all module paths
        if self.modules.is_empty() {
            return true;
        }
        // if a prefix of module_path is in `self.modules`, it must
        // be located at the first location before
        // where module_path would be.
        match self.modules.binary_search_by(|module| module.as_str().cmp(&module_path)) {
            Ok(_) => {
                // Found exact module: return true
                true
            }
            Err(0) => {
                // if there's no item which would be located before module_path, no prefix is there
                false
            }
            Err(i) => {
                is_submodule(&self.modules[i - 1], module_path)
            }
        }
    }

    /// sets the the logger as active
    pub fn init(&self) -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
                            max_log_level.set(self.log_level_filter());

                            Box::new(self.clone())
                        })
    }
}

impl Default for StdErrLog {
    fn default() -> Self {
        StdErrLog::new()
    }
}

/// creates a new stderr logger
pub fn new() -> StdErrLog {
    StdErrLog::new()
}

fn is_submodule(parent: &str, possible_child: &str) -> bool {
    parent.split("::").zip(possible_child.split("::")).all(|(a, b)| a == b)
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_default_level() {
        extern crate log;

        super::new().module(module_path!()).init().unwrap();

        assert_eq!(log::LogLevel::Error, log::max_log_level())
    }
}
<|endoftext|>


<commit_before>#![feature(slicing_syntax)]

extern crate lzf;
use std::str;
use lzf::decompress;
use std::io::MemReader;
use std::io;
use formatter::RdbParseFormatter;

pub use plain_formatter::PlainFormatter;

mod helper;

pub mod formatter;
pub mod plain_formatter;

mod version {
    pub const SUPPORTED_MINIMUM : u32 = 1;
    pub const SUPPORTED_MAXIMUM : u32 = 7;
}

mod constants {
    pub const RDB_6BITLEN : u8 = 0;
    pub const RDB_14BITLEN : u8 = 1;
    pub const RDB_ENCVAL : u8 = 3;
    pub const RDB_MAGIC : &'static str = "REDIS";
}

mod op_codes {
    pub const AUX : u8 = 250;
    pub const RESIZEDB : u8 = 251;
    pub const EXPIRETIME_MS : u8 = 252;
    pub const EXPIRETIME : u8 = 253;
    pub const SELECTDB   : u8 = 254;
    pub const EOF : u8 = 255;
}

mod types {
    pub const STRING : u8 = 0;
    pub const LIST : u8 = 1;
    pub const SET : u8 = 2;
    pub const ZSET : u8 = 3;
    pub const HASH : u8 = 4;
    pub const HASH_ZIPMAP : u8 = 9;
    pub const LIST_ZIPLIST : u8 = 10;
    pub const SET_INTSET : u8 = 11;
    pub const ZSET_ZIPLIST : u8 = 12;
    pub const HASH_ZIPLIST : u8 = 13;
    pub const LIST_QUICKLIST : u8 = 14;
}

mod encoding {
    pub const INT8 : u32 = 0;
    pub const INT16 : u32 = 1;
    pub const INT32 : u32 = 2;
    pub const LZF : u32 = 3;
}

#[derive(Show)]
pub enum DataType {
    String(Vec<u8>),
    Number(i64),
    ListOfTypes(Vec<DataType>),
    HashOfTypes(Vec<DataType>),
    SortedSetOfTypes(Vec<DataType>),
    Intset(Vec<i64>),
    List(Vec<Vec<u8>>),
    Set(Vec<Vec<u8>>),
    SortedSet(Vec<(f64,Vec<u8>)>),
    Hash(Vec<Vec<u8>>),
    Unknown
}

pub fn parse<R: Reader, F: RdbParseFormatter>(input: &mut R, formatter: &mut F) {
    assert!(verify_magic(input));
    assert!(verify_version(input));

    formatter.start_rdb();

    let mut out = io::stdout();
    let mut last_database : u32 = 0;
    loop {
        let next_op = input.read_byte().unwrap();

        match next_op {
            op_codes::SELECTDB => {
                last_database = read_length(input);
                formatter.start_database(last_database);
            },
            op_codes::EOF => {
                formatter.end_database(last_database);
                formatter.end_rdb();

                let checksum = input.read_to_end().unwrap();
                formatter.checksum(checksum);
                break ;
            },
            op_codes::EXPIRETIME_MS => {
                let expiretime_ms = input.read_le_u64().unwrap();
                println!("EXPIRETIME_MS: {}", expiretime_ms);
            },
            op_codes::EXPIRETIME => {
                let expiretime = input.read_be_u32().unwrap();
                println!("EXPIRETIME: {}", expiretime);
            },
            op_codes::RESIZEDB => {
                let db_size = read_length(input);
                let expires_size = read_length(input);
                println!("DB Size: {}, Expires Size: {}",
                         db_size, expires_size);
            },
            op_codes::AUX => {
                let auxkey = read_blob(input);
                let auxval = read_blob(input);

                formatter.aux_field(auxkey, auxval);
            },
            _ => {
                let key = read_blob(input);

                match read_type(next_op, input) {
                    DataType::String(t) => {
                        formatter.set(key, t);
                    },
                    DataType::Number(t) => { println!("{}", t) },
                    DataType::ListOfTypes(t) => { println!("{}", t) },
                    DataType::Intset(t) => { println!("{}", t) },
                    DataType::Hash(t) => {
                        for val in t.iter() {
                            let _ = out.write(val[]);
                            let _ = out.write_str(", ");
                        }
                        let _ = out.write_str("\n");
                    },
                    DataType::HashOfTypes(t) => { println!("{}", t) },
                    _ => {}
                }
            }
        }

    }

    return;

}

pub fn verify_magic<R: Reader>(input: &mut R) -> bool {
    let magic = input.read_exact(5).unwrap();

    // Meeeeeh.
    magic[0] == constants::RDB_MAGIC.as_bytes()[0] &&
        magic[1] == constants::RDB_MAGIC.as_bytes()[1] &&
        magic[2] == constants::RDB_MAGIC.as_bytes()[2] &&
        magic[3] == constants::RDB_MAGIC.as_bytes()[3] &&
        magic[4] == constants::RDB_MAGIC.as_bytes()[4]
}

pub fn verify_version<R: Reader>(input: &mut R) -> bool {
    let version = input.read_exact(4).unwrap();

    let version = (version[0]-48) as u32 * 1000 +
        (version[1]-48) as u32 * 100 +
        (version[2]-48) as u32 * 10 +
        (version[3]-48) as u32;

    version >= version::SUPPORTED_MINIMUM &&
        version <= version::SUPPORTED_MAXIMUM
}

fn read_linked_list<R: Reader>(input: &mut R) -> Vec<Vec<u8>> {
    let mut len = read_length(input);
    let mut list = vec![];

    while len > 0 {
        let blob = read_blob(input);
        list.push(blob);
        len -= 1;
    }

    list
}

fn read_sorted_set<R: Reader>(input: &mut R) -> Vec<(f64,Vec<u8>)> {
    let mut set = vec![];
    let mut set_items = read_length(input);

    while set_items > 0 {
        let val = read_blob(input);
        let score_length = input.read_byte().unwrap();
        let score = input.read_exact(score_length as uint).unwrap();
        let score = unsafe{str::from_utf8_unchecked(score[])}.
            parse::<f64>().unwrap();

        set.push((score, val));

        set_items -= 1;
    }

    set
}

fn read_hash<R: Reader>(input: &mut R) -> Vec<Vec<u8>> {
    let mut hash = vec![];
    let mut hash_items = read_length(input);
    hash_items = 2*hash_items;

    while hash_items > 0 {
        let val = read_blob(input);
        hash.push(val);

        hash_items -= 1;
    }

    hash
}

fn read_ziplist_entry<R: Reader>(input: &mut R) -> DataType {
    // 1. 1 or 5 bytes length of previous entry
    match input.read_byte().unwrap() {
        254 => {
            let _ = input.read_exact(4).unwrap();
        },
        _ => {}
    }

    let mut length : u64;
    let mut number_value : i64;

    // 2. Read flag or number value
    let flag = input.read_byte().unwrap();

    match (flag & 0xC0) >> 6 {
        0 => { length = (flag & 0x3F) as u64 },
        1 => {
            let next_byte = input.read_byte().unwrap();
            length = (((flag & 0x3F) as u64) << 8) | next_byte as u64;
        },
        2 => {
            length = input.read_be_u32().unwrap() as u64;
        },
        _ => {
            match (flag & 0xF0) >> 4 {
                0xC => { number_value = input.read_le_i16().unwrap() as i64 },
                0xD => { number_value = input.read_le_i32().unwrap() as i64 },
                0xE => { number_value = input.read_le_i64().unwrap() as i64 },
                0xF => {
                    match flag & 0xF {
                        0 => {
                            let bytes = input.read_exact(3).unwrap();
                            number_value = ((bytes[0] as i64) << 16) ^
                                ((bytes[1] as i64) << 8) ^
                                (bytes[2] as i64);
                        },
                        0xE => {
                            number_value = input.read_byte().unwrap() as i64 },
                            _ => { number_value = (flag & 0xF) as i64 - 1; }
                    }
                },
                _ => {
                    println!("Flag not handled: {}", flag);
                    return DataType::Number(-42);
                }

            }

            return DataType::Number(number_value)
        }
    }

    // 3. Read value
    let rawval = input.read_exact(length as uint).unwrap();
    DataType::String(rawval)
}

fn read_list_ziplist<R: Reader>(input: &mut R) -> Vec<DataType> {
    let ziplist = read_blob(input);

    let mut reader = MemReader::new(ziplist);

    let _zlbytes = reader.read_le_u32().unwrap();
    let _zltail = reader.read_le_u32().unwrap();
    let zllen = reader.read_le_u16().unwrap();
    let mut list = Vec::with_capacity(zllen as uint);

    for _ in range(0, zllen) {
        list.push(read_ziplist_entry(&mut reader));
    }

    assert!(reader.read_byte().unwrap() == 0xFF);

    list
}

fn read_zipmap_entry<R: Reader>(next_byte: u8, input: &mut R) -> Vec<u8> {
    let mut elem_len;
    match next_byte {
        253 => { elem_len = input.read_le_u32().unwrap() },
        254 | 255 => {
            panic!("Invalid length value in zipmap: {}", next_byte)
        },
        _ => { elem_len = next_byte as u32 }
    }

    input.read_exact(elem_len as uint).unwrap()
}

fn read_hash_zipmap<R: Reader>(input: &mut R) -> Vec<Vec<u8>> {
    let zipmap = read_blob(input);

    let mut reader = MemReader::new(zipmap);

    let zmlen = reader.read_byte().unwrap();

    let mut length;
    let mut hash;
    if zmlen <= 254 {
        length = zmlen as uint;
        hash = Vec::with_capacity(length);
    } else {
        length = -1;
        hash = Vec::with_capacity(255);
    }

    loop {
        let next_byte = reader.read_byte().unwrap();

        if next_byte == 0xFF {
            break; // End of list.
        }

        let key = read_zipmap_entry(next_byte, &mut reader);
        hash.push(key);

        let next_byte = reader.read_byte().unwrap();
        let _free = reader.read_byte().unwrap();
        let value = read_zipmap_entry(next_byte, &mut reader);
        hash.push(value);

        if length > 0 {
            length -= 1;
        }

        if length == 0 {
            assert!(reader.read_byte().unwrap() == 0xFF);
            break;
        }
    }

    hash
}

fn read_set_intset<R: Reader>(input: &mut R) -> Vec<i64> {
    let mut set = vec![];

    let intset = read_blob(input);

    let mut reader = MemReader::new(intset);
    let byte_size = reader.read_le_u32().unwrap();
    let intset_length = reader.read_le_u32().unwrap();

    for _ in range(0, intset_length) {
        let val = match byte_size {
            2 => reader.read_le_i16().unwrap() as i64,
            4 => reader.read_le_i32().unwrap() as i64,
            8 => reader.read_le_i64().unwrap(),
            _ => panic!("unhandled byte size in intset: {}", byte_size)
        };

        set.push(val);
    }

    set
}

fn read_type<R: Reader>(value_type: u8, input: &mut R) -> DataType {
    match value_type {
        types::STRING => {
            DataType::String(read_blob(input))
        },
        types::LIST => {
            DataType::List(read_linked_list(input))
        },
        types::SET => {
            DataType::Set(read_linked_list(input))
        },
        types::ZSET => {
            DataType::SortedSet(read_sorted_set(input))
        },
        types::HASH => {
            DataType::Hash(read_hash(input))
        },
        types::HASH_ZIPMAP => {
            DataType::Hash(read_hash_zipmap(input))
        },
        types::LIST_ZIPLIST => {
            DataType::ListOfTypes(read_list_ziplist(input))
        },
        types::SET_INTSET => {
            DataType::Intset(read_set_intset(input))
        },
        types::ZSET_ZIPLIST => {
            DataType::SortedSetOfTypes(read_list_ziplist(input))
        },
        types::HASH_ZIPLIST => {
            DataType::ListOfTypes(read_list_ziplist(input))
        },
        types::LIST_QUICKLIST => {
            panic!("Quicklist not yet implemented");
        },
        _ => { panic!("Value Type not implemented: {}", value_type) }
    }
}

pub fn read_length_with_encoding<R: Reader>(input: &mut R) -> (u32, bool) {
    let mut length;
    let mut is_encoded = false;

    let enc_type = input.read_byte().unwrap();

    match (enc_type & 0xC0) >> 6 {
        constants::RDB_ENCVAL => {
            is_encoded = true;
            length = (enc_type & 0x3F) as u32;
        },
        constants::RDB_6BITLEN => {
            length = (enc_type & 0x3F) as u32;
        },
        constants::RDB_14BITLEN => {
            let next_byte = input.read_byte().unwrap();
            length = (((enc_type & 0x3F) as u32) <<8) | next_byte as u32;
        },
        _ => {
            length = input.read_be_u32().unwrap();
        }
    }

    (length, is_encoded)
}

pub fn read_length<R: Reader>(input: &mut R) -> u32 {
    let (length, _) = read_length_with_encoding(input);
    length
}

pub fn read_blob<R: Reader>(input: &mut R) -> Vec<u8> {
    let (length, is_encoded) = read_length_with_encoding(input);

    if is_encoded {
        match length {
            encoding::INT8 => { helper::int_to_vec(input.read_i8().unwrap() as i32) },
            encoding::INT16 => { helper::int_to_vec(input.read_le_i16().unwrap() as i32) },
            encoding::INT32 => { helper::int_to_vec(input.read_le_i32().unwrap() as i32) },
            encoding::LZF => {
                let compressed_length = read_length(input);
                let real_length = read_length(input);
                let data = input.read_exact(compressed_length as uint).unwrap();
                lzf::decompress(data.as_slice(), real_length as uint).unwrap()
            },
            _ => { panic!("Unknown encoding: {}", length) }
        }
    } else {
        input.read_exact(length as uint).unwrap()
    }
}
<commit_msg>Parse quicklist correctly<commit_after>#![feature(slicing_syntax)]

extern crate lzf;
use std::str;
use lzf::decompress;
use std::io::MemReader;
use std::io;
use formatter::RdbParseFormatter;

pub use plain_formatter::PlainFormatter;

mod helper;

pub mod formatter;
pub mod plain_formatter;

mod version {
    pub const SUPPORTED_MINIMUM : u32 = 1;
    pub const SUPPORTED_MAXIMUM : u32 = 7;
}

mod constants {
    pub const RDB_6BITLEN : u8 = 0;
    pub const RDB_14BITLEN : u8 = 1;
    pub const RDB_ENCVAL : u8 = 3;
    pub const RDB_MAGIC : &'static str = "REDIS";
}

mod op_codes {
    pub const AUX : u8 = 250;
    pub const RESIZEDB : u8 = 251;
    pub const EXPIRETIME_MS : u8 = 252;
    pub const EXPIRETIME : u8 = 253;
    pub const SELECTDB   : u8 = 254;
    pub const EOF : u8 = 255;
}

mod types {
    pub const STRING : u8 = 0;
    pub const LIST : u8 = 1;
    pub const SET : u8 = 2;
    pub const ZSET : u8 = 3;
    pub const HASH : u8 = 4;
    pub const HASH_ZIPMAP : u8 = 9;
    pub const LIST_ZIPLIST : u8 = 10;
    pub const SET_INTSET : u8 = 11;
    pub const ZSET_ZIPLIST : u8 = 12;
    pub const HASH_ZIPLIST : u8 = 13;
    pub const LIST_QUICKLIST : u8 = 14;
}

mod encoding {
    pub const INT8 : u32 = 0;
    pub const INT16 : u32 = 1;
    pub const INT32 : u32 = 2;
    pub const LZF : u32 = 3;
}

#[derive(Show,Clone)]
pub enum DataType {
    String(Vec<u8>),
    Number(i64),
    ListOfTypes(Vec<DataType>),
    HashOfTypes(Vec<DataType>),
    SortedSetOfTypes(Vec<DataType>),
    Intset(Vec<i64>),
    List(Vec<Vec<u8>>),
    Set(Vec<Vec<u8>>),
    SortedSet(Vec<(f64,Vec<u8>)>),
    Hash(Vec<Vec<u8>>),
    Unknown
}

pub fn parse<R: Reader, F: RdbParseFormatter>(input: &mut R, formatter: &mut F) {
    assert!(verify_magic(input));
    assert!(verify_version(input));

    formatter.start_rdb();

    let mut out = io::stdout();
    let mut last_database : u32 = 0;
    loop {
        let next_op = input.read_byte().unwrap();

        match next_op {
            op_codes::SELECTDB => {
                last_database = read_length(input);
                formatter.start_database(last_database);
            },
            op_codes::EOF => {
                formatter.end_database(last_database);
                formatter.end_rdb();

                let checksum = input.read_to_end().unwrap();
                formatter.checksum(checksum);
                break ;
            },
            op_codes::EXPIRETIME_MS => {
                let expiretime_ms = input.read_le_u64().unwrap();
                println!("EXPIRETIME_MS: {}", expiretime_ms);
            },
            op_codes::EXPIRETIME => {
                let expiretime = input.read_be_u32().unwrap();
                println!("EXPIRETIME: {}", expiretime);
            },
            op_codes::RESIZEDB => {
                let db_size = read_length(input);
                let expires_size = read_length(input);
                println!("DB Size: {}, Expires Size: {}",
                         db_size, expires_size);
            },
            op_codes::AUX => {
                let auxkey = read_blob(input);
                let auxval = read_blob(input);

                formatter.aux_field(auxkey, auxval);
            },
            _ => {
                let key = read_blob(input);

                match read_type(next_op, input) {
                    DataType::String(t) => {
                        formatter.set(key, t);
                    },
                    DataType::Number(t) => { println!("{}", t) },
                    DataType::ListOfTypes(t) => { println!("{}", t) },
                    DataType::Intset(t) => { println!("{}", t) },
                    DataType::Hash(t) => {
                        for val in t.iter() {
                            let _ = out.write(val[]);
                            let _ = out.write_str(", ");
                        }
                        let _ = out.write_str("\n");
                    },
                    DataType::HashOfTypes(t) => { println!("{}", t) },
                    _ => {}
                }
            }
        }

    }

    return;

}

pub fn verify_magic<R: Reader>(input: &mut R) -> bool {
    let magic = input.read_exact(5).unwrap();

    // Meeeeeh.
    magic[0] == constants::RDB_MAGIC.as_bytes()[0] &&
        magic[1] == constants::RDB_MAGIC.as_bytes()[1] &&
        magic[2] == constants::RDB_MAGIC.as_bytes()[2] &&
        magic[3] == constants::RDB_MAGIC.as_bytes()[3] &&
        magic[4] == constants::RDB_MAGIC.as_bytes()[4]
}

pub fn verify_version<R: Reader>(input: &mut R) -> bool {
    let version = input.read_exact(4).unwrap();

    let version = (version[0]-48) as u32 * 1000 +
        (version[1]-48) as u32 * 100 +
        (version[2]-48) as u32 * 10 +
        (version[3]-48) as u32;

    version >= version::SUPPORTED_MINIMUM &&
        version <= version::SUPPORTED_MAXIMUM
}

fn read_linked_list<R: Reader>(input: &mut R) -> Vec<Vec<u8>> {
    let mut len = read_length(input);
    let mut list = vec![];

    while len > 0 {
        let blob = read_blob(input);
        list.push(blob);
        len -= 1;
    }

    list
}

fn read_sorted_set<R: Reader>(input: &mut R) -> Vec<(f64,Vec<u8>)> {
    let mut set = vec![];
    let mut set_items = read_length(input);

    while set_items > 0 {
        let val = read_blob(input);
        let score_length = input.read_byte().unwrap();
        let score = input.read_exact(score_length as uint).unwrap();
        let score = unsafe{str::from_utf8_unchecked(score[])}.
            parse::<f64>().unwrap();

        set.push((score, val));

        set_items -= 1;
    }

    set
}

fn read_hash<R: Reader>(input: &mut R) -> Vec<Vec<u8>> {
    let mut hash = vec![];
    let mut hash_items = read_length(input);
    hash_items = 2*hash_items;

    while hash_items > 0 {
        let val = read_blob(input);
        hash.push(val);

        hash_items -= 1;
    }

    hash
}

fn read_ziplist_entry<R: Reader>(input: &mut R) -> DataType {
    // 1. 1 or 5 bytes length of previous entry
    match input.read_byte().unwrap() {
        254 => {
            let _ = input.read_exact(4).unwrap();
        },
        _ => {}
    }

    let mut length : u64;
    let mut number_value : i64;

    // 2. Read flag or number value
    let flag = input.read_byte().unwrap();

    match (flag & 0xC0) >> 6 {
        0 => { length = (flag & 0x3F) as u64 },
        1 => {
            let next_byte = input.read_byte().unwrap();
            length = (((flag & 0x3F) as u64) << 8) | next_byte as u64;
        },
        2 => {
            length = input.read_be_u32().unwrap() as u64;
        },
        _ => {
            match (flag & 0xF0) >> 4 {
                0xC => { number_value = input.read_le_i16().unwrap() as i64 },
                0xD => { number_value = input.read_le_i32().unwrap() as i64 },
                0xE => { number_value = input.read_le_i64().unwrap() as i64 },
                0xF => {
                    match flag & 0xF {
                        0 => {
                            let bytes = input.read_exact(3).unwrap();
                            number_value = ((bytes[0] as i64) << 16) ^
                                ((bytes[1] as i64) << 8) ^
                                (bytes[2] as i64);
                        },
                        0xE => {
                            number_value = input.read_byte().unwrap() as i64 },
                            _ => { number_value = (flag & 0xF) as i64 - 1; }
                    }
                },
                _ => {
                    println!("Flag not handled: {}", flag);
                    return DataType::Number(-42);
                }

            }

            return DataType::Number(number_value)
        }
    }

    // 3. Read value
    let rawval = input.read_exact(length as uint).unwrap();
    DataType::String(rawval)
}

fn read_list_ziplist<R: Reader>(input: &mut R) -> Vec<DataType> {
    let ziplist = read_blob(input);

    let mut reader = MemReader::new(ziplist);

    let _zlbytes = reader.read_le_u32().unwrap();
    let _zltail = reader.read_le_u32().unwrap();
    let zllen = reader.read_le_u16().unwrap();
    let mut list = Vec::with_capacity(zllen as uint);

    for _ in range(0, zllen) {
        list.push(read_ziplist_entry(&mut reader));
    }

    assert!(reader.read_byte().unwrap() == 0xFF);

    list
}

fn read_zipmap_entry<R: Reader>(next_byte: u8, input: &mut R) -> Vec<u8> {
    let mut elem_len;
    match next_byte {
        253 => { elem_len = input.read_le_u32().unwrap() },
        254 | 255 => {
            panic!("Invalid length value in zipmap: {}", next_byte)
        },
        _ => { elem_len = next_byte as u32 }
    }

    input.read_exact(elem_len as uint).unwrap()
}

fn read_hash_zipmap<R: Reader>(input: &mut R) -> Vec<Vec<u8>> {
    let zipmap = read_blob(input);

    let mut reader = MemReader::new(zipmap);

    let zmlen = reader.read_byte().unwrap();

    let mut length;
    let mut hash;
    if zmlen <= 254 {
        length = zmlen as uint;
        hash = Vec::with_capacity(length);
    } else {
        length = -1;
        hash = Vec::with_capacity(255);
    }

    loop {
        let next_byte = reader.read_byte().unwrap();

        if next_byte == 0xFF {
            break; // End of list.
        }

        let key = read_zipmap_entry(next_byte, &mut reader);
        hash.push(key);

        let next_byte = reader.read_byte().unwrap();
        let _free = reader.read_byte().unwrap();
        let value = read_zipmap_entry(next_byte, &mut reader);
        hash.push(value);

        if length > 0 {
            length -= 1;
        }

        if length == 0 {
            assert!(reader.read_byte().unwrap() == 0xFF);
            break;
        }
    }

    hash
}

fn read_set_intset<R: Reader>(input: &mut R) -> Vec<i64> {
    let mut set = vec![];

    let intset = read_blob(input);

    let mut reader = MemReader::new(intset);
    let byte_size = reader.read_le_u32().unwrap();
    let intset_length = reader.read_le_u32().unwrap();

    for _ in range(0, intset_length) {
        let val = match byte_size {
            2 => reader.read_le_i16().unwrap() as i64,
            4 => reader.read_le_i32().unwrap() as i64,
            8 => reader.read_le_i64().unwrap(),
            _ => panic!("unhandled byte size in intset: {}", byte_size)
        };

        set.push(val);
    }

    set
}

fn read_quicklist<R: Reader>(input: &mut R) -> Vec<DataType> {
    let len = read_length(input);

    let mut list = vec![];
    for _ in range(0, len) {
        let zl = read_list_ziplist(input);
        list.push_all(zl.as_slice());
    }
    list
}

fn read_type<R: Reader>(value_type: u8, input: &mut R) -> DataType {
    match value_type {
        types::STRING => {
            DataType::String(read_blob(input))
        },
        types::LIST => {
            DataType::List(read_linked_list(input))
        },
        types::SET => {
            DataType::Set(read_linked_list(input))
        },
        types::ZSET => {
            DataType::SortedSet(read_sorted_set(input))
        },
        types::HASH => {
            DataType::Hash(read_hash(input))
        },
        types::HASH_ZIPMAP => {
            DataType::Hash(read_hash_zipmap(input))
        },
        types::LIST_ZIPLIST => {
            DataType::ListOfTypes(read_list_ziplist(input))
        },
        types::SET_INTSET => {
            DataType::Intset(read_set_intset(input))
        },
        types::ZSET_ZIPLIST => {
            DataType::SortedSetOfTypes(read_list_ziplist(input))
        },
        types::HASH_ZIPLIST => {
            DataType::ListOfTypes(read_list_ziplist(input))
        },
        types::LIST_QUICKLIST => {
            DataType::ListOfTypes(read_quicklist(input))
        },
        _ => { panic!("Value Type not implemented: {}", value_type) }
    }
}

pub fn read_length_with_encoding<R: Reader>(input: &mut R) -> (u32, bool) {
    let mut length;
    let mut is_encoded = false;

    let enc_type = input.read_byte().unwrap();

    match (enc_type & 0xC0) >> 6 {
        constants::RDB_ENCVAL => {
            is_encoded = true;
            length = (enc_type & 0x3F) as u32;
        },
        constants::RDB_6BITLEN => {
            length = (enc_type & 0x3F) as u32;
        },
        constants::RDB_14BITLEN => {
            let next_byte = input.read_byte().unwrap();
            length = (((enc_type & 0x3F) as u32) <<8) | next_byte as u32;
        },
        _ => {
            length = input.read_be_u32().unwrap();
        }
    }

    (length, is_encoded)
}

pub fn read_length<R: Reader>(input: &mut R) -> u32 {
    let (length, _) = read_length_with_encoding(input);
    length
}

pub fn read_blob<R: Reader>(input: &mut R) -> Vec<u8> {
    let (length, is_encoded) = read_length_with_encoding(input);

    if is_encoded {
        match length {
            encoding::INT8 => { helper::int_to_vec(input.read_i8().unwrap() as i32) },
            encoding::INT16 => { helper::int_to_vec(input.read_le_i16().unwrap() as i32) },
            encoding::INT32 => { helper::int_to_vec(input.read_le_i32().unwrap() as i32) },
            encoding::LZF => {
                let compressed_length = read_length(input);
                let real_length = read_length(input);
                let data = input.read_exact(compressed_length as uint).unwrap();
                lzf::decompress(data.as_slice(), real_length as uint).unwrap()
            },
            _ => { panic!("Unknown encoding: {}", length) }
        }
    } else {
        input.read_exact(length as uint).unwrap()
    }
}
<|endoftext|>


<commit_before>//! File locking via POSIX advisory record locks.
//!
//! This crate provides the facility to lock and unlock a file following the
//! advisory record lock scheme as specified by UNIX IEEE Std 1003.1-2001
//! (POSIX.1) via `fcntl()`.
//!
//! # Examples
//!
//! ```
//! extern crate file_lock;
//! extern crate tempfile;
//!
//! use file_lock::*;
//! use std::os::unix::io::AsRawFd;
//!
//! fn main() {
//!     let f = tempfile::TempFile::new().unwrap();
//!
//!     match Lock::new(f.as_raw_fd()).lock(LockKind::NonBlocking) {
//!         Ok(_)  => println!("Got lock"),
//!         Err(Error::Errno(i))
//!               => println!("Got filesystem error {}", i),
//!     }
//! }
//! ```

extern crate libc;

use std::os::unix::io::RawFd;

/// Represents a lock on a file.
#[derive(Debug, Eq, PartialEq)]
pub struct Lock {
  fd: RawFd,
}

/// Represents the error that occurred while trying to lock or unlock a file.
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
  /// caused when the error occurred at the filesystem layer (see
  /// [errno](https://crates.io/crates/errno)).
  Errno(i32),
}

#[derive(Clone, Debug)]
pub enum LockKind {
    /// Indicates a lock file which
    NonBlocking,
    Blocking,
}

impl Into<i32> for LockKind {
    fn into(self) -> i32 {
        match self {
            LockKind::NonBlocking => 0,
            LockKind::Blocking => 1,
        }
    }
}

extern {
  fn c_lock(fd: i32, should_block: i32) -> i32;
  fn c_unlock(fd: i32) -> i32;
}

impl Lock {
    /// Create a new lock instance from the given file descriptor `fd`.
    ///
    /// You will have to call `lock()` on it to acquire any lock.
    ///
    // TODO(ST): doc update once API has settled
    /// Locks the specified file.
    ///
    /// The `lock()` and `lock_wait()` functions try to perform a lock on the
    /// specified file. The difference between the two is what they do when
    /// another process has a lock on the same file:
    ///
    /// * lock() - immediately return with an `Errno` error.
    /// * lock_wait() - waits (i.e. blocks the running thread) for the current
    /// owner of the lock to relinquish the lock.
    ///
    /// # Example
    ///
    /// ```
    /// extern crate file_lock;
    /// extern crate tempfile;
    ///
    /// use file_lock::*;
    /// use std::os::unix::io::AsRawFd;
    ///
    /// fn main() {
    ///     let f = tempfile::TempFile::new().unwrap();
    ///
    ///     match Lock::new(f.as_raw_fd()).lock(LockKind::NonBlocking) {
    ///         Ok(_)  => println!("Got lock"),
    ///         Err(Error::Errno(i))
    ///               => println!("Got filesystem error {}", i),
    ///     }
    /// }
    /// ```
    pub fn new(fd: RawFd) -> Lock {
        Lock {
            fd:   fd,
        }
    }

    /// Lock the file-descriptor
    pub fn lock(&self, kind: LockKind) -> Result<(), Error> {
        let errno = unsafe { c_lock(self.fd, kind.into()) };

        return match errno {
           0 => Ok(()),
           _ => Err(Error::Errno(errno)),
        }
    }

    /// Unlocks the file held by `Lock`.
    ///
    /// In reality, you shouldn't need to call `unlock()`. As `Lock` implements
    /// the `Drop` trait, once the `Lock` reference goes out of scope, `unlock()`
    /// will be called automatically.
    ///
    /// # Example
    ///
    /// ```
    /// extern crate file_lock;
    /// extern crate tempfile;
    ///
    /// use file_lock::*;
    /// use std::os::unix::io::AsRawFd;
    ///
    /// fn main() {
    ///     let f = tempfile::TempFile::new().unwrap();
    ///     let l = Lock::new(f.as_raw_fd());
    ///     l.lock(LockKind::NonBlocking).unwrap();
    ///     if l.unlock().is_ok() {
    ///         println!("unlock successful");
    ///     }
    /// }
    /// ```

    pub fn unlock(&self) -> Result<(), Error> {
      unsafe {
        let errno = c_unlock(self.fd);

        return match errno {
           0 => Ok(()),
           _ => Err(Error::Errno(errno)),
        }
      }
    }
}

#[allow(unused_must_use)]
impl Drop for Lock {
  fn drop(&mut self) {
    self.unlock().ok();
  }
}

#[cfg(test)]
mod test {
    use libc;

    use std::env;
    use std::fs::{File, OpenOptions, remove_file};
    use std::path::PathBuf;
    use std::os::unix::io::{RawFd, AsRawFd};

    use super::*;

    struct TempFile {
        inner: File,
        path: PathBuf
    }

    impl TempFile {
        fn new(name: &str) -> TempFile {
            let mut path = env::temp_dir();
            path.push(name);

            TempFile {
                inner: OpenOptions::new()
                                   .create(true)
                                   .write(true)
                                   .open(&path).unwrap(),
                path: path,
            }
        }

        fn fd(&self) -> RawFd {
            self.inner.as_raw_fd()
        }
    }

    impl Drop for TempFile {
        fn drop(&mut self) {
            remove_file(&self.path).ok();
        }
    }

    //
    // unfortunately we can't abstract this out for lock() and lock_wait()
    // into a macro because string concat doesn't exist
    //

    // lock_wait() tests

    #[test]
    fn invalid_fd() {
        for fd in &[-1 as RawFd, 40125] {
            for kind in &[LockKind::Blocking, LockKind::NonBlocking] {
                assert_eq!(Lock::new(*fd).lock(kind.clone()),
                           Err(Error::Errno(libc::consts::os::posix88::EBADF)));
            }

            assert_eq!(Lock::new(*fd).unlock(),
                       Err(Error::Errno(libc::consts::os::posix88::EBADF)));
        }
    }

    #[test]
    fn lock_ok() {
        let tmp = TempFile::new("file-lock-test");
        for kind in &[LockKind::Blocking, LockKind::NonBlocking] {
            assert_eq!(Lock::new(tmp.fd()).lock(kind.clone()), Ok(()));
        }
    }

    #[test]
    fn unlock_error() {
        let tmp = TempFile::new("file-lock-test");
        for kind in &[LockKind::Blocking, LockKind::NonBlocking] {
            assert_eq!(Lock::new(tmp.fd()).lock(kind.clone()), Ok(()));

            // fcntl() will only allow us to hold a single lock on a file at a time
            // so this test can't work :(
            assert_eq!(Lock::new(tmp.fd()).lock(kind.clone()), Ok(()));

            // unlock without prior lock
            assert_eq!(Lock::new(tmp.fd()).unlock(), Ok(()));
        }
    }

    #[test]
    fn unlock_ok() {
        let tmp = TempFile::new("file-lock-test");
        for kind in &[LockKind::Blocking, LockKind::NonBlocking] {
            let l = Lock::new(tmp.fd());

            assert_eq!(l.lock(kind.clone()), Ok(()));
            assert_eq!(l.unlock(), Ok(()));
            assert!(l.unlock().is_ok(), "extra unlocks are fine");
        }
    }
}
<commit_msg>docs(lock): updated to represent current state<commit_after>//! File locking via POSIX advisory record locks.
//!
//! This crate provides the facility to obtain a write-lock and unlock a file
//! following the advisory record lock scheme as specified by UNIX IEEE Std 1003.1-2001
//! (POSIX.1) via `fcntl()`.
//!
//! # Examples
//!
//! Please note that the examples use `tempfile` merely to quickly create a file
//! which is removed automatically. In the common case, you would want to lock
//! a file which is known to multiple processes.
//!
//! ```
//! extern crate file_lock;
//! extern crate tempfile;
//!
//! use file_lock::*;
//! use std::os::unix::io::AsRawFd;
//!
//! fn main() {
//!     let f = tempfile::TempFile::new().unwrap();
//!
//!     match Lock::new(f.as_raw_fd()).lock(LockKind::NonBlocking) {
//!         Ok(_)  => println!("Got lock"),
//!         Err(Error::Errno(i))
//!               => println!("Got filesystem error {}", i),
//!     }
//! }
//! ```

extern crate libc;

use std::os::unix::io::RawFd;

/// Represents a write lock on a file.
///
/// The `lock(LockKind)` method tries to obtain a write-lock on the
/// file identified by a file-descriptor.
/// One can obtain different kinds of write-locks.
///
/// * LockKind::NonBlocking - immediately return with an `Errno` error.
/// * LockKind::Blocking - waits (i.e. blocks the running thread) for the current
/// owner of the lock to relinquish the lock.
///
/// # Example
///
/// Please note that the examples use `tempfile` merely to quickly create a file
/// which is removed automatically. In the common case, you would want to lock
/// a file which is known to multiple processes.
///
/// ```
/// extern crate file_lock;
/// extern crate tempfile;
///
/// use file_lock::*;
/// use std::os::unix::io::AsRawFd;
///
/// fn main() {
///     let f = tempfile::TempFile::new().unwrap();
///
///     match Lock::new(f.as_raw_fd()).lock(LockKind::NonBlocking) {
///         Ok(_)  => {
///             // we have a lock, which is discarded automatically. Otherwise you could call
///             // `unlock()` to make it explicit
///             //
///             println!("Got lock");
///         },
///         Err(Error::Errno(i))
///               => println!("Got filesystem error {}", i),
///     }
/// }
/// ```
#[derive(Debug, Eq, PartialEq)]
pub struct Lock {
  fd: RawFd,
}

/// Represents the error that occurred while trying to lock or unlock a file.
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
  /// caused when the error occurred at the filesystem layer (see
  /// [errno](https://crates.io/crates/errno)).
  Errno(i32),
}

/// Represents the kind of lock (e.g. *blocking*, *non-blocking*)
#[derive(Clone, Debug)]
pub enum LockKind {
    /// Indicates a lock file which
    NonBlocking,
    Blocking,
}

impl Into<i32> for LockKind {
    fn into(self) -> i32 {
        match self {
            LockKind::NonBlocking => 0,
            LockKind::Blocking => 1,
        }
    }
}

extern {
  fn c_lock(fd: i32, should_block: i32) -> i32;
  fn c_unlock(fd: i32) -> i32;
}

impl Lock {
    /// Create a new lock instance from the given file descriptor `fd`.
    ///
    /// You will have to call `lock(...)` on it to acquire any lock.
    pub fn new(fd: RawFd) -> Lock {
        Lock {
            fd:   fd,
        }
    }

    /// Obtain a write-lock the file-descriptor
    ///
    /// For an example, please see the documentation of the [`Lock`](struct.Lock.html) structure.
    pub fn lock(&self, kind: LockKind) -> Result<(), Error> {
        let errno = unsafe { c_lock(self.fd, kind.into()) };

        return match errno {
           0 => Ok(()),
           _ => Err(Error::Errno(errno)),
        }
    }

    /// Unlocks the file held by `Lock`.
    ///
    /// In reality, you shouldn't need to call `unlock()`. As `Lock` implements
    /// the `Drop` trait, once the `Lock` reference goes out of scope, `unlock()`
    /// will be called automatically.
    ///
    /// For an example, please see the documentation of the [`Lock`](struct.Lock.html) structure.
    pub fn unlock(&self) -> Result<(), Error> {
      unsafe {
        let errno = c_unlock(self.fd);

        return match errno {
           0 => Ok(()),
           _ => Err(Error::Errno(errno)),
        }
      }
    }
}

#[allow(unused_must_use)]
impl Drop for Lock {
  fn drop(&mut self) {
    self.unlock().ok();
  }
}

#[cfg(test)]
mod test {
    use libc;

    use std::env;
    use std::fs::{File, OpenOptions, remove_file};
    use std::path::PathBuf;
    use std::os::unix::io::{RawFd, AsRawFd};

    use super::*;

    struct TempFile {
        inner: File,
        path: PathBuf
    }

    impl TempFile {
        fn new(name: &str) -> TempFile {
            let mut path = env::temp_dir();
            path.push(name);

            TempFile {
                inner: OpenOptions::new()
                                   .create(true)
                                   .write(true)
                                   .open(&path).unwrap(),
                path: path,
            }
        }

        fn fd(&self) -> RawFd {
            self.inner.as_raw_fd()
        }
    }

    impl Drop for TempFile {
        fn drop(&mut self) {
            remove_file(&self.path).ok();
        }
    }

    //
    // unfortunately we can't abstract this out for lock() and lock_wait()
    // into a macro because string concat doesn't exist
    //

    // lock_wait() tests

    #[test]
    fn invalid_fd() {
        for fd in &[-1 as RawFd, 40125] {
            for kind in &[LockKind::Blocking, LockKind::NonBlocking] {
                assert_eq!(Lock::new(*fd).lock(kind.clone()),
                           Err(Error::Errno(libc::consts::os::posix88::EBADF)));
            }

            assert_eq!(Lock::new(*fd).unlock(),
                       Err(Error::Errno(libc::consts::os::posix88::EBADF)));
        }
    }

    #[test]
    fn lock_ok() {
        let tmp = TempFile::new("file-lock-test");
        for kind in &[LockKind::Blocking, LockKind::NonBlocking] {
            assert_eq!(Lock::new(tmp.fd()).lock(kind.clone()), Ok(()));
        }
    }

    #[test]
    fn unlock_error() {
        let tmp = TempFile::new("file-lock-test");
        for kind in &[LockKind::Blocking, LockKind::NonBlocking] {
            assert_eq!(Lock::new(tmp.fd()).lock(kind.clone()), Ok(()));

            // fcntl() will only allow us to hold a single lock on a file at a time
            // so this test can't work :(
            assert_eq!(Lock::new(tmp.fd()).lock(kind.clone()), Ok(()));

            // unlock without prior lock
            assert_eq!(Lock::new(tmp.fd()).unlock(), Ok(()));
        }
    }

    #[test]
    fn unlock_ok() {
        let tmp = TempFile::new("file-lock-test");
        for kind in &[LockKind::Blocking, LockKind::NonBlocking] {
            let l = Lock::new(tmp.fd());

            assert_eq!(l.lock(kind.clone()), Ok(()));
            assert_eq!(l.unlock(), Ok(()));
            assert!(l.unlock().is_ok(), "extra unlocks are fine");
        }
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate regex;

use std::collections::HashMap;

use hyper::server::{Handler, Request, Response};
use regex::RegexSet;

pub type RouterFn = fn(Request, Response);

const MIN_ROUTES: usize = 2;

pub struct Router {
    not_found: Option<RouterFn>,
    routes: RegexSet,
    route_list: Vec<String>,
    route_map: HashMap<String, RouterFn>
}

impl Handler for Router {
    // The handle method for the router simply tries to match the URI against
    // the first pattern that it can which in turn calls its associated handle
    // function passing the hyper Request and Response structures.
    fn handle(&self, req: Request, res: Response) {
        let uri = format!("{}", req.uri);
        let matches = self.routes.matches(&uri);
        let route = matches.iter().next();
        match route {
            Some(r) => {
                let key = &self.route_list[r];
                let handler = self.route_map.get(key).unwrap();
                handler(req, res);
            },
            None => self.not_found.unwrap()(req, res)
        }
    }
}

impl Router {
    /// Construct a new Router to maintain the routes and their handler
    /// functions.
    pub fn new() -> Router {
        Router {
            not_found: None,
            routes: RegexSet::new(&["", ""]).unwrap(),
            route_list: Vec::new(),
            route_map: HashMap::new(),
        }
    }

    /// Add a route to the router and give it a function to call when the route
    /// is matched against.
    pub fn add_route(&mut self, route: &str, handler: RouterFn) {
        self.route_list.push(route.to_owned());
        self.route_map.insert(route.to_owned(), handler);
    }

    /// This function ensures that a valid RegexSet could be made from the route
    /// vector that was built while using the `add_route` function. It also
    /// requires that there exist two or more routes so that the RegexSet can be
    /// successfully constructed.
    ///
    /// It will also ensure that there is a handler for routes that do not match
    /// any available in the set.
    pub fn finalize(&mut self) -> Result<(), String> {
        if self.route_list.len() < MIN_ROUTES {
            return Err("The router must contain 2 or more routes".to_owned());
        }

        // Check if the user added a 404 handler, else use the default.
        match self.not_found {
            Some(_) => {},
            None => { self.not_found = Some(default_not_found); }
        }

        let re_routes = RegexSet::new(self.route_list.iter());
        match re_routes {
            Ok(r) => {
                self.routes = r;
                Ok(())
            }
            Err(e) => {
                Err(format!("Error making RegexSet: {}", e))
            }
        }
    }

    /// Add a function to handle routes that get no matches.
    pub fn add_not_found(&mut self, not_found: RouterFn) {
        self.not_found = Some(not_found)
    }
}

fn default_not_found(req: Request, res: Response) {
    let message = format!("No route handler found for {}", req.uri);
    res.send(message.as_bytes()).unwrap();
}

fn test_handler(_: Request, _: Response) {}

#[test]
#[should_panic]
fn less_than_two_routes() {
    let mut router = Router::new();
    router.add_route("/", test_handler);
    router.finalize().unwrap();
}
<commit_msg>move the stub into the test function to get rid of warning<commit_after>extern crate hyper;
extern crate regex;

use std::collections::HashMap;

use hyper::server::{Handler, Request, Response};
use regex::RegexSet;

pub type RouterFn = fn(Request, Response);

const MIN_ROUTES: usize = 2;

pub struct Router {
    not_found: Option<RouterFn>,
    routes: RegexSet,
    route_list: Vec<String>,
    route_map: HashMap<String, RouterFn>
}

impl Handler for Router {
    // The handle method for the router simply tries to match the URI against
    // the first pattern that it can which in turn calls its associated handle
    // function passing the hyper Request and Response structures.
    fn handle(&self, req: Request, res: Response) {
        let uri = format!("{}", req.uri);
        let matches = self.routes.matches(&uri);
        let route = matches.iter().next();
        match route {
            Some(r) => {
                let key = &self.route_list[r];
                let handler = self.route_map.get(key).unwrap();
                handler(req, res);
            },
            None => self.not_found.unwrap()(req, res)
        }
    }
}

impl Router {
    /// Construct a new Router to maintain the routes and their handler
    /// functions.
    pub fn new() -> Router {
        Router {
            not_found: None,
            routes: RegexSet::new(&["", ""]).unwrap(),
            route_list: Vec::new(),
            route_map: HashMap::new(),
        }
    }

    /// Add a route to the router and give it a function to call when the route
    /// is matched against.
    pub fn add_route(&mut self, route: &str, handler: RouterFn) {
        self.route_list.push(route.to_owned());
        self.route_map.insert(route.to_owned(), handler);
    }

    /// This function ensures that a valid RegexSet could be made from the route
    /// vector that was built while using the `add_route` function. It also
    /// requires that there exist two or more routes so that the RegexSet can be
    /// successfully constructed.
    ///
    /// It will also ensure that there is a handler for routes that do not match
    /// any available in the set.
    pub fn finalize(&mut self) -> Result<(), String> {
        if self.route_list.len() < MIN_ROUTES {
            return Err("The router must contain 2 or more routes".to_owned());
        }

        // Check if the user added a 404 handler, else use the default.
        match self.not_found {
            Some(_) => {},
            None => { self.not_found = Some(default_not_found); }
        }

        let re_routes = RegexSet::new(self.route_list.iter());
        match re_routes {
            Ok(r) => {
                self.routes = r;
                Ok(())
            }
            Err(e) => {
                Err(format!("Error making RegexSet: {}", e))
            }
        }
    }

    /// Add a function to handle routes that get no matches.
    pub fn add_not_found(&mut self, not_found: RouterFn) {
        self.not_found = Some(not_found)
    }
}

fn default_not_found(req: Request, res: Response) {
    let message = format!("No route handler found for {}", req.uri);
    res.send(message.as_bytes()).unwrap();
}

#[test]
#[should_panic]
fn less_than_two_routes() {
    fn test_handler(_: Request, _: Response) {}

    let mut router = Router::new();
    router.add_route("/", test_handler);
    router.finalize().unwrap();
}
<|endoftext|>