<commit_before>#![feature(test)]
#![allow(dead_code)]
extern crate test;
#[macro_use]
extern crate log;
extern crate env_logger;
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt;
use std::ops::{Drop, Deref, DerefMut};
use std::convert::AsRef;

pub trait Recycleable {
  fn new() -> Self;
  fn reset(&mut self);
}

pub trait InitializeWith<T> {
  fn initialize_with(&mut self, source: T);
}

impl Recycleable for String {
  #[inline(always)]
  fn new() -> String {
    String::new()
  }
  #[inline(always)]
  fn reset(&mut self) {
    self.clear();
  }
}

impl <T> Recycleable for Vec<T> {
  #[inline(always)]
  fn new() -> Vec<T> {
    Vec::new()
  }
  #[inline(always)]
  fn reset(&mut self) {
    self.clear();
  }
}

impl <A> InitializeWith<A> for String where A : AsRef<str> {
  #[inline(always)]
  fn initialize_with(&mut self, source: A) {
    let s : &str = source.as_ref();
    self.push_str(s);
  }
}

pub struct Recycled<T> where T : Recycleable {
  pub value: Option<T>,
  pool: Rc<RefCell<Vec<T>>>
}

impl <T> Drop for Recycled<T> where T : Recycleable {
  #[inline(always)]
  fn drop(&mut self) {
    if let Some(mut value) = self.value.take() {
      value.reset();
      self.pool.borrow_mut().push(value);
    }
  }
}

impl <T> fmt::Display for Recycled<T> where T : fmt::Display + Recycleable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self.value {
      Some(ref s) => s.fmt(f),
      None => write!(f, "Empty Recycled<T>")
    }
  }
}

impl <T> Deref for Recycled<T> where T : Recycleable {
  type Target = T;
  #[inline(always)]
  fn deref<'a>(&'a self) -> &'a T {
    match self.value.as_ref() {
      Some(v) => v,
      None => panic!("Recycleable wrapper missing its value.")
    }
  }
}

impl <T> DerefMut for Recycled<T> where T : Recycleable {
  #[inline(always)]
  fn deref_mut<'a>(&'a mut self) -> &'a mut T {
    match self.value.as_mut() {
      Some(v) => v,
      None => panic!("Recycleable wrapper missing its value.")
    }
  }
}

impl <T> Recycled<T> where T : Recycleable {
  #[inline(always)]
  pub fn new(pool: Rc<RefCell<Vec<T>>>, value: T) -> Recycled<T> {
    Recycled {
      value: Some(value),
      pool: pool
    }
  }

  #[inline(always)]
  pub fn new_from<A>(pool: Rc<RefCell<Vec<T>>>, mut value: T, source: A) -> Recycled<T> where T : InitializeWith<A> {
    value.initialize_with(source);
    Recycled {
      value: Some(value),
      pool: pool
    }
  }

  #[inline(always)]
  pub fn detach(mut self) -> T {
    let value = self.value.take().unwrap();
    drop(self);
    value
  }
}

pub struct Pool <T> where T : Recycleable {
  values: Rc<RefCell<Vec<T>>>,
}

impl <T> Pool <T> where T: Recycleable {
  #[inline(always)]
  pub fn with_size(size: u32) -> Pool <T> {
    let values: Vec<T> =
      (0..size)
      .map(|_| T::new() )
      .collect();
    Pool {
      values: Rc::new(RefCell::new(values)),
    }
  }

  #[inline(always)]
  pub fn attach(&mut self, mut value: T) {
    value.reset();
    self.values.borrow_mut().push(value);
  }

  #[inline(always)]
  pub fn detached(&mut self) -> T {
    match self.values.borrow_mut().pop() {
      Some(v) => v,
      None => T::new()
    }
  }

  #[inline(always)]
  pub fn new(&mut self) -> Recycled<T> {
    let t = self.detached();
    let pool_reference = self.values.clone();
    Recycled::new(pool_reference, t)
  }

  #[inline(always)]
  pub fn new_from<A>(&mut self, source: A) -> Recycled<T> where T: InitializeWith<A> {
    let t = self.detached();
    let pool_reference = self.values.clone();
    Recycled::new_from(pool_reference, t, source)
  }

  #[inline(always)]
  pub fn size(&self) -> usize {
    self.values.borrow().len()
  }
}

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

  const ITERATIONS : u32 = 10_000;

  // Calling String::new() is very close to a no-op; no actual allocation
  // is performed until bytes are pushed onto the end of the String. As such,
  // we need to explicitly ask for some space to be available to trigger allocation.
  const EMPTY_STRING_CAPACITY : usize = 4;

  #[bench]
  fn bench01_standard_allocation_speed(b: &mut Bencher) {
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
      }
    });
  }

  #[bench]
  fn bench02_pooled_allocation_speed(b: &mut Bencher) {
    let mut pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
      }
    });
  }

  #[bench]
  fn bench03_standard_initialized_allocation_speed(b: &mut Bencher) {
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = "man".to_owned();
        let _string = "dog".to_owned();
        let _string = "cat".to_owned();
        let _string = "mouse".to_owned();
        let _string = "cheese".to_owned();
      }
    });
  }

  #[bench]
  fn bench04_pooled_initialized_allocation_speed(b: &mut Bencher) {
    let _ = env_logger::init();
    let mut pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new_from("man");
        let _string = pool.new_from("dog");
        let _string = pool.new_from("cat");
        let _string = pool.new_from("mouse");
        let _string = pool.new_from("cheese");
      }
    });
  }
  #[bench]
  fn bench05_allocate_vec_vec_str(bencher: &mut Bencher) {
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = Vec::new();
              for _ in 0..100 {
                  v2.push(("test!").to_owned());
              }
              v1.push(v2);
          }
          v1
      });
  }

  #[bench]
  fn bench06_pooled_vec_vec_str(bencher: &mut Bencher) {
      let mut vec_str_pool : Pool<Vec<Recycled<String>>> = Pool::with_size(100);
      let mut str_pool : Pool<String> = Pool::with_size(10000);
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = vec_str_pool.new();
              for _ in 0..100 {
                  v2.push(str_pool.new_from("test!"));
              }
              v1.push(v2);
          }
          v1
      });
  }

  #[test]
  fn test_deref() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", *rstring);
  }

  #[test]
  fn test_deref_mut() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      (*rstring).push_str("s love eating mice");
      println!("{}", *rstring);
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_recycle() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_detached() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.detached();
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(0, str_pool.size());
  }

}
<commit_msg>Downgraded #[inline(always)] to #[inline] most places.<commit_after>#![feature(test)]
#![allow(dead_code)]
extern crate test;
#[macro_use]
extern crate log;
extern crate env_logger;
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt;
use std::ops::{Drop, Deref, DerefMut};
use std::convert::AsRef;

pub trait Recycleable {
  fn new() -> Self;
  fn reset(&mut self);
}

pub trait InitializeWith<T> {
  fn initialize_with(&mut self, source: T);
}

impl Recycleable for String {
  #[inline]
  fn new() -> String {
    String::new()
  }
  #[inline]
  fn reset(&mut self) {
    self.clear();
  }
}

impl <T> Recycleable for Vec<T> {
  #[inline]
  fn new() -> Vec<T> {
    Vec::new()
  }
  #[inline]
  fn reset(&mut self) {
    self.clear();
  }
}

impl <A> InitializeWith<A> for String where A : AsRef<str> {
  #[inline]
  fn initialize_with(&mut self, source: A) {
    let s : &str = source.as_ref();
    self.push_str(s);
  }
}

pub struct Recycled<T> where T : Recycleable {
  pub value: Option<T>,
  pool: Rc<RefCell<Vec<T>>>
}

impl <T> Drop for Recycled<T> where T : Recycleable {
  #[inline]
  fn drop(&mut self) {
    if let Some(mut value) = self.value.take() {
      value.reset();
      self.pool.borrow_mut().push(value);
    }
  }
}

impl <T> fmt::Display for Recycled<T> where T : fmt::Display + Recycleable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self.value {
      Some(ref s) => s.fmt(f),
      None => write!(f, "Empty Recycled<T>")
    }
  }
}

impl <T> Deref for Recycled<T> where T : Recycleable {
  type Target = T;
  #[inline]
  fn deref<'a>(&'a self) -> &'a T {
    match self.value.as_ref() {
      Some(v) => v,
      None => panic!("Recycleable wrapper missing its value.")
    }
  }
}

impl <T> DerefMut for Recycled<T> where T : Recycleable {
  #[inline]
  fn deref_mut<'a>(&'a mut self) -> &'a mut T {
    match self.value.as_mut() {
      Some(v) => v,
      None => panic!("Recycleable wrapper missing its value.")
    }
  }
}

impl <T> Recycled<T> where T : Recycleable {
  #[inline]
  pub fn new(pool: Rc<RefCell<Vec<T>>>, value: T) -> Recycled<T> {
    Recycled {
      value: Some(value),
      pool: pool
    }
  }

  #[inline]
  pub fn new_from<A>(pool: Rc<RefCell<Vec<T>>>, mut value: T, source: A) -> Recycled<T> where T : InitializeWith<A> {
    value.initialize_with(source);
    Recycled {
      value: Some(value),
      pool: pool
    }
  }

  #[inline]
  pub fn detach(mut self) -> T {
    let value = self.value.take().unwrap();
    drop(self);
    value
  }
}

pub struct Pool <T> where T : Recycleable {
  values: Rc<RefCell<Vec<T>>>,
}

impl <T> Pool <T> where T: Recycleable {
  #[inline]
  pub fn with_size(size: u32) -> Pool <T> {
    let values: Vec<T> =
      (0..size)
      .map(|_| T::new() )
      .collect();
    Pool {
      values: Rc::new(RefCell::new(values)),
    }
  }

  #[inline]
  pub fn attach(&mut self, mut value: T) {
    value.reset();
    self.values.borrow_mut().push(value);
  }

  #[inline]
  pub fn detached(&mut self) -> T {
    match self.values.borrow_mut().pop() {
      Some(v) => v,
      None => T::new()
    }
  }

  #[inline]
  pub fn new(&mut self) -> Recycled<T> {
    let t = self.detached();
    let pool_reference = self.values.clone();
    Recycled::new(pool_reference, t)
  }

  #[inline(always)]
  pub fn new_from<A>(&mut self, source: A) -> Recycled<T> where T: InitializeWith<A> {
    let t = self.detached();
    let pool_reference = self.values.clone();
    Recycled::new_from(pool_reference, t, source)
  }

  #[inline]
  pub fn size(&self) -> usize {
    self.values.borrow().len()
  }
}

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

  const ITERATIONS : u32 = 10_000;

  // Calling String::new() is very close to a no-op; no actual allocation
  // is performed until bytes are pushed onto the end of the String. As such,
  // we need to explicitly ask for some space to be available to trigger allocation.
  const EMPTY_STRING_CAPACITY : usize = 4;

  #[bench]
  fn bench01_standard_allocation_speed(b: &mut Bencher) {
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
      }
    });
  }

  #[bench]
  fn bench02_pooled_allocation_speed(b: &mut Bencher) {
    let mut pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
      }
    });
  }

  #[bench]
  fn bench03_standard_initialized_allocation_speed(b: &mut Bencher) {
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = "man".to_owned();
        let _string = "dog".to_owned();
        let _string = "cat".to_owned();
        let _string = "mouse".to_owned();
        let _string = "cheese".to_owned();
      }
    });
  }

  #[bench]
  fn bench04_pooled_initialized_allocation_speed(b: &mut Bencher) {
    let _ = env_logger::init();
    let mut pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new_from("man");
        let _string = pool.new_from("dog");
        let _string = pool.new_from("cat");
        let _string = pool.new_from("mouse");
        let _string = pool.new_from("cheese");
      }
    });
  }
  #[bench]
  fn bench05_allocate_vec_vec_str(bencher: &mut Bencher) {
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = Vec::new();
              for _ in 0..100 {
                  v2.push(("test!").to_owned());
              }
              v1.push(v2);
          }
          v1
      });
  }

  #[bench]
  fn bench06_pooled_vec_vec_str(bencher: &mut Bencher) {
      let mut vec_str_pool : Pool<Vec<Recycled<String>>> = Pool::with_size(100);
      let mut str_pool : Pool<String> = Pool::with_size(10000);
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = vec_str_pool.new();
              for _ in 0..100 {
                  v2.push(str_pool.new_from("test!"));
              }
              v1.push(v2);
          }
          v1
      });
  }

  #[test]
  fn test_deref() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", *rstring);
  }

  #[test]
  fn test_deref_mut() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      (*rstring).push_str("s love eating mice");
      println!("{}", *rstring);
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_recycle() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_detached() {
      let mut str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.detached();
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(0, str_pool.size());
  }

}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;

const LF: u8 = 10;
const CR: u8 = 13;
const QUOTE: u8 = 34;
const COMMA: u8 = 44;

enum CSVState {
    Start,
    NonQuotedValue,
    NonQuotedQuote,
    QuotedValue,
    QuoteQuote,
    ExpectLF,
    Error,
}

#[derive(Debug, PartialEq, Eq)]
pub struct CSVError {
    pub line: i32,
    pub col: i32,
}

pub fn errors_for_csv(file: File) -> Vec<CSVError> {
    let mut state = CSVState::Start;
    let mut line = 1;
    let mut col = 0;

    let mut errors: Vec<CSVError> = Vec::new();

    for byte in file.bytes() {
        let b = byte.unwrap();

        match (&state, b) {
            // At the start of a field, are we a quoted field, non-quoted, or empty?
            (&CSVState::Start, QUOTE) => {
                state = CSVState::QuotedValue;
            },
            (&CSVState::Start, COMMA) => {
                state = CSVState::Start
            },
            (&CSVState::Start, _) => {
                state = CSVState::NonQuotedValue;
            },

            // In a non-quoted field, watch for quotes
            (&CSVState::NonQuotedValue, COMMA) => {
                state = CSVState::Start;
            }
            (&CSVState::NonQuotedValue, CR) => {
                state = CSVState::ExpectLF;
            }
            (&CSVState::NonQuotedValue, LF) => {
                state = CSVState::Start;
            }
            (&CSVState::NonQuotedValue, QUOTE) => {
                state = CSVState::NonQuotedQuote;
            }

            // A quote in a non-quoted field needs a matching quote to
            // close it out.
            (&CSVState::NonQuotedQuote, QUOTE) => {
                state = CSVState::NonQuotedValue;
            }
            (&CSVState::NonQuotedQuote, COMMA) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Error;
            }
            (&CSVState::NonQuotedQuote, CR) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Start;
            }
            (&CSVState::NonQuotedQuote, LF) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Start;
            }

            // In a quoted field, watch for quotes or newlines
            (&CSVState::QuotedValue, QUOTE) => {
                state = CSVState::QuoteQuote;
            },
            (&CSVState::QuotedValue, CR) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Start;
            },
            (&CSVState::QuotedValue, LF) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Start;
            },

            // A quote in a quote needs a quote immediately following
            // to be an escaped quote. If there's a comma or newline
            // following, it means that the field is done. Otherwise,
            // it's an error.
            (&CSVState::QuoteQuote, QUOTE) => {
                state = CSVState::QuotedValue;
            },
            (&CSVState::QuoteQuote, COMMA) => {
                state = CSVState::Start;
            },
            (&CSVState::QuoteQuote, CR) => {
                state = CSVState::ExpectLF;
            },
            (&CSVState::QuoteQuote, LF) => {
                state = CSVState::Start;
            },
            (&CSVState::QuoteQuote, _) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Error;
            },

            // If we're looking at CR, we need a LF to follow
            (&CSVState::ExpectLF, LF) => {
                state = CSVState::Start;
            },
            (&CSVState::ExpectLF, _) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Error;
            },

            // If we're in an error state, once we reach a newline, we
            // start over.
            (&CSVState::Error, LF) => {
                state = CSVState::Start;
            },
            _ => ()
        };

        if b == 10 {
            line = line + 1;
            col = 0;
        } else {
            col = col + 1;
        }
    }

    errors
}
<commit_msg>Use the const<commit_after>use std::fs::File;
use std::io::Read;

const LF: u8 = 10;
const CR: u8 = 13;
const QUOTE: u8 = 34;
const COMMA: u8 = 44;

enum CSVState {
    Start,
    NonQuotedValue,
    NonQuotedQuote,
    QuotedValue,
    QuoteQuote,
    ExpectLF,
    Error,
}

#[derive(Debug, PartialEq, Eq)]
pub struct CSVError {
    pub line: i32,
    pub col: i32,
}

pub fn errors_for_csv(file: File) -> Vec<CSVError> {
    let mut state = CSVState::Start;
    let mut line = 1;
    let mut col = 0;

    let mut errors: Vec<CSVError> = Vec::new();

    for byte in file.bytes() {
        let b = byte.unwrap();

        match (&state, b) {
            // At the start of a field, are we a quoted field, non-quoted, or empty?
            (&CSVState::Start, QUOTE) => {
                state = CSVState::QuotedValue;
            },
            (&CSVState::Start, COMMA) => {
                state = CSVState::Start
            },
            (&CSVState::Start, _) => {
                state = CSVState::NonQuotedValue;
            },

            // In a non-quoted field, watch for quotes
            (&CSVState::NonQuotedValue, COMMA) => {
                state = CSVState::Start;
            }
            (&CSVState::NonQuotedValue, CR) => {
                state = CSVState::ExpectLF;
            }
            (&CSVState::NonQuotedValue, LF) => {
                state = CSVState::Start;
            }
            (&CSVState::NonQuotedValue, QUOTE) => {
                state = CSVState::NonQuotedQuote;
            }

            // A quote in a non-quoted field needs a matching quote to
            // close it out.
            (&CSVState::NonQuotedQuote, QUOTE) => {
                state = CSVState::NonQuotedValue;
            }
            (&CSVState::NonQuotedQuote, COMMA) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Error;
            }
            (&CSVState::NonQuotedQuote, CR) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Start;
            }
            (&CSVState::NonQuotedQuote, LF) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Start;
            }

            // In a quoted field, watch for quotes or newlines
            (&CSVState::QuotedValue, QUOTE) => {
                state = CSVState::QuoteQuote;
            },
            (&CSVState::QuotedValue, CR) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Start;
            },
            (&CSVState::QuotedValue, LF) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Start;
            },

            // A quote in a quote needs a quote immediately following
            // to be an escaped quote. If there's a comma or newline
            // following, it means that the field is done. Otherwise,
            // it's an error.
            (&CSVState::QuoteQuote, QUOTE) => {
                state = CSVState::QuotedValue;
            },
            (&CSVState::QuoteQuote, COMMA) => {
                state = CSVState::Start;
            },
            (&CSVState::QuoteQuote, CR) => {
                state = CSVState::ExpectLF;
            },
            (&CSVState::QuoteQuote, LF) => {
                state = CSVState::Start;
            },
            (&CSVState::QuoteQuote, _) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Error;
            },

            // If we're looking at CR, we need a LF to follow
            (&CSVState::ExpectLF, LF) => {
                state = CSVState::Start;
            },
            (&CSVState::ExpectLF, _) => {
                errors.push(CSVError { line: line, col: col });
                state = CSVState::Error;
            },

            // If we're in an error state, once we reach a newline, we
            // start over.
            (&CSVState::Error, LF) => {
                state = CSVState::Start;
            },
            _ => ()
        };

        if b == LF {
            line = line + 1;
            col = 0;
        } else {
            col = col + 1;
        }
    }

    errors
}
<|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![$(::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![$(bind_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>More global paths<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]) => {
    ::holmes::var_to_evar(clause_match!($vars, $n, $hexpr_name))
  };
  ($vars:ident, $n:ident, ($hexpr:expr)) => {
    ::holmes::native_types::Expr::EVal(::holmes::native_types::ToHValue::to_hvalue($hexpr))
  };
  ($vars:ident, $n:ident, {$hexpr_func:ident($($hexpr_arg:tt),*)}) => {
    ::holmes::native_types::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![$(bind_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>//! Simple crate that parses athenahealth Watertown's Nourish menu, and posts it to a slack channel
//! of choice.

#![warn(missing_docs)]

#[macro_use]
extern crate lazy_static;

extern crate chrono;
extern crate hyper;
extern crate inflector;
extern crate linked_hash_map;
extern crate regex;
extern crate select;
extern crate url;

use std::cmp;
use std::fmt::Write;

use chrono::{Datelike, Duration, NaiveDate};
use inflector::Inflector;
use linked_hash_map::LinkedHashMap;
use regex::Regex;
use select::document::Document;
use select::predicate::{Attr, Name};
use url::Url;

lazy_static! {
    /// This Regex matches nutritional information, to filter it out from the menu output.
    static ref NUTRITION_RE: Regex = Regex::new(r"Cal.*Fat.*Sat.*Sod.*Carbs.*Fib.*Pro").unwrap();

    /// This Regex matches the price information, to filter it out from the menu output.
    static ref PRICE_RE: Regex = Regex::new(r"^\d+\.\d+( / \d+\.\d+)?$").unwrap();
}

/// The Nourish menu for a given date.
#[derive(Debug, Clone, Default)]
pub struct Menu(LinkedHashMap<String, Vec<String>>);

impl Menu {
    /// Renders the menu as a Markdown string.
    pub fn to_markdown(&self) -> String {
        let mut output = String::default();

        for (ref category, ref items) in &self.0 {
            writeln!(output, "*{}*\n", category).unwrap();

            for item in items.iter() {
                writeln!(output, "• {}", item).unwrap();
            }

            writeln!(output, "").unwrap();
        }

        writeln!(output,
                 "> Made with :cnr: by @anrussell. Source available at \
                  https://github.com/euclio/nourishbot")
            .unwrap();

        output
    }
}

/// Returns the URL pointing to the Nourish menu for a given day. On the weekends, returns Friday's
/// menu.
pub fn url_for_date(date: &NaiveDate) -> Url {
    let days_from_monday = cmp::min(date.weekday().num_days_from_monday(), 4);
    let monday = *date - Duration::days(date.weekday().num_days_from_monday() as i64);

    Url::parse(&format!("http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                       nsf/weeklyMenuLaunch/8DURSE~{}/$file/day{}.htm",
                        monday.format("%m-%d-%Y"),
                        days_from_monday + 1))
        .unwrap()
}

/// Parses the menu information out of HTML.
pub fn parse_menu(html: &str) -> Menu {
    let document = Document::from(html);

    let mut menu: LinkedHashMap<String, Vec<String>> = LinkedHashMap::new();

    let mut last_category = None;

    let menu_node = document.find(Attr("id", "center_text")).first().unwrap();
    for node in menu_node.find(Name("div")).iter() {
        let text = node.text().trim().to_owned();

        if NUTRITION_RE.is_match(&text) || PRICE_RE.is_match(&text) {
            continue;
        }

        if let Some("font-weight:bold;") = node.attr("style") {
            last_category = Some(text.to_lower_case().to_title_case());
        } else {
            if let Some(ref category) = last_category {
                if menu.contains_key(category) {
                    let mut entries = menu.get_mut(category).unwrap();
                    entries.push(text);
                } else {
                    menu.insert(category.to_owned(), vec![text]);
                }

            }
        }
    }

    Menu(menu)
}

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

    use chrono::NaiveDate;

    #[test]
    fn monday() {
        let expected_url = "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                            nsf/weeklyMenuLaunch/8DURSE~04-18-2016/$file/day1.htm";

        assert_eq!(expected_url,
                   &url_for_date(&NaiveDate::from_ymd(2016, 4, 18)).serialize());
    }

    #[test]
    fn saturday() {
        let expected_url = "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                            nsf/weeklyMenuLaunch/8DURSE~04-11-2016/$file/day5.htm";

        assert_eq!(expected_url,
                   &url_for_date(&NaiveDate::from_ymd(2016, 4, 16)).serialize());

    }
}
<commit_msg>filter out breakfast<commit_after>//! Simple crate that parses athenahealth Watertown's Nourish menu, and posts it to a slack channel
//! of choice.

#![warn(missing_docs)]

#[macro_use]
extern crate lazy_static;

extern crate chrono;
extern crate hyper;
extern crate inflector;
extern crate linked_hash_map;
extern crate regex;
extern crate select;
extern crate url;

use std::cmp;
use std::fmt::Write;

use chrono::{Datelike, Duration, NaiveDate};
use inflector::Inflector;
use linked_hash_map::LinkedHashMap;
use regex::Regex;
use select::document::Document;
use select::predicate::{Attr, Name};
use url::Url;

lazy_static! {
    /// This Regex matches nutritional information, to filter it out from the menu output.
    static ref NUTRITION_RE: Regex = Regex::new(r"Cal.*Fat.*Sat.*Sod.*Carbs.*Fib.*Pro").unwrap();

    /// This Regex matches the price information, to filter it out from the menu output.
    static ref PRICE_RE: Regex = Regex::new(r"^\d+\.\d+( / \d+\.\d+)?$").unwrap();
}

/// The Nourish menu for a given date.
#[derive(Debug, Clone, Default)]
pub struct Menu(LinkedHashMap<String, Vec<String>>);

impl Menu {
    /// Renders the menu as a Markdown string.
    pub fn to_markdown(&self) -> String {
        let mut output = String::default();

        for (ref category, ref items) in &self.0 {
            writeln!(output, "*{}*\n", category).unwrap();

            for item in items.iter() {
                writeln!(output, "• {}", item).unwrap();
            }

            writeln!(output, "").unwrap();
        }

        writeln!(output,
                 "> Made with :cnr: by @anrussell. Source available at \
                  https://github.com/euclio/nourishbot.")
            .unwrap();

        output
    }
}

/// Returns the URL pointing to the Nourish menu for a given day. On the weekends, returns Friday's
/// menu.
pub fn url_for_date(date: &NaiveDate) -> Url {
    let days_from_monday = cmp::min(date.weekday().num_days_from_monday(), 4);
    let monday = *date - Duration::days(date.weekday().num_days_from_monday() as i64);

    Url::parse(&format!("http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                       nsf/weeklyMenuLaunch/8DURSE~{}/$file/day{}.htm",
                        monday.format("%m-%d-%Y"),
                        days_from_monday + 1))
        .unwrap()
}

/// Parses the menu information out of HTML.
pub fn parse_menu(html: &str) -> Menu {
    let document = Document::from(html);

    let mut menu: LinkedHashMap<String, Vec<String>> = LinkedHashMap::new();

    let mut last_category = None;

    let menu_node = document.find(Attr("id", "center_text")).first().unwrap();
    for node in menu_node.find(Name("div")).iter() {
        let text = node.text().trim().to_owned();

        if NUTRITION_RE.is_match(&text) || PRICE_RE.is_match(&text) {
            continue;
        }

        if let Some("font-weight:bold;") = node.attr("style") {
            let category = text.to_lower_case().to_title_case();

            // Filter out breakfast.
            if category != "Breakfast Special" {
                last_category = Some(text.to_lower_case().to_title_case());
            }
        } else {
            if let Some(ref category) = last_category {
                if menu.contains_key(category) {
                    let mut entries = menu.get_mut(category).unwrap();
                    entries.push(text);
                } else {
                    menu.insert(category.to_owned(), vec![text]);
                }

            }
        }
    }

    Menu(menu)
}

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

    use chrono::NaiveDate;

    #[test]
    fn monday() {
        let expected_url = "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                            nsf/weeklyMenuLaunch/8DURSE~04-18-2016/$file/day1.htm";

        assert_eq!(expected_url,
                   &url_for_date(&NaiveDate::from_ymd(2016, 4, 18)).serialize());
    }

    #[test]
    fn saturday() {
        let expected_url = "http://dining.guckenheimer.com/clients/athenahealth/fss/fss.\
                            nsf/weeklyMenuLaunch/8DURSE~04-11-2016/$file/day5.htm";

        assert_eq!(expected_url,
                   &url_for_date(&NaiveDate::from_ymd(2016, 4, 16)).serialize());

    }
}
<|endoftext|>


<commit_before>
pub fn puzzle(input: &str) -> u32 {
    0
}

pub fn supports_tls(candidate: &str) -> bool {
    false
}

pub fn contains_abba(candidate: &str) -> bool {
    let by_chars: Vec<char> = candidate.chars().collect();

    for (i, &c) in by_chars.iter().enumerate() {
        if i + 3 < by_chars.len() &&
            c != by_chars[i + 1] &&
            by_chars[i + 1] == by_chars[i + 2] &&
            by_chars[i + 3] == c {
            return true
        }
    }
    false
}

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

    #[test]
    fn does_support_tls() {
        assert!(supports_tls("abba[mnop]qrst"));
        assert!(supports_tls("ioxxoj[asdfgh]zxcvbn"));
        assert!(supports_tls("abcd[efgh]ijk[lmn]oppo"))
    }

    #[test]
    fn does_not_support_tls() {
        assert!( ! supports_tls("abcd[bddb]xyyx") );
        assert!( ! supports_tls("aaaa[qwer]tyui") );
        assert!( ! supports_tls("aaaa[qwer]tyui[foof]poop") );
    }

    #[test]
    fn does_contain_abba() {
        assert!(contains_abba("abba"));
        assert!(contains_abba("ioxxoj"));
    }

    #[test]
    fn does_not_contain_abba() {
        assert!( ! contains_abba("mnop") );
        assert!( ! contains_abba("aaaa") );
    }
}
<commit_msg>Ok this code doesn't work, fails one test and hangs on another<commit_after>
pub fn puzzle(input: &str) -> u32 {
    0
}

pub fn supports_tls(candidate: &str) -> bool {
    let mut start_index = 0;
    let end_index = candidate.len();

    let mut abba_outside = false;

    while let Some((i, c)) = candidate.match_indices(|c| c == '[' || c == ']').next() {
        if c == "[" {
            abba_outside = abba_outside || contains_abba(&candidate[start_index..i]);
        } else {
            if contains_abba(&candidate[start_index..i]) {
                return false;
            }
        }
        start_index = i + 1;
    }

    abba_outside = abba_outside || contains_abba(&candidate[start_index..end_index]);

    abba_outside
}

pub fn contains_abba(candidate: &str) -> bool {
    let by_chars: Vec<char> = candidate.chars().collect();

    for (i, &c) in by_chars.iter().enumerate() {
        if i + 3 < by_chars.len() &&
            c != by_chars[i + 1] &&
            by_chars[i + 1] == by_chars[i + 2] &&
            by_chars[i + 3] == c {
            return true
        }
    }
    false
}

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

    #[test]
    fn does_support_tls() {
        assert!(supports_tls("abba[mnop]qrst"));
        assert!(supports_tls("ioxxoj[asdfgh]zxcvbn"));
        assert!(supports_tls("abcd[efgh]ijk[lmn]oppo"))
    }

    #[test]
    fn does_not_support_tls() {
        assert!( ! supports_tls("abcd[bddb]xyyx") );
        assert!( ! supports_tls("aaaa[qwer]tyui") );
        assert!( ! supports_tls("aaaa[qwer]tyui[foof]poop") );
    }

    #[test]
    fn does_contain_abba() {
        assert!(contains_abba("abba"));
        assert!(contains_abba("ioxxoj"));
    }

    #[test]
    fn does_not_contain_abba() {
        assert!( ! contains_abba("mnop") );
        assert!( ! contains_abba("aaaa") );
    }
}
<|endoftext|>


<commit_before>mod raft_log;
mod progress;
mod log_unstable;

pub mod storage;
pub mod errors;

pub use self::storage::{RaftState, Storage};

pub use self::errors::{Result, Error, StorageError};
<commit_msg>raft: export struct only.<commit_after>mod raft_log;
mod storage;
mod progress;
mod errors;
mod log_unstable;

pub use self::storage::{RaftState, Storage};

pub use self::errors::{Result, Error, StorageError};
<|endoftext|>


<commit_before>#[crate_id = "rustful#0.1-pre"];

#[comment = "RESTful web framework"];
#[license = "MIT"];
#[crate_type = "lib"];
#[crate_type = "rlib"];

#[doc(html_root_url = "http://www.rust-ci.org/Ogeon/rustful/doc/")];

#[cfg(test)]
extern crate test;

extern crate collections;
extern crate url;
extern crate time;
extern crate http;

pub use router::Router;
pub use server::Server;
pub use request::Request;
pub use response::Response;

pub mod router;
pub mod server;
pub mod request;
pub mod response;<commit_msg>Changed to the new inner attribute syntax<commit_after>#![crate_id = "rustful#0.1-pre"]

#![comment = "RESTful web framework"]
#![license = "MIT"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

#![doc(html_root_url = "http://www.rust-ci.org/Ogeon/rustful/doc/")]

#[cfg(test)]
extern crate test;

extern crate collections;
extern crate url;
extern crate time;
extern crate http;

pub use router::Router;
pub use server::Server;
pub use request::Request;
pub use response::Response;

pub mod router;
pub mod server;
pub mod request;
pub mod response;<|endoftext|>


<commit_before>//! De-/serialization helpers for other ruma crates

use serde::de::{Deserialize, IntoDeserializer};

pub mod duration;
pub mod empty;
pub mod json_string;
pub mod test;
pub mod time;
pub mod urlencoded;

pub use empty::vec_as_map_of_empty;

/// Check whether a value is equal to its default value.
pub fn is_default<T: Default + PartialEq>(val: &T) -> bool {
    val == &T::default()
}

/// Simply returns `true`.
///
/// Useful for `#[serde(default = ...)]`.
pub fn default_true() -> bool {
    true
}

/// Simplfy dereferences the given bool.
///
/// Useful for `#[serde(skip_serializing_if = ...)]`
pub fn is_true(b: &bool) -> bool {
    *b
}

/// Serde deserialization decorator to map empty Strings to None,
/// and forward non-empty Strings to the Deserialize implementation for T.
/// Useful for the typical
/// "A room with an X event with an absent, null, or empty Y field
/// should be treated the same as a room with no such event."
/// formulation in the spec.
///
/// To be used like this:
/// `#[serde(deserialize_with = "empty_string_as_none"]`
/// Relevant serde issue: https://github.com/serde-rs/serde/issues/1425
pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
    D: serde::Deserializer<'de>,
    T: serde::Deserialize<'de>,
{
    let opt = Option::<String>::deserialize(de)?;
    // TODO: Switch to and remove this attribute `opt.as_deref()` once MSRV is >= 1.40
    #[allow(clippy::option_as_ref_deref, clippy::unknown_clippy_lints)]
    let opt = opt.as_ref().map(String::as_str);
    match opt {
        None | Some("") => Ok(None),
        // If T = String, like in m.room.name, the second deserialize is actually superfluous.
        // TODO: optimize that somehow?
        Some(s) => T::deserialize(s.into_deserializer()).map(Some),
    }
}
<commit_msg>Silence clippy warning<commit_after>//! De-/serialization helpers for other ruma crates

use serde::de::{Deserialize, IntoDeserializer};

pub mod duration;
pub mod empty;
pub mod json_string;
pub mod test;
pub mod time;
pub mod urlencoded;

pub use empty::vec_as_map_of_empty;

/// Check whether a value is equal to its default value.
pub fn is_default<T: Default + PartialEq>(val: &T) -> bool {
    val == &T::default()
}

/// Simply returns `true`.
///
/// Useful for `#[serde(default = ...)]`.
pub fn default_true() -> bool {
    true
}

/// Simplfy dereferences the given bool.
///
/// Useful for `#[serde(skip_serializing_if = ...)]`
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn is_true(b: &bool) -> bool {
    *b
}

/// Serde deserialization decorator to map empty Strings to None,
/// and forward non-empty Strings to the Deserialize implementation for T.
/// Useful for the typical
/// "A room with an X event with an absent, null, or empty Y field
/// should be treated the same as a room with no such event."
/// formulation in the spec.
///
/// To be used like this:
/// `#[serde(deserialize_with = "empty_string_as_none"]`
/// Relevant serde issue: https://github.com/serde-rs/serde/issues/1425
pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
    D: serde::Deserializer<'de>,
    T: serde::Deserialize<'de>,
{
    let opt = Option::<String>::deserialize(de)?;
    // TODO: Switch to and remove this attribute `opt.as_deref()` once MSRV is >= 1.40
    #[allow(clippy::option_as_ref_deref, clippy::unknown_clippy_lints)]
    let opt = opt.as_ref().map(String::as_str);
    match opt {
        None | Some("") => Ok(None),
        // If T = String, like in m.room.name, the second deserialize is actually superfluous.
        // TODO: optimize that somehow?
        Some(s) => T::deserialize(s.into_deserializer()).map(Some),
    }
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types,non_upper_case_globals,non_snake_case)]

extern crate CoreFoundation_sys;
extern crate libc;
extern crate mach;

use CoreFoundation_sys::{CFTypeRef,CFDictionaryRef,CFMutableDictionaryRef,CFStringRef,CFAllocatorRef};
use libc::{c_void,c_char,c_int,size_t,uintptr_t};

use mach::boolean::boolean_t;
use mach::clock_types::mach_timespec_t;
use mach::kern_return::kern_return_t;
use mach::port::mach_port_t;
use mach::types::task_port_t;
use mach::vm_types::{mach_vm_address_t,mach_vm_size_t};

pub use io_return::*;
pub use serial::*;
pub use types::*;

mod io_return;
mod serial;
mod types;

// exports from <IOKit/IOKitLib.h>

#[repr(C)]
struct IONotificationPort;
pub type IONotificationPortRef = *mut IONotificationPort;

pub type IOServiceMatchingCallback = extern fn (refcon: *mut c_void, iterator: io_iterator_t);
pub type IOServiceInterestCallback = extern fn (refcon: *mut c_void, service: io_service_t, messageType: u32, messageArgument: *mut c_void);

// options for IOServiceAuthorize()
pub const kIOServiceInteractionAllowed: u32 = 0x00000001;

// options for IORegistryCreateIterator(), IORegistryEntryCreateIterator(), IORegistryEntrySearchCFProperty()
pub const kIORegistryIterateRecursively: IOOptionBits = 0x00000001;
pub const kIORegistryIterateParents:     IOOptionBits = 0x00000002;

type IOAsyncCallback0 = extern fn (refcon: *mut c_void, result: IOReturn);
type IOAsyncCallback1 = extern fn (refcon: *mut c_void, result: IOReturn, arg0: *mut c_void);
type IOAsyncCallback2 = extern fn (refcon: *mut c_void, result: IOReturn, arg0: *mut c_void, arg1: *mut c_void);
type IOAsyncCallback = extern fn (refcon: *mut c_void, result: IOReturn, args: *mut *mut c_void, numArgs: u32);

extern "C" {
    pub static kIOMasterPortDefault: mach_port_t;

    pub fn IOMasterPort(bootstrapPort: mach_port_t, masterPort: *mut mach_port_t) -> kern_return_t;

    pub fn IONotificationPortCreate(masterPort: mach_port_t) -> IONotificationPortRef;
    pub fn IONotificationPortDestroy(notify: IONotificationPortRef);
    pub fn IONotificationPortGetMachPort(notify: IONotificationPortRef) -> mach_port_t;

    pub fn IOCreateReceivePort(msgType: u32, recvPort: *mut mach_port_t) -> kern_return_t;

    // IOObject

    pub fn IOObjectRelease(object: io_object_t) -> kern_return_t;
    pub fn IOObjectRetain(object: io_object_t) -> kern_return_t;
    pub fn IOObjectGetClass(object: io_object_t, className: *mut c_char) -> kern_return_t;
    pub fn IOObjectCopyClass(object: io_object_t) -> CFStringRef;
    pub fn IOObjectCopySuperclassForClass(classname: CFStringRef) -> CFStringRef;
    pub fn IOObjectCopyBundleIdentifierForClass(classname: CFStringRef) -> CFStringRef;
    pub fn IOObjectConformsTo(object: io_object_t, className: *const c_char) -> boolean_t;
    pub fn IOObjectGetKernelRetainCount(object: io_object_t) -> u32;
    pub fn IOObjectGetUserRetainCount(object: io_object_t) -> u32;
    pub fn IOObjectGetRetainCount(object: io_object_t) -> u32;

    // IOIterator, subclass of IOObject

    pub fn IOIteratorNext(iterator: io_iterator_t) -> io_object_t;
    pub fn IOIteratorReset(iterator: io_iterator_t);
    pub fn IOIteratorIsValid(iterator: io_iterator_t) -> boolean_t;

    // IOService, subclass of IORegistryEntry

    pub fn IOServiceGetMatchingService(masterPort: mach_port_t, matching: CFDictionaryRef) -> io_service_t;
    pub fn IOServiceGetMatchingServices(masterPort: mach_port_t, matching: CFDictionaryRef, existing: *mut io_iterator_t) -> kern_return_t;
    pub fn IOServiceAddNotification(masterPort: mach_port_t, notificationType: *const c_char, matching: CFDictionaryRef, wakePort: mach_port_t, reference: uintptr_t, notification: *mut io_iterator_t) -> kern_return_t;
    pub fn IOServiceAddMatchingNotification(notifyPort: IONotificationPortRef, notificationType: *const c_char, matching: CFDictionaryRef, callback: IOServiceMatchingCallback, refCon: *mut c_void, notification: *mut io_iterator_t) -> kern_return_t;
    pub fn IOServiceAddInterestNotification(notifyPort: IONotificationPortRef, service: io_service_t, interestType: *const c_char, callback: IOServiceInterestCallback, refCon: *mut c_void, notification: *mut io_object_t) -> kern_return_t;
    pub fn IOServiceMatchPropertyTable(service: io_service_t, matching: CFDictionaryRef, matches: *mut boolean_t) -> kern_return_t;
    pub fn IOServiceGetBusyState(service: io_service_t, busyState: *mut u32) -> kern_return_t;
    pub fn IOServiceWaitQuiet(service: io_service_t, waitTime: *mut mach_timespec_t) -> kern_return_t;

    pub fn IOKitGetBusyState(masterPort: mach_port_t, busyState: *mut u32) -> kern_return_t;
    pub fn IOKitWaitQuiet(masterPort: mach_port_t, waitTime: *mut mach_timespec_t) -> kern_return_t;

    pub fn IOServiceOpen(service: io_service_t, owningTask: task_port_t, connectionType: u32, connect: *mut io_connect_t) -> kern_return_t;
    pub fn IOServiceRequestProbe(service: io_service_t, options: u32) -> kern_return_t;
    pub fn IOServiceAuthorize(service: io_service_t, options: u32) -> kern_return_t;
    pub fn IOServiceOpenAsFileDescriptor(service: io_service_t, oflag: c_int) -> c_int;

    // IOService connection

    pub fn IOServiceClose(connect: io_connect_t) -> kern_return_t;
    pub fn IOConnectAddRef(connect: io_connect_t) -> kern_return_t;
    pub fn IOConnectRelease(connect: io_connect_t) -> kern_return_t;
    pub fn IOConnectGetService(connect: io_connect_t, service: *mut io_service_t) -> kern_return_t;
    pub fn IOConnectSetNotificationPort(connect: io_connect_t, notificationType: u32, port: mach_port_t, reference: uintptr_t) -> kern_return_t;
    pub fn IOConnectMapMemory64(connect: io_connect_t, memoryType: u32, intoTask: task_port_t, atAddress: *mut mach_vm_address_t, ofSize: *mut mach_vm_size_t, options: IOOptionBits) -> kern_return_t;
    pub fn IOConnectUnmapMemory64(connect: io_connect_t, memoryType: u32, fromTask: task_port_t, atAddress: mach_vm_address_t) -> kern_return_t;
    pub fn IOConnectSetCFProperties(connect: io_connect_t, properties: CFTypeRef) -> kern_return_t;
    pub fn IOConnectSetCFProperty(conect: io_connect_t, propertyName: CFStringRef, property: CFTypeRef) -> kern_return_t;

    pub fn IOConnectCallMethod(connection: mach_port_t, selector: u32, input: *const u64, inputCnt: u32, inputStruct: *const c_void, inputStructCnt: size_t, output: *mut u64, outputCnt: *mut u32, outputStruct: *mut c_void, outputStructCnt: *mut size_t) -> kern_return_t;
    pub fn IOConnectCallAsyncMethod(connection: mach_port_t, selector: u32, wake_port: mach_port_t, reference: *mut u64, referenceCnt: u32, input: *const u64, inputStruct: *const c_void, inputStructCnt: size_t, output: *mut u64, outputCnt: *mut u32, outputStruct: *mut c_void, outputStructCnt: *mut size_t) -> kern_return_t;
    pub fn IOConnectCallStructMethod(connection: mach_port_t, selector: u32, inputStruct: *const c_void, inputStructCnt: size_t, outputStruct: *mut c_void, outputStructCnt: *mut size_t) -> kern_return_t;
    pub fn IOConnectCallAsyncStructMethod(connection: mach_port_t, selector: u32, wake_port: mach_port_t, reference: *mut u64, referenceCnt: u32, inputStruct: *const c_void, inputStructCnt: size_t, outputStruct: *mut c_void, outputStructCnt: *mut size_t) -> kern_return_t;
    pub fn IOConnectCallScalarMethod(connection: mach_port_t, selector: u32, input: *const u64, inputCnt: u32, output: *mut u64, outputCnt: *mut u32) -> kern_return_t;
    pub fn IOConnectCallAsyncScalarMethod(connection: mach_port_t, selector: u32, wake_port: mach_port_t, reference: *mut u64, referenceCnt: u32, input: *const u64, inputCnt: u32, output: *mut u64, outputCnt: *mut u32) -> kern_return_t;

    pub fn IOConnectTrap0(connect: io_connect_t, index: u32) -> kern_return_t;
    pub fn IOConnectTrap1(connect: io_connect_t, index: u32, p1: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap2(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap3(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t, p3: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap4(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t, p3: uintptr_t, p4: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap5(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t, p3: uintptr_t, p4: uintptr_t, p5: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap6(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t, p3: uintptr_t, p4: uintptr_t, p5: uintptr_t, p6: uintptr_t) -> kern_return_t;
    pub fn IOConnectAddClient(connect: io_connect_t, client: io_connect_t) -> kern_return_t;

    // IORegistry accessors

    pub fn IORegistryGetRootEntry(masterPort: mach_port_t) -> io_registry_entry_t;
    pub fn IORegistryEntryFromPath(masterPort: mach_port_t, path: *const c_char) -> io_registry_entry_t;
    pub fn IORegistryCreateIterator(masterPort: mach_port_t, plane: *const c_char, options: IOOptionBits, iterator: *mut io_iterator_t) -> kern_return_t;
    pub fn IORegistryEntryCreateIterator(masterPort: mach_port_t, plane: *const c_char, options: IOOptionBits, iterator: *mut io_iterator_t) -> kern_return_t;

    // IORegistryIterator, subclass of IOIterator

    pub fn IORegistryIteratorEnterEntry(iterator: io_iterator_t) -> kern_return_t;
    pub fn IORegistryIteratorExitEntry(iterator: io_iterator_t) -> kern_return_t;

    // IORegistryEntry, subclass of IOObject

    pub fn IORegistryEntryGetName(entry: io_registry_entry_t, name: *mut c_char) -> kern_return_t;
    pub fn IORegistryEntryGetNameInPlane(entry: io_registry_entry_t, plane: *const c_char, name: *mut c_char) -> kern_return_t;
    pub fn IORegistryEntryGetLocationInPlane(entry: io_registry_entry_t, plane: *const c_char, location: *mut c_char) -> kern_return_t;
    pub fn IORegistryEntryGetPath(entry: io_registry_entry_t, plane: *const c_char, path: *mut c_char) -> kern_return_t;
    pub fn IORegistryEntryGetRegistryEntryID(entry: io_registry_entry_t, entryID: *mut u64) -> kern_return_t;
    pub fn IORegistryEntryCreateCFProperties(entry: io_registry_entry_t, properties: *mut CFMutableDictionaryRef, allocator: CFAllocatorRef, options: IOOptionBits) -> kern_return_t;
    pub fn IORegistryEntryCreateCFProperty(entry: io_registry_entry_t, key: CFStringRef, allocator: CFAllocatorRef, options: IOOptionBits) -> CFTypeRef;
    pub fn IORegistryEntrySetCFProperties(entry: io_registry_entry_t, properties: CFTypeRef) -> kern_return_t;
    pub fn IORegistryEntrySetCFProperty(entry: io_registry_entry_t, propertyName: CFStringRef, property: CFTypeRef) -> kern_return_t;
    pub fn IORegistryEntryGetChildIterator(entry: io_registry_entry_t, plane: *const c_char, iterator: *mut io_iterator_t) -> kern_return_t;
    pub fn IORegistryEntryGetChildEntry(entry: io_registry_entry_t, plane: *const c_char, child: *mut io_registry_entry_t) -> kern_return_t;
    pub fn IORegistryEntryGetParentIterator(entry: io_registry_entry_t, plane: *const c_char, iterator: *mut io_iterator_t) -> kern_return_t;
    pub fn IORegistryEntryGetParentEntry(entry: io_registry_entry_t, plane: *const c_char, parent: *mut io_registry_entry_t) -> kern_return_t;
    pub fn IORegistryEntryInPlane(entry: io_registry_entry_t, plane: *const c_char) -> boolean_t;

    // matching dictionary creation helpers

    pub fn IOServiceMatching(name: *const c_char) -> CFMutableDictionaryRef;
    pub fn IOServiceNameMatching(name: *const c_char) -> CFMutableDictionaryRef;
    pub fn IOBSDNameMatching(masterPort: mach_port_t, options: u32, bsdName: *const c_char) -> CFMutableDictionaryRef;
    pub fn IORegistryEntryIDMatching(entryID: u64) -> CFMutableDictionaryRef;
}
<commit_msg>fixes zero-sized struct in FFI<commit_after>#![allow(non_camel_case_types,non_upper_case_globals,non_snake_case)]

extern crate CoreFoundation_sys;
extern crate libc;
extern crate mach;

use CoreFoundation_sys::{CFTypeRef,CFDictionaryRef,CFMutableDictionaryRef,CFStringRef,CFAllocatorRef};
use libc::{c_void,c_char,c_int,size_t,uintptr_t};

use mach::boolean::boolean_t;
use mach::clock_types::mach_timespec_t;
use mach::kern_return::kern_return_t;
use mach::port::mach_port_t;
use mach::types::task_port_t;
use mach::vm_types::{mach_vm_address_t,mach_vm_size_t};

pub use io_return::*;
pub use serial::*;
pub use types::*;

mod io_return;
mod serial;
mod types;

// exports from <IOKit/IOKitLib.h>

#[repr(C)]
struct IONotificationPort {
    __private: c_void,
}

pub type IONotificationPortRef = *mut IONotificationPort;

pub type IOServiceMatchingCallback = extern fn (refcon: *mut c_void, iterator: io_iterator_t);
pub type IOServiceInterestCallback = extern fn (refcon: *mut c_void, service: io_service_t, messageType: u32, messageArgument: *mut c_void);

// options for IOServiceAuthorize()
pub const kIOServiceInteractionAllowed: u32 = 0x00000001;

// options for IORegistryCreateIterator(), IORegistryEntryCreateIterator(), IORegistryEntrySearchCFProperty()
pub const kIORegistryIterateRecursively: IOOptionBits = 0x00000001;
pub const kIORegistryIterateParents:     IOOptionBits = 0x00000002;

type IOAsyncCallback0 = extern fn (refcon: *mut c_void, result: IOReturn);
type IOAsyncCallback1 = extern fn (refcon: *mut c_void, result: IOReturn, arg0: *mut c_void);
type IOAsyncCallback2 = extern fn (refcon: *mut c_void, result: IOReturn, arg0: *mut c_void, arg1: *mut c_void);
type IOAsyncCallback = extern fn (refcon: *mut c_void, result: IOReturn, args: *mut *mut c_void, numArgs: u32);

extern "C" {
    pub static kIOMasterPortDefault: mach_port_t;

    pub fn IOMasterPort(bootstrapPort: mach_port_t, masterPort: *mut mach_port_t) -> kern_return_t;

    pub fn IONotificationPortCreate(masterPort: mach_port_t) -> IONotificationPortRef;
    pub fn IONotificationPortDestroy(notify: IONotificationPortRef);
    pub fn IONotificationPortGetMachPort(notify: IONotificationPortRef) -> mach_port_t;

    pub fn IOCreateReceivePort(msgType: u32, recvPort: *mut mach_port_t) -> kern_return_t;

    // IOObject

    pub fn IOObjectRelease(object: io_object_t) -> kern_return_t;
    pub fn IOObjectRetain(object: io_object_t) -> kern_return_t;
    pub fn IOObjectGetClass(object: io_object_t, className: *mut c_char) -> kern_return_t;
    pub fn IOObjectCopyClass(object: io_object_t) -> CFStringRef;
    pub fn IOObjectCopySuperclassForClass(classname: CFStringRef) -> CFStringRef;
    pub fn IOObjectCopyBundleIdentifierForClass(classname: CFStringRef) -> CFStringRef;
    pub fn IOObjectConformsTo(object: io_object_t, className: *const c_char) -> boolean_t;
    pub fn IOObjectGetKernelRetainCount(object: io_object_t) -> u32;
    pub fn IOObjectGetUserRetainCount(object: io_object_t) -> u32;
    pub fn IOObjectGetRetainCount(object: io_object_t) -> u32;

    // IOIterator, subclass of IOObject

    pub fn IOIteratorNext(iterator: io_iterator_t) -> io_object_t;
    pub fn IOIteratorReset(iterator: io_iterator_t);
    pub fn IOIteratorIsValid(iterator: io_iterator_t) -> boolean_t;

    // IOService, subclass of IORegistryEntry

    pub fn IOServiceGetMatchingService(masterPort: mach_port_t, matching: CFDictionaryRef) -> io_service_t;
    pub fn IOServiceGetMatchingServices(masterPort: mach_port_t, matching: CFDictionaryRef, existing: *mut io_iterator_t) -> kern_return_t;
    pub fn IOServiceAddNotification(masterPort: mach_port_t, notificationType: *const c_char, matching: CFDictionaryRef, wakePort: mach_port_t, reference: uintptr_t, notification: *mut io_iterator_t) -> kern_return_t;
    pub fn IOServiceAddMatchingNotification(notifyPort: IONotificationPortRef, notificationType: *const c_char, matching: CFDictionaryRef, callback: IOServiceMatchingCallback, refCon: *mut c_void, notification: *mut io_iterator_t) -> kern_return_t;
    pub fn IOServiceAddInterestNotification(notifyPort: IONotificationPortRef, service: io_service_t, interestType: *const c_char, callback: IOServiceInterestCallback, refCon: *mut c_void, notification: *mut io_object_t) -> kern_return_t;
    pub fn IOServiceMatchPropertyTable(service: io_service_t, matching: CFDictionaryRef, matches: *mut boolean_t) -> kern_return_t;
    pub fn IOServiceGetBusyState(service: io_service_t, busyState: *mut u32) -> kern_return_t;
    pub fn IOServiceWaitQuiet(service: io_service_t, waitTime: *mut mach_timespec_t) -> kern_return_t;

    pub fn IOKitGetBusyState(masterPort: mach_port_t, busyState: *mut u32) -> kern_return_t;
    pub fn IOKitWaitQuiet(masterPort: mach_port_t, waitTime: *mut mach_timespec_t) -> kern_return_t;

    pub fn IOServiceOpen(service: io_service_t, owningTask: task_port_t, connectionType: u32, connect: *mut io_connect_t) -> kern_return_t;
    pub fn IOServiceRequestProbe(service: io_service_t, options: u32) -> kern_return_t;
    pub fn IOServiceAuthorize(service: io_service_t, options: u32) -> kern_return_t;
    pub fn IOServiceOpenAsFileDescriptor(service: io_service_t, oflag: c_int) -> c_int;

    // IOService connection

    pub fn IOServiceClose(connect: io_connect_t) -> kern_return_t;
    pub fn IOConnectAddRef(connect: io_connect_t) -> kern_return_t;
    pub fn IOConnectRelease(connect: io_connect_t) -> kern_return_t;
    pub fn IOConnectGetService(connect: io_connect_t, service: *mut io_service_t) -> kern_return_t;
    pub fn IOConnectSetNotificationPort(connect: io_connect_t, notificationType: u32, port: mach_port_t, reference: uintptr_t) -> kern_return_t;
    pub fn IOConnectMapMemory64(connect: io_connect_t, memoryType: u32, intoTask: task_port_t, atAddress: *mut mach_vm_address_t, ofSize: *mut mach_vm_size_t, options: IOOptionBits) -> kern_return_t;
    pub fn IOConnectUnmapMemory64(connect: io_connect_t, memoryType: u32, fromTask: task_port_t, atAddress: mach_vm_address_t) -> kern_return_t;
    pub fn IOConnectSetCFProperties(connect: io_connect_t, properties: CFTypeRef) -> kern_return_t;
    pub fn IOConnectSetCFProperty(conect: io_connect_t, propertyName: CFStringRef, property: CFTypeRef) -> kern_return_t;

    pub fn IOConnectCallMethod(connection: mach_port_t, selector: u32, input: *const u64, inputCnt: u32, inputStruct: *const c_void, inputStructCnt: size_t, output: *mut u64, outputCnt: *mut u32, outputStruct: *mut c_void, outputStructCnt: *mut size_t) -> kern_return_t;
    pub fn IOConnectCallAsyncMethod(connection: mach_port_t, selector: u32, wake_port: mach_port_t, reference: *mut u64, referenceCnt: u32, input: *const u64, inputStruct: *const c_void, inputStructCnt: size_t, output: *mut u64, outputCnt: *mut u32, outputStruct: *mut c_void, outputStructCnt: *mut size_t) -> kern_return_t;
    pub fn IOConnectCallStructMethod(connection: mach_port_t, selector: u32, inputStruct: *const c_void, inputStructCnt: size_t, outputStruct: *mut c_void, outputStructCnt: *mut size_t) -> kern_return_t;
    pub fn IOConnectCallAsyncStructMethod(connection: mach_port_t, selector: u32, wake_port: mach_port_t, reference: *mut u64, referenceCnt: u32, inputStruct: *const c_void, inputStructCnt: size_t, outputStruct: *mut c_void, outputStructCnt: *mut size_t) -> kern_return_t;
    pub fn IOConnectCallScalarMethod(connection: mach_port_t, selector: u32, input: *const u64, inputCnt: u32, output: *mut u64, outputCnt: *mut u32) -> kern_return_t;
    pub fn IOConnectCallAsyncScalarMethod(connection: mach_port_t, selector: u32, wake_port: mach_port_t, reference: *mut u64, referenceCnt: u32, input: *const u64, inputCnt: u32, output: *mut u64, outputCnt: *mut u32) -> kern_return_t;

    pub fn IOConnectTrap0(connect: io_connect_t, index: u32) -> kern_return_t;
    pub fn IOConnectTrap1(connect: io_connect_t, index: u32, p1: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap2(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap3(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t, p3: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap4(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t, p3: uintptr_t, p4: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap5(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t, p3: uintptr_t, p4: uintptr_t, p5: uintptr_t) -> kern_return_t;
    pub fn IOConnectTrap6(connect: io_connect_t, index: u32, p1: uintptr_t, p2: uintptr_t, p3: uintptr_t, p4: uintptr_t, p5: uintptr_t, p6: uintptr_t) -> kern_return_t;
    pub fn IOConnectAddClient(connect: io_connect_t, client: io_connect_t) -> kern_return_t;

    // IORegistry accessors

    pub fn IORegistryGetRootEntry(masterPort: mach_port_t) -> io_registry_entry_t;
    pub fn IORegistryEntryFromPath(masterPort: mach_port_t, path: *const c_char) -> io_registry_entry_t;
    pub fn IORegistryCreateIterator(masterPort: mach_port_t, plane: *const c_char, options: IOOptionBits, iterator: *mut io_iterator_t) -> kern_return_t;
    pub fn IORegistryEntryCreateIterator(masterPort: mach_port_t, plane: *const c_char, options: IOOptionBits, iterator: *mut io_iterator_t) -> kern_return_t;

    // IORegistryIterator, subclass of IOIterator

    pub fn IORegistryIteratorEnterEntry(iterator: io_iterator_t) -> kern_return_t;
    pub fn IORegistryIteratorExitEntry(iterator: io_iterator_t) -> kern_return_t;

    // IORegistryEntry, subclass of IOObject

    pub fn IORegistryEntryGetName(entry: io_registry_entry_t, name: *mut c_char) -> kern_return_t;
    pub fn IORegistryEntryGetNameInPlane(entry: io_registry_entry_t, plane: *const c_char, name: *mut c_char) -> kern_return_t;
    pub fn IORegistryEntryGetLocationInPlane(entry: io_registry_entry_t, plane: *const c_char, location: *mut c_char) -> kern_return_t;
    pub fn IORegistryEntryGetPath(entry: io_registry_entry_t, plane: *const c_char, path: *mut c_char) -> kern_return_t;
    pub fn IORegistryEntryGetRegistryEntryID(entry: io_registry_entry_t, entryID: *mut u64) -> kern_return_t;
    pub fn IORegistryEntryCreateCFProperties(entry: io_registry_entry_t, properties: *mut CFMutableDictionaryRef, allocator: CFAllocatorRef, options: IOOptionBits) -> kern_return_t;
    pub fn IORegistryEntryCreateCFProperty(entry: io_registry_entry_t, key: CFStringRef, allocator: CFAllocatorRef, options: IOOptionBits) -> CFTypeRef;
    pub fn IORegistryEntrySetCFProperties(entry: io_registry_entry_t, properties: CFTypeRef) -> kern_return_t;
    pub fn IORegistryEntrySetCFProperty(entry: io_registry_entry_t, propertyName: CFStringRef, property: CFTypeRef) -> kern_return_t;
    pub fn IORegistryEntryGetChildIterator(entry: io_registry_entry_t, plane: *const c_char, iterator: *mut io_iterator_t) -> kern_return_t;
    pub fn IORegistryEntryGetChildEntry(entry: io_registry_entry_t, plane: *const c_char, child: *mut io_registry_entry_t) -> kern_return_t;
    pub fn IORegistryEntryGetParentIterator(entry: io_registry_entry_t, plane: *const c_char, iterator: *mut io_iterator_t) -> kern_return_t;
    pub fn IORegistryEntryGetParentEntry(entry: io_registry_entry_t, plane: *const c_char, parent: *mut io_registry_entry_t) -> kern_return_t;
    pub fn IORegistryEntryInPlane(entry: io_registry_entry_t, plane: *const c_char) -> boolean_t;

    // matching dictionary creation helpers

    pub fn IOServiceMatching(name: *const c_char) -> CFMutableDictionaryRef;
    pub fn IOServiceNameMatching(name: *const c_char) -> CFMutableDictionaryRef;
    pub fn IOBSDNameMatching(masterPort: mach_port_t, options: u32, bsdName: *const c_char) -> CFMutableDictionaryRef;
    pub fn IORegistryEntryIDMatching(entryID: u64) -> CFMutableDictionaryRef;
}
<|endoftext|>


<commit_before>static LOOKUP: [(usize, &'static str); 13] = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
                                              (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
                                              (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")];

pub fn to_arabic(roman: &str) -> Option<usize> {

    let iter = roman
              .chars()
              .map(roman_char_to_arabic_digit);

    let mut num = 0;
    let mut prev = 0;

    for d in iter {

        if d <= 0 {
            return None;
        }

        num += d;

        if prev != 0 && d > prev {
            num -= prev * 2;
        }

        prev = d;
    }

    Some(num)
}

fn roman_char_to_arabic_digit(roman: char) -> usize {

    let mut i = 0;

    for &(n, s) in &LOOKUP {
        if i % 2 == 0 && s.chars().nth(0) == roman.to_uppercase().next() {
            return n;
        }
        i+=1;
    }

    0
}

pub fn to_roman(number: usize) -> Option<String> {

    if number < 1 || number >= 5000 {
        return None;
    }

    let mut roman = String::new();
    let mut num = number;

    while num > 0 {
        for &(n, s) in &LOOKUP {
            if n <= num {
                roman.push_str(s);
                num-= n;
                break;
            }
        }
    }

    Some(roman)
}

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

    #[test]
    fn test_to_arabic() {
        assert_eq!(to_arabic("CliiI"), Some(153));
        assert_eq!(to_arabic("XxXiV"), Some(34));
        assert_eq!(to_arabic("CM"), Some(900));
        assert_eq!(to_arabic("ABC0"), None);
        assert_eq!(to_arabic("vii"), Some(7));
        assert_eq!(to_arabic("IC"), Some(99));
        assert_eq!(to_arabic("XCIX"), Some(99));
        assert_eq!(to_arabic("XA"), None);
    }

    #[test]
    fn test_to_roman() {
        assert_eq!(to_roman(5), Some("V".to_string()));
        assert_eq!(to_roman(101), Some("CI".to_string()));
        assert_eq!(to_roman(99), Some("XCIX".to_string()));
        assert_eq!(to_roman(4999), Some("MMMMCMXCIX".to_string()));
        assert_eq!(to_roman(5000), None);
        assert_eq!(to_roman(5001), None);
        assert_eq!(to_roman(10000), None);
        assert_eq!(to_roman(0), None);
    }

}<commit_msg>Added another test assert<commit_after>static LOOKUP: [(usize, &'static str); 13] = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
                                              (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
                                              (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")];

pub fn to_arabic(roman: &str) -> Option<usize> {

    let iter = roman
              .chars()
              .map(roman_char_to_arabic_digit);

    let mut num = 0;
    let mut prev = 0;

    for d in iter {

        if d <= 0 {
            return None;
        }

        num += d;

        if prev != 0 && d > prev {
            num -= prev * 2;
        }

        prev = d;
    }

    Some(num)
}

fn roman_char_to_arabic_digit(roman: char) -> usize {

    let mut i = 0;

    for &(n, s) in &LOOKUP {
        if i % 2 == 0 && s.chars().nth(0) == roman.to_uppercase().next() {
            return n;
        }
        i+=1;
    }

    0
}

pub fn to_roman(number: usize) -> Option<String> {

    if number < 1 || number >= 5000 {
        return None;
    }

    let mut roman = String::new();
    let mut num = number;

    while num > 0 {
        for &(n, s) in &LOOKUP {
            if n <= num {
                roman.push_str(s);
                num-= n;
                break;
            }
        }
    }

    Some(roman)
}

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

    #[test]
    fn test_to_arabic() {
        assert_eq!(to_arabic("CliiI"), Some(153));
        assert_eq!(to_arabic("XxXiV"), Some(34));
        assert_eq!(to_arabic("CM"), Some(900));
        assert_eq!(to_arabic("ABC0"), None);
        assert_eq!(to_arabic("vii"), Some(7));
        assert_eq!(to_arabic("IC"), Some(99));
        assert_eq!(to_arabic("XCIX"), Some(99));
        assert_eq!(to_arabic("XA"), None);
        assert_eq!(to_arabic("abc"), None);
    }

    #[test]
    fn test_to_roman() {
        assert_eq!(to_roman(5), Some("V".to_string()));
        assert_eq!(to_roman(101), Some("CI".to_string()));
        assert_eq!(to_roman(99), Some("XCIX".to_string()));
        assert_eq!(to_roman(4999), Some("MMMMCMXCIX".to_string()));
        assert_eq!(to_roman(5000), None);
        assert_eq!(to_roman(5001), None);
        assert_eq!(to_roman(10000), None);
        assert_eq!(to_roman(0), None);
    }

}<|endoftext|>