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

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

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

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

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

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

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

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

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

}

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

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

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

}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

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

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

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

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

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

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

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

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

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

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

        let key = 1u64;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#[macro_export]
macro_rules! lmdb_not_found {
    (
        $x:expr
    ) => {
        match $x {
            Err(lmdb::MdbError::NotFound) => true,
            _ => false
        }
    };
}

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

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

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

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

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

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

}

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

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

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

}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

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

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

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

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

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

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

        fn dbflags() -> lmdb::DbFlags {
            lmdb::core::DbIntKey | lmdb::core::DbAllowDups | lmdb::core::DbAllowIntDups | lmdb::core::DbDupFixed
        }

        // XXX: pass MdbResult back
        fn prepare_database(db: &lmdb::Database) {
            db.set_dupsort(sort_reverse::<Id64>).unwrap()
        }
    }

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

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

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

        let key = 1u64;

        let mut cursor = table.new_cursor().unwrap();
        cursor.to_key(&1u64).unwrap(); //  positions on first item of key
        assert_eq!((1u64, 200u64), cursor.get().unwrap());

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

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

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

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

        assert!(lmdb_not_found!(cursor.to_next_item()));

        let mut cursor = table.new_cursor().unwrap();
        {
            assert!(lmdb_not_found!(cursor.to_item(&2u64, &3u64)));

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

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

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

            assert!(lmdb_not_found!(cursor.to_next_item()));
        }
    }
}

<|endoftext|>


<commit_before>pub mod character_set;
pub mod text_image;
<commit_msg>Document top level txtpic_lib<commit_after>//! The txtpic library
pub mod character_set;
pub mod text_image;
<|endoftext|>


<commit_before>//! Replaces the deprecated functionality of std::os::num_cpus.
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![allow(non_snake_case)]

#[cfg(not(windows))]
extern crate libc;

/// Returns the number of CPUs of the current machine.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

/// Returns the number of physical cores of the current machine.
/// If not possible on the particular architecture returns same as get() which
/// is the logical CPUs.
#[inline]
pub fn get_physical() -> usize {
    get_num_physical_cpus()
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn get_num_physical_cpus() -> usize {
    // Not implemented, fallback
    get_num_cpus()
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
    use std::io::BufReader;
    use std::io::BufRead;
    use std::fs::File;
    use std::collections::HashSet;

    let file = match File::open("/proc/cpuinfo") {
        Ok(val) => val,
        Err(_) => {return get_num_cpus()},
    };
    let reader = BufReader::new(file);
    let mut set = HashSet::new();
    let mut coreid: u32 = 0;
    let mut physid: u32 = 0;
    let mut chgcount = 0;
    for line in reader.lines().filter_map(|result| result.ok()) {
        let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
        if parts.len() != 2 {
            continue
        }
        if parts[0] == "core id" || parts[0] == "physical id" {
            let value = match parts[1].trim().parse() {
              Ok(val) => val,
              Err(_) => break,
            };
            match parts[0] {
                "core id"     => coreid = value,
                "physical id" => physid = value,
                _ => {},
            }
            chgcount += 1;
        }
        if chgcount == 2 {
            set.insert((physid, coreid));
            chgcount = 0;
        }
    }
    let count = set.len();
    if count == 0 { get_num_cpus() } else { count }
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    #[repr(C)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: u32,
        lpMinimumApplicationAddress: *mut u8,
        lpMaximumApplicationAddress: *mut u8,
        dwActiveProcessorMask: *mut u8,
        dwNumberOfProcessors: u32,
        dwProcessorType: u32,
        dwAllocationGranularity: u32,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    unsafe {
        let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "bitrig",
          target_os = "netbsd"))]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }
    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        unsafe {
            libc::sysctl(mib.as_mut_ptr(),
                         2,
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
                         0 as *mut _,
                         0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }
    cpus as usize
}

#[cfg(target_os = "openbsd")]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    unsafe {
        libc::sysctl(mib.as_mut_ptr(),
                     2,
                     &mut cpus as *mut _ as *mut _,
                     &mut cpus_size as *mut _ as *mut _,
                     0 as *mut _,
                     0);
    }
    if cpus < 1 {
        cpus = 1;
    }
    cpus as usize
}

#[cfg(target_os = "linux")]
fn get_num_cpus() -> usize {
    let mut set:  libc::cpu_set_t = unsafe { std::mem::zeroed() };
    if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
        let mut count: u32 = 0;
        for i in 0..libc::CPU_SETSIZE as usize {
            if unsafe { libc::CPU_ISSET(i, &set) } {
                count += 1
            }
        }
        count as usize
    } else {
        unsafe {
            libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
        }
    }
}

#[cfg(any(
    target_os = "nacl",
    target_os = "macos",
    target_os = "ios",
    target_os = "android",
    target_os = "solaris",
    target_os = "fuchsia")
)]
fn get_num_cpus() -> usize {
    unsafe {
        libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
    }
}
#[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
fn get_num_cpus() -> usize {
    1
}

#[test]
fn lower_bound() {
    assert!(get() > 0);
    assert!(get_physical() > 0);
}

#[test]
fn upper_bound() {
    assert!(get() < 236_451);
    assert!(get_physical() < 236_451);
}
<commit_msg>improve docs a little<commit_after>//! # num_cpus
//!
//! A crate with utilities to determine the number of CPUs available on the
//! current system.
//!
//! ## Example
//!
//! ```
//! let cpus = num_cpus::get();
//! ```
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![allow(non_snake_case)]

#[cfg(not(windows))]
extern crate libc;

/// Returns the number of available CPUs of the current system.
///
/// # Note
///
/// This will check sched affinity on Linux.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

/// Returns the number of physical cores of the current system.
///
/// If not possible on the particular architecture, returns same as `get()`
/// which is the logical CPUs.
#[inline]
pub fn get_physical() -> usize {
    get_num_physical_cpus()
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn get_num_physical_cpus() -> usize {
    // Not implemented, fallback
    get_num_cpus()
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
    use std::io::BufReader;
    use std::io::BufRead;
    use std::fs::File;
    use std::collections::HashSet;

    let file = match File::open("/proc/cpuinfo") {
        Ok(val) => val,
        Err(_) => {return get_num_cpus()},
    };
    let reader = BufReader::new(file);
    let mut set = HashSet::new();
    let mut coreid: u32 = 0;
    let mut physid: u32 = 0;
    let mut chgcount = 0;
    for line in reader.lines().filter_map(|result| result.ok()) {
        let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
        if parts.len() != 2 {
            continue
        }
        if parts[0] == "core id" || parts[0] == "physical id" {
            let value = match parts[1].trim().parse() {
              Ok(val) => val,
              Err(_) => break,
            };
            match parts[0] {
                "core id"     => coreid = value,
                "physical id" => physid = value,
                _ => {},
            }
            chgcount += 1;
        }
        if chgcount == 2 {
            set.insert((physid, coreid));
            chgcount = 0;
        }
    }
    let count = set.len();
    if count == 0 { get_num_cpus() } else { count }
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    #[repr(C)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: u32,
        lpMinimumApplicationAddress: *mut u8,
        lpMaximumApplicationAddress: *mut u8,
        dwActiveProcessorMask: *mut u8,
        dwNumberOfProcessors: u32,
        dwProcessorType: u32,
        dwAllocationGranularity: u32,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    unsafe {
        let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "bitrig",
          target_os = "netbsd"))]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }
    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        unsafe {
            libc::sysctl(mib.as_mut_ptr(),
                         2,
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
                         0 as *mut _,
                         0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }
    cpus as usize
}

#[cfg(target_os = "openbsd")]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    unsafe {
        libc::sysctl(mib.as_mut_ptr(),
                     2,
                     &mut cpus as *mut _ as *mut _,
                     &mut cpus_size as *mut _ as *mut _,
                     0 as *mut _,
                     0);
    }
    if cpus < 1 {
        cpus = 1;
    }
    cpus as usize
}

#[cfg(target_os = "linux")]
fn get_num_cpus() -> usize {
    let mut set:  libc::cpu_set_t = unsafe { std::mem::zeroed() };
    if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
        let mut count: u32 = 0;
        for i in 0..libc::CPU_SETSIZE as usize {
            if unsafe { libc::CPU_ISSET(i, &set) } {
                count += 1
            }
        }
        count as usize
    } else {
        unsafe {
            libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
        }
    }
}

#[cfg(any(
    target_os = "nacl",
    target_os = "macos",
    target_os = "ios",
    target_os = "android",
    target_os = "solaris",
    target_os = "fuchsia")
)]
fn get_num_cpus() -> usize {
    unsafe {
        libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
    }
}
#[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
fn get_num_cpus() -> usize {
    1
}

#[test]
fn lower_bound() {
    assert!(get() > 0);
    assert!(get_physical() > 0);
}

#[test]
fn upper_bound() {
    assert!(get() < 236_451);
    assert!(get_physical() < 236_451);
}
<|endoftext|>


<commit_before>//! This library provides an API client for [Diffbot](https://www.diffbot.com)
//!
//! See also [the diffbot documentation](https://www.diffbot.com/dev/docs/).

extern crate url;
extern crate hyper;
extern crate rustc_serialize;

use hyper::header::ContentType;
use hyper::mime::{Mime,TopLevel,SubLevel};

use std::io;

use rustc_serialize::json;

/// Diffbot API client.
///
/// # Example
///
/// ```
/// # extern crate diffbot;
/// # use diffbot::*;
/// # fn main() {
/// let diffbot = Diffbot::v3("token");
/// let result = diffbot.call(API::Analyze, "http://diffbot.com");
/// # println!("{:?}", result);
/// # }
/// ```
pub struct Diffbot {
    token: String,
    version: u32,

    client: hyper::Client,
}

/// One of the possible diffbot API
pub enum API {
    Analyze,
    Article,
    Product,
    Discussion,
    Image,
    Video,
}

impl API {
    fn get_str(&self) -> &'static str {
        match self {
            &API::Analyze => "analyze",
            &API::Article => "article",
            &API::Product => "product",
            &API::Discussion => "discussion",
            &API::Image => "image",
            &API::Video => "video",
        }
    }

    fn get_url(&self) -> String {
        format!("https://diffbot.com/api/{}", self.get_str())
    }
}

/// Error occuring during a call.
#[derive(Debug)]
pub enum Error {
    Api(u32,String),
    Json,
    Io(io::Error),
    // TODO: don't expose hyper
    Http(hyper::Error),
}

impl From<json::ParserError> for Error {
    fn from(err: json::ParserError) -> Self {
        match err {
            json::ParserError::SyntaxError(_,_,_) => Error::Json,
            json::ParserError::IoError(err) => Error::Io(err),
        }
    }
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Self {
        Error::Http(err)
    }
}

/// Result from a call.
pub type DiffbotResult = Result<json::Object, Error>;

impl Diffbot {
    /// Returns a Diffbot client that uses the given token and version.
    ///
    /// Valid versions: `1`, `2`, `3`.
    pub fn new<S: ToString>(token: S, version: u32) -> Self {
        Diffbot {
            token: token.to_string(),
            version: version,
            client: hyper::Client::new(),
        }
    }

    /// Convenient method to use a v1 client.
    pub fn v1<S: ToString>(token: S) -> Self {
        Diffbot::new(token, 1)
    }

    /// Convenient method to use a v2 client.
    pub fn v2<S: ToString>(token: S) -> Self {
        Diffbot::new(token, 2)
    }

    /// Convenient method to use a v3 client (recommended).
    pub fn v3<S: ToString>(token: S) -> Self {
        Diffbot::new(token, 3)
    }

    /// Makes an API call without extra options.
    ///
    /// Just calls `call_with_options` with an empty option list.
    pub fn call(&self, api: API, target_url: &str) -> DiffbotResult {
        self.call_with_options::<String>(api, target_url, &[])
    }

    /// Makes an API call
    ///
    /// Runs `target_url` through the diffbot endpoint specified by `api`.
    /// Add each (key,value) pair in `options` to the query string.
    /// Read the [diffbot documentation](https://www.diffbot.com/dev/docs/)
    /// for information on supported values.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate diffbot;
    /// # use diffbot::*;
    /// # fn main() {
    /// # let diffbot = Diffbot::v3("token");
    /// # println!("{:?}",
    /// diffbot.call_with_options(API::Article,
    ///                           "http://diffbot.com",
    ///                           &[("paging", "false")])
    /// # );
    /// # }
    /// ```
    pub fn call_with_options<S: ToString>(&self,
                                          api: API, target_url: &str,
                                          options: &[(S,S)]) -> DiffbotResult {
        let url = self.prepare_url(api, target_url, options);

        let builder = self.client.get(url);
        Diffbot::process_request(builder)
    }

    /// Posti an entire html body to the API, without extra options.
    ///
    /// See `call_with_options` for information on the arguments.
    ///
    /// `target_url` here is the URL the page would have. It doesn't have to be accessible, but
    /// will be used when resolving links.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate diffbot;
    /// # use diffbot::*;
    /// # fn main() {
    /// # let diffbot = Diffbot::v3("token");
    /// # println!("{:?}", {
    /// let body = b"<html>...</html>";
    /// diffbot.post_body(API::Article,
    ///                   "http://my.website.com",
    ///                   body)
    /// # } );
    /// # }
    /// ```
    pub fn post_body(&self, api: API, target_url: &str, body: &[u8]) -> DiffbotResult {
        self.post_body_with_options::<String>(api, target_url, body, &[])
    }

    /// Posti an entire html body to the API.
    ///
    /// See `call_with_options` for information on the arguments.
    ///
    /// `target_url` here is the URL the page would have. It doesn't have to be accessible, but
    /// will be used when resolving links.
    pub fn post_body_with_options<S: ToString>(&self,
                                               api: API, target_url: &str,
                                               body: &[u8],
                                               options: &[(S,S)]) -> DiffbotResult {
        let url = self.prepare_url(api, target_url, options);

        let header = ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![]));
        let builder = self.client.post(url).body(body).header(header);
        Diffbot::process_request(builder)
    }

    // Process a request and analyze the result
    fn process_request(builder: hyper::client::RequestBuilder) -> DiffbotResult {
        let mut result = try!(builder.send());

        let json_result = match try!(json::Json::from_reader(&mut result)) {
            json::Json::Object(obj) => obj,
            _ => return Err(Error::Json),
        };

        if json_result.contains_key("error") {
            let error_code = json_result.get("errorCode")
                .and_then(|c| c.as_u64())
                .unwrap_or(0u64);
            let error = json_result["error"].as_string().unwrap_or("");
            return Err(Error::Api(error_code as u32, error.to_string()));
        }

        Ok(json_result)
    }

    // Returns the diffbot URL for the given call
    fn prepare_url<S: ToString>(&self,
                                api: API, target_url: &str,
                                options: &[(S,S)]) -> hyper::Url {

        let mut params = Vec::<(String,String)>::new();
        params.push(("token".to_string(), self.token.clone()));
        params.push(("version".to_string(), self.version.to_string()));
        params.push(("url".to_string(), target_url.to_string()));
        for &(ref key, ref value) in options.iter() {
            params.push((key.to_string(), value.to_string()));
        }

        // We control the URL, it should always be valid.
        let mut url = hyper::Url::parse(&api.get_url()).unwrap();
        url.set_query_from_pairs(&params);

        url
    }
}

#[test]
fn test_call() {
    // Use `cargo test -- --nocapture` to see the output
    let diffbot = Diffbot::v3("insert_your_token_here");
    println!("{:?}", diffbot.call(API::Analyze, "http://diffbot.com"));
}

#[test]
fn test_call_with_options() {
    // Use `cargo test -- --nocapture` to see the output
    let diffbot = Diffbot::v3("insert_your_token_here");
    println!("{:?}", diffbot.call_with_options(API::Analyze,
                                               "http://diffbot.com",
                                               &[("fields", "links,meta")]));
}

#[test]
fn test_post() {
    // Use `cargo test -- --nocapture` to see the output
    let diffbot = Diffbot::v3("insert_your_token_here");
    let res = diffbot.post_body(API::Article, "http://my.website.com", br#"

        <title>My Website</title>

My Website

This is a fake website, yet we will analyze its content.
           Isn't it interesting?

"#);

    println!("{:?}", res);
}
<commit_msg>Add documentation and Error implementation<commit_after>//! This library provides an API client for [Diffbot](https://www.diffbot.com)
//!
//! See also [the diffbot documentation](https://www.diffbot.com/dev/docs/).
//!
//! # Example
//!
//! ```
//! extern crate diffbot;
//! use diffbot::*;
//!
//! fn main() {
//!     let client = Diffbot::v3("insert_your_token_here");
//!     match client.call(API::Analyze, "http://www.diffbot.com") {
//!         Ok(result) => println!("{:?}", result),
//!         Err(Error::Api(code, msg)) => println!("API returned error {}: {}", code, msg),
//!         Err(err) => println!("Other error: {:?}", err),
//!     };
//! }
//! ```

extern crate url;
extern crate hyper;
extern crate rustc_serialize;

use hyper::header::ContentType;
use hyper::mime::{Mime,TopLevel,SubLevel};

use std::error::{self, Error as StdError};
use std::io;
use std::fmt;

use rustc_serialize::json;

/// Diffbot API client.
///
/// # Example
///
/// ```
/// # extern crate diffbot;
/// # use diffbot::*;
/// # fn main() {
/// let diffbot = Diffbot::v3("token");
/// let result = diffbot.call(API::Analyze, "http://diffbot.com");
/// # println!("{:?}", result);
/// # }
/// ```
pub struct Diffbot {
    token: String,
    version: u32,

    client: hyper::Client,
}

/// One of the possible diffbot API.
///
/// See [the diffbot documentation](https://www.diffbot.com/dev/docs/).
pub enum API {
    /// The analyze API automatically detects the page type.
    Analyze,
    /// The article API for news article.
    Article,
    /// The product API for products in online shops.
    Product,
    /// The discussion API for forums.
    Discussion,
    /// The image API for image-central pages.
    Image,
    /// The video API for video pages (youtube, ...).
    Video,
}

impl API {
    fn get_str(&self) -> &'static str {
        match self {
            &API::Analyze => "analyze",
            &API::Article => "article",
            &API::Product => "product",
            &API::Discussion => "discussion",
            &API::Image => "image",
            &API::Video => "video",
        }
    }

    fn get_url(&self) -> String {
        format!("https://diffbot.com/api/{}", self.get_str())
    }
}

/// Error occuring during a call.
#[derive(Debug)]
pub enum Error {
    /// The API returned an error.
    Api(u32,String),
    /// An error occured when decoding JSON from the API.
    Json,
    /// An error occured with the network.
    Io(io::Error),
    // TODO: don't expose hyper
    /// An HTTP error occured with the webserver.
    Http(hyper::Error),
}

impl From<json::ParserError> for Error {
    fn from(err: json::ParserError) -> Self {
        match err {
            json::ParserError::SyntaxError(_,_,_) => Error::Json,
            json::ParserError::IoError(err) => Error::Io(err),
        }
    }
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Self {
        Error::Http(err)
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error::Api(_, ref msg) => msg,
            &Error::Json => "invalid JSON",
            &Error::Io(ref err) => err.description(),
            &Error::Http(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error::Api(_,_) => None,
            &Error::Json => None,
            &Error::Io(ref err) => Some(err),
            &Error::Http(ref err) => Some(err),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.description())
    }
}

/// Result from a call.
pub type DiffbotResult = Result<json::Object, Error>;

impl Diffbot {
    /// Returns a Diffbot client that uses the given token and version.
    ///
    /// Valid versions: `1`, `2`, `3`.
    pub fn new<S: ToString>(token: S, version: u32) -> Self {
        Diffbot {
            token: token.to_string(),
            version: version,
            client: hyper::Client::new(),
        }
    }

    /// Convenient method to use a v1 client.
    pub fn v1<S: ToString>(token: S) -> Self {
        Diffbot::new(token, 1)
    }

    /// Convenient method to use a v2 client.
    pub fn v2<S: ToString>(token: S) -> Self {
        Diffbot::new(token, 2)
    }

    /// Convenient method to use a v3 client (recommended).
    pub fn v3<S: ToString>(token: S) -> Self {
        Diffbot::new(token, 3)
    }

    /// Makes an API call without extra options.
    ///
    /// Just calls `call_with_options` with an empty option list.
    pub fn call(&self, api: API, target_url: &str) -> DiffbotResult {
        self.call_with_options::<String>(api, target_url, &[])
    }

    /// Makes an API call
    ///
    /// Runs `target_url` through the diffbot endpoint specified by `api`.
    /// Add each (key,value) pair in `options` to the query string.
    /// Read the [diffbot documentation](https://www.diffbot.com/dev/docs/)
    /// for information on supported values.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate diffbot;
    /// # use diffbot::*;
    /// # fn main() {
    /// # let diffbot = Diffbot::v3("token");
    /// # println!("{:?}",
    /// diffbot.call_with_options(API::Article,
    ///                           "http://diffbot.com",
    ///                           &[("paging", "false")])
    /// # );
    /// # }
    /// ```
    pub fn call_with_options<S: ToString>(&self,
                                          api: API, target_url: &str,
                                          options: &[(S,S)]) -> DiffbotResult {
        let url = self.prepare_url(api, target_url, options);

        let builder = self.client.get(url);
        Diffbot::process_request(builder)
    }

    /// Posti an entire html body to the API, without extra options.
    ///
    /// See `call_with_options` for information on the arguments.
    ///
    /// `target_url` here is the URL the page would have. It doesn't have to be accessible, but
    /// will be used when resolving links.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate diffbot;
    /// # use diffbot::*;
    /// # fn main() {
    /// # let diffbot = Diffbot::v3("token");
    /// # println!("{:?}", {
    /// let body = b"<html>...</html>";
    /// diffbot.post_body(API::Article,
    ///                   "http://my.website.com",
    ///                   body)
    /// # } );
    /// # }
    /// ```
    pub fn post_body(&self, api: API, target_url: &str, body: &[u8]) -> DiffbotResult {
        self.post_body_with_options::<String>(api, target_url, body, &[])
    }

    /// Posti an entire html body to the API.
    ///
    /// See `call_with_options` for information on the arguments.
    ///
    /// `target_url` here is the URL the page would have. It doesn't have to be accessible, but
    /// will be used when resolving links.
    pub fn post_body_with_options<S: ToString>(&self,
                                               api: API, target_url: &str,
                                               body: &[u8],
                                               options: &[(S,S)]) -> DiffbotResult {
        let url = self.prepare_url(api, target_url, options);

        let header = ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![]));
        let builder = self.client.post(url).body(body).header(header);
        Diffbot::process_request(builder)
    }

    // Process a request and analyze the result
    fn process_request(builder: hyper::client::RequestBuilder) -> DiffbotResult {
        let mut result = try!(builder.send());

        let json_result = match try!(json::Json::from_reader(&mut result)) {
            json::Json::Object(obj) => obj,
            _ => return Err(Error::Json),
        };

        if json_result.contains_key("error") {
            let error_code = json_result.get("errorCode")
                .and_then(|c| c.as_u64())
                .unwrap_or(0u64);
            let error = json_result["error"].as_string().unwrap_or("");
            return Err(Error::Api(error_code as u32, error.to_string()));
        }

        Ok(json_result)
    }

    // Returns the diffbot URL for the given call
    fn prepare_url<S: ToString>(&self,
                                api: API, target_url: &str,
                                options: &[(S,S)]) -> hyper::Url {

        let mut params = Vec::<(String,String)>::new();
        params.push(("token".to_string(), self.token.clone()));
        params.push(("version".to_string(), self.version.to_string()));
        params.push(("url".to_string(), target_url.to_string()));
        for &(ref key, ref value) in options.iter() {
            params.push((key.to_string(), value.to_string()));
        }

        // We control the URL, it should always be valid.
        let mut url = hyper::Url::parse(&api.get_url()).unwrap();
        url.set_query_from_pairs(&params);

        url
    }
}

#[test]
fn test_call() {
    // Use `cargo test -- --nocapture` to see the output
    let diffbot = Diffbot::v3("insert_your_token_here");
    println!("{:?}", diffbot.call(API::Analyze, "http://diffbot.com"));
}

#[test]
fn test_call_with_options() {
    // Use `cargo test -- --nocapture` to see the output
    let diffbot = Diffbot::v3("insert_your_token_here");
    println!("{:?}", diffbot.call_with_options(API::Analyze,
                                               "http://diffbot.com",
                                               &[("fields", "links,meta")]));
}

#[test]
fn test_post() {
    // Use `cargo test -- --nocapture` to see the output
    let diffbot = Diffbot::v3("insert_your_token_here");
    let res = diffbot.post_body(API::Article, "http://my.website.com", br#"

        <title>My Website</title>

My Website

This is a fake website, yet we will analyze its content.
           Isn't it interesting?

"#);

    println!("{:?}", res);
}
<|endoftext|>


<commit_before>//! Rustic bindings to [libnotify](https://developer.gnome.org/libnotify/)
//!
//! ```rust
//! extern crate libnotify;
//!
//! fn main() {
//!     // Create a libnotify context
//!     let notify = libnotify::Context::new("myapp").unwrap();
//!     // Create a new notification and show it
//!     let n = notify.new_notification("Summary",
//!                                     Some("Optional Body"),
//!                                     None).unwrap();
//!     n.show().unwrap();
//!     // You can also use the .show() convenience method on the context
//!     notify.show("I am another notification", None, None).unwrap();
//! }
//!
//! ```

#![warn(missing_docs)]

extern crate gdk_pixbuf;
extern crate gdk_pixbuf_sys;
extern crate glib;
extern crate glib_sys;
extern crate gtypes;
extern crate libnotify_sys as sys;

use gdk_pixbuf_sys::GdkPixbuf;
use glib::translate::*;
use gtypes::{TRUE, FALSE};
use std::error::Error;
use std::ffi::{self, CStr, CString};
use std::fmt;
use std::marker::PhantomData;
use std::os::raw::c_int;

/// The urgency level of the notification.
pub enum Urgency {
    /// Low urgency. Used for unimportant notifications.
    Low,
    /// Normal urgency. Used for most standard notifications.
    Normal,
    /// Critical urgency. Used for very important notifications.
    Critical,
}

impl From<sys::NotifyUrgency> for Urgency {
    fn from(urgency: sys::NotifyUrgency) -> Urgency {
        match urgency {
            sys::NotifyUrgency::NotifyUrgencyLow => Urgency::Low,
            sys::NotifyUrgency::NotifyUrgencyNormal => Urgency::Normal,
            sys::NotifyUrgency::NotifyUrgencyCritical => Urgency::Critical,
        }
    }
}

impl From<Urgency> for sys::NotifyUrgency {
    fn from(urgency: Urgency) -> sys::NotifyUrgency {
        match urgency {
            Urgency::Low => sys::NotifyUrgency::NotifyUrgencyLow,
            Urgency::Normal => sys::NotifyUrgency::NotifyUrgencyNormal,
            Urgency::Critical => sys::NotifyUrgency::NotifyUrgencyCritical,
        }
    }
}

/// Error that can happen on context creation
#[derive(Debug)]
pub enum ContextCreationError {
    /// Context already exists.
    AlreadyExists,
    /// Failed to initialize libnotify.
    InitError,
    /// A nul byte was found in the provided string.
    NulError(ffi::NulError),
}

impl fmt::Display for ContextCreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use ContextCreationError::*;
        match *self {
            AlreadyExists => write!(f, "A Libnotify context already exists."),
            InitError => write!(f, "Failed to initialize libnotify."),
            NulError(ref e) => write!(f, "{}", e),
        }
    }
}

impl From<ffi::NulError> for ContextCreationError {
    fn from(src: ffi::NulError) -> Self {
        ContextCreationError::NulError(src)
    }
}

#[derive(Debug)]
/// An error that can happen when attempting to create a notification.
pub enum NotificationCreationError {
    /// A nul byte was found in the provided string.
    NulError(ffi::NulError),
    /// An unknown error happened.
    Unknown,
    /// Invalid parameter passed to a glib function
    InvalidParameter,
}

impl fmt::Display for NotificationCreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use NotificationCreationError::*;
        match *self {
            NulError(ref e) => write!(f, "{}", e),
            Unknown => write!(f, "Unknown error"),
            InvalidParameter => write!(f, "An invalid parameter was passed"),
        }
    }
}

impl From<ffi::NulError> for NotificationCreationError {
    fn from(src: ffi::NulError) -> Self {
        NotificationCreationError::NulError(src)
    }
}

impl Error for NotificationCreationError {
    fn description(&self) -> &str {
        "notification creation error"
    }
}

/// The context which within libnotify operates.
///
/// Only one context can exist at a time.
pub struct Context;

impl Context {
    /// Create a new context
    ///
    /// Arguments:
    ///
    /// - app_name: The name of the application using the context
    pub fn new(app_name: &str) -> Result<Context, ContextCreationError> {
        unsafe {
            if sys::notify_is_initted() == TRUE {
                return Err(ContextCreationError::AlreadyExists);
            }
            let app_name = try!(CString::new(app_name));
            if sys::notify_init(app_name.as_ptr()) == FALSE {
                return Err(ContextCreationError::InitError);
            }
        }
        Ok(Context)
    }

    /// Creates a new Notification.
    ///
    /// Arguments:
    ///
    /// - summary: Required summary text
    /// - body: Optional body text
    /// - icon: Optional icon theme icon name or filename
    pub fn new_notification
        (&self,
         summary: &str,
         body: Option<&str>,
         icon: Option<&str>)
         -> Result<Notification, NotificationCreationError> {
        let summary = try!(CString::new(summary));
        let body = match body {
            Some(body) => Some(try!(CString::new(body))),
            None => None,
        };
        let body_ptr = match body {
            Some(ref body) => body.as_ptr(),
            None => std::ptr::null(),
        };
        let icon = match icon {
            Some(icon) => Some(try!(CString::new(icon))),
            None => None,
        };
        let icon_ptr = match icon {
            Some(ref icon) => icon.as_ptr(),
            None => std::ptr::null(),
        };

        unsafe {
            let n = sys::notify_notification_new(summary.as_ptr(),
                                                 body_ptr,
                                                 icon_ptr);
            if n.is_null() {
                return Err(NotificationCreationError::Unknown);
            }

            Ok(Notification {
                   handle: n,
                   _phantom: PhantomData,
               })
        }
    }

    /// Show a notification.
    ///
    /// This is a convenience method that creates a new notification,
    /// and shows it in one step.
    pub fn show(&self,
                summary: &str,
                body: Option<&str>,
                icon: Option<&str>)
                -> Result<(), Box<Error>> {
        let notif = try!(self.new_notification(summary, body, icon));
        try!(notif.show());
        Ok(())
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            sys::notify_uninit();
        }
    }
}

/// A passive pop-up notification
pub struct Notification<'a> {
    handle: *mut sys::NotifyNotification,
    _phantom: PhantomData<&'a Context>,
}

impl<'a> Notification<'a> {
    /// Tells the notification server to display the notification
    /// on the screen.
    pub fn show(&'a self) -> Result<(), NotificationShowError> {
        unsafe {
            let mut err: *mut glib_sys::GError = std::ptr::null_mut();
            sys::notify_notification_show(self.handle, &mut err);
            if !err.is_null() {
                let result = Err(NotificationShowError {
                                     message: CStr::from_ptr((*err).message)
                                         .to_string_lossy()
                                         .into_owned(),
                                 });
                glib_sys::g_error_free(err);
                return result;
            }
            Ok(())
        }
    }

    /// Set the notification timeout. Note that the server might ignore
    /// the timeout.
    pub fn set_notification_timeout(&self, timeout: i32) {
        let _timeout: c_int = From::from(timeout);

        unsafe { sys::notify_notification_set_timeout(self.handle, _timeout) }
    }

    /// Updates the notification text and icon. This won't send the update
    /// out and display it on the screen. For that, you will need to
    /// call `.show()`.
    pub fn update(&self,
                  summary: &str,
                  body: Option<&str>,
                  icon: Option<&str>)
                  -> Result<(), NotificationCreationError> {
        let summary = try!(CString::new(summary));
        let body = match body {
            Some(body) => Some(try!(CString::new(body))),
            None => None,
        };
        let body_ptr = match body {
            Some(ref body) => body.as_ptr(),
            None => std::ptr::null(),
        };
        let icon = match icon {
            Some(icon) => Some(try!(CString::new(icon))),
            None => None,
        };
        let icon_ptr = match icon {
            Some(ref icon) => icon.as_ptr(),
            None => std::ptr::null(),
        };

        unsafe {
            let b = sys::notify_notification_update(self.handle,
                                                    summary.as_ptr(),
                                                    body_ptr,
                                                    icon_ptr);
            if b == FALSE {
                return Err(NotificationCreationError::InvalidParameter);
            }
        }

        return Ok(());
    }

    /// Sets a hint for `key` with value `value`. If value is `None`,
    /// then key is unset.
    pub fn set_hint(&self,
                    key: &str,
                    value: Option<glib::variant::Variant>)
                    -> Result<(), NotificationCreationError> {
        let key = try!(CString::new(key));

        let gvalue: *mut glib_sys::GVariant = {
            match value {
                Some(ref value) => value.to_glib_none().0,
                None => std::ptr::null_mut(),
            }
        };

        unsafe {
            sys::notify_notification_set_hint(self.handle, key.as_ptr(), gvalue)
        }

        return Ok(());
    }

    /// Sets the category of this notification. This can be used by the
    /// notification server to filter or display the data in a certain way.
    pub fn set_category(&self, category: &str) -> Result<(), NotificationCreationError> {
        let category = try!(CString::new(category));
        unsafe {
            sys::notify_notification_set_category(self.handle,
                                                   category.as_ptr());
        }

        return Ok(());
    }

    /// Sets the urgency level of this notification.
    pub fn set_urgency(&self, urgency: Urgency) {
        let urgency: sys::NotifyUrgency = From::from(urgency);

        unsafe {
            sys::notify_notification_set_urgency(self.handle,
                                             urgency);
        }
    }

    /// Sets the image in the notification from a Pixbuf.
    pub fn set_image_from_pixbuf(&self, pixbuf: &gdk_pixbuf::Pixbuf) {
        let pixbuf: *mut GdkPixbuf = pixbuf.to_glib_none().0;

        unsafe {
            sys::notify_notification_set_image_from_pixbuf(self.handle,
                                                            pixbuf);
        }
    }

    /// Clears all hints from the notification.
    pub fn clear_hints(&self) {
        unsafe {
            sys::notify_notification_clear_hints(self.handle);
        }
    }

    /// Synchronously tells the notification server to hide the
    /// notification on the screen.
    pub fn close(&self) -> Result<(), NotificationShowError> {
        unsafe {
            let mut err: *mut glib_sys::GError = std::ptr::null_mut();
            sys::notify_notification_close(self.handle,
                                           &mut err);

            if !err.is_null() {
                let result = Err(NotificationShowError {
                                     message: CStr::from_ptr((*err).message)
                                         .to_string_lossy()
                                         .into_owned(),
                                 });
                glib_sys::g_error_free(err);
                return result;
            }
        }
        return Ok(());
    }
}

/// An error that can happen when attempting to show a notification.
#[derive(Debug)]
pub struct NotificationShowError {
    message: String,
}

impl fmt::Display for NotificationShowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Error showing notification: {}", self.message)
    }
}

impl Error for NotificationShowError {
    fn description(&self) -> &str {
        "notification show error"
    }
}
<commit_msg>Add lifetime specifiers to &self<commit_after>//! Rustic bindings to [libnotify](https://developer.gnome.org/libnotify/)
//!
//! ```rust
//! extern crate libnotify;
//!
//! fn main() {
//!     // Create a libnotify context
//!     let notify = libnotify::Context::new("myapp").unwrap();
//!     // Create a new notification and show it
//!     let n = notify.new_notification("Summary",
//!                                     Some("Optional Body"),
//!                                     None).unwrap();
//!     n.show().unwrap();
//!     // You can also use the .show() convenience method on the context
//!     notify.show("I am another notification", None, None).unwrap();
//! }
//!
//! ```

#![warn(missing_docs)]

extern crate gdk_pixbuf;
extern crate gdk_pixbuf_sys;
extern crate glib;
extern crate glib_sys;
extern crate gtypes;
extern crate libnotify_sys as sys;

use gdk_pixbuf_sys::GdkPixbuf;
use glib::translate::*;
use gtypes::{TRUE, FALSE};
use std::error::Error;
use std::ffi::{self, CStr, CString};
use std::fmt;
use std::marker::PhantomData;
use std::os::raw::c_int;

/// The urgency level of the notification.
pub enum Urgency {
    /// Low urgency. Used for unimportant notifications.
    Low,
    /// Normal urgency. Used for most standard notifications.
    Normal,
    /// Critical urgency. Used for very important notifications.
    Critical,
}

impl From<sys::NotifyUrgency> for Urgency {
    fn from(urgency: sys::NotifyUrgency) -> Urgency {
        match urgency {
            sys::NotifyUrgency::NotifyUrgencyLow => Urgency::Low,
            sys::NotifyUrgency::NotifyUrgencyNormal => Urgency::Normal,
            sys::NotifyUrgency::NotifyUrgencyCritical => Urgency::Critical,
        }
    }
}

impl From<Urgency> for sys::NotifyUrgency {
    fn from(urgency: Urgency) -> sys::NotifyUrgency {
        match urgency {
            Urgency::Low => sys::NotifyUrgency::NotifyUrgencyLow,
            Urgency::Normal => sys::NotifyUrgency::NotifyUrgencyNormal,
            Urgency::Critical => sys::NotifyUrgency::NotifyUrgencyCritical,
        }
    }
}

/// Error that can happen on context creation
#[derive(Debug)]
pub enum ContextCreationError {
    /// Context already exists.
    AlreadyExists,
    /// Failed to initialize libnotify.
    InitError,
    /// A nul byte was found in the provided string.
    NulError(ffi::NulError),
}

impl fmt::Display for ContextCreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use ContextCreationError::*;
        match *self {
            AlreadyExists => write!(f, "A Libnotify context already exists."),
            InitError => write!(f, "Failed to initialize libnotify."),
            NulError(ref e) => write!(f, "{}", e),
        }
    }
}

impl From<ffi::NulError> for ContextCreationError {
    fn from(src: ffi::NulError) -> Self {
        ContextCreationError::NulError(src)
    }
}

#[derive(Debug)]
/// An error that can happen when attempting to create a notification.
pub enum NotificationCreationError {
    /// A nul byte was found in the provided string.
    NulError(ffi::NulError),
    /// An unknown error happened.
    Unknown,
    /// Invalid parameter passed to a glib function
    InvalidParameter,
}

impl fmt::Display for NotificationCreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use NotificationCreationError::*;
        match *self {
            NulError(ref e) => write!(f, "{}", e),
            Unknown => write!(f, "Unknown error"),
            InvalidParameter => write!(f, "An invalid parameter was passed"),
        }
    }
}

impl From<ffi::NulError> for NotificationCreationError {
    fn from(src: ffi::NulError) -> Self {
        NotificationCreationError::NulError(src)
    }
}

impl Error for NotificationCreationError {
    fn description(&self) -> &str {
        "notification creation error"
    }
}

/// The context which within libnotify operates.
///
/// Only one context can exist at a time.
pub struct Context;

impl Context {
    /// Create a new context
    ///
    /// Arguments:
    ///
    /// - app_name: The name of the application using the context
    pub fn new(app_name: &str) -> Result<Context, ContextCreationError> {
        unsafe {
            if sys::notify_is_initted() == TRUE {
                return Err(ContextCreationError::AlreadyExists);
            }
            let app_name = try!(CString::new(app_name));
            if sys::notify_init(app_name.as_ptr()) == FALSE {
                return Err(ContextCreationError::InitError);
            }
        }
        Ok(Context)
    }

    /// Creates a new Notification.
    ///
    /// Arguments:
    ///
    /// - summary: Required summary text
    /// - body: Optional body text
    /// - icon: Optional icon theme icon name or filename
    pub fn new_notification
        (&self,
         summary: &str,
         body: Option<&str>,
         icon: Option<&str>)
         -> Result<Notification, NotificationCreationError> {
        let summary = try!(CString::new(summary));
        let body = match body {
            Some(body) => Some(try!(CString::new(body))),
            None => None,
        };
        let body_ptr = match body {
            Some(ref body) => body.as_ptr(),
            None => std::ptr::null(),
        };
        let icon = match icon {
            Some(icon) => Some(try!(CString::new(icon))),
            None => None,
        };
        let icon_ptr = match icon {
            Some(ref icon) => icon.as_ptr(),
            None => std::ptr::null(),
        };

        unsafe {
            let n = sys::notify_notification_new(summary.as_ptr(),
                                                 body_ptr,
                                                 icon_ptr);
            if n.is_null() {
                return Err(NotificationCreationError::Unknown);
            }

            Ok(Notification {
                   handle: n,
                   _phantom: PhantomData,
               })
        }
    }

    /// Show a notification.
    ///
    /// This is a convenience method that creates a new notification,
    /// and shows it in one step.
    pub fn show(&self,
                summary: &str,
                body: Option<&str>,
                icon: Option<&str>)
                -> Result<(), Box<Error>> {
        let notif = try!(self.new_notification(summary, body, icon));
        try!(notif.show());
        Ok(())
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            sys::notify_uninit();
        }
    }
}

/// A passive pop-up notification
pub struct Notification<'a> {
    handle: *mut sys::NotifyNotification,
    _phantom: PhantomData<&'a Context>,
}

impl<'a> Notification<'a> {
    /// Tells the notification server to display the notification
    /// on the screen.
    pub fn show(&'a self) -> Result<(), NotificationShowError> {
        unsafe {
            let mut err: *mut glib_sys::GError = std::ptr::null_mut();
            sys::notify_notification_show(self.handle, &mut err);
            if !err.is_null() {
                let result = Err(NotificationShowError {
                                     message: CStr::from_ptr((*err).message)
                                         .to_string_lossy()
                                         .into_owned(),
                                 });
                glib_sys::g_error_free(err);
                return result;
            }
            Ok(())
        }
    }

    /// Set the notification timeout. Note that the server might ignore
    /// the timeout.
    pub fn set_notification_timeout(&'a self, timeout: i32) {
        let _timeout: c_int = From::from(timeout);

        unsafe { sys::notify_notification_set_timeout(self.handle, _timeout) }
    }

    /// Updates the notification text and icon. This won't send the update
    /// out and display it on the screen. For that, you will need to
    /// call `.show()`.
    pub fn update(&'a self,
                  summary: &str,
                  body: Option<&str>,
                  icon: Option<&str>)
                  -> Result<(), NotificationCreationError> {
        let summary = try!(CString::new(summary));
        let body = match body {
            Some(body) => Some(try!(CString::new(body))),
            None => None,
        };
        let body_ptr = match body {
            Some(ref body) => body.as_ptr(),
            None => std::ptr::null(),
        };
        let icon = match icon {
            Some(icon) => Some(try!(CString::new(icon))),
            None => None,
        };
        let icon_ptr = match icon {
            Some(ref icon) => icon.as_ptr(),
            None => std::ptr::null(),
        };

        unsafe {
            let b = sys::notify_notification_update(self.handle,
                                                    summary.as_ptr(),
                                                    body_ptr,
                                                    icon_ptr);
            if b == FALSE {
                return Err(NotificationCreationError::InvalidParameter);
            }
        }

        return Ok(());
    }

    /// Sets a hint for `key` with value `value`. If value is `None`,
    /// then key is unset.
    pub fn set_hint(&'a self,
                    key: &str,
                    value: Option<glib::variant::Variant>)
                    -> Result<(), NotificationCreationError> {
        let key = try!(CString::new(key));

        let gvalue: *mut glib_sys::GVariant = {
            match value {
                Some(ref value) => value.to_glib_none().0,
                None => std::ptr::null_mut(),
            }
        };

        unsafe {
            sys::notify_notification_set_hint(self.handle, key.as_ptr(), gvalue)
        }

        return Ok(());
    }

    /// Sets the category of this notification. This can be used by the
    /// notification server to filter or display the data in a certain way.
    pub fn set_category(&'a self, category: &str) -> Result<(), NotificationCreationError> {
        let category = try!(CString::new(category));
        unsafe {
            sys::notify_notification_set_category(self.handle,
                                                   category.as_ptr());
        }

        return Ok(());
    }

    /// Sets the urgency level of this notification.
    pub fn set_urgency(&'a self, urgency: Urgency) {
        let urgency: sys::NotifyUrgency = From::from(urgency);

        unsafe {
            sys::notify_notification_set_urgency(self.handle,
                                             urgency);
        }
    }

    /// Sets the image in the notification from a Pixbuf.
    pub fn set_image_from_pixbuf(&'a self, pixbuf: &gdk_pixbuf::Pixbuf) {
        let pixbuf: *mut GdkPixbuf = pixbuf.to_glib_none().0;

        unsafe {
            sys::notify_notification_set_image_from_pixbuf(self.handle,
                                                            pixbuf);
        }
    }

    /// Clears all hints from the notification.
    pub fn clear_hints(&'a self) {
        unsafe {
            sys::notify_notification_clear_hints(self.handle);
        }
    }

    /// Synchronously tells the notification server to hide the
    /// notification on the screen.
    pub fn close(&'a self) -> Result<(), NotificationShowError> {
        unsafe {
            let mut err: *mut glib_sys::GError = std::ptr::null_mut();
            sys::notify_notification_close(self.handle,
                                           &mut err);

            if !err.is_null() {
                let result = Err(NotificationShowError {
                                     message: CStr::from_ptr((*err).message)
                                         .to_string_lossy()
                                         .into_owned(),
                                 });
                glib_sys::g_error_free(err);
                return result;
            }
        }
        return Ok(());
    }
}

/// An error that can happen when attempting to show a notification.
#[derive(Debug)]
pub struct NotificationShowError {
    message: String,
}

impl fmt::Display for NotificationShowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Error showing notification: {}", self.message)
    }
}

impl Error for NotificationShowError {
    fn description(&self) -> &str {
        "notification show error"
    }
}
<|endoftext|>


<commit_before>extern crate heatmap;
extern crate hsl;
extern crate lodepng;
extern crate rusttype;

use std::path::Path;

use heatmap::Heatmap;
use hsl::HSL;
use lodepng::encode_file;
use rusttype::{FontCollection, PixelsXY, point, PositionedGlyph};

#[allow(dead_code)]
pub struct Waterfall {
    config: Config,
}

pub struct Config { }

impl Default for Config {
    fn default() -> Config {
        Config {}
    }
}

impl Config {
    pub fn new() -> Config {
        Default::default()
    }

    pub fn build(self) -> Waterfall {
        Waterfall::configured(self)
    }
}

struct Label {
    value: u64,
    text: String,
}

impl Default for Waterfall {
    fn default() -> Waterfall {
        Waterfall::configured(Config::default())
    }
}

impl Waterfall {
    pub fn new() -> Waterfall {
        Default::default()
    }

    pub fn configure() -> Config {
        Config::default()
    }

    fn configured(config: Config) -> Waterfall {
        Waterfall { config: config }
    }

    pub fn render_png(&mut self, heatmap: &Heatmap, file: String) {
        let height = heatmap.num_slices() as usize;
        let width = heatmap.histogram_buckets() as usize;

        // build buffer from data
        let mut buffer = ImageBuffer::<ColorRgb>::new(width, height);
        let max = find_max(&heatmap);
        let mut x;
        let mut y = 0;

        // loop to color the pixels
        for slice in heatmap {
            x = 0;
            for bucket in &slice.histogram() {
                let pixel = color_from_value(bucket.count() / bucket.width(), max);
                buffer.set_pixel(x, y, pixel);
                x += 1;
            }
            y += 1;
        }

        // latency annotations
        let labels: Vec<Label> = vec!(
			Label{ value: 200, text: "200nS".to_string() },
			Label{ value: 500, text: "500nS".to_string() },
			Label{ value: 1000, text: "1uS".to_string() },
			Label{ value: 2000, text: "2uS".to_string() },
			Label{ value: 5000, text: "5uS".to_string() },
			Label{ value: 10000, text: "10uS".to_string() },
			Label{ value: 20000, text: "20uS".to_string() },
			Label{ value: 50000, text: "50uS".to_string() },
			Label{ value: 100000, text: "100uS".to_string() },
			Label{ value: 200000, text: "200uS".to_string() },
			Label{ value: 500000, text: "500uS".to_string() },
			Label{ value: 1000000, text: "1mS".to_string() },
			Label{ value: 2000000, text: "2mS".to_string() },
			Label{ value: 5000000, text: "5mS".to_string() },
			Label{ value: 10000000, text: "10mS".to_string() },
			Label{ value: 20000000, text: "20mS".to_string() },
			Label{ value: 50000000, text: "50mS".to_string() },
			Label{ value: 100000000, text: "100mS".to_string() },
			Label{ value: 200000000, text: "200mS".to_string() },
			Label{ value: 500000000, text: "500mS".to_string() },
			);

        let mut l = 0;
        y = 0;

        for slice in heatmap {
            x = 0;
            for bucket in &slice.histogram() {
                if (y % 60) == 0 {
                    if x == 0 {
                        let hour = y / 3600;
                        let minute = y / 60;
                        let time = format!("{:02}:{:02}", hour, minute);
                        let overlay = string_buffer(time.to_string(), 25.0);
                        buffer.overlay(&overlay, x, y);
                        buffer.horizontal_line(y, ColorRgb { r: 0, g: 0, b: 0 });
                    }
                    let v = bucket.value();
                    if (l < labels.len()) && (v >= labels[l].value) {
                        let overlay = string_buffer(labels[l].text.clone(), 25.0);
                        buffer.overlay(&overlay, x, y);
                        buffer.vertical_line(x, ColorRgb { r: 0, g: 0, b: 0 });
                        l += 1;
                    }
                }
                x += 1;
            }
            y += 1;
        }

        let _ = buffer.write_png(file.clone());
    }
}

fn find_max(heatmap: &Heatmap) -> u64 {
    let mut max = 0_u64;

    for slice in heatmap {
        for bucket in &slice.histogram() {
            let value = bucket.count() / bucket.width();
            if value > max {
                max = value;
            }
        }
    }
    max
}

fn string_buffer(string: String, size: f32) -> ImageBuffer<ColorRgb> {
    // load font
    let font_data = include_bytes!("../assets/ubuntumono/UbuntuMono-Regular.ttf");
    let collection = FontCollection::from_bytes(font_data as &[u8]);
    let font = collection.into_font().unwrap();

    // size and scaling
    let height: f32 = size;
    let pixel_height = height.ceil() as usize;
    let scale = PixelsXY(height * 1.0, height);

    let v_metrics = font.v_metrics(scale);
    let offset = point(0.0, v_metrics.ascent);

    let glyphs: Vec<PositionedGlyph> = font.layout(&string, scale, offset).collect();

    let width = glyphs.iter()
        .map(|g| g.h_metrics().advance_width)
        .fold(0.0, |x, y| x + y)
        .ceil() as usize;

    let mut overlay = ImageBuffer::<ColorRgb>::new(width, pixel_height);

    for g in glyphs {
        if let Some(bb) = g.pixel_bounding_box() {
            g.draw(|x, y, v| {
                let x = (x as i32 + bb.min.x) as usize;
                let y = (y as i32 + bb.min.y) as usize;
                if v > 0.25 {
                    overlay.set_pixel(x,
                                      y,
                                      ColorRgb {
                                          r: 255,
                                          g: 255,
                                          b: 255,
                                      });
                }
            })
        }
    }

    overlay
}

fn color_from_value(value: u64, max: u64) -> ColorRgb {
    let value = (value as f64) / (max as f64);

    let knee = 0.20_f64;

    let hsl: HSL;

    if value < knee {
        let l = 0.25_f64 + 0.25_f64 * value / knee;
        hsl = HSL {
            h: 236_f64,
            s: 1_f64,
            l: l,
        };
    } else {
        let h_per_deg: f64 = 236_f64 / (1.0_f64 - knee);
        let deg = (value - knee) * h_per_deg;

        hsl = HSL {
            h: (236_f64 - deg),
            s: 1_f64,
            l: 0.50_f64,
        };
    }

    let (r, g, b) = hsl.to_rgb();

    ColorRgb { r: r, g: g, b: b }
}

#[derive(Clone, Copy, Debug, PartialEq)]
struct ColorRgb {
    r: u8,
    g: u8,
    b: u8,
}

struct ImageBuffer<T> {
    buffer: Vec<Vec<T>>,
    height: usize,
    width: usize,
}

impl ImageBuffer<ColorRgb> {
    pub fn new(width: usize, height: usize) -> ImageBuffer<ColorRgb> {
        let background = ColorRgb { r: 0, g: 0, b: 0 };
        let mut row = Vec::<ColorRgb>::with_capacity(width);
        for _ in 0..width {
            row.push(background);
        }
        let mut buffer = Vec::<Vec<ColorRgb>>::with_capacity(height);
        for _ in 0..height {
            buffer.push(row.clone());
        }
        ImageBuffer {
            buffer: buffer,
            height: height,
            width: width,
        }
    }

    pub fn write_png(self, file: String) -> Result<(), lodepng::ffi::Error> {
        let mut buffer = Vec::<u8>::with_capacity((self.height * self.width));
        for row in 0..self.height {
            for col in 0..self.width {
                let pixel = self.buffer[row][col];
                buffer.push(pixel.r);
                buffer.push(pixel.g);
                buffer.push(pixel.b);
            }
        }
        let path = &Path::new(&file);
        encode_file(path, &buffer, self.width, self.height, lodepng::LCT_RGB, 8)
    }

    pub fn set_pixel(&mut self, x: usize, y: usize, value: ColorRgb) {
        if x < self.width && y < self.height {
            self.buffer[y][x] = value;
        }
    }

    pub fn overlay(&mut self, other: &ImageBuffer<ColorRgb>, x: usize, y: usize) {
        let ignore = ColorRgb { r: 0, g: 0, b: 0 };
        for sx in 0..other.width {
            for sy in 0..other.height {
                if (other.buffer[sy][sx] != ignore) &&
                   (((sy + y) < self.height) && ((sx + x) < self.width)) {
                    self.buffer[(sy + y)][(sx + x)] = other.buffer[sy][sx];
                }
            }
        }
    }

    pub fn horizontal_line(&mut self, y: usize, color: ColorRgb) {
        for x in 0..self.width {
            self.buffer[y][x] = color;
        }
    }

    pub fn vertical_line(&mut self, x: usize, color: ColorRgb) {
        for y in 0..self.height {
            self.buffer[y][x] = color;
        }
    }
}
<commit_msg>bugfix: colorize (#8)<commit_after>extern crate heatmap;
extern crate hsl;
extern crate lodepng;
extern crate rusttype;

use std::path::Path;

use heatmap::Heatmap;
use hsl::HSL;
use lodepng::encode_file;
use rusttype::{FontCollection, PixelsXY, point, PositionedGlyph};

#[allow(dead_code)]
pub struct Waterfall {
    config: Config,
}

pub struct Config { }

impl Default for Config {
    fn default() -> Config {
        Config {}
    }
}

impl Config {
    pub fn new() -> Config {
        Default::default()
    }

    pub fn build(self) -> Waterfall {
        Waterfall::configured(self)
    }
}

struct Label {
    value: u64,
    text: String,
}

impl Default for Waterfall {
    fn default() -> Waterfall {
        Waterfall::configured(Config::default())
    }
}

impl Waterfall {
    pub fn new() -> Waterfall {
        Default::default()
    }

    pub fn configure() -> Config {
        Config::default()
    }

    fn configured(config: Config) -> Waterfall {
        Waterfall { config: config }
    }

    pub fn render_png(&mut self, heatmap: &Heatmap, file: String) {
        let height = heatmap.num_slices() as usize;
        let width = heatmap.histogram_buckets() as usize;

        // build buffer from data
        let mut buffer = ImageBuffer::<ColorRgb>::new(width, height);
        let max = find_max(heatmap);
        let mut x;
        let mut y = 0;

        // loop to color the pixels
        for slice in heatmap {
            x = 0;
            for bucket in &slice.histogram() {
                let value = (bucket.count() as f64) / (bucket.width() as f64);
                let pixel = color_from_value(value, max);
                buffer.set_pixel(x, y, pixel);
                x += 1;
            }
            y += 1;
        }

        // latency annotations
        let labels: Vec<Label> = vec!(
			Label{ value: 200, text: "200nS".to_string() },
			Label{ value: 500, text: "500nS".to_string() },
			Label{ value: 1000, text: "1uS".to_string() },
			Label{ value: 2000, text: "2uS".to_string() },
			Label{ value: 5000, text: "5uS".to_string() },
			Label{ value: 10000, text: "10uS".to_string() },
			Label{ value: 20000, text: "20uS".to_string() },
			Label{ value: 50000, text: "50uS".to_string() },
			Label{ value: 100000, text: "100uS".to_string() },
			Label{ value: 200000, text: "200uS".to_string() },
			Label{ value: 500000, text: "500uS".to_string() },
			Label{ value: 1000000, text: "1mS".to_string() },
			Label{ value: 2000000, text: "2mS".to_string() },
			Label{ value: 5000000, text: "5mS".to_string() },
			Label{ value: 10000000, text: "10mS".to_string() },
			Label{ value: 20000000, text: "20mS".to_string() },
			Label{ value: 50000000, text: "50mS".to_string() },
			Label{ value: 100000000, text: "100mS".to_string() },
			Label{ value: 200000000, text: "200mS".to_string() },
			Label{ value: 500000000, text: "500mS".to_string() },
			);

        let mut l = 0;
        y = 0;

        for slice in heatmap {
            x = 0;
            for bucket in &slice.histogram() {
                if (y % 60) == 0 {
                    if x == 0 {
                        let hour = y / 3600;
                        let minute = y / 60;
                        let time = format!("{:02}:{:02}", hour, minute);
                        let overlay = string_buffer(time.to_string(), 25.0);
                        buffer.overlay(&overlay, x, y);
                        buffer.horizontal_line(y, ColorRgb { r: 0, g: 0, b: 0 });
                    }
                    let v = bucket.value();
                    if (l < labels.len()) && (v >= labels[l].value) {
                        let overlay = string_buffer(labels[l].text.clone(), 25.0);
                        buffer.overlay(&overlay, x, y);
                        buffer.vertical_line(x, ColorRgb { r: 0, g: 0, b: 0 });
                        l += 1;
                    }
                }
                x += 1;
            }
            y += 1;
        }

        let _ = buffer.write_png(file.clone());
    }
}

fn find_max(heatmap: &Heatmap) -> f64 {
    let mut max = 0.0;

    for slice in heatmap {
        for bucket in &slice.histogram() {
            let value = (bucket.count() as f64) / (bucket.width() as f64);
            if value > max {
                max = value;
            }
        }
    }
    max
}

fn string_buffer(string: String, size: f32) -> ImageBuffer<ColorRgb> {
    // load font
    let font_data = include_bytes!("../assets/ubuntumono/UbuntuMono-Regular.ttf");
    let collection = FontCollection::from_bytes(font_data as &[u8]);
    let font = collection.into_font().unwrap();

    // size and scaling
    let height: f32 = size;
    let pixel_height = height.ceil() as usize;
    let scale = PixelsXY(height * 1.0, height);

    let v_metrics = font.v_metrics(scale);
    let offset = point(0.0, v_metrics.ascent);

    let glyphs: Vec<PositionedGlyph> = font.layout(&string, scale, offset).collect();

    let width = glyphs.iter()
        .map(|g| g.h_metrics().advance_width)
        .fold(0.0, |x, y| x + y)
        .ceil() as usize;

    let mut overlay = ImageBuffer::<ColorRgb>::new(width, pixel_height);

    for g in glyphs {
        if let Some(bb) = g.pixel_bounding_box() {
            g.draw(|x, y, v| {
                let x = (x as i32 + bb.min.x) as usize;
                let y = (y as i32 + bb.min.y) as usize;
                if v > 0.25 {
                    overlay.set_pixel(x,
                                      y,
                                      ColorRgb {
                                          r: 255,
                                          g: 255,
                                          b: 255,
                                      });
                }
            })
        }
    }

    overlay
}

fn color_from_value(value: f64, max: f64) -> ColorRgb {
    let value = value / max;

    let knee = 0.20_f64;

    let hsl = if value < knee {
        let l = 0.25_f64 + 0.25_f64 * value / knee;
        HSL {
            h: 236_f64,
            s: 1_f64,
            l: l,
        }
    } else {
        let h_per_deg: f64 = 236_f64 / (1.0_f64 - knee);
        let deg = (value - knee) * h_per_deg;

        HSL {
            h: (236_f64 - deg),
            s: 1_f64,
            l: 0.50_f64,
        }
    };

    let (r, g, b) = hsl.to_rgb();

    ColorRgb { r: r, g: g, b: b }
}

#[derive(Clone, Copy, Debug, PartialEq)]
struct ColorRgb {
    r: u8,
    g: u8,
    b: u8,
}

struct ImageBuffer<T> {
    buffer: Vec<Vec<T>>,
    height: usize,
    width: usize,
}

impl ImageBuffer<ColorRgb> {
    pub fn new(width: usize, height: usize) -> ImageBuffer<ColorRgb> {
        let background = ColorRgb { r: 0, g: 0, b: 0 };
        let mut row = Vec::<ColorRgb>::with_capacity(width);
        for _ in 0..width {
            row.push(background);
        }
        let mut buffer = Vec::<Vec<ColorRgb>>::with_capacity(height);
        for _ in 0..height {
            buffer.push(row.clone());
        }
        ImageBuffer {
            buffer: buffer,
            height: height,
            width: width,
        }
    }

    pub fn write_png(self, file: String) -> Result<(), lodepng::ffi::Error> {
        let mut buffer = Vec::<u8>::with_capacity((self.height * self.width));
        for row in 0..self.height {
            for col in 0..self.width {
                let pixel = self.buffer[row][col];
                buffer.push(pixel.r);
                buffer.push(pixel.g);
                buffer.push(pixel.b);
            }
        }
        let path = &Path::new(&file);
        encode_file(path, &buffer, self.width, self.height, lodepng::LCT_RGB, 8)
    }

    pub fn set_pixel(&mut self, x: usize, y: usize, value: ColorRgb) {
        if x < self.width && y < self.height {
            self.buffer[y][x] = value;
        }
    }

    pub fn overlay(&mut self, other: &ImageBuffer<ColorRgb>, x: usize, y: usize) {
        let ignore = ColorRgb { r: 0, g: 0, b: 0 };
        for sx in 0..other.width {
            for sy in 0..other.height {
                if (other.buffer[sy][sx] != ignore) &&
                   (((sy + y) < self.height) && ((sx + x) < self.width)) {
                    self.buffer[(sy + y)][(sx + x)] = other.buffer[sy][sx];
                }
            }
        }
    }

    pub fn horizontal_line(&mut self, y: usize, color: ColorRgb) {
        for x in 0..self.width {
            self.buffer[y][x] = color;
        }
    }

    pub fn vertical_line(&mut self, x: usize, color: ColorRgb) {
        for y in 0..self.height {
            self.buffer[y][x] = color;
        }
    }
}
<|endoftext|>