<commit_before>extern crate byteorder;
extern crate crc;
extern crate time;

use std::io::{Cursor, Write};
use std::vec::Vec;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crc::crc32;
use time::Timespec;

#[derive(Debug)]
pub struct Entry<'a> {
    key: &'a[u8],
    value: &'a[u8],
    timestamp: Timespec,
    deleted: bool,
}

const STATIC_SIZE: usize = 14; // crc(4) + timestamp(4) + key_size(2) + value_size(4)
const TOMBSTONE: u32 = !0;

impl<'a> Entry<'a> {
    pub fn new(key: &'a [u8], value: &'a [u8]) -> Entry<'a> {
        assert!(value.len() < TOMBSTONE as usize);
        Entry {
            key: key,
            value: value,
            timestamp: time::now().to_timespec(),
            deleted: false,
        }
    }

    pub fn deleted(&self) -> Entry<'a> {
        Entry {
            key: self.key,
            value: &[],
            timestamp: time::now().to_timespec(),
            deleted: true,
        }
    }

    pub fn size(&self) -> usize {
        STATIC_SIZE + self.key.len() + self.value.len()
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        let mut cursor = Cursor::new(Vec::with_capacity(self.size()));
        cursor.set_position(4);
        cursor.write_u32::<LittleEndian>(self.timestamp.sec as u32).unwrap();
        cursor.write_u16::<LittleEndian>(self.key.len() as u16).unwrap();

        if self.deleted {
            cursor.write_u32::<LittleEndian>(TOMBSTONE).unwrap();
            cursor.write(&self.key).unwrap();
        } else {
            cursor.write_u32::<LittleEndian>(self.value.len() as u32).unwrap();
            cursor.write(&self.key).unwrap();
            cursor.write(&self.value).unwrap();
        }

        let checksum = crc32::checksum_ieee(&cursor.get_ref()[4..]);
        cursor.set_position(0);
        cursor.write_u32::<LittleEndian>(checksum).unwrap();

        cursor.into_inner()
    }

    pub fn from_bytes(bytes: &'a [u8]) -> Entry<'a> {
        let mut cursor = Cursor::new(bytes);

        let checksum = cursor.read_u32::<LittleEndian>().unwrap();
        assert_eq!(crc32::checksum_ieee(&bytes[4..]), checksum);

        let timestamp = cursor.read_u32::<LittleEndian>().unwrap();
        let key_size = cursor.read_u16::<LittleEndian>().unwrap();
        let value_size = cursor.read_u32::<LittleEndian>().unwrap();

        Entry {
            key: &bytes[STATIC_SIZE..STATIC_SIZE + key_size as usize],
            value: &bytes[STATIC_SIZE + key_size as usize..],
            timestamp: Timespec::new(timestamp as i64, 0),
            deleted: value_size == TOMBSTONE
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>file entry timestamp as u32<commit_after>extern crate byteorder;
extern crate crc;
extern crate time;

use std::io::{Cursor, Write};
use std::vec::Vec;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crc::crc32;

#[derive(Debug)]
pub struct Entry<'a> {
    key: &'a[u8],
    value: &'a[u8],
    timestamp: u32,
    deleted: bool,
}

const STATIC_SIZE: usize = 14; // crc(4) + timestamp(4) + key_size(2) + value_size(4)
const TOMBSTONE: u32 = !0;

impl<'a> Entry<'a> {
    pub fn new(key: &'a [u8], value: &'a [u8]) -> Entry<'a> {
        assert!(value.len() < TOMBSTONE as usize);
        Entry {
            key: key,
            value: value,
            timestamp: time::now().to_timespec().sec as u32,
            deleted: false,
        }
    }

    pub fn deleted(&self) -> Entry<'a> {
        Entry {
            key: self.key,
            value: &[],
            timestamp: time::now().to_timespec().sec as u32,
            deleted: true,
        }
    }

    pub fn size(&self) -> usize {
        STATIC_SIZE + self.key.len() + self.value.len()
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        let mut cursor = Cursor::new(Vec::with_capacity(self.size()));
        cursor.set_position(4);
        cursor.write_u32::<LittleEndian>(self.timestamp).unwrap();
        cursor.write_u16::<LittleEndian>(self.key.len() as u16).unwrap();

        if self.deleted {
            cursor.write_u32::<LittleEndian>(TOMBSTONE).unwrap();
            cursor.write(&self.key).unwrap();
        } else {
            cursor.write_u32::<LittleEndian>(self.value.len() as u32).unwrap();
            cursor.write(&self.key).unwrap();
            cursor.write(&self.value).unwrap();
        }

        let checksum = crc32::checksum_ieee(&cursor.get_ref()[4..]);
        cursor.set_position(0);
        cursor.write_u32::<LittleEndian>(checksum).unwrap();

        cursor.into_inner()
    }

    pub fn from_bytes(bytes: &'a [u8]) -> Entry<'a> {
        let mut cursor = Cursor::new(bytes);

        let checksum = cursor.read_u32::<LittleEndian>().unwrap();
        assert_eq!(crc32::checksum_ieee(&bytes[4..]), checksum);

        let timestamp = cursor.read_u32::<LittleEndian>().unwrap();
        let key_size = cursor.read_u16::<LittleEndian>().unwrap();
        let value_size = cursor.read_u32::<LittleEndian>().unwrap();

        Entry {
            key: &bytes[STATIC_SIZE..STATIC_SIZE + key_size as usize],
            value: &bytes[STATIC_SIZE + key_size as usize..],
            timestamp: timestamp,
            deleted: value_size == TOMBSTONE
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>extern crate postgres;
extern crate postgres_array;
extern crate rustc_serialize;

pub mod fact_db;
pub mod pg_db;
pub mod engine;

pub mod native_types;

#[derive (Clone)]
pub enum DB {
  Postgres(String)
}

pub enum Error {
  NoDB,
  PgConnect(::postgres::error::ConnectError),
  PgErr(::postgres::error::Error),
  PgDbErr(pg_db::DBError),
  IOErr(::std::io::Error),
  PgConnectStr(Box<::std::error::Error + Send + Sync>),
  EngineErr(engine::Error)
}

use self::Error::*;

use engine::Engine;
use pg_db::PgDB;
use fact_db::FactDB;
use native_types::*;

pub type Result<T> = ::std::result::Result<T, Error>;

use std::fmt::{Debug, Display, Formatter};

impl Display for Error {
  fn fmt(&self, fmt : &mut Formatter)
        -> ::std::result::Result<(),::std::fmt::Error> {
    match *self {
      NoDB             => fmt.write_str("No database specified"),
      PgConnect(ref e) => fmt.write_fmt(format_args!("Connection failed: {}", e)),
      PgErr(ref e)     => fmt.write_fmt(format_args!("Postgres error: {}", e)),
      PgDbErr(ref e)   => fmt.write_fmt(format_args!("Deductive DB (postgres) error: {}", e)),
      PgConnectStr(ref e) => fmt.write_fmt(format_args!("Connection string failed to parse: {}", e)),
      IOErr(ref e) => fmt.write_fmt(format_args!("IO failed: {}", e)),
      EngineErr(ref e) => fmt.write_fmt(format_args!("Engine Error: {}", e))
    }
  }
}

impl Debug for Error  {
  fn fmt(&self, fmt : &mut Formatter)
        -> ::std::result::Result<(),::std::fmt::Error> {
    Display::fmt(self, fmt)
  }
}

impl ::std::error::Error for Error {
  fn description(&self) -> &str {
    match self {
      &NoDB              => "No database specified",
      &PgConnect(ref e)  => e.description(),
      &PgErr(ref e)      => e.description(),
      &PgDbErr(ref e)    => e.description(),
      &IOErr(ref e)      => e.description(),
      &EngineErr(ref e)  => e.description(),
      &PgConnectStr(_)   => "Connection string failed to parse"
    }
  }
}

impl From<::postgres::error::ConnectError> for Error {
  fn from(ce : ::postgres::error::ConnectError) -> Error {PgConnect(ce)}
}

impl From<::postgres::error::Error> for Error {
  fn from(e : ::postgres::error::Error) -> Error {PgErr(e)}
}

impl From<pg_db::DBError> for Error {
  fn from(e : pg_db::DBError) -> Error {PgDbErr(e)}
}

impl From<engine::Error> for Error {
  fn from(e : engine::Error) -> Error {EngineErr(e)}
}

impl From<::std::io::Error> for Error {
  fn from(e : ::std::io::Error) -> Error {IOErr(e)}
}

impl<'a> DB {
  fn destroy(&self) -> Result<()> {
    match self {
      &DB::Postgres(ref str) => {
        use postgres::{Connection, SslMode, IntoConnectParams};
        let mut params = try!(str.into_connect_params().map_err(PgConnectStr));
        let old_db = try!(params.database.ok_or(NoDB));
        params.database = Some("postgres".to_owned());
        let conn = try!(Connection::connect(params, SslMode::None));
        let disco_query = format!("SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '{}' AND pid <> pg_backend_pid()", &old_db);
        try!(conn.execute(&disco_query, &[]));
        let drop_query = format!("DROP DATABASE {}", &old_db);
        try!(conn.execute(&drop_query, &[]));
      }
    }
    Ok(())
  }
  fn create(&self) -> Result<Box<FactDB>> {
    match self {
      &DB::Postgres(ref str) => {
        use postgres::{Connection, SslMode, IntoConnectParams};
        let mut params = try!(str.into_connect_params().map_err(PgConnectStr));
        let old_db = try!(params.database.ok_or(NoDB));
        params.database = Some("postgres".to_owned());
        let conn = try!(Connection::connect(params, SslMode::None));
        let create_query = format!("CREATE DATABASE {}", &old_db);
        //TODO accept success or db already exists, not other errors
        let _ = conn.execute(&create_query, &[]);
        Ok(Box::new(try!(PgDB::new(str))))
      }
    }
  }
}

pub struct Holmes {
  db : DB,
  engine : Engine
}

impl Holmes {
  pub fn new(db : DB) -> Result<Holmes> {
    Ok(Holmes {
      engine : Engine::new(try!(db.create())),
      db : db
    })
  }
  pub fn destroy(self) -> Result<()> {
    let db = self.db.clone();
    drop(self);
    db.destroy()
  }
  pub fn add_predicate(&mut self, pred : &native_types::Predicate)
                      -> Result<()> {
    self.engine.new_predicate(pred).map_err(|e| {EngineErr(e)})
  }
  pub fn add_fact(&mut self, fact : &native_types::Fact)
                      -> Result<()> {
    self.engine.new_fact(fact).map_err(|e| {EngineErr(e)})
  }
  pub fn query(&mut self, query : &Vec<native_types::Clause>) -> Result<Vec<Vec<native_types::HValue>>> {
    self.engine.derive(query).map_err(|e| {EngineErr(e)})
  }
  pub fn add_rule(&mut self, rule : &native_types::Rule) -> Result<()> {
    self.engine.new_rule(rule).map_err(|e| {EngineErr(e)})
  }
  pub fn reg_func(&mut self,
                  name : String,
                  input_type  : native_types::HType,
                  output_type : native_types::HType,
                  func : Box<Fn(native_types::HValue) -> native_types::HValue>) -> Result<()> {
    self.engine.reg_func(name, native_types::HFunc {
      input_type  : input_type,
      output_type : output_type,
      run : func
    });
    Ok(())
  }
}

#[macro_export]
macro_rules! htype_raw {
  (string) => { ::holmes::native_types::HType::HString};
  (blob  ) => { ::holmes::native_types::HType::Blob};
  (uint64) => { ::holmes::native_types::HType::UInt64};
}

#[macro_export]
macro_rules! htype {
  ([$t:tt]) => { ::holmes::native_types::HType::List(Box::new(htype!($t))) };
  (($($t:tt),*)) => { ::holmes::native_types::HType::Tuple(vec![$(htype!($t)),*]) };
  ($i:ident) => { htype_raw!($i) };
}

#[macro_export]
macro_rules! holmes_exec {
  ($holmes:ident, { $( $action:expr );* }) => {
      {
        $( try!($action($holmes)); );*
        let res : ::holmes::Result<()> = Ok(());
        res
      }
  };
}

#[macro_export]
macro_rules! predicate {
  ($holmes:ident, $pred_name:ident($($t:tt),*)) => {
    $holmes.add_predicate(&::holmes::native_types::Predicate {
      name  : stringify!($pred_name).to_string(),
      types : vec![$(htype!($t),)*]
    })
  };
  ($pred_name:ident($($t:tt),*)) => { |holmes : &mut Holmes| {
    let res : ::holmes::Result<()> = predicate!(holmes, $pred_name($($t),*));
    res
  }};
}

#[macro_export]
macro_rules! fact {
  ($holmes:ident, $pred_name:ident($($a:expr),*)) => {
    $holmes.add_fact(&::holmes::native_types::Fact {
      pred_name : stringify!($pred_name).to_string(),
      args : vec![$($a.to_hvalue()),*]
    })
  };
  ($pred_name:ident($($a:expr),*)) => { |holmes : &mut Holmes| {
    let res : ::holmes::Result<()> = fact!(holmes, $pred_name($($a),*));
    res
  }};
}

#[macro_export]
macro_rules! bind_match {
  ($vars:ident, $n:ident, [$bm:tt]) => { ::holmes::native_types::BindExpr::Iterate(Box::new(bind_match!($vars, $n, $bm))) };
  ($vars:ident, $n:ident, |$($bm:tt),*|) => {
    ::holmes::native_types::BindExpr::Destructure(vec![$(bind_match!($vars, $n, $bm)),*])
  };
  ($vars:ident, $n:ident, $cm:tt) => { ::holmes::native_types::BindExpr::Normal(clause_match!($vars, $n, $cm)) };
}

#[macro_export]
macro_rules! clause_match {
  ($vars:ident, $n:ident, [_]) => { ::holmes::native_types::MatchExpr::Unbound };
  ($vars:ident, $n:ident, ($v:expr)) => { ::holmes::native_types::MatchExpr::HConst($v.to_hvalue()) };
  ($vars:ident, $n:ident, $m:ident) => {{
    use std::collections::hash_map::Entry::*;
    use ::holmes::native_types::MatchExpr::*;
    match $vars.entry(stringify!($m).to_string()) {
      Occupied(entry) => Var(*entry.get()),
      Vacant(entry) => {
        $n = $n + 1;
        entry.insert($n - 1);
        Var($n - 1)
      }
    }
  }};
}

#[macro_export]
macro_rules! query {
  ($holmes:ident, $($pred_name:ident($($m:tt),*))&*) => {{
    use std::collections::HashMap;
    let mut vars : HashMap<String, ::holmes::native_types::HVar> = HashMap::new();
    let mut n : ::holmes::native_types::HVar = 0;
    $holmes.query(&vec![$(::holmes::native_types::Clause {
      pred_name : stringify!($pred_name).to_string(),
      args : vec![$(clause_match!(vars, n, $m)),*]
    }),*])
  }}
}

pub fn var_to_evar(var : MatchExpr) -> Expr {
  match var {
    MatchExpr::Var(var_no) => Expr::EVar(var_no),
    x => panic!("var_to_evar was passed nonvar: {:?}", x)
  }
}

#[macro_export]
macro_rules! hexpr {
  ($vars:ident, $n:ident, [$hexpr_name:ident]) => {
    var_to_evar(clause_match!($vars, $n, $hexpr_name))
  };
  ($vars:ident, $n:ident, ($hexpr:expr)) => {
    Expr::EVal($hexpr.to_hvalue())
  };
  ($vars:ident, $n:ident, {$hexpr_func:ident($($hexpr_arg:tt),*)}) => {
    Expr::EApp(stringify!($hexpr_func).to_string(), vec![$(hexpr!($vars, $n, $hexpr_arg)),*])
  };
}

#[macro_export]
macro_rules! rule {
  ($holmes:ident, $head_name:ident($($m:tt),*) <= $($body_name:ident($($mb:tt),*))&*,
   {$(let $bind:tt = $hexpr:tt);*}) => {{
    use std::collections::HashMap;
    let mut vars : HashMap<String, ::holmes::native_types::HVar> = HashMap::new();
    let mut n : ::holmes::native_types::HVar = 0;
    $holmes.add_rule(&::holmes::native_types::Rule {
      body : vec![$(::holmes::native_types::Clause {
        pred_name : stringify!($body_name).to_string(),
        args : vec![$(clause_match!(vars, n, $mb)),*]
      }),*],
      head : ::holmes::native_types::Clause {
        pred_name : stringify!($head_name).to_string(),
        args : vec![$(clause_match!(vars, n, $m)),*]
      },
      wheres : vec! [$(::holmes::native_types::WhereClause {
        lhs   : bind_match!(vars, n, $bind),
        rhs   : hexpr!(vars, n, $hexpr)
      }),*]
    })
  }};
  ($holmes:ident, $head_name:ident($($m:tt),*) <= $($body_name:ident($($mb:tt),*))&*,
   {$(let $($bind:tt),* = $hexpr:tt);*}) => {{
    use std::collections::HashMap;
    let mut vars : HashMap<String, ::holmes::native_types::HVar> = HashMap::new();
    let mut n : ::holmes::native_types::HVar = 0;
    $holmes.add_rule(&::holmes::native_types::Rule {
      body : vec![$(::holmes::native_types::Clause {
        pred_name : stringify!($body_name).to_string(),
        args : vec![$(clause_match!(vars, n, $mb)),*]
      }),*],
      head : ::holmes::native_types::Clause {
        pred_name : stringify!($head_name).to_string(),
        args : vec![$(clause_match!(vars, n, $m)),*]
      },
      wheres : vec! [$(::holmes::native_types::WhereClause {
        lhs   : ::holmes::native_types::BindExpr::Destructure(
                  vec![$(::holmes::native_types::BindExpr::Normal(
                    clause_match!(vars, n, $bind))),*]),
        rhs   : hexpr!(vars, n, $hexpr)
      }),*]
    })
  }};
  ($($head_name:ident($($m:tt),*)),* <= $($body_name:ident($($mb:tt),*))&*) => {
    |holmes : &mut Holmes| {
      rule!(holmes, $($head_name($($m),*)),* <= $($body_name($($mb),*))&*, {})
    }
  };
  ($($head_name:ident($($m:tt),*)),* <= $($body_name:ident($($mb:tt),*))&*, {$(let $($bind:tt),* = $hexpr:tt);*}) => {
    |holmes : &mut Holmes| {
      rule!(holmes, $($head_name($($m),*)),* <= $($body_name($($mb),*))&*, {$(let $($bind),* = $hexpr);*})
    }
  };

}

#[macro_export]
macro_rules! func {
  ($holmes:ident, let $name:ident : $src:tt -> $dst:tt = $body:expr) => {
    $holmes.reg_func(stringify!($name).to_string(),
                     htype!($src),
                     htype!($dst),
                     Box::new($body))
  };
  (let $name:ident : $src:tt -> $dst:tt = $body:expr) => {
    |holmes : &mut Holmes| {
      func!(holmes, let $name : $src -> $dst = $body)
    }
  };
}
<commit_msg>Use global paths for ToHValue trait<commit_after>extern crate postgres;
extern crate postgres_array;
extern crate rustc_serialize;

pub mod fact_db;
pub mod pg_db;
pub mod engine;

pub mod native_types;

#[derive (Clone)]
pub enum DB {
  Postgres(String)
}

pub enum Error {
  NoDB,
  PgConnect(::postgres::error::ConnectError),
  PgErr(::postgres::error::Error),
  PgDbErr(pg_db::DBError),
  IOErr(::std::io::Error),
  PgConnectStr(Box<::std::error::Error + Send + Sync>),
  EngineErr(engine::Error)
}

use self::Error::*;

use engine::Engine;
use pg_db::PgDB;
use fact_db::FactDB;
use native_types::*;

pub type Result<T> = ::std::result::Result<T, Error>;

use std::fmt::{Debug, Display, Formatter};

impl Display for Error {
  fn fmt(&self, fmt : &mut Formatter)
        -> ::std::result::Result<(),::std::fmt::Error> {
    match *self {
      NoDB             => fmt.write_str("No database specified"),
      PgConnect(ref e) => fmt.write_fmt(format_args!("Connection failed: {}", e)),
      PgErr(ref e)     => fmt.write_fmt(format_args!("Postgres error: {}", e)),
      PgDbErr(ref e)   => fmt.write_fmt(format_args!("Deductive DB (postgres) error: {}", e)),
      PgConnectStr(ref e) => fmt.write_fmt(format_args!("Connection string failed to parse: {}", e)),
      IOErr(ref e) => fmt.write_fmt(format_args!("IO failed: {}", e)),
      EngineErr(ref e) => fmt.write_fmt(format_args!("Engine Error: {}", e))
    }
  }
}

impl Debug for Error  {
  fn fmt(&self, fmt : &mut Formatter)
        -> ::std::result::Result<(),::std::fmt::Error> {
    Display::fmt(self, fmt)
  }
}

impl ::std::error::Error for Error {
  fn description(&self) -> &str {
    match self {
      &NoDB              => "No database specified",
      &PgConnect(ref e)  => e.description(),
      &PgErr(ref e)      => e.description(),
      &PgDbErr(ref e)    => e.description(),
      &IOErr(ref e)      => e.description(),
      &EngineErr(ref e)  => e.description(),
      &PgConnectStr(_)   => "Connection string failed to parse"
    }
  }
}

impl From<::postgres::error::ConnectError> for Error {
  fn from(ce : ::postgres::error::ConnectError) -> Error {PgConnect(ce)}
}

impl From<::postgres::error::Error> for Error {
  fn from(e : ::postgres::error::Error) -> Error {PgErr(e)}
}

impl From<pg_db::DBError> for Error {
  fn from(e : pg_db::DBError) -> Error {PgDbErr(e)}
}

impl From<engine::Error> for Error {
  fn from(e : engine::Error) -> Error {EngineErr(e)}
}

impl From<::std::io::Error> for Error {
  fn from(e : ::std::io::Error) -> Error {IOErr(e)}
}

impl<'a> DB {
  fn destroy(&self) -> Result<()> {
    match self {
      &DB::Postgres(ref str) => {
        use postgres::{Connection, SslMode, IntoConnectParams};
        let mut params = try!(str.into_connect_params().map_err(PgConnectStr));
        let old_db = try!(params.database.ok_or(NoDB));
        params.database = Some("postgres".to_owned());
        let conn = try!(Connection::connect(params, SslMode::None));
        let disco_query = format!("SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '{}' AND pid <> pg_backend_pid()", &old_db);
        try!(conn.execute(&disco_query, &[]));
        let drop_query = format!("DROP DATABASE {}", &old_db);
        try!(conn.execute(&drop_query, &[]));
      }
    }
    Ok(())
  }
  fn create(&self) -> Result<Box<FactDB>> {
    match self {
      &DB::Postgres(ref str) => {
        use postgres::{Connection, SslMode, IntoConnectParams};
        let mut params = try!(str.into_connect_params().map_err(PgConnectStr));
        let old_db = try!(params.database.ok_or(NoDB));
        params.database = Some("postgres".to_owned());
        let conn = try!(Connection::connect(params, SslMode::None));
        let create_query = format!("CREATE DATABASE {}", &old_db);
        //TODO accept success or db already exists, not other errors
        let _ = conn.execute(&create_query, &[]);
        Ok(Box::new(try!(PgDB::new(str))))
      }
    }
  }
}

pub struct Holmes {
  db : DB,
  engine : Engine
}

impl Holmes {
  pub fn new(db : DB) -> Result<Holmes> {
    Ok(Holmes {
      engine : Engine::new(try!(db.create())),
      db : db
    })
  }
  pub fn destroy(self) -> Result<()> {
    let db = self.db.clone();
    drop(self);
    db.destroy()
  }
  pub fn add_predicate(&mut self, pred : &native_types::Predicate)
                      -> Result<()> {
    self.engine.new_predicate(pred).map_err(|e| {EngineErr(e)})
  }
  pub fn add_fact(&mut self, fact : &native_types::Fact)
                      -> Result<()> {
    self.engine.new_fact(fact).map_err(|e| {EngineErr(e)})
  }
  pub fn query(&mut self, query : &Vec<native_types::Clause>) -> Result<Vec<Vec<native_types::HValue>>> {
    self.engine.derive(query).map_err(|e| {EngineErr(e)})
  }
  pub fn add_rule(&mut self, rule : &native_types::Rule) -> Result<()> {
    self.engine.new_rule(rule).map_err(|e| {EngineErr(e)})
  }
  pub fn reg_func(&mut self,
                  name : String,
                  input_type  : native_types::HType,
                  output_type : native_types::HType,
                  func : Box<Fn(native_types::HValue) -> native_types::HValue>) -> Result<()> {
    self.engine.reg_func(name, native_types::HFunc {
      input_type  : input_type,
      output_type : output_type,
      run : func
    });
    Ok(())
  }
}

#[macro_export]
macro_rules! htype_raw {
  (string) => { ::holmes::native_types::HType::HString};
  (blob  ) => { ::holmes::native_types::HType::Blob};
  (uint64) => { ::holmes::native_types::HType::UInt64};
}

#[macro_export]
macro_rules! htype {
  ([$t:tt]) => { ::holmes::native_types::HType::List(Box::new(htype!($t))) };
  (($($t:tt),*)) => { ::holmes::native_types::HType::Tuple(vec![$(htype!($t)),*]) };
  ($i:ident) => { htype_raw!($i) };
}

#[macro_export]
macro_rules! holmes_exec {
  ($holmes:ident, { $( $action:expr );* }) => {
      {
        $( try!($action($holmes)); );*
        let res : ::holmes::Result<()> = Ok(());
        res
      }
  };
}

#[macro_export]
macro_rules! predicate {
  ($holmes:ident, $pred_name:ident($($t:tt),*)) => {
    $holmes.add_predicate(&::holmes::native_types::Predicate {
      name  : stringify!($pred_name).to_string(),
      types : vec![$(htype!($t),)*]
    })
  };
  ($pred_name:ident($($t:tt),*)) => { |holmes : &mut Holmes| {
    let res : ::holmes::Result<()> = predicate!(holmes, $pred_name($($t),*));
    res
  }};
}

#[macro_export]
macro_rules! fact {
  ($holmes:ident, $pred_name:ident($($a:expr),*)) => {
    $holmes.add_fact(&::holmes::native_types::Fact {
      pred_name : stringify!($pred_name).to_string(),
      args : vec![$(::holmes::native_types::ToHValue::to_hvalue($a)),*]
    })
  };
  ($pred_name:ident($($a:expr),*)) => { |holmes : &mut Holmes| {
    let res : ::holmes::Result<()> = fact!(holmes, $pred_name($($a),*));
    res
  }};
}

#[macro_export]
macro_rules! bind_match {
  ($vars:ident, $n:ident, [$bm:tt]) => { ::holmes::native_types::BindExpr::Iterate(Box::new(bind_match!($vars, $n, $bm))) };
  ($vars:ident, $n:ident, |$($bm:tt),*|) => {
    ::holmes::native_types::BindExpr::Destructure(vec![$(bind_match!($vars, $n, $bm)),*])
  };
  ($vars:ident, $n:ident, $cm:tt) => { ::holmes::native_types::BindExpr::Normal(clause_match!($vars, $n, $cm)) };
}

#[macro_export]
macro_rules! clause_match {
  ($vars:ident, $n:ident, [_]) => { ::holmes::native_types::MatchExpr::Unbound };
  ($vars:ident, $n:ident, ($v:expr)) => {
      ::holmes::native_types::MatchExpr::HConst(::holmes::native_types::ToHValue::to_hvalue($v)) };
  ($vars:ident, $n:ident, $m:ident) => {{
    use std::collections::hash_map::Entry::*;
    use ::holmes::native_types::MatchExpr::*;
    match $vars.entry(stringify!($m).to_string()) {
      Occupied(entry) => Var(*entry.get()),
      Vacant(entry) => {
        $n = $n + 1;
        entry.insert($n - 1);
        Var($n - 1)
      }
    }
  }};
}

#[macro_export]
macro_rules! query {
  ($holmes:ident, $($pred_name:ident($($m:tt),*))&*) => {{
    use std::collections::HashMap;
    let mut vars : HashMap<String, ::holmes::native_types::HVar> = HashMap::new();
    let mut n : ::holmes::native_types::HVar = 0;
    $holmes.query(&vec![$(::holmes::native_types::Clause {
      pred_name : stringify!($pred_name).to_string(),
      args : vec![$(clause_match!(vars, n, $m)),*]
    }),*])
  }}
}

pub fn var_to_evar(var : MatchExpr) -> Expr {
  match var {
    MatchExpr::Var(var_no) => Expr::EVar(var_no),
    x => panic!("var_to_evar was passed nonvar: {:?}", x)
  }
}

#[macro_export]
macro_rules! hexpr {
  ($vars:ident, $n:ident, [$hexpr_name:ident]) => {
    var_to_evar(clause_match!($vars, $n, $hexpr_name))
  };
  ($vars:ident, $n:ident, ($hexpr:expr)) => {
    Expr::EVal(::holmes::native_types::ToHValue::to_hvalue($hexpr))
  };
  ($vars:ident, $n:ident, {$hexpr_func:ident($($hexpr_arg:tt),*)}) => {
    Expr::EApp(stringify!($hexpr_func).to_string(), vec![$(hexpr!($vars, $n, $hexpr_arg)),*])
  };
}

#[macro_export]
macro_rules! rule {
  ($holmes:ident, $head_name:ident($($m:tt),*) <= $($body_name:ident($($mb:tt),*))&*,
   {$(let $bind:tt = $hexpr:tt);*}) => {{
    use std::collections::HashMap;
    let mut vars : HashMap<String, ::holmes::native_types::HVar> = HashMap::new();
    let mut n : ::holmes::native_types::HVar = 0;
    $holmes.add_rule(&::holmes::native_types::Rule {
      body : vec![$(::holmes::native_types::Clause {
        pred_name : stringify!($body_name).to_string(),
        args : vec![$(clause_match!(vars, n, $mb)),*]
      }),*],
      head : ::holmes::native_types::Clause {
        pred_name : stringify!($head_name).to_string(),
        args : vec![$(clause_match!(vars, n, $m)),*]
      },
      wheres : vec! [$(::holmes::native_types::WhereClause {
        lhs   : bind_match!(vars, n, $bind),
        rhs   : hexpr!(vars, n, $hexpr)
      }),*]
    })
  }};
  ($holmes:ident, $head_name:ident($($m:tt),*) <= $($body_name:ident($($mb:tt),*))&*,
   {$(let $($bind:tt),* = $hexpr:tt);*}) => {{
    use std::collections::HashMap;
    let mut vars : HashMap<String, ::holmes::native_types::HVar> = HashMap::new();
    let mut n : ::holmes::native_types::HVar = 0;
    $holmes.add_rule(&::holmes::native_types::Rule {
      body : vec![$(::holmes::native_types::Clause {
        pred_name : stringify!($body_name).to_string(),
        args : vec![$(clause_match!(vars, n, $mb)),*]
      }),*],
      head : ::holmes::native_types::Clause {
        pred_name : stringify!($head_name).to_string(),
        args : vec![$(clause_match!(vars, n, $m)),*]
      },
      wheres : vec! [$(::holmes::native_types::WhereClause {
        lhs   : ::holmes::native_types::BindExpr::Destructure(
                  vec![$(::holmes::native_types::BindExpr::Normal(
                    clause_match!(vars, n, $bind))),*]),
        rhs   : hexpr!(vars, n, $hexpr)
      }),*]
    })
  }};
  ($($head_name:ident($($m:tt),*)),* <= $($body_name:ident($($mb:tt),*))&*) => {
    |holmes : &mut Holmes| {
      rule!(holmes, $($head_name($($m),*)),* <= $($body_name($($mb),*))&*, {})
    }
  };
  ($($head_name:ident($($m:tt),*)),* <= $($body_name:ident($($mb:tt),*))&*, {$(let $($bind:tt),* = $hexpr:tt);*}) => {
    |holmes : &mut Holmes| {
      rule!(holmes, $($head_name($($m),*)),* <= $($body_name($($mb),*))&*, {$(let $($bind),* = $hexpr);*})
    }
  };

}

#[macro_export]
macro_rules! func {
  ($holmes:ident, let $name:ident : $src:tt -> $dst:tt = $body:expr) => {
    $holmes.reg_func(stringify!($name).to_string(),
                     htype!($src),
                     htype!($dst),
                     Box::new($body))
  };
  (let $name:ident : $src:tt -> $dst:tt = $body:expr) => {
    |holmes : &mut Holmes| {
      func!(holmes, let $name : $src -> $dst = $body)
    }
  };
}
<|endoftext|>


<commit_before>extern crate libc;
use libc::{c_char, c_int, c_void, size_t};

// `libmagic` API as in "magic.h"

// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
#[repr(C)]
pub struct Magic {
    _data: [u8; 0],
    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}

pub const MAGIC_NONE: c_int = 0x0000000;
pub const MAGIC_DEBUG: c_int = 0x0000001;
pub const MAGIC_SYMLINK: c_int = 0x0000002;
pub const MAGIC_COMPRESS: c_int = 0x0000004;
pub const MAGIC_DEVICES: c_int = 0x0000008;
pub const MAGIC_MIME_TYPE: c_int = 0x0000010;
pub const MAGIC_CONTINUE: c_int = 0x0000020;
pub const MAGIC_CHECK: c_int = 0x0000040;
pub const MAGIC_PRESERVE_ATIME: c_int = 0x0000080;
pub const MAGIC_RAW: c_int = 0x0000100;
pub const MAGIC_ERROR: c_int = 0x0000200;
pub const MAGIC_MIME_ENCODING: c_int = 0x0000400;
pub const MAGIC_MIME: c_int = (MAGIC_MIME_TYPE | MAGIC_MIME_ENCODING);
pub const MAGIC_APPLE: c_int = 0x000800;
#[cfg(feature = "libmagic-abi-v523")]
pub const MAGIC_EXTENSION: c_int = 0x1000000;
#[cfg(feature = "libmagic-abi-v523")]
pub const MAGIC_COMPRESS_TRANSP: c_int = 0x2000000;
#[cfg(feature = "libmagic-abi-v523")]
pub const MAGIC_NODESC: c_int = (MAGIC_EXTENSION | MAGIC_MIME | MAGIC_APPLE);

pub const MAGIC_NO_CHECK_COMPRESS: c_int = 0x0001000;
pub const MAGIC_NO_CHECK_TAR: c_int = 0x0002000;
pub const MAGIC_NO_CHECK_SOFT: c_int = 0x0004000;
pub const MAGIC_NO_CHECK_APPTYPE: c_int = 0x0008000;
pub const MAGIC_NO_CHECK_ELF: c_int = 0x0010000;
pub const MAGIC_NO_CHECK_TEXT: c_int = 0x0020000;
pub const MAGIC_NO_CHECK_CDF: c_int = 0x0040000;
#[cfg(feature = "libmagic-abi-v538")]
pub const MAGIC_NO_CHECK_CSV: c_int = 0x0080000;
pub const MAGIC_NO_CHECK_TOKENS: c_int = 0x0100000;
pub const MAGIC_NO_CHECK_ENCODING: c_int = 0x0200000;
#[cfg(feature = "libmagic-abi-v535")]
pub const MAGIC_NO_CHECK_JSON: c_int = 0x0400000;

#[cfg(all(feature = "libmagic-abi-v505", not(feature = "libmagic-abi-v510")))]
pub const MAGIC_NO_CHECK_BUILTIN: c_int = 0x3fb000;
#[cfg(all(feature = "libmagic-abi-v510", not(feature = "libmagic-abi-v535")))]
pub const MAGIC_NO_CHECK_BUILTIN: c_int = MAGIC_NO_CHECK_COMPRESS |
MAGIC_NO_CHECK_TAR      |
/* MAGIC_NO_CHECK_SOFT | */
MAGIC_NO_CHECK_APPTYPE  |
MAGIC_NO_CHECK_ELF      |
MAGIC_NO_CHECK_TEXT     |
MAGIC_NO_CHECK_CDF      |
MAGIC_NO_CHECK_TOKENS   |
MAGIC_NO_CHECK_ENCODING;
#[cfg(all(feature = "libmagic-abi-v535", not(feature = "libmagic-abi-v538")))]
pub const MAGIC_NO_CHECK_BUILTIN: c_int = MAGIC_NO_CHECK_COMPRESS |
MAGIC_NO_CHECK_TAR      |
/* MAGIC_NO_CHECK_SOFT | */
MAGIC_NO_CHECK_APPTYPE  |
MAGIC_NO_CHECK_ELF      |
MAGIC_NO_CHECK_TEXT     |
MAGIC_NO_CHECK_CDF      |
MAGIC_NO_CHECK_TOKENS   |
MAGIC_NO_CHECK_ENCODING |
MAGIC_NO_CHECK_JSON |
0;
#[cfg(feature = "libmagic-abi-v538")]
pub const MAGIC_NO_CHECK_BUILTIN: c_int = MAGIC_NO_CHECK_COMPRESS |
MAGIC_NO_CHECK_TAR      |
/* MAGIC_NO_CHECK_SOFT | */
MAGIC_NO_CHECK_APPTYPE  |
MAGIC_NO_CHECK_ELF      |
MAGIC_NO_CHECK_TEXT     |
MAGIC_NO_CHECK_CSV |
MAGIC_NO_CHECK_CDF      |
MAGIC_NO_CHECK_TOKENS   |
MAGIC_NO_CHECK_ENCODING |
MAGIC_NO_CHECK_JSON |
0;

#[deprecated]
pub const MAGIC_NO_CHECK_ASCII: c_int = MAGIC_NO_CHECK_TEXT;

#[deprecated]
pub const MAGIC_NO_CHECK_FORTRAN: c_int = 0x000000;
#[deprecated]
pub const MAGIC_NO_CHECK_TROFF: c_int = 0x000000;

// TODO: MAGIC_VERSION string

// TODO: MAGIC_SNPRINTB bytes

#[cfg(feature = "libmagic-abi-v521")]
pub const MAGIC_PARAM_INDIR_MAX: c_int = 0;
#[cfg(feature = "libmagic-abi-v521")]
pub const MAGIC_PARAM_NAME_MAX: c_int = 1;
#[cfg(feature = "libmagic-abi-v521")]
pub const MAGIC_PARAM_ELF_PHNUM_MAX: c_int = 2;
#[cfg(feature = "libmagic-abi-v521")]
pub const MAGIC_PARAM_ELF_SHNUM_MAX: c_int = 3;
#[cfg(feature = "libmagic-abi-v522")]
pub const MAGIC_PARAM_ELF_NOTES_MAX: c_int = 4;
#[cfg(feature = "libmagic-abi-v525")]
pub const MAGIC_PARAM_REGEX_MAX: c_int = 5;
#[cfg(feature = "libmagic-abi-v527")]
pub const MAGIC_PARAM_BYTES_MAX: c_int = 6;
#[cfg(feature = "libmagic-abi-v540")]
pub const MAGIC_PARAM_ENCODING_MAX: c_int = 7;

// NOTE: the following are from `file.h`, but part of `magic.h` API
#[cfg(feature = "libmagic-abi-v504")]
pub const FILE_LOAD: c_int = 0;
#[cfg(feature = "libmagic-abi-v504")]
pub const FILE_CHECK: c_int = 1;
#[cfg(feature = "libmagic-abi-v504")]
pub const FILE_COMPILE: c_int = 2;
#[cfg(feature = "libmagic-abi-v505")]
pub const FILE_LIST: c_int = 3;

extern "C" {
    pub fn magic_open(flags: c_int) -> *const Magic;
    pub fn magic_close(cookie: *const Magic);

    #[cfg(feature = "libmagic-abi-v504")]
    pub fn magic_getpath(magicfile: *const c_char, action: c_int) -> *const c_char;
    pub fn magic_file(cookie: *const Magic, filename: *const c_char) -> *const c_char;
    pub fn magic_descriptor(cookie: *const Magic, fd: c_int) -> *const c_char;
    pub fn magic_buffer(cookie: *const Magic, buffer: *const u8, length: size_t) -> *const c_char;

    pub fn magic_error(cookie: *const Magic) -> *const c_char;
    #[cfg(feature = "libmagic-abi-v532")]
    pub fn magic_getflags(cookie: *const Magic) -> c_int;
    #[must_use]
    pub fn magic_setflags(cookie: *const Magic, flags: c_int) -> c_int;

    #[cfg(feature = "libmagic-abi-v513")]
    pub fn magic_version() -> c_int;
    #[must_use]
    pub fn magic_load(cookie: *const Magic, filename: *const c_char) -> c_int;
    #[cfg(feature = "libmagic-abi-v520")]
    #[must_use]
    pub fn magic_load_buffers(
        cookie: *const Magic,
        buffers: *const *const c_void,
        sizes: *const size_t,
        nbuffers: size_t,
    ) -> c_int;

    #[must_use]
    pub fn magic_compile(cookie: *const Magic, filename: *const c_char) -> c_int;
    #[must_use]
    pub fn magic_check(cookie: *const Magic, filename: *const c_char) -> c_int;
    #[cfg(feature = "libmagic-abi-v505")]
    #[must_use]
    pub fn magic_list(cookie: *const Magic, filename: *const c_char) -> c_int;
    pub fn magic_errno(cookie: *const Magic) -> *const c_int;

    #[cfg(feature = "libmagic-abi-v521")]
    #[must_use]
    pub fn magic_setparam(cookie: *const Magic, param: c_int, value: *const c_void) -> c_int;
    #[cfg(feature = "libmagic-abi-v521")]
    #[must_use]
    pub fn magic_getparam(cookie: *const Magic, param: c_int, value: *mut c_void) -> c_int;
}
<commit_msg>Add tests for all linked functions<commit_after>extern crate libc;
use libc::{c_char, c_int, c_void, size_t};

// `libmagic` API as in "magic.h"

// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
#[repr(C)]
pub struct Magic {
    _data: [u8; 0],
    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}

pub const MAGIC_NONE: c_int = 0x0000000;
pub const MAGIC_DEBUG: c_int = 0x0000001;
pub const MAGIC_SYMLINK: c_int = 0x0000002;
pub const MAGIC_COMPRESS: c_int = 0x0000004;
pub const MAGIC_DEVICES: c_int = 0x0000008;
pub const MAGIC_MIME_TYPE: c_int = 0x0000010;
pub const MAGIC_CONTINUE: c_int = 0x0000020;
pub const MAGIC_CHECK: c_int = 0x0000040;
pub const MAGIC_PRESERVE_ATIME: c_int = 0x0000080;
pub const MAGIC_RAW: c_int = 0x0000100;
pub const MAGIC_ERROR: c_int = 0x0000200;
pub const MAGIC_MIME_ENCODING: c_int = 0x0000400;
pub const MAGIC_MIME: c_int = (MAGIC_MIME_TYPE | MAGIC_MIME_ENCODING);
pub const MAGIC_APPLE: c_int = 0x000800;
#[cfg(feature = "libmagic-abi-v523")]
pub const MAGIC_EXTENSION: c_int = 0x1000000;
#[cfg(feature = "libmagic-abi-v523")]
pub const MAGIC_COMPRESS_TRANSP: c_int = 0x2000000;
#[cfg(feature = "libmagic-abi-v523")]
pub const MAGIC_NODESC: c_int = (MAGIC_EXTENSION | MAGIC_MIME | MAGIC_APPLE);

pub const MAGIC_NO_CHECK_COMPRESS: c_int = 0x0001000;
pub const MAGIC_NO_CHECK_TAR: c_int = 0x0002000;
pub const MAGIC_NO_CHECK_SOFT: c_int = 0x0004000;
pub const MAGIC_NO_CHECK_APPTYPE: c_int = 0x0008000;
pub const MAGIC_NO_CHECK_ELF: c_int = 0x0010000;
pub const MAGIC_NO_CHECK_TEXT: c_int = 0x0020000;
pub const MAGIC_NO_CHECK_CDF: c_int = 0x0040000;
#[cfg(feature = "libmagic-abi-v538")]
pub const MAGIC_NO_CHECK_CSV: c_int = 0x0080000;
pub const MAGIC_NO_CHECK_TOKENS: c_int = 0x0100000;
pub const MAGIC_NO_CHECK_ENCODING: c_int = 0x0200000;
#[cfg(feature = "libmagic-abi-v535")]
pub const MAGIC_NO_CHECK_JSON: c_int = 0x0400000;

#[cfg(all(feature = "libmagic-abi-v505", not(feature = "libmagic-abi-v510")))]
pub const MAGIC_NO_CHECK_BUILTIN: c_int = 0x3fb000;
#[cfg(all(feature = "libmagic-abi-v510", not(feature = "libmagic-abi-v535")))]
pub const MAGIC_NO_CHECK_BUILTIN: c_int = MAGIC_NO_CHECK_COMPRESS |
MAGIC_NO_CHECK_TAR      |
/* MAGIC_NO_CHECK_SOFT | */
MAGIC_NO_CHECK_APPTYPE  |
MAGIC_NO_CHECK_ELF      |
MAGIC_NO_CHECK_TEXT     |
MAGIC_NO_CHECK_CDF      |
MAGIC_NO_CHECK_TOKENS   |
MAGIC_NO_CHECK_ENCODING;
#[cfg(all(feature = "libmagic-abi-v535", not(feature = "libmagic-abi-v538")))]
pub const MAGIC_NO_CHECK_BUILTIN: c_int = MAGIC_NO_CHECK_COMPRESS |
MAGIC_NO_CHECK_TAR      |
/* MAGIC_NO_CHECK_SOFT | */
MAGIC_NO_CHECK_APPTYPE  |
MAGIC_NO_CHECK_ELF      |
MAGIC_NO_CHECK_TEXT     |
MAGIC_NO_CHECK_CDF      |
MAGIC_NO_CHECK_TOKENS   |
MAGIC_NO_CHECK_ENCODING |
MAGIC_NO_CHECK_JSON |
0;
#[cfg(feature = "libmagic-abi-v538")]
pub const MAGIC_NO_CHECK_BUILTIN: c_int = MAGIC_NO_CHECK_COMPRESS |
MAGIC_NO_CHECK_TAR      |
/* MAGIC_NO_CHECK_SOFT | */
MAGIC_NO_CHECK_APPTYPE  |
MAGIC_NO_CHECK_ELF      |
MAGIC_NO_CHECK_TEXT     |
MAGIC_NO_CHECK_CSV |
MAGIC_NO_CHECK_CDF      |
MAGIC_NO_CHECK_TOKENS   |
MAGIC_NO_CHECK_ENCODING |
MAGIC_NO_CHECK_JSON |
0;

#[deprecated]
pub const MAGIC_NO_CHECK_ASCII: c_int = MAGIC_NO_CHECK_TEXT;

#[deprecated]
pub const MAGIC_NO_CHECK_FORTRAN: c_int = 0x000000;
#[deprecated]
pub const MAGIC_NO_CHECK_TROFF: c_int = 0x000000;

// TODO: MAGIC_VERSION string

// TODO: MAGIC_SNPRINTB bytes

#[cfg(feature = "libmagic-abi-v521")]
pub const MAGIC_PARAM_INDIR_MAX: c_int = 0;
#[cfg(feature = "libmagic-abi-v521")]
pub const MAGIC_PARAM_NAME_MAX: c_int = 1;
#[cfg(feature = "libmagic-abi-v521")]
pub const MAGIC_PARAM_ELF_PHNUM_MAX: c_int = 2;
#[cfg(feature = "libmagic-abi-v521")]
pub const MAGIC_PARAM_ELF_SHNUM_MAX: c_int = 3;
#[cfg(feature = "libmagic-abi-v522")]
pub const MAGIC_PARAM_ELF_NOTES_MAX: c_int = 4;
#[cfg(feature = "libmagic-abi-v525")]
pub const MAGIC_PARAM_REGEX_MAX: c_int = 5;
#[cfg(feature = "libmagic-abi-v527")]
pub const MAGIC_PARAM_BYTES_MAX: c_int = 6;
#[cfg(feature = "libmagic-abi-v540")]
pub const MAGIC_PARAM_ENCODING_MAX: c_int = 7;

// NOTE: the following are from `file.h`, but part of `magic.h` API
#[cfg(feature = "libmagic-abi-v504")]
pub const FILE_LOAD: c_int = 0;
#[cfg(feature = "libmagic-abi-v504")]
pub const FILE_CHECK: c_int = 1;
#[cfg(feature = "libmagic-abi-v504")]
pub const FILE_COMPILE: c_int = 2;
#[cfg(feature = "libmagic-abi-v505")]
pub const FILE_LIST: c_int = 3;

extern "C" {
    pub fn magic_open(flags: c_int) -> *const Magic;
    pub fn magic_close(cookie: *const Magic);

    #[cfg(feature = "libmagic-abi-v504")]
    pub fn magic_getpath(magicfile: *const c_char, action: c_int) -> *const c_char;
    pub fn magic_file(cookie: *const Magic, filename: *const c_char) -> *const c_char;
    pub fn magic_descriptor(cookie: *const Magic, fd: c_int) -> *const c_char;
    pub fn magic_buffer(cookie: *const Magic, buffer: *const u8, length: size_t) -> *const c_char;

    pub fn magic_error(cookie: *const Magic) -> *const c_char;
    #[cfg(feature = "libmagic-abi-v532")]
    pub fn magic_getflags(cookie: *const Magic) -> c_int;
    #[must_use]
    pub fn magic_setflags(cookie: *const Magic, flags: c_int) -> c_int;

    #[cfg(feature = "libmagic-abi-v513")]
    pub fn magic_version() -> c_int;
    #[must_use]
    pub fn magic_load(cookie: *const Magic, filename: *const c_char) -> c_int;
    #[cfg(feature = "libmagic-abi-v520")]
    #[must_use]
    pub fn magic_load_buffers(
        cookie: *const Magic,
        buffers: *const *const c_void,
        sizes: *const size_t,
        nbuffers: size_t,
    ) -> c_int;

    #[must_use]
    pub fn magic_compile(cookie: *const Magic, filename: *const c_char) -> c_int;
    #[must_use]
    pub fn magic_check(cookie: *const Magic, filename: *const c_char) -> c_int;
    #[cfg(feature = "libmagic-abi-v505")]
    #[must_use]
    pub fn magic_list(cookie: *const Magic, filename: *const c_char) -> c_int;
    pub fn magic_errno(cookie: *const Magic) -> *const c_int;

    #[cfg(feature = "libmagic-abi-v521")]
    #[must_use]
    pub fn magic_setparam(cookie: *const Magic, param: c_int, value: *const c_void) -> c_int;
    #[cfg(feature = "libmagic-abi-v521")]
    #[must_use]
    pub fn magic_getparam(cookie: *const Magic, param: c_int, value: *mut c_void) -> c_int;
}

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

    // Those tests are mostly just sanity checks to see if linkage works,
    // they are NOT testing `libmagic` API/implementation

    #[test]
    fn test_magic_open() {
        unsafe {
            magic_open(MAGIC_NONE);
        }
    }

    #[test]
    fn test_magic_close() {
        unsafe {
            magic_close(std::ptr::null());
        }
    }

    #[cfg(feature = "libmagic-abi-v504")]
    #[test]
    fn test_magic_getpath() {
        unsafe {
            magic_getpath(std::ptr::null(), FILE_CHECK);
        }
    }

    #[test]
    fn test_magic_file() {
        unsafe {
            magic_file(std::ptr::null(), std::ptr::null());
        }
    }

    #[test]
    fn test_magic_descriptor() {
        unsafe {
            magic_descriptor(std::ptr::null(), -1);
        }
    }

    #[test]
    fn test_magic_buffer() {
        unsafe {
            magic_buffer(std::ptr::null(), std::ptr::null(), 0);
        }
    }

    #[test]
    fn test_magic_error() {
        unsafe {
            magic_error(std::ptr::null());
        }
    }

    #[cfg(feature = "libmagic-abi-v532")]
    #[test]
    fn test_magic_getflags() {
        unsafe {
            magic_getflags(std::ptr::null());
        }
    }

    #[test]
    fn test_magic_setflags() {
        unsafe {
            let _ = magic_setflags(std::ptr::null(), MAGIC_NONE);
        }
    }

    #[cfg(feature = "libmagic-abi-v513")]
    #[test]
    fn test_magic_version() {
        unsafe {
            magic_version();
        }
    }

    #[test]
    fn test_magic_load() {
        unsafe {
            let _ = magic_load(std::ptr::null(), std::ptr::null());
        }
    }

    #[cfg(feature = "libmagic-abi-v520")]
    #[test]
    fn test_magic_load_buffers() {
        unsafe {
            let _ = magic_load_buffers(std::ptr::null(), std::ptr::null(), std::ptr::null(), 0);
        }
    }

    #[test]
    fn test_magic_compile() {
        unsafe {
            let _ = magic_compile(std::ptr::null(), std::ptr::null());
        }
    }

    #[test]
    fn test_magic_check() {
        unsafe {
            let _ = magic_check(std::ptr::null(), std::ptr::null());
        }
    }

    #[cfg(feature = "libmagic-abi-v505")]
    #[test]
    fn test_magic_list() {
        unsafe {
            let _ = magic_list(std::ptr::null(), std::ptr::null());
        }
    }

    #[test]
    fn test_magic_errno() {
        unsafe {
            magic_errno(std::ptr::null());
        }
    }

    #[cfg(feature = "libmagic-abi-v521")]
    #[test]
    fn test_magic_setparam() {
        unsafe {
            let _ = magic_setparam(std::ptr::null(), MAGIC_PARAM_INDIR_MAX, std::ptr::null());
        }
    }

    #[cfg(feature = "libmagic-abi-v521")]
    #[test]
    fn test_magic_getparam() {
        unsafe {
            let _ = magic_getparam(
                std::ptr::null(),
                MAGIC_PARAM_INDIR_MAX,
                std::ptr::null_mut(),
            );
        }
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(not(target_family = "unix"), no_std)]
#![crate_name = "klogger"]
#![crate_type = "lib"]

#[cfg(not(target_family = "unix"))]
mod std {
    use core::fmt;
    use core::ops;
}

#[cfg(target_family = "unix")]
use std::fmt;

#[cfg(target_family = "unix")]
use std::ops;

#[macro_use]
pub mod macros;

extern crate log;
extern crate termcodes;

#[macro_use]
extern crate raw_cpuid;

#[cfg(target_arch = "x86_64")]
extern crate x86;

#[cfg(all(target_arch = "x86_64", target_os = "none"))]
#[path = "arch/x86.rs"]
mod arch;

#[cfg(target_family = "unix")]
#[path = "arch/unix.rs"]
mod arch;

use log::{Level, Metadata, Record, SetLoggerError};
use termcodes::color;

static mut LOGGER: KLogger = KLogger {
    has_tsc: false,
    has_invariant_tsc: false,

    tsc_start: 0,
    tsc_frequency: 2_000_000_000,
};

pub struct Writer;

impl Writer {
    /// Obtain a logger for the specified module.
    pub fn get_module(module: &str) -> Writer {
        use std::fmt::Write;
        let mut ret = Writer;
        write!(&mut ret, "[{}] ", module).expect("Writer");
        ret
    }

    pub fn get() -> Writer {
        Writer
    }
}

impl ops::Drop for Writer {
    /// Release the logger.
    fn drop(&mut self) {
        use std::fmt::Write;
        write!(self, "\n").expect("Newline");
    }
}

impl fmt::Write for Writer {
    /// Write stuff to serial out.
    fn write_str(&mut self, s: &str) -> fmt::Result {
        unsafe {
            arch::puts(s);
        }
        Ok(())
    }
}

pub struct WriterNoDrop;

impl WriterNoDrop {
    pub fn get() -> WriterNoDrop {
        WriterNoDrop
    }
}

impl fmt::Write for WriterNoDrop {
    /// Write stuff to serial out.
    fn write_str(&mut self, s: &str) -> fmt::Result {
        unsafe {
            arch::puts(s);
        }
        Ok(())
    }
}

#[derive(Debug)]
struct KLogger {
    has_tsc: bool,
    has_invariant_tsc: bool,
    tsc_start: u64,
    /// Frequency in Hz
    tsc_frequency: u64,
}

impl KLogger {
    /// Time in nano seconds since KLogger init.
    fn elapsed_ns(&self) -> u64 {
        if self.has_tsc {
            let cur = unsafe { x86::time::rdtsc() };
            let elapsed = (cur - self.tsc_start) as f64;
            (elapsed / (self.tsc_frequency as f64 / 1_000_000_000.0)) as u64
        } else {
            0
        }
    }
}

impl log::Log for KLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Trace
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            let color = match record.level() {
                Level::Error => color::AnsiValue(202),
                Level::Warn => color::AnsiValue(167),
                Level::Info => color::AnsiValue(136),
                Level::Debug => color::AnsiValue(64),
                Level::Trace => color::AnsiValue(32),
            };

            sprintln!(
                "{}{:>10}{} [{}{:5}{}] - {}: {}{}{}",
                color::Fg(color::LightYellow),
                self.elapsed_ns(),
                color::Fg(color::Reset),
                color::Fg(color),
                record.level(),
                color::Fg(color::Reset),
                record.target(),
                color::Fg(color::LightWhite),
                record.args(),
                color::Fg(color::Reset),
            );
        }
    }

    fn flush(&self) {}
}

pub fn init(level: Level) -> Result<(), SetLoggerError> {
    let cpuid = raw_cpuid::CpuId::new();

    unsafe {
        (&mut LOGGER).has_tsc = cpuid
            .get_feature_info()
            .map_or(false, |finfo| finfo.has_tsc());
        (&mut LOGGER).has_invariant_tsc = cpuid
            .get_extended_function_info()
            .map_or(false, |efinfo| efinfo.has_invariant_tsc());
        if LOGGER.has_tsc {
            (&mut LOGGER).tsc_start = x86::time::rdtsc();
        }
        let hypervisor_base = cpuid!(0x40000000, 0);
        let is_kvm = hypervisor_base.eax == 0x40000010
            && hypervisor_base.ebx == 0x4b4d564b
            && hypervisor_base.ecx == 0x564b4d56
            && hypervisor_base.edx == 0x4d;

        if cpuid.get_tsc_info().is_some() {
            // Nominal TSC frequency = ( CPUID.15H.ECX[31:0] * CPUID.15H.EBX[31:0] ) ÷ CPUID.15H.EAX[31:0]
            (&mut LOGGER).tsc_frequency = cpuid
                .get_tsc_info()
                .map_or(2_000_000_000, |tinfo| tinfo.tsc_frequency());
        } else if cpuid.get_processor_frequency_info().is_some() {
            (&mut LOGGER).tsc_frequency = cpuid
                .get_processor_frequency_info()
                .map_or(2_000_000_000, |pinfo| {
                    pinfo.processor_max_frequency() as u64 * 1000000
                });
        } else if is_kvm {
            // vm aware tsc frequency retrieval: https://lwn.net/Articles/301888/
            // # EAX: (Virtual) TSC frequency in kHz.
            // # EBX: (Virtual) Bus (local apic timer) frequency in kHz.
            // # ECX, EDX: RESERVED (Per above, reserved fields are set to zero).
            let virt_tinfo = cpuid!(0x40000010, 0);
            (&mut LOGGER).tsc_frequency = virt_tinfo.eax as u64 * 1000;
        } else {
            (&mut LOGGER).tsc_frequency = 2_000_000_000;
        }

        // Another way that segfaults in KVM:
        // The scalable bus frequency is encoded in the bit field MSR_PLATFORM_INFO[15:8]
        // and the nominal TSC frequency can be determined by multiplying this number
        // by a bus speed of 100 MHz.
        //(&mut LOGGER).tsc_frequency =
        //    ((x86::msr::rdmsr(x86::msr::MSR_PLATFORM_INFO) >> 8) & 0xff) * 1000000;

        log::set_logger(&LOGGER).map(|()| log::set_max_level(level.to_level_filter()))
    }
}
<commit_msg>Fix for no_std case.<commit_after>#![cfg_attr(not(target_family = "unix"), no_std)]
#![crate_name = "klogger"]
#![crate_type = "lib"]

#[cfg(not(target_family = "unix"))]
mod std {
    pub use core::fmt;
    pub use core::ops;
}

use std::fmt;
use std::ops;

#[macro_use]
pub mod macros;

extern crate log;
extern crate termcodes;

#[macro_use]
extern crate raw_cpuid;

#[cfg(target_arch = "x86_64")]
extern crate x86;

#[cfg(all(target_arch = "x86_64", target_os = "none"))]
#[path = "arch/x86.rs"]
mod arch;

#[cfg(target_family = "unix")]
#[path = "arch/unix.rs"]
mod arch;

use log::{Level, Metadata, Record, SetLoggerError};
use termcodes::color;

static mut LOGGER: KLogger = KLogger {
    has_tsc: false,
    has_invariant_tsc: false,

    tsc_start: 0,
    tsc_frequency: 2_000_000_000,
};

pub struct Writer;

impl Writer {
    /// Obtain a logger for the specified module.
    pub fn get_module(module: &str) -> Writer {
        use std::fmt::Write;
        let mut ret = Writer;
        write!(&mut ret, "[{}] ", module).expect("Writer");
        ret
    }

    pub fn get() -> Writer {
        Writer
    }
}

impl ops::Drop for Writer {
    /// Release the logger.
    fn drop(&mut self) {
        use std::fmt::Write;
        write!(self, "\n").expect("Newline");
    }
}

impl fmt::Write for Writer {
    /// Write stuff to serial out.
    fn write_str(&mut self, s: &str) -> fmt::Result {
        unsafe {
            arch::puts(s);
        }
        Ok(())
    }
}

pub struct WriterNoDrop;

impl WriterNoDrop {
    pub fn get() -> WriterNoDrop {
        WriterNoDrop
    }
}

impl fmt::Write for WriterNoDrop {
    /// Write stuff to serial out.
    fn write_str(&mut self, s: &str) -> fmt::Result {
        unsafe {
            arch::puts(s);
        }
        Ok(())
    }
}

#[derive(Debug)]
struct KLogger {
    has_tsc: bool,
    has_invariant_tsc: bool,
    tsc_start: u64,
    /// Frequency in Hz
    tsc_frequency: u64,
}

impl KLogger {
    /// Time in nano seconds since KLogger init.
    fn elapsed_ns(&self) -> u64 {
        if self.has_tsc {
            let cur = unsafe { x86::time::rdtsc() };
            let elapsed = (cur - self.tsc_start) as f64;
            (elapsed / (self.tsc_frequency as f64 / 1_000_000_000.0)) as u64
        } else {
            0
        }
    }
}

impl log::Log for KLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Trace
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            let color = match record.level() {
                Level::Error => color::AnsiValue(202),
                Level::Warn => color::AnsiValue(167),
                Level::Info => color::AnsiValue(136),
                Level::Debug => color::AnsiValue(64),
                Level::Trace => color::AnsiValue(32),
            };

            sprintln!(
                "{}{:>10}{} [{}{:5}{}] - {}: {}{}{}",
                color::Fg(color::LightYellow),
                self.elapsed_ns(),
                color::Fg(color::Reset),
                color::Fg(color),
                record.level(),
                color::Fg(color::Reset),
                record.target(),
                color::Fg(color::LightWhite),
                record.args(),
                color::Fg(color::Reset),
            );
        }
    }

    fn flush(&self) {}
}

pub fn init(level: Level) -> Result<(), SetLoggerError> {
    let cpuid = raw_cpuid::CpuId::new();

    unsafe {
        (&mut LOGGER).has_tsc = cpuid
            .get_feature_info()
            .map_or(false, |finfo| finfo.has_tsc());
        (&mut LOGGER).has_invariant_tsc = cpuid
            .get_extended_function_info()
            .map_or(false, |efinfo| efinfo.has_invariant_tsc());
        if LOGGER.has_tsc {
            (&mut LOGGER).tsc_start = x86::time::rdtsc();
        }
        let hypervisor_base = cpuid!(0x40000000, 0);
        let is_kvm = hypervisor_base.eax == 0x40000010
            && hypervisor_base.ebx == 0x4b4d564b
            && hypervisor_base.ecx == 0x564b4d56
            && hypervisor_base.edx == 0x4d;

        if cpuid.get_tsc_info().is_some() {
            // Nominal TSC frequency = ( CPUID.15H.ECX[31:0] * CPUID.15H.EBX[31:0] ) ÷ CPUID.15H.EAX[31:0]
            (&mut LOGGER).tsc_frequency = cpuid
                .get_tsc_info()
                .map_or(2_000_000_000, |tinfo| tinfo.tsc_frequency());
        } else if cpuid.get_processor_frequency_info().is_some() {
            (&mut LOGGER).tsc_frequency = cpuid
                .get_processor_frequency_info()
                .map_or(2_000_000_000, |pinfo| {
                    pinfo.processor_max_frequency() as u64 * 1000000
                });
        } else if is_kvm {
            // vm aware tsc frequency retrieval: https://lwn.net/Articles/301888/
            // # EAX: (Virtual) TSC frequency in kHz.
            // # EBX: (Virtual) Bus (local apic timer) frequency in kHz.
            // # ECX, EDX: RESERVED (Per above, reserved fields are set to zero).
            let virt_tinfo = cpuid!(0x40000010, 0);
            (&mut LOGGER).tsc_frequency = virt_tinfo.eax as u64 * 1000;
        } else {
            (&mut LOGGER).tsc_frequency = 2_000_000_000;
        }

        // Another way that segfaults in KVM:
        // The scalable bus frequency is encoded in the bit field MSR_PLATFORM_INFO[15:8]
        // and the nominal TSC frequency can be determined by multiplying this number
        // by a bus speed of 100 MHz.
        //(&mut LOGGER).tsc_frequency =
        //    ((x86::msr::rdmsr(x86::msr::MSR_PLATFORM_INFO) >> 8) & 0xff) * 1000000;

        log::set_logger(&LOGGER).map(|()| log::set_max_level(level.to_level_filter()))
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate multipart;
extern crate serde_json;

use hyper::net::Streaming;
use hyper::method::Method;
use hyper::client::Request;

use hyper::client::RequestBuilder;
use hyper::Client;
use hyper::Url;

use multipart::client::Multipart;

use serde_json::value::Value;

use std::result::Result;
use std::sync::Arc;
use std::io::Read;
use std::{fmt, error};

pub mod types {
    extern crate serde_json;
    use serde_json::value::Value;

    include!(concat!(env!("OUT_DIR"), "/types.rs"));
}

pub mod args;
pub mod builders;

use types::*;

// TODO: Organize this mess and get rid of most unwraps

#[derive(Debug)]
pub enum BotError {
    Http (hyper::error::Error),
    Api {
        error_code: i64,
        description: String,
        parameters: Option<ResponseParameters>
    }
}

impl fmt::Display for BotError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &BotError::Http(ref e) => {
                write!(f, "{}", e)
            },
            &BotError::Api { error_code, ref description, parameters: _ } => {
                write!(f, "Error {0}: {1}", error_code, description)
            }
        }
    }
}

impl error::Error for BotError {
    fn description(&self) -> &str {
        "Something unexpected occured while talking to the telegram bot api." // meh
    }
}

fn parse_request(response: Result<hyper::client::Response, hyper::Error>) -> Result<Value, BotError> {
    match response {
        Ok(mut res) => {
            let mut body = String::new();
            res.read_to_string(&mut body).unwrap();
            let got_me: ApiResult = serde_json::from_str(&body).unwrap();
            if let Some(val) = got_me.result {
                Ok(val)
            } else {
                Err(BotError::Api{
                    error_code: got_me.error_code.unwrap(),
                    description: got_me.description.unwrap(),
                    parameters: got_me.parameters
                })
            }
        },
        Err(e) => {
            Err(BotError::Http(e))
        }
    }
}

#[derive(Debug)]
pub struct BotApi {
    base_url: Url,
    client: Client,
}

impl Clone for BotApi {
    fn clone(&self) -> BotApi {
        BotApi {
            base_url: self.base_url.clone(),
            client: Client::new(),
        }
    }
}

impl BotApi {
    pub fn new(bot_token: &str) -> BotApi {
        let url = format!("https://api.telegram.org/bot{}/", bot_token);

        BotApi {
            base_url: url.parse().unwrap(),
            client: Client::new(),
        }
    }

    pub fn get_me(&self) -> Result<User, BotError> {
        let url = self.base_url.join("getMe").unwrap();
        let res = parse_request(self.client.get(url).send());
        match res {
            Ok(val) => Ok(serde_json::value::from_value(val).unwrap()),
            Err(e) => Err(e),
        }
    }

    pub fn get_updates(&self, params: &args::GetUpdates) -> Result<Vec<Update>, BotError> {
        let url = self.base_url.join("getUpdates").unwrap();

        // All these unwraps make me uncomfortable, someone is going to yell
        // at me to get some good error handling. And they would be right
        // but I'm really lazy and I don't want to deal with this. I should
        // though because it's good practice and no real software should ever
        // just throw errors around like they don't matter.

        // Manage your errors

        let req = Request::new(Method::Post, url).unwrap();

        let mut multi = Multipart::from_request(req).unwrap();

        if let Some(offset) = params.offset {
            multi.write_text("offset", offset.to_string()).unwrap();
        }

        if let Some(limit) = params.limit {
            multi.write_text("limit", limit.to_string()).unwrap();
        }

        if let Some(timeout) = params.timeout {
            multi.write_text("timeout", timeout.to_string()).unwrap();
        }

        if let Some(allowed_updates) = params.allowed_updates {
            for allowed_update in allowed_updates {
                multi.write_text("allowed_updates[]", allowed_update).unwrap();
            }
        }

        match parse_request(multi.send()) {
            Ok(val) => Ok(serde_json::value::from_value(val).unwrap()),
            Err(e) => Err(e),
        }
    }

    pub fn send_message(&self, params: &args::SendMessage) -> Result<Message, BotError> {
        let url = self.base_url.join("sendMessage").unwrap();
        let req = Request::new(Method::Post, url).unwrap();
        let mut multi = Multipart::from_request(req).unwrap();

        if let Some(chat_id) = params.chat_id {
            multi.write_text("chat_id", chat_id.to_string()).unwrap();
        } else
        if let Some(chat_username) = params.chat_username {
            multi.write_text("chat_id", chat_username).unwrap();
        }

        multi.write_text("text", params.text).unwrap();

        if let Some(parse_mode) = params.parse_mode {
            multi.write_text("parse_mode", parse_mode).unwrap();
        }

        if let Some(disable_web_page_preview) = params.disable_web_page_preview {
            multi.write_text("disable_web_page_preview", disable_web_page_preview.to_string()).unwrap();
        }

        if let Some(disable_notification) = params.disable_notification {
            multi.write_text("disable_notification", disable_notification.to_string()).unwrap();
        }

        if let Some(reply_to_message_id) = params.reply_to_message_id {
            multi.write_text("reply_to_message_id", reply_to_message_id.to_string()).unwrap();
        }

        // if let Some(reply_markup) = params.reply_markup {
        //     multi.write_text("reply_markup", reply_markup).unwrap();
        // }

        // 1: TODO create macro or just a function that will take a serde_json::Value
        // and multi.write_text correctly all of its stuff. then some function for
        // arrays that is essentially just a loop

        match parse_request(multi.send()) {
            Ok(val) => Ok(serde_json::value::from_value(val).unwrap()),
            Err(e) => Err(e),
        }
    }

} // 2: TODO don't use multipart when you don't have to

#[cfg(test)]
mod tests { // These aren't going to be the actual tests, just a place for me to easily test things as I go along
    use super::*;
    extern crate serde_json;
    #[test]
    fn it_works() {
        let token = "";
        let bot = BotApi::new(token);

        let mut update_args = args::GetUpdates {
            offset: Some(0),
            limit: None,
            timeout: Some(600),
            allowed_updates: None,
        };

        'update_loop: loop {
            let updates = bot.get_updates(&update_args).unwrap();

            for update in updates {
                update_args.offset = Some(update.update_id + 1);
                println!("{:?}", update_args.offset);

                if let Some(message) = update.message {
                    let from = message.from.unwrap();
                    let text = format!("Hi {} [{}]!", from.first_name,
                                                      from.id);

                    let mut msg_args = args::SendMessage {
                        chat_id: None,
                        chat_username: None,
                        text: "",
                        parse_mode: Some("Markdown"),
                        disable_web_page_preview: None,
                        disable_notification: None,
                        reply_to_message_id: None,
                        reply_markup: None,
                    };

                    msg_args.text = &text;
                    msg_args.chat_id = Some(message.chat.id);
                    msg_args.reply_to_message_id = Some(message.message_id);

                    bot.send_message(&msg_args);

                    if let Some(text) = message.text {
                        if text == "/exit" {
                            break 'update_loop;
                        }
                    }
                }
            }
        }
        bot.get_updates(&update_args);
        // Alright, so if you ever decide to have a function that terminates
        // your bot, make sure you have a check at the beginning of the loop
        // that makes sure you aren't processing old messages. You could also
        // just make a getUpdates at the end of the execution that just uses
        // the latest offset. This prevents you from having to reread any
        // updates that were in the update array you got that had the terminate
        // command. Because telegram only will stop sending you the update
        // after you have used an offset greater than its. So if you never make
        // another getUpdates, you will boot up, and terminate, forever. :)
    }
}
<commit_msg>[WIP] added function to write value to multipart<commit_after>extern crate hyper;
extern crate multipart;
extern crate serde_json;

use hyper::net::Streaming;
use hyper::method::Method;
use hyper::client::Request;

use hyper::client::RequestBuilder;
use hyper::Client;
use hyper::Url;

use multipart::client::Multipart;

use serde_json::value::Value;

use std::result::Result;
use std::sync::Arc;
use std::io::Read;
use std::{fmt, error};

pub mod types {
    extern crate serde_json;
    use serde_json::value::Value;

    include!(concat!(env!("OUT_DIR"), "/types.rs"));
}

pub mod args;
pub mod builders;

use types::*;

// TODO: Organize this mess and get rid of most unwraps

#[derive(Debug)]
pub enum BotError {
    Http (hyper::error::Error),
    Api {
        error_code: i64,
        description: String,
        parameters: Option<ResponseParameters>
    }
}

impl fmt::Display for BotError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &BotError::Http(ref e) => {
                write!(f, "{}", e)
            },
            &BotError::Api { error_code, ref description, parameters: _ } => {
                write!(f, "Error {0}: {1}", error_code, description)
            }
        }
    }
}

impl error::Error for BotError {
    fn description(&self) -> &str {
        "Something unexpected occured while talking to the telegram bot api." // meh
    }
}

fn parse_request(response: Result<hyper::client::Response, hyper::Error>) -> Result<Value, BotError> {
    match response {
        Ok(mut res) => {
            let mut body = String::new();
            res.read_to_string(&mut body).unwrap();
            let got_me: ApiResult = serde_json::from_str(&body).unwrap();
            if let Some(val) = got_me.result {
                Ok(val)
            } else {
                Err(BotError::Api{
                    error_code: got_me.error_code.unwrap(),
                    description: got_me.description.unwrap(),
                    parameters: got_me.parameters
                })
            }
        },
        Err(e) => {
            Err(BotError::Http(e))
        }
    }
}

#[derive(Debug)]
pub struct BotApi {
    base_url: Url,
    client: Client,
}

impl Clone for BotApi {
    fn clone(&self) -> BotApi {
        BotApi {
            base_url: self.base_url.clone(),
            client: Client::new(),
        }
    }
}

impl BotApi {
    pub fn new(bot_token: &str) -> BotApi {
        let url = format!("https://api.telegram.org/bot{}/", bot_token);

        BotApi {
            base_url: url.parse().unwrap(),
            client: Client::new(),
        }
    }

    pub fn get_me(&self) -> Result<User, BotError> {
        let url = self.base_url.join("getMe").unwrap();
        let res = parse_request(self.client.get(url).send());
        match res {
            Ok(val) => Ok(serde_json::value::from_value(val).unwrap()),
            Err(e) => Err(e),
        }
    }

    pub fn get_updates(&self, params: &args::GetUpdates) -> Result<Vec<Update>, BotError> {
        let url = self.base_url.join("getUpdates").unwrap();

        // All these unwraps make me uncomfortable, someone is going to yell
        // at me to get some good error handling. And they would be right
        // but I'm really lazy and I don't want to deal with this. I should
        // though because it's good practice and no real software should ever
        // just throw errors around like they don't matter.

        // Manage your errors

        let req = Request::new(Method::Post, url).unwrap();

        let mut multi = Multipart::from_request(req).unwrap();

        if let Some(offset) = params.offset {
            multi.write_text("offset", offset.to_string()).unwrap();
        }

        if let Some(limit) = params.limit {
            multi.write_text("limit", limit.to_string()).unwrap();
        }

        if let Some(timeout) = params.timeout {
            multi.write_text("timeout", timeout.to_string()).unwrap();
        }

        if let Some(allowed_updates) = params.allowed_updates {
            for allowed_update in allowed_updates {
                multi.write_text("allowed_updates[]", allowed_update).unwrap();
            }
        }

        match parse_request(multi.send()) {
            Ok(val) => Ok(serde_json::value::from_value(val).unwrap()),
            Err(e) => Err(e),
        }
    }

    pub fn send_message(&self, params: &args::SendMessage) -> Result<Message, BotError> {
        let url = self.base_url.join("sendMessage").unwrap();
        let req = Request::new(Method::Post, url).unwrap();
        let mut multi = Multipart::from_request(req).unwrap();

        if let Some(chat_id) = params.chat_id {
            multi.write_text("chat_id", chat_id.to_string()).unwrap();
        } else
        if let Some(chat_username) = params.chat_username {
            multi.write_text("chat_id", chat_username).unwrap();
        }

        multi.write_text("text", params.text).unwrap();

        if let Some(parse_mode) = params.parse_mode {
            multi.write_text("parse_mode", parse_mode).unwrap();
        }

        if let Some(disable_web_page_preview) = params.disable_web_page_preview {
            multi.write_text("disable_web_page_preview", disable_web_page_preview.to_string()).unwrap();
        }

        if let Some(disable_notification) = params.disable_notification {
            multi.write_text("disable_notification", disable_notification.to_string()).unwrap();
        }

        if let Some(reply_to_message_id) = params.reply_to_message_id {
            multi.write_text("reply_to_message_id", reply_to_message_id.to_string()).unwrap();
        }

        if let Some(reply_markup) = params.reply_markup {
            value_to_multi("reply_markup", serde_json::to_value(reply_markup)).unwrap();
        }

        match parse_request(multi.send()) {
            Ok(val) => Ok(serde_json::value::from_value(val).unwrap()),
            Err(e) => Err(e),
        }
    }
} // 2: TODO don't use multipart when you don't have to

fn value_to_multi(&mut multi: multipart::client::Multipart, key: &str, val: Value, from_obj: bool) {
    match val {
        Value::Null => {
            multi.write_text(key, "null").unwrap();
        }
        Value::Bool(v) |
        Value::I64(v) |
        Value::U64(v) |
        Value::F64(v) |
        Value::String(v) => {
            if from_obj {
                multi.write_text(&format!(key, ""), v.to_string()).unwrap();
            } else {
                multi.write_text(key, v.to_string()).unwrap();
            }
        }
        Value::Array(a) => {
            let n = format!("{}[]", key);
            for item in a {
                multi.write_text(&n, v.to_string()).unwrap();
            }
        }
        Value::Object(map) => {
            for (key, value) in map {
                value_to_multi(&mut multi, &format!(name, key), value, true);
            }
        }
    }
}

#[cfg(test)]
mod tests { // These aren't going to be the actual tests, just a place for me to easily test things as I go along
    use super::*;
    extern crate serde_json;
    #[test]
    fn it_works() {
        let token = "";
        let bot = BotApi::new(token);

        let mut update_args = args::GetUpdates {
            offset: Some(0),
            limit: None,
            timeout: Some(600),
            allowed_updates: None,
        };

        'update_loop: loop {
            let updates = bot.get_updates(&update_args).unwrap();

            for update in updates {
                update_args.offset = Some(update.update_id + 1);
                println!("{:?}", update_args.offset);

                if let Some(message) = update.message {
                    let from = message.from.unwrap();
                    let text = format!("Hi {} [{}]!", from.first_name,
                                                      from.id);

                    let mut msg_args = args::SendMessage {
                        chat_id: None,
                        chat_username: None,
                        text: "",
                        parse_mode: Some("Markdown"),
                        disable_web_page_preview: None,
                        disable_notification: None,
                        reply_to_message_id: None,
                        reply_markup: None,
                    };

                    msg_args.text = &text;
                    msg_args.chat_id = Some(message.chat.id);
                    msg_args.reply_to_message_id = Some(message.message_id);

                    bot.send_message(&msg_args);

                    if let Some(text) = message.text {
                        if text == "/exit" {
                            break 'update_loop;
                        }
                    }
                }
            }
        }
        bot.get_updates(&update_args);
        // Alright, so if you ever decide to have a function that terminates
        // your bot, make sure you have a check at the beginning of the loop
        // that makes sure you aren't processing old messages. You could also
        // just make a getUpdates at the end of the execution that just uses
        // the latest offset. This prevents you from having to reread any
        // updates that were in the update array you got that had the terminate
        // command. Because telegram only will stop sending you the update
        // after you have used an offset greater than its. So if you never make
        // another getUpdates, you will boot up, and terminate, forever. :)
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate serde_json;
extern crate sputnikvm;

mod blockchain;

use blockchain::JSONVectorBlock;

use sputnikvm::{read_hex, Gas, U256, Address};
use sputnikvm::vm::{Machine, VectorMachine};
use sputnikvm::blockchain::Block;
use sputnikvm::transaction::{Transaction, VectorTransaction};

use serde_json::{Value, Error};
use std::fs::File;
use std::path::Path;
use std::io::BufReader;

fn test_transaction(v: &Value) {
    let mut block = JSONVectorBlock::new(&v["env"]);

    let current_gas_limit = Gas::from_str(v["env"]["currentGasLimit"].as_str().unwrap()).unwrap();
    let address = Address::from_str(v["exec"]["address"].as_str().unwrap()).unwrap();
    let caller = Address::from_str(v["exec"]["caller"].as_str().unwrap()).unwrap();
    let code = read_hex(v["exec"]["code"].as_str().unwrap()).unwrap();
    let data = read_hex(v["exec"]["data"].as_str().unwrap()).unwrap();
    let gas = Gas::from_str(v["exec"]["gas"].as_str().unwrap()).unwrap();
    let gas_price = Gas::from_str(v["exec"]["gasPrice"].as_str().unwrap()).unwrap();
    let origin = Address::from_str(v["exec"]["origin"].as_str().unwrap()).unwrap();
    let value = U256::from_str(v["exec"]["value"].as_str().unwrap()).unwrap();

    let transaction = VectorTransaction::message_call(
        caller, address, value, data.as_ref(), current_gas_limit
    );

    let out = v["out"].as_str().unwrap();

    let ref pre_addresses = v["pre"];

    for (address, data) in pre_addresses.as_object().unwrap() {
        let address = Address::from_str(address.as_str()).unwrap();
        let balance = U256::from_str(data["balance"].as_str().unwrap()).unwrap();
        let code = read_hex(data["code"].as_str().unwrap()).unwrap();

        block.set_account_code(address, code.as_ref());
        block.set_balance(address, balance);

        let storage = data["storage"].as_object().unwrap();
        for (index, value) in storage {
            let index = U256::from_str(index.as_str()).unwrap();
            let value = U256::from_str(value.as_str().unwrap()).unwrap();
            block.set_account_storage(address, index, value);
        }
    }

    let mut machine: VectorMachine<JSONVectorBlock, Box<JSONVectorBlock>> =
                                   VectorMachine::new(code.as_ref(), data.as_ref(), gas,
                                                      transaction, Box::new(block));
    machine.fire();

    let out = read_hex(out).unwrap();
    let out_ref: &[u8] = out.as_ref();
    assert!(machine.return_values() == out_ref);

    let ref post_addresses = v["post"];

    for (address, data) in post_addresses.as_object().unwrap() {
        let address = Address::from_str(address.as_str()).unwrap();
        let balance = U256::from_str(data["balance"].as_str().unwrap()).unwrap();
        let code = read_hex(data["code"].as_str().unwrap()).unwrap();

        assert!(Some(code.as_ref()) == machine.block().account_code(address));
        assert!(Some(balance) == machine.block().balance(address));

        let storage = data["storage"].as_object().unwrap();
        for (index, value) in storage {
            let index = U256::from_str(index.as_str()).unwrap();
            let value = U256::from_str(value.as_str().unwrap()).unwrap();
            assert!(value == machine.block().account_storage(address, index));
        }
    }
}

fn main() {
    let app = clap_app!(jsonlighter =>
        (version: "0.1.0")
        (author: "SputnikVM Contributors")
        (@arg FILE: -f --file +takes_value +required "ethereumproject/tests JSON file to run for this test")
        (@arg TEST: -t --test +takes_value +required "test to run in the given file")
    ).get_matches();

    let path = Path::new(app.value_of("FILE").unwrap());
    let file = File::open(&path).unwrap();
    let reader = BufReader::new(file);
    let json: Value = serde_json::from_reader(reader).unwrap();
    let test = app.value_of("TEST").unwrap();

    test_transaction(&json[test]);
}
<commit_msg>Problem: jsonlighter can only run single test Solution: implement testing a whole file<commit_after>#[macro_use]
extern crate clap;
extern crate serde_json;
extern crate sputnikvm;

mod blockchain;

use blockchain::JSONVectorBlock;

use sputnikvm::{read_hex, Gas, U256, Address};
use sputnikvm::vm::{Machine, VectorMachine};
use sputnikvm::blockchain::Block;
use sputnikvm::transaction::{Transaction, VectorTransaction};

use serde_json::{Value, Error};
use std::fs::File;
use std::path::Path;
use std::io::{BufReader, Write, stdout};

fn test_transaction(name: &str, v: &Value) {
    print!("Testing {} ...", name);
    stdout().flush();

    let mut block = JSONVectorBlock::new(&v["env"]);

    let current_gas_limit = Gas::from_str(v["env"]["currentGasLimit"].as_str().unwrap()).unwrap();
    let address = Address::from_str(v["exec"]["address"].as_str().unwrap()).unwrap();
    let caller = Address::from_str(v["exec"]["caller"].as_str().unwrap()).unwrap();
    let code = read_hex(v["exec"]["code"].as_str().unwrap()).unwrap();
    let data = read_hex(v["exec"]["data"].as_str().unwrap()).unwrap();
    let gas = Gas::from_str(v["exec"]["gas"].as_str().unwrap()).unwrap();
    let gas_price = Gas::from_str(v["exec"]["gasPrice"].as_str().unwrap()).unwrap();
    let origin = Address::from_str(v["exec"]["origin"].as_str().unwrap()).unwrap();
    let value = U256::from_str(v["exec"]["value"].as_str().unwrap()).unwrap();

    let transaction = VectorTransaction::message_call(
        caller, address, value, data.as_ref(), current_gas_limit
    );

    let out = v["out"].as_str().unwrap();

    let ref pre_addresses = v["pre"];

    for (address, data) in pre_addresses.as_object().unwrap() {
        let address = Address::from_str(address.as_str()).unwrap();
        let balance = U256::from_str(data["balance"].as_str().unwrap()).unwrap();
        let code = read_hex(data["code"].as_str().unwrap()).unwrap();

        block.set_account_code(address, code.as_ref());
        block.set_balance(address, balance);

        let storage = data["storage"].as_object().unwrap();
        for (index, value) in storage {
            let index = U256::from_str(index.as_str()).unwrap();
            let value = U256::from_str(value.as_str().unwrap()).unwrap();
            block.set_account_storage(address, index, value);
        }
    }

    let mut machine: VectorMachine<JSONVectorBlock, Box<JSONVectorBlock>> =
                                   VectorMachine::new(code.as_ref(), data.as_ref(), gas,
                                                      transaction, Box::new(block));
    machine.fire();

    let out = read_hex(out).unwrap();
    let out_ref: &[u8] = out.as_ref();
    assert!(machine.return_values() == out_ref);

    let ref post_addresses = v["post"];

    for (address, data) in post_addresses.as_object().unwrap() {
        let address = Address::from_str(address.as_str()).unwrap();
        let balance = U256::from_str(data["balance"].as_str().unwrap()).unwrap();
        let code = read_hex(data["code"].as_str().unwrap()).unwrap();

        assert!(Some(code.as_ref()) == machine.block().account_code(address));
        assert!(Some(balance) == machine.block().balance(address));

        let storage = data["storage"].as_object().unwrap();
        for (index, value) in storage {
            let index = U256::from_str(index.as_str()).unwrap();
            let value = U256::from_str(value.as_str().unwrap()).unwrap();
            assert!(value == machine.block().account_storage(address, index));
        }
    }

    println!(" OK");
}

fn main() {
    let app = clap_app!(jsonlighter =>
        (version: "0.1.0")
        (author: "SputnikVM Contributors")
        (@arg FILE: -f --file +takes_value +required "ethereumproject/tests JSON file to run for this test")
        (@arg TEST: -t --test +takes_value "test to run in the given file")
    ).get_matches();

    let path = Path::new(app.value_of("FILE").unwrap());
    let file = File::open(&path).unwrap();
    let reader = BufReader::new(file);
    let json: Value = serde_json::from_reader(reader).unwrap();

    match app.value_of("TEST") {
        Some(test) => {
            test_transaction(test, &json[test]);
        },
        None => {
            for (test, data) in json.as_object().unwrap() {
                test_transaction(test, &data);
            }
        },
    }
}
<|endoftext|>


<commit_before>#![crate_name = "simple_csv"]
#![feature(collections)]
#![feature(io)]
#![feature(test)]

pub use reader::SimpleCsvReader;
pub use writer::SimpleCsvWriter;

pub mod reader;
pub mod writer;

#[cfg(test)]
mod tests {
    extern crate test;
    use {SimpleCsvWriter, SimpleCsvReader};

    #[test]
    fn reader_and_writer_test() {
        let data = vec![
            vec!["1".to_string(),"2\r\n".to_string(),"3".to_string()],
            vec!["4".to_string(),"5\"".to_string(),"6".to_string()]];
        let mut writer = SimpleCsvWriter::new(Vec::new());
        let _ = writer.write_all(&*data);

        let written_data = writer.as_inner();

        let mut reader = SimpleCsvReader::new(&*written_data);
        assert_eq!(reader.next_row(), Ok(&*data[0]));
        assert_eq!(reader.next_row(), Ok(&*data[1]));
        assert!(reader.next_row().is_err());

    }
}
<commit_msg>rustup<commit_after>#![crate_name = "simple_csv"]
#![feature(collections)]
#![feature(io)]
#![feature(test)]
#![feature(core)]

pub use reader::SimpleCsvReader;
pub use writer::SimpleCsvWriter;

pub mod reader;
pub mod writer;

#[cfg(test)]
mod tests {
    extern crate test;
    use {SimpleCsvWriter, SimpleCsvReader};

    #[test]
    fn reader_and_writer_test() {
        let data = vec![
            vec!["1".to_string(),"2\r\n".to_string(),"3".to_string()],
            vec!["4".to_string(),"5\"".to_string(),"6".to_string()]];
        let mut writer = SimpleCsvWriter::new(Vec::new());
        let _ = writer.write_all(&*data);

        let written_data = writer.as_inner();

        let mut reader = SimpleCsvReader::new(&*written_data);
        assert_eq!(reader.next_row(), Ok(&*data[0]));
        assert_eq!(reader.next_row(), Ok(&*data[1]));
        assert!(reader.next_row().is_err());

    }
}
<|endoftext|>