<commit_before>/// Functions for use in euler projects

#[warn(non_camel_case_types)]
#[warn(non_snake_case)]
#[warn(unused_qualifications)]
#[warn(non_upper_case_globals)]
#[warn(missing_docs)]

extern crate primes;

use std::collections::HashSet;

use std::collections::HashMap;
use std::collections::hash_map;
use std::collections::hash_map::Entry;

#[cfg(test)]
use std::num::Int;

pub use primes::PrimeSet;

/// Count the number of occurrences of each value in an iterator
pub fn counter<K, I>(mut list : I) -> HashMap<K, u64>
	where 	K : Eq + std::hash::Hash<hash_map::Hasher>,
			I : Iterator<Item=K>
{
	let mut counter : HashMap<K, u64> = HashMap::new();
	for key in list {
		match counter.entry(key) {
			Entry::Vacant(entry) => {entry.insert(1);},
			Entry::Occupied(entry) => {(*entry.into_mut()) += 1;}
		}
	}
	counter
}

pub fn isqrt_opt(n : u64) -> Option<u64> {
	if n <= 1 {return Some(n);}
	let mut x = n / 2;
	let mut usedset = HashSet::new();

	//println!("New x: {}", x);

	while x*x != n {
		usedset.insert(x);
		// let addx = x + n / x;
		x = (x + n / x + 1) / 2;
		//println!("New x: {} ({})", x, addx);
		if usedset.contains(&x) {
			//println!("Seen! {} ({}, {})", x, x*x, n);
			return None;
		}
	}
	Some(x)
}

pub fn isqrt(n : u64) -> u64 {
	if n <= 1 { return n; }
	let mut x = n / 2;
	let mut usedset = HashSet::new();

	while x*x != n {
		usedset.insert(x);
		let lastx = x;
		x = (x + n / x + 1) / 2;
		//if usedset.contains(&x) {
		if x == lastx {
			if x*x < n {
				return x;
			} else {
				return x-1;
			}
		}
	}
	return x
}

pub fn is_palindrome(n : u64) -> bool {
	let s = n.to_string();
	//let s_rev = String::from_utf8(s.into_bytes().iter().rev().collect());
	let s_rev = s.as_bytes().iter().rev();
	let s_rev_vec : Vec<u8> = s_rev.map(|&b| b).collect();
	let s_rev = String::from_utf8(s_rev_vec);
	match s_rev {
		Ok(s2) => {s == s2},
		_ => false
	}
}

#[deriving(Clone)]
pub struct Pairs<'a, T :'a>{
	vector : &'a [T],
	first : usize,
	second : usize
}

impl<'a, T> Iterator for Pairs<'a, T> {
	type Item=(&'a T, &'a T);
	fn next(&mut self) -> Option<(&'a T, &'a T)> {
		let l = self.vector.len();
		if self.second >= (l-1) && self.first >= (l-2) {return None;}
		self.second += 1;

		if self.second >= l {
			self.first += 1;
			self.second = self.first + 1;
		}

		return unsafe { Some((self.vector.get_unchecked(self.first), self.vector.get_unchecked(self.second)))}
	}
}

pub fn pairs<'a, T>(vec : &'a [T]) -> Pairs<'a, T>{
	Pairs {
		vector : vec,
		first : 0,
		second : 0
	}
}

#[test]
fn test_square(){
	assert_eq!(isqrt_opt(4), Some(2));
	assert_eq!(isqrt_opt(5), None);

	let mut ntests = vec![1,7,8,9,10,11,12,189654,4294967295];

	for _ in (0u64..1000){
		let mut n = std::rand::random::<u64>();
		n = n % 2u64.pow(std::u64::BITS / 2);
		ntests.push(n);
	}

	for &n in ntests.iter(){
		println!("n: {}, n*n: {}", n, n*n);
		assert!(n < 2u64.pow(std::u64::BITS / 2));
		assert_eq!(isqrt_opt(n*n), Some(n));
		if n > 1 {
			assert_eq!(isqrt_opt(n*n - 1), None);
		} else {
			assert_eq!(isqrt_opt(n*n - 1), Some(0));
		}
		assert_eq!(isqrt_opt(n*n + 1), None);
	}
}

#[test]
fn test_isqrt(){
	assert_eq!(isqrt(0), 0);
	let mut ntests = vec![1,7,8,9,10,11,12,189654,4294967295];

	for i in (1u64..1001){
		let mut n = std::rand::random::<u64>();
		n = n % 2u64.pow(std::u64::BITS / 2);
		ntests.push(n);
		ntests.push(i);
	}

	for &n in ntests.iter() {
		assert!(n < 2u64.pow(std::u64::BITS / 2));
		let x = isqrt(n);
		assert!(x*x <= n);
		assert!((x+1)*(x+1) > n);
		assert_eq!(isqrt(n*n - 1), n-1);
		assert_eq!(isqrt(n*n + 1), n);
	}
}

#[test]
fn test_palindrome(){
	assert!(is_palindrome(1));
	assert!(is_palindrome(2));
	assert!(is_palindrome(8));
	assert!(!is_palindrome(10));
	assert!(!is_palindrome(12));
	assert!(!is_palindrome(100));
	assert!(!is_palindrome(11110));
	assert!(!is_palindrome(21111));
	assert!(!is_palindrome(12131));
	assert!(is_palindrome(11));
	assert!(is_palindrome(111));
	assert!(is_palindrome(232));
	assert!(is_palindrome(181));
}

#[test]
fn test_pairs(){
	let v = [1,2,5,4u64];
	let mut my_pairs = pairs(&v);

	assert_eq!(my_pairs.next(), Some((&v[0], &v[1])));
	assert_eq!(my_pairs.next(), Some((&v[0], &v[2])));
	assert_eq!(my_pairs.next(), Some((&v[0], &v[3])));
	assert_eq!(my_pairs.next(), Some((&v[1], &v[2])));
	assert_eq!(my_pairs.next(), Some((&v[1], &v[3])));
	assert_eq!(my_pairs.next(), Some((&v[2], &v[3])));
	//~ assert_eq!(my_pairs.next(), Some((&1, &5)));
	//~ assert_eq!(my_pairs.next(), Some((&1, &4)));
	//~ assert_eq!(my_pairs.next(), Some((&2, &5)));
	//~ assert_eq!(my_pairs.next(), Some((&2, &4)));
	//~ assert_eq!(my_pairs.next(), Some((&5, &4)));
	assert_eq!(my_pairs.next(), None);
	assert_eq!(my_pairs.next(), None);
}
<commit_msg>Updated to newest Rust (deriving -> derive)<commit_after>/// Functions for use in euler projects

#[warn(non_camel_case_types)]
#[warn(non_snake_case)]
#[warn(unused_qualifications)]
#[warn(non_upper_case_globals)]
#[warn(missing_docs)]

extern crate primes;

use std::collections::HashSet;

use std::collections::HashMap;
use std::collections::hash_map;
use std::collections::hash_map::Entry;

#[cfg(test)]
use std::num::Int;

pub use primes::PrimeSet;

/// Count the number of occurrences of each value in an iterator
pub fn counter<K, I>(mut list : I) -> HashMap<K, u64>
	where 	K : Eq + std::hash::Hash<hash_map::Hasher>,
			I : Iterator<Item=K>
{
	let mut counter : HashMap<K, u64> = HashMap::new();
	for key in list {
		match counter.entry(key) {
			Entry::Vacant(entry) => {entry.insert(1);},
			Entry::Occupied(entry) => {(*entry.into_mut()) += 1;}
		}
	}
	counter
}

pub fn isqrt_opt(n : u64) -> Option<u64> {
	if n <= 1 {return Some(n);}
	let mut x = n / 2;
	let mut usedset = HashSet::new();

	//println!("New x: {}", x);

	while x*x != n {
		usedset.insert(x);
		// let addx = x + n / x;
		x = (x + n / x + 1) / 2;
		//println!("New x: {} ({})", x, addx);
		if usedset.contains(&x) {
			//println!("Seen! {} ({}, {})", x, x*x, n);
			return None;
		}
	}
	Some(x)
}

pub fn isqrt(n : u64) -> u64 {
	if n <= 1 { return n; }
	let mut x = n / 2;
	let mut usedset = HashSet::new();

	while x*x != n {
		usedset.insert(x);
		let lastx = x;
		x = (x + n / x + 1) / 2;
		//if usedset.contains(&x) {
		if x == lastx {
			if x*x < n {
				return x;
			} else {
				return x-1;
			}
		}
	}
	return x
}

pub fn is_palindrome(n : u64) -> bool {
	let s = n.to_string();
	//let s_rev = String::from_utf8(s.into_bytes().iter().rev().collect());
	let s_rev = s.as_bytes().iter().rev();
	let s_rev_vec : Vec<u8> = s_rev.map(|&b| b).collect();
	let s_rev = String::from_utf8(s_rev_vec);
	match s_rev {
		Ok(s2) => {s == s2},
		_ => false
	}
}

#[derive(Clone)]
pub struct Pairs<'a, T :'a>{
	vector : &'a [T],
	first : usize,
	second : usize
}

impl<'a, T> Iterator for Pairs<'a, T> {
	type Item=(&'a T, &'a T);
	fn next(&mut self) -> Option<(&'a T, &'a T)> {
		let l = self.vector.len();
		if self.second >= (l-1) && self.first >= (l-2) {return None;}
		self.second += 1;

		if self.second >= l {
			self.first += 1;
			self.second = self.first + 1;
		}

		return unsafe { Some((self.vector.get_unchecked(self.first), self.vector.get_unchecked(self.second)))}
	}
}

pub fn pairs<'a, T>(vec : &'a [T]) -> Pairs<'a, T>{
	Pairs {
		vector : vec,
		first : 0,
		second : 0
	}
}

#[test]
fn test_square(){
	assert_eq!(isqrt_opt(4), Some(2));
	assert_eq!(isqrt_opt(5), None);

	let mut ntests = vec![1,7,8,9,10,11,12,189654,4294967295];

	for _ in (0u64..1000){
		let mut n = std::rand::random::<u64>();
		n = n % 2u64.pow(std::u64::BITS / 2);
		ntests.push(n);
	}

	for &n in ntests.iter(){
		println!("n: {}, n*n: {}", n, n*n);
		assert!(n < 2u64.pow(std::u64::BITS / 2));
		assert_eq!(isqrt_opt(n*n), Some(n));
		if n > 1 {
			assert_eq!(isqrt_opt(n*n - 1), None);
		} else {
			assert_eq!(isqrt_opt(n*n - 1), Some(0));
		}
		assert_eq!(isqrt_opt(n*n + 1), None);
	}
}

#[test]
fn test_isqrt(){
	assert_eq!(isqrt(0), 0);
	let mut ntests = vec![1,7,8,9,10,11,12,189654,4294967295];

	for i in (1u64..1001){
		let mut n = std::rand::random::<u64>();
		n = n % 2u64.pow(std::u64::BITS / 2);
		ntests.push(n);
		ntests.push(i);
	}

	for &n in ntests.iter() {
		assert!(n < 2u64.pow(std::u64::BITS / 2));
		let x = isqrt(n);
		assert!(x*x <= n);
		assert!((x+1)*(x+1) > n);
		assert_eq!(isqrt(n*n - 1), n-1);
		assert_eq!(isqrt(n*n + 1), n);
	}
}

#[test]
fn test_palindrome(){
	assert!(is_palindrome(1));
	assert!(is_palindrome(2));
	assert!(is_palindrome(8));
	assert!(!is_palindrome(10));
	assert!(!is_palindrome(12));
	assert!(!is_palindrome(100));
	assert!(!is_palindrome(11110));
	assert!(!is_palindrome(21111));
	assert!(!is_palindrome(12131));
	assert!(is_palindrome(11));
	assert!(is_palindrome(111));
	assert!(is_palindrome(232));
	assert!(is_palindrome(181));
}

#[test]
fn test_pairs(){
	let v = [1,2,5,4u64];
	let mut my_pairs = pairs(&v);

	assert_eq!(my_pairs.next(), Some((&v[0], &v[1])));
	assert_eq!(my_pairs.next(), Some((&v[0], &v[2])));
	assert_eq!(my_pairs.next(), Some((&v[0], &v[3])));
	assert_eq!(my_pairs.next(), Some((&v[1], &v[2])));
	assert_eq!(my_pairs.next(), Some((&v[1], &v[3])));
	assert_eq!(my_pairs.next(), Some((&v[2], &v[3])));
	//~ assert_eq!(my_pairs.next(), Some((&1, &5)));
	//~ assert_eq!(my_pairs.next(), Some((&1, &4)));
	//~ assert_eq!(my_pairs.next(), Some((&2, &5)));
	//~ assert_eq!(my_pairs.next(), Some((&2, &4)));
	//~ assert_eq!(my_pairs.next(), Some((&5, &4)));
	assert_eq!(my_pairs.next(), None);
	assert_eq!(my_pairs.next(), None);
}
<|endoftext|>


<commit_before>extern crate libc;

use std::os::raw;
use std::ffi::CString;
use std::mem::{transmute, size_of};

#[link(name = "klee")]
extern {
  fn klee_make_symbolic(data: *const raw::c_void, length: libc::size_t, name: *const raw::c_char);
  fn klee_int(name: *const raw::c_char) -> raw::c_int;
}

pub unsafe fn any(data: *const raw::c_void, length: usize, name: &str) {
  let name_cstr = CString::new(name).unwrap();
  klee_make_symbolic(data, length as libc::size_t, name_cstr.as_ptr());
}

pub fn new_i32(name: &str) -> i32 {
  let result;
  let name_cstr = CString::new(name).unwrap();
  unsafe {
    result = klee_int(name_cstr);
  }
  result
}

pub fn i32(data: *const i32, name: &str) {
  unsafe { any(transmute(data), size_of::<i32>(), name); }
}

pub fn u32(data: *const u32, name: &str) {
  unsafe { any(transmute(data), size_of::<u32>(), name); }
}

pub fn i64(data: *const i64, name: &str) {
  unsafe { any(transmute(data), size_of::<i64>(), name); }
}

pub fn u64(data: *const u64, name: &str) {
  unsafe { any(transmute(data), size_of::<u64>(), name); }
}

pub fn str(data: &str, name: &str) {
  unsafe { any(transmute(data.as_ptr()), data.len(), name); }
}

pub fn bytes(data: &[u8], name: &str) {
  unsafe { any(transmute(data.as_ptr()), data.len(), name); }
}
<commit_msg>Update lib.rs<commit_after>extern crate libc;

use std::os::raw;
use std::ffi::CString;
use std::mem::{transmute, size_of};

#[link(name = "klee")]
extern {
  fn klee_make_symbolic(data: *const raw::c_void, length: libc::size_t, name: *const raw::c_char);
  fn klee_int(name: *const raw::c_char) -> raw::c_int;
}

pub unsafe fn any(data: *const raw::c_void, length: usize, name: &str) {
  let name_cstr = CString::new(name).unwrap();
  klee_make_symbolic(data, length as libc::size_t, name_cstr.as_ptr());
}

pub fn new_i32(name: &str) -> i32 {
  let result;
  let name_cstr = CString::new(name).unwrap();
  unsafe {
    result = klee_int(name_cstr.as_ptr());
  }
  result
}

pub fn i32(data: *const i32, name: &str) {
  unsafe { any(transmute(data), size_of::<i32>(), name); }
}

pub fn u32(data: *const u32, name: &str) {
  unsafe { any(transmute(data), size_of::<u32>(), name); }
}

pub fn i64(data: *const i64, name: &str) {
  unsafe { any(transmute(data), size_of::<i64>(), name); }
}

pub fn u64(data: *const u64, name: &str) {
  unsafe { any(transmute(data), size_of::<u64>(), name); }
}

pub fn str(data: &str, name: &str) {
  unsafe { any(transmute(data.as_ptr()), data.len(), name); }
}

pub fn bytes(data: &[u8], name: &str) {
  unsafe { any(transmute(data.as_ptr()), data.len(), name); }
}
<|endoftext|>


<commit_before>//! Lorem ipsum generator.
//!
//! This crate contains functions for generating pseudo-Latin lorem
//! ipsum placeholder text. The traditional lorem ipsum text start
//! like this:
//!
//! > Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
//! > eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
//! > enim ad minim veniam, quis nostrud exercitation ullamco laboris
//! > nisi ut aliquip ex ea commodo consequat. [...]
//!
//! This text is in the [`LOREM_IPSUM`] constant. Random text looking
//! like the above can be generated using the [`lipsum`] function.
//! This function allows you to generate as much text as desired and
//! each invocation will generate different text. This is done using a
//! [Markov chain] based on both the [`LOREM_IPSUM`] and
//! [`LIBER_PRIMUS`] texts. The latter constant holds the full text of
//! the first book of a work by Cicero, of which the lorem ipsum text
//! is a scrambled subset.
//!
//! The random looking text is generatd using a Markov chain of order
//! two, which simply means that the next word is based on the
//! previous two words in the input texts. The Markov chain can be
//! used with other input texts by creating an instance of
//! [`MarkovChain`] and calling its [`learn`] method.
//!
//! [`LOREM_IPSUM`]: constant.LOREM_IPSUM.html
//! [`LIBER_PRIMUS`]: constant.LIBER_PRIMUS.html
//! [`lipsum`]: fn.lipsum.html
//! [`MarkovChain`]: struct.MarkovChain.html
//! [`learn`]: struct.MarkovChain.html#method.learn
//! [Markov chain]: https://en.wikipedia.org/wiki/Markov_chain

extern crate rand;

use std::cell::RefCell;
use std::collections::HashMap;
use rand::Rng;

/// A bigram is simply two consecutive words.
pub type Bigram<'a> = (&'a str, &'a str);

/// Simple order two Markov chain implementation.
///
/// The [Markov chain] is a chain of order two, which means that it
/// will use the previous two words (a bigram) when predicting the
/// next word. This is normally enough to generate random text that
/// looks somewhat plausible. The implementation is based on
/// [Generating arbitrary text with Markov chains in Rust][blog post].
///
/// [Markov chain]: https://en.wikipedia.org/wiki/Markov_chain
/// [blog post]: https://blakewilliams.me/posts/generating-arbitrary-text-with-markov-chains-in-rust
pub struct MarkovChain<'a, R: Rng> {
    map: HashMap<Bigram<'a>, Vec<&'a str>>,
    keys: Vec<Bigram<'a>>,
    rng: R,
}

impl<'a> MarkovChain<'a, rand::ThreadRng> {
    /// Create a new Markov chain. It will use a default thread-local
    /// random number generator.
    pub fn new() -> MarkovChain<'a, rand::ThreadRng> {
        MarkovChain {
            map: HashMap::new(),
            keys: Vec::new(),
            rng: rand::thread_rng(),
        }
    }
}

impl<'a, R: Rng> MarkovChain<'a, R> {
    /// Create a new Markov chain that uses the given random number
    /// generator.
    pub fn new_with_rng(rng: R) -> MarkovChain<'a, R> {
        MarkovChain {
            map: HashMap::new(),
            keys: Vec::new(),
            rng: rng,
        }
    }

    /// Add new text to the Markov chain. This can be called several
    /// times to build up the chain.
    ///
    /// # Examples
    ///
    /// ```
    /// use lipsum::MarkovChain;
    ///
    /// let mut chain = MarkovChain::new();
    /// chain.learn("red green blue");
    /// assert_eq!(chain.words(("red", "green")), Some(&vec!["blue"]));
    ///
    /// chain.learn("red green yellow");
    /// assert_eq!(chain.words(("red", "green")), Some(&vec!["blue", "yellow"]));
    /// ```
    pub fn learn(&mut self, sentence: &'a str) {
        let words = sentence.split_whitespace().collect::<Vec<&str>>();
        for window in words.windows(3) {
            let (a, b, c) = (window[0], window[1], window[2]);
            self.map.entry((a, b)).or_insert_with(Vec::new).push(c);
        }
        // Sync the keys with the current map.
        self.keys = self.map.keys().cloned().collect();
        self.keys.sort();
    }

    /// Get the possible words following the given bigram, or `None`
    /// if the state is invalid.
    ///
    /// # Examples
    ///
    /// ```
    /// use lipsum::MarkovChain;
    ///
    /// let mut chain = MarkovChain::new();
    /// chain.learn("red green blue");
    /// assert_eq!(chain.words(("red", "green")), Some(&vec!["blue"]));
    /// assert_eq!(chain.words(("foo", "bar")), None);
    /// ```
    pub fn words(&self, state: Bigram<'a>) -> Option<&Vec<&str>> {
        self.map.get(&state)
    }

    /// Generate `n` words worth of lorem ipsum text. The text will
    /// start from a random point in the Markov chain.
    ///
    /// See [`generate_from`] if you want to control the starting
    /// point for the generated text.
    ///
    /// # Examples
    ///
    /// Generating the sounds of a grandfather clock:
    ///
    /// ```
    /// use lipsum::MarkovChain;
    ///
    /// let mut chain = MarkovChain::new();
    /// chain.learn("Tick, Tock, Tick, Tock, Ding! Tick, Tock, Ding! Ding!");
    /// println!("{}", chain.generate(15));
    /// ```
    ///
    /// The output looks like this:
    ///
    /// > Ding! Tick, Tock, Tick, Tock, Ding! Ding! Tock, Ding! Tick,
    /// > Tock, Tick, Tock, Tick, Tock
    ///
    /// [`generate_from`]: struct.MarkovChain.html#method.generate_from
    pub fn generate(&mut self, n: usize) -> String {
        join_words(self.iter().take(n))
    }

    /// Generate `n` words worth of lorem ipsum text. The text will
    /// start from the given bigram.
    ///
    /// Use [`generate`] if the starting point is not important.
    ///
    /// [`generate`]: struct.MarkovChain.html#method.generate
    pub fn generate_from(&mut self, n: usize, from: Bigram<'a>) -> String {
        join_words(self.iter_from(from).take(n))
    }

    /// Make a never-ending iterator over the words in the Markov
    /// chain. The iterator starts at a random point in the chain.
    pub fn iter(&mut self) -> Words {
        let state = if self.keys.is_empty() {
            ("", "")
        } else {
            *choose(&mut self.rng, &self.keys).unwrap()
        };
        Words {
            map: &self.map,
            rng: &mut self.rng,
            keys: &self.keys,
            state: state,
        }
    }

    /// Make a never-ending iterator over the words in the Markov
    /// chain. The iterator starts at the given bigram.
    pub fn iter_from(&mut self, from: Bigram<'a>) -> Words {
        Words {
            map: &self.map,
            rng: &mut self.rng,
            keys: &self.keys,
            state: from,
        }
    }
}

pub struct Words<'a> {
    map: &'a HashMap<Bigram<'a>, Vec<&'a str>>,
    rng: &'a mut rand::Rng,
    keys: &'a Vec<Bigram<'a>>,
    state: Bigram<'a>,
}

impl<'a> Iterator for Words<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        if self.map.is_empty() {
            return None;
        }

        let result = Some(self.state.0);

        while !self.map.contains_key(&self.state) {
            self.state = *choose(self.rng, self.keys).unwrap();
        }
        let next_words = &self.map[&self.state];
        let next = choose(self.rng, next_words).unwrap();
        self.state = (self.state.1, next);
        result
    }
}

/// Choose a random element from a slice.
///
/// Unlike `Rng::choose`, this function does not require the RNG to be
/// Sized and thus works on any random number generator.
fn choose<'a, T>(rng: &mut Rng, values: &'a [T]) -> Option<&'a T> {
    if values.is_empty() {
        None
    } else {
        let idx = (values.len() as f32 * rng.next_f32()) as usize;
        Some(&values[idx])
    }
}

fn join_words<'a, I: Iterator<Item = &'a str>>(mut words: I) -> String {
    match words.next() {
        None => String::new(),
        Some(word) => {
            let mut sentence = String::from(word);
            for word in words {
                sentence.push(' ');
                sentence.push_str(word);
            }
            sentence
        }
    }
}

/// The traditional lorem ipsum text as given in [Wikipedia]. Using
/// this text alone for a Markov chain of order two doesn't work very
/// well since each bigram (two consequtive words) is followed by just
/// one other word. In other words, the Markov chain will always
/// produce the same output and recreate the lorem ipsum text
/// precisely. However, combining it with the full text in
/// [`LIBER_PRIMUS`] works well.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Lorem_ipsum
/// [`LIBER_PRIMUS`]: constant.LIBER_PRIMUS.html
pub const LOREM_IPSUM: &'static str = include_str!("lorem-ipsum.txt");

/// The first book in Cicero's work De finibus bonorum et malorum ("On
/// the ends of good and evil"). The lorem ipsum text in
/// [`LOREM_IPSUM`] is derived from part of this text.
///
/// [`LOREM_IPSUM`]: constant.LOREM_IPSUM.html
pub const LIBER_PRIMUS: &'static str = include_str!("liber-primus.txt");

thread_local! {
    // Markov chain generating lorem ipsum text.
    static LOREM_IPSUM_CHAIN: RefCell<MarkovChain<'static, rand::ThreadRng>> = {
        let mut chain = MarkovChain::new();
        // The cost of learning increases as more and more text is
        // added, so we start with the smallest text.
        chain.learn(LOREM_IPSUM);
        chain.learn(LIBER_PRIMUS);
        RefCell::new(chain)
    }
}

/// Generate `n` words of lorem ipsum text. The output starts with
/// "Lorem ipsum" and continues with the standard lorem ipsum text
/// from [`LOREM_IPSUM`]. The text will become random if sufficiently
/// long output is requested.
///
/// [`LOREM_IPSUM`]: constant.LOREM_IPSUM.html
pub fn lipsum(n: usize) -> String {
    LOREM_IPSUM_CHAIN.with(|cell| {
                               let mut chain = cell.borrow_mut();
                               chain.generate_from(n, ("Lorem", "ipsum"))
                           })
}

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

    #[test]
    fn starts_with_lorem_ipsum() {
        assert_eq!(&lipsum(10)[..11], "Lorem ipsum");
    }

    #[test]
    fn generate_zero_words() {
        assert_eq!(lipsum(0).split_whitespace().count(), 0);
    }

    #[test]
    fn generate_one_word() {
        assert_eq!(lipsum(1).split_whitespace().count(), 1);
    }

    #[test]
    fn generate_two_words() {
        assert_eq!(lipsum(2).split_whitespace().count(), 2);
    }

    #[test]
    fn empty_chain() {
        let mut chain = MarkovChain::new();
        assert_eq!(chain.generate(10), "");
    }

    #[test]
    fn generate_from() {
        let mut chain = MarkovChain::new();
        chain.learn("red orange yellow green blue indigo violet");
        assert_eq!(chain.generate_from(5, ("orange", "yellow")),
                   "orange yellow green blue indigo");
    }

    #[test]
    fn generate_last_bigram() {
        // The bigram "yyy zzz" will not be present in the Markov
        // chain's map, and so we will not generate "xxx yyy zzz" as
        // one would expect. The chain moves from state "xxx yyy" to
        // "yyy zzz", but sees that as invalid state and resets itsel
        // back to "xxx yyy".
        let mut chain = MarkovChain::new();
        chain.learn("xxx yyy zzz");
        // We use assert! instead of assert_ne! to support early
        // versions of Rust.
        assert!(chain.generate_from(3, ("xxx", "yyy")) != "xxx yyy zzz");
    }

    #[test]
    fn generate_from_no_panic() {
        // No panic when asked to generate a chain from a starting
        // point that doesn't exist in the chain.
        let mut chain = MarkovChain::new();
        chain.learn("foo bar baz");
        chain.generate_from(3, ("xxx", "yyy"));
    }

    #[test]
    fn chain_map() {
        let mut chain = MarkovChain::new();
        chain.learn("foo bar baz quuz");
        let map = &chain.map;

        assert_eq!(map.len(), 2);
        assert_eq!(map[&("foo", "bar")], vec!["baz"]);
        assert_eq!(map[&("bar", "baz")], vec!["quuz"]);
    }

    #[test]
    fn new_with_rng() {
        extern crate rand;
        use rand::SeedableRng;

        let rng = rand::XorShiftRng::from_seed([1, 2, 3, 4]);
        let mut chain = MarkovChain::new_with_rng(rng);
        chain.learn("foo bar x y z");
        chain.learn("foo bar a b c");

        assert_eq!(chain.generate(15), "a b b x y b x y x y x y bar x y");
    }
}
<commit_msg>MarkovChain: use new_with_rng from new<commit_after>//! Lorem ipsum generator.
//!
//! This crate contains functions for generating pseudo-Latin lorem
//! ipsum placeholder text. The traditional lorem ipsum text start
//! like this:
//!
//! > Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
//! > eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
//! > enim ad minim veniam, quis nostrud exercitation ullamco laboris
//! > nisi ut aliquip ex ea commodo consequat. [...]
//!
//! This text is in the [`LOREM_IPSUM`] constant. Random text looking
//! like the above can be generated using the [`lipsum`] function.
//! This function allows you to generate as much text as desired and
//! each invocation will generate different text. This is done using a
//! [Markov chain] based on both the [`LOREM_IPSUM`] and
//! [`LIBER_PRIMUS`] texts. The latter constant holds the full text of
//! the first book of a work by Cicero, of which the lorem ipsum text
//! is a scrambled subset.
//!
//! The random looking text is generatd using a Markov chain of order
//! two, which simply means that the next word is based on the
//! previous two words in the input texts. The Markov chain can be
//! used with other input texts by creating an instance of
//! [`MarkovChain`] and calling its [`learn`] method.
//!
//! [`LOREM_IPSUM`]: constant.LOREM_IPSUM.html
//! [`LIBER_PRIMUS`]: constant.LIBER_PRIMUS.html
//! [`lipsum`]: fn.lipsum.html
//! [`MarkovChain`]: struct.MarkovChain.html
//! [`learn`]: struct.MarkovChain.html#method.learn
//! [Markov chain]: https://en.wikipedia.org/wiki/Markov_chain

extern crate rand;

use std::cell::RefCell;
use std::collections::HashMap;
use rand::Rng;

/// A bigram is simply two consecutive words.
pub type Bigram<'a> = (&'a str, &'a str);

/// Simple order two Markov chain implementation.
///
/// The [Markov chain] is a chain of order two, which means that it
/// will use the previous two words (a bigram) when predicting the
/// next word. This is normally enough to generate random text that
/// looks somewhat plausible. The implementation is based on
/// [Generating arbitrary text with Markov chains in Rust][blog post].
///
/// [Markov chain]: https://en.wikipedia.org/wiki/Markov_chain
/// [blog post]: https://blakewilliams.me/posts/generating-arbitrary-text-with-markov-chains-in-rust
pub struct MarkovChain<'a, R: Rng> {
    map: HashMap<Bigram<'a>, Vec<&'a str>>,
    keys: Vec<Bigram<'a>>,
    rng: R,
}

impl<'a> MarkovChain<'a, rand::ThreadRng> {
    /// Create a new Markov chain. It will use a default thread-local
    /// random number generator.
    pub fn new() -> MarkovChain<'a, rand::ThreadRng> {
        MarkovChain::new_with_rng(rand::thread_rng())
    }
}

impl<'a, R: Rng> MarkovChain<'a, R> {
    /// Create a new Markov chain that uses the given random number
    /// generator.
    pub fn new_with_rng(rng: R) -> MarkovChain<'a, R> {
        MarkovChain {
            map: HashMap::new(),
            keys: Vec::new(),
            rng: rng,
        }
    }

    /// Add new text to the Markov chain. This can be called several
    /// times to build up the chain.
    ///
    /// # Examples
    ///
    /// ```
    /// use lipsum::MarkovChain;
    ///
    /// let mut chain = MarkovChain::new();
    /// chain.learn("red green blue");
    /// assert_eq!(chain.words(("red", "green")), Some(&vec!["blue"]));
    ///
    /// chain.learn("red green yellow");
    /// assert_eq!(chain.words(("red", "green")), Some(&vec!["blue", "yellow"]));
    /// ```
    pub fn learn(&mut self, sentence: &'a str) {
        let words = sentence.split_whitespace().collect::<Vec<&str>>();
        for window in words.windows(3) {
            let (a, b, c) = (window[0], window[1], window[2]);
            self.map.entry((a, b)).or_insert_with(Vec::new).push(c);
        }
        // Sync the keys with the current map.
        self.keys = self.map.keys().cloned().collect();
        self.keys.sort();
    }

    /// Get the possible words following the given bigram, or `None`
    /// if the state is invalid.
    ///
    /// # Examples
    ///
    /// ```
    /// use lipsum::MarkovChain;
    ///
    /// let mut chain = MarkovChain::new();
    /// chain.learn("red green blue");
    /// assert_eq!(chain.words(("red", "green")), Some(&vec!["blue"]));
    /// assert_eq!(chain.words(("foo", "bar")), None);
    /// ```
    pub fn words(&self, state: Bigram<'a>) -> Option<&Vec<&str>> {
        self.map.get(&state)
    }

    /// Generate `n` words worth of lorem ipsum text. The text will
    /// start from a random point in the Markov chain.
    ///
    /// See [`generate_from`] if you want to control the starting
    /// point for the generated text.
    ///
    /// # Examples
    ///
    /// Generating the sounds of a grandfather clock:
    ///
    /// ```
    /// use lipsum::MarkovChain;
    ///
    /// let mut chain = MarkovChain::new();
    /// chain.learn("Tick, Tock, Tick, Tock, Ding! Tick, Tock, Ding! Ding!");
    /// println!("{}", chain.generate(15));
    /// ```
    ///
    /// The output looks like this:
    ///
    /// > Ding! Tick, Tock, Tick, Tock, Ding! Ding! Tock, Ding! Tick,
    /// > Tock, Tick, Tock, Tick, Tock
    ///
    /// [`generate_from`]: struct.MarkovChain.html#method.generate_from
    pub fn generate(&mut self, n: usize) -> String {
        join_words(self.iter().take(n))
    }

    /// Generate `n` words worth of lorem ipsum text. The text will
    /// start from the given bigram.
    ///
    /// Use [`generate`] if the starting point is not important.
    ///
    /// [`generate`]: struct.MarkovChain.html#method.generate
    pub fn generate_from(&mut self, n: usize, from: Bigram<'a>) -> String {
        join_words(self.iter_from(from).take(n))
    }

    /// Make a never-ending iterator over the words in the Markov
    /// chain. The iterator starts at a random point in the chain.
    pub fn iter(&mut self) -> Words {
        let state = if self.keys.is_empty() {
            ("", "")
        } else {
            *choose(&mut self.rng, &self.keys).unwrap()
        };
        Words {
            map: &self.map,
            rng: &mut self.rng,
            keys: &self.keys,
            state: state,
        }
    }

    /// Make a never-ending iterator over the words in the Markov
    /// chain. The iterator starts at the given bigram.
    pub fn iter_from(&mut self, from: Bigram<'a>) -> Words {
        Words {
            map: &self.map,
            rng: &mut self.rng,
            keys: &self.keys,
            state: from,
        }
    }
}

pub struct Words<'a> {
    map: &'a HashMap<Bigram<'a>, Vec<&'a str>>,
    rng: &'a mut rand::Rng,
    keys: &'a Vec<Bigram<'a>>,
    state: Bigram<'a>,
}

impl<'a> Iterator for Words<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        if self.map.is_empty() {
            return None;
        }

        let result = Some(self.state.0);

        while !self.map.contains_key(&self.state) {
            self.state = *choose(self.rng, self.keys).unwrap();
        }
        let next_words = &self.map[&self.state];
        let next = choose(self.rng, next_words).unwrap();
        self.state = (self.state.1, next);
        result
    }
}

/// Choose a random element from a slice.
///
/// Unlike `Rng::choose`, this function does not require the RNG to be
/// Sized and thus works on any random number generator.
fn choose<'a, T>(rng: &mut Rng, values: &'a [T]) -> Option<&'a T> {
    if values.is_empty() {
        None
    } else {
        let idx = (values.len() as f32 * rng.next_f32()) as usize;
        Some(&values[idx])
    }
}

fn join_words<'a, I: Iterator<Item = &'a str>>(mut words: I) -> String {
    match words.next() {
        None => String::new(),
        Some(word) => {
            let mut sentence = String::from(word);
            for word in words {
                sentence.push(' ');
                sentence.push_str(word);
            }
            sentence
        }
    }
}

/// The traditional lorem ipsum text as given in [Wikipedia]. Using
/// this text alone for a Markov chain of order two doesn't work very
/// well since each bigram (two consequtive words) is followed by just
/// one other word. In other words, the Markov chain will always
/// produce the same output and recreate the lorem ipsum text
/// precisely. However, combining it with the full text in
/// [`LIBER_PRIMUS`] works well.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Lorem_ipsum
/// [`LIBER_PRIMUS`]: constant.LIBER_PRIMUS.html
pub const LOREM_IPSUM: &'static str = include_str!("lorem-ipsum.txt");

/// The first book in Cicero's work De finibus bonorum et malorum ("On
/// the ends of good and evil"). The lorem ipsum text in
/// [`LOREM_IPSUM`] is derived from part of this text.
///
/// [`LOREM_IPSUM`]: constant.LOREM_IPSUM.html
pub const LIBER_PRIMUS: &'static str = include_str!("liber-primus.txt");

thread_local! {
    // Markov chain generating lorem ipsum text.
    static LOREM_IPSUM_CHAIN: RefCell<MarkovChain<'static, rand::ThreadRng>> = {
        let mut chain = MarkovChain::new();
        // The cost of learning increases as more and more text is
        // added, so we start with the smallest text.
        chain.learn(LOREM_IPSUM);
        chain.learn(LIBER_PRIMUS);
        RefCell::new(chain)
    }
}

/// Generate `n` words of lorem ipsum text. The output starts with
/// "Lorem ipsum" and continues with the standard lorem ipsum text
/// from [`LOREM_IPSUM`]. The text will become random if sufficiently
/// long output is requested.
///
/// [`LOREM_IPSUM`]: constant.LOREM_IPSUM.html
pub fn lipsum(n: usize) -> String {
    LOREM_IPSUM_CHAIN.with(|cell| {
                               let mut chain = cell.borrow_mut();
                               chain.generate_from(n, ("Lorem", "ipsum"))
                           })
}

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

    #[test]
    fn starts_with_lorem_ipsum() {
        assert_eq!(&lipsum(10)[..11], "Lorem ipsum");
    }

    #[test]
    fn generate_zero_words() {
        assert_eq!(lipsum(0).split_whitespace().count(), 0);
    }

    #[test]
    fn generate_one_word() {
        assert_eq!(lipsum(1).split_whitespace().count(), 1);
    }

    #[test]
    fn generate_two_words() {
        assert_eq!(lipsum(2).split_whitespace().count(), 2);
    }

    #[test]
    fn empty_chain() {
        let mut chain = MarkovChain::new();
        assert_eq!(chain.generate(10), "");
    }

    #[test]
    fn generate_from() {
        let mut chain = MarkovChain::new();
        chain.learn("red orange yellow green blue indigo violet");
        assert_eq!(chain.generate_from(5, ("orange", "yellow")),
                   "orange yellow green blue indigo");
    }

    #[test]
    fn generate_last_bigram() {
        // The bigram "yyy zzz" will not be present in the Markov
        // chain's map, and so we will not generate "xxx yyy zzz" as
        // one would expect. The chain moves from state "xxx yyy" to
        // "yyy zzz", but sees that as invalid state and resets itsel
        // back to "xxx yyy".
        let mut chain = MarkovChain::new();
        chain.learn("xxx yyy zzz");
        // We use assert! instead of assert_ne! to support early
        // versions of Rust.
        assert!(chain.generate_from(3, ("xxx", "yyy")) != "xxx yyy zzz");
    }

    #[test]
    fn generate_from_no_panic() {
        // No panic when asked to generate a chain from a starting
        // point that doesn't exist in the chain.
        let mut chain = MarkovChain::new();
        chain.learn("foo bar baz");
        chain.generate_from(3, ("xxx", "yyy"));
    }

    #[test]
    fn chain_map() {
        let mut chain = MarkovChain::new();
        chain.learn("foo bar baz quuz");
        let map = &chain.map;

        assert_eq!(map.len(), 2);
        assert_eq!(map[&("foo", "bar")], vec!["baz"]);
        assert_eq!(map[&("bar", "baz")], vec!["quuz"]);
    }

    #[test]
    fn new_with_rng() {
        extern crate rand;
        use rand::SeedableRng;

        let rng = rand::XorShiftRng::from_seed([1, 2, 3, 4]);
        let mut chain = MarkovChain::new_with_rng(rng);
        chain.learn("foo bar x y z");
        chain.learn("foo bar a b c");

        assert_eq!(chain.generate(15), "a b b x y b x y x y x y bar x y");
    }
}
<|endoftext|>


<commit_before>extern crate toml;
extern crate reqwest;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate glob;
extern crate nix;

#[macro_use]
extern crate lazy_static;

use std::io::{Write, BufRead};
use std::ffi::CStr;
extern crate libc;

mod structs;
use structs::Config;
mod ghclient;
mod buffer;
use buffer::Buffer;
mod cstructs;
use cstructs::{Passwd, Spwd, Group};
mod runfiles;
mod statics;
use statics::{CLIENT, CONFIG};

#[allow(dead_code)]
enum NssStatus {
    TryAgain,
    Unavail,
    NotFound,
    Success,
}

impl From<NssStatus> for libc::c_int {
    fn from(status: NssStatus) -> libc::c_int {
        match status {
            NssStatus::TryAgain => -2,
            NssStatus::Unavail => -1,
            NssStatus::NotFound => 0,
            NssStatus::Success => 1,
        }
    }
}

fn string_from(cstrptr: *const libc::c_char) -> String {
    let cstr: &CStr = unsafe { CStr::from_ptr(cstrptr) };
    String::from(cstr.to_str().unwrap())
}

macro_rules! succeed {
    ($ret_struct_p:ident,$ret_struct:ident) => {{
        unsafe { *$ret_struct_p = $ret_struct };
        return libc::c_int::from(NssStatus::Success);
    }};
    ($ret_struct_p:ident,$ret_struct:ident,$finalize:expr) => {{
        $finalize;
        unsafe { *$ret_struct_p = $ret_struct };
        return libc::c_int::from(NssStatus::Success);
    }}
}

macro_rules! fail {
    ($ret_struct_p:ident, $return_val:expr) => {{
        unsafe { *$ret_struct_p = std::ptr::null_mut() };
        return $return_val;
    }}
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwnam_r(cnameptr: *const libc::c_char,
                                         pw: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         pwbufp: *mut *mut Passwd)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(pwbufp, libc::c_int::from(NssStatus::NotFound)),
    };
    match team.members.get(&name) {
        Some(member) => {
            match unsafe { (*pw).pack_args(&mut buffer, &member.login, member.id, team.id, &CONFIG) } {
                Ok(_) => succeed!(pwbufp, pw),
                Err(_) => fail!(pwbufp, nix::Errno::ERANGE as libc::c_int),
            }
        }
        None => fail!(pwbufp, libc::c_int::from(NssStatus::NotFound)),
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwuid_r(uid: libc::uid_t,
                                         pw: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         pwbufp: *mut *mut Passwd)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(pwbufp, libc::c_int::from(NssStatus::NotFound)),
    };
    for member in team.members.values() {
        if uid == member.id as libc::uid_t {
            match unsafe { (*pw).pack_args(&mut buffer, &member.login, member.id, team.id, &CONFIG) } {
                Ok(_) => succeed!(pwbufp, pw),
                Err(_) => fail!(pwbufp, nix::Errno::ERANGE as libc::c_int),
            }
        }
    }
    fail!(pwbufp, libc::c_int::from(NssStatus::NotFound))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setpwent() -> libc::c_int {
    let mut list_file = match runfiles::create() {
        Ok(ret) => ret,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    for member in team.members.values() {
        list_file.write(format!("{}\t{}\t{}\n", member.login, member.id, team.id).as_bytes())
                 .unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwent_r(pwbuf: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         pwbufp: *mut *mut Passwd)
                                         -> libc::c_int {
    let (idx, idx_file, list) = match runfiles::open() {
        Ok(ret) => ret,
        Err(_) => fail!(pwbufp, libc::c_int::from(NssStatus::Unavail)),
    };
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let words: Vec<&str> = line.split("\t").collect();
        let id = words[1].parse::<u64>().unwrap();
        let gid = words[2].parse::<u64>().unwrap();
        match unsafe { (*pwbuf).pack_args(&mut buffer, words[0], id, gid, &CONFIG) } {
            Ok(_) => succeed!(pwbufp, pwbuf, runfiles::increment(idx, idx_file)),
            Err(_) => fail!(pwbufp, nix::Errno::ERANGE as libc::c_int),
        }
    }
    fail!(pwbufp, libc::c_int::from(NssStatus::Unavail))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endpwent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getspnam_r(cnameptr: *const libc::c_char,
                                         spwd: *mut Spwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         spbufp: *mut *mut Spwd)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(spbufp, libc::c_int::from(NssStatus::NotFound)),
    };
    match team.members.get(&name) {
        Some(member) => {
            match unsafe { (*spwd).pack_args(&mut buffer, &member.login, &CONFIG) } {
                Ok(_) => succeed!(spbufp, spwd),
                Err(_) => fail!(spbufp, nix::Errno::ERANGE as libc::c_int),
            }
        }
        None => fail!(spbufp, libc::c_int::from(NssStatus::NotFound)),
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setspent() -> libc::c_int {
    let mut list_file = match runfiles::create() {
        Ok(ret) => ret,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    for member in team.members.values() {
        list_file.write(format!("{}\t{}\n", member.login, member.id).as_bytes())
                 .unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getspent_r(spbuf: *mut Spwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         spbufp: *mut *mut Spwd)
                                         -> libc::c_int {
    let (idx, idx_file, list) = match runfiles::open() {
        Ok(ret) => ret,
        Err(_) => fail!(spbufp, libc::c_int::from(NssStatus::Unavail)),
    };
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let words: Vec<&str> = line.split("\t").collect();
        match unsafe { (*spbuf).pack_args(&mut buffer, words[0], &CONFIG) } {
            Ok(_) => succeed!(spbufp, spbuf, runfiles::increment(idx, idx_file)),
            Err(_) => fail!(spbufp, nix::Errno::ERANGE as libc::c_int),
        }
    }
    fail!(spbufp, libc::c_int::from(NssStatus::Unavail))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endspent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrgid_r(gid: libc::gid_t,
                                         group: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         grbufp: *mut *mut Group)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(grbufp, libc::c_int::from(NssStatus::NotFound)),
    };
    let members: Vec<&str> = team.members.values().map(|m| m.login.as_str()).collect();
    if gid as u64 == team.id {
        match unsafe { (*group).pack_args(&mut buffer, &team.name, gid as u64, &members) } {
            Ok(_) => succeed!(grbufp, group),
            Err(_) => fail!(grbufp, nix::Errno::ERANGE as libc::c_int),
        }
    } else {
        fail!(grbufp, libc::c_int::from(NssStatus::NotFound))
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrnam_r(cnameptr: *const libc::c_char,
                                         group: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         grbufp: *mut *mut Group)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(grbufp, libc::c_int::from(NssStatus::NotFound)),
    };
    let members: Vec<&str> = team.members.values().map(|m| m.login.as_str()).collect();
    if name == team.name {
        match unsafe { (*group).pack_args(&mut buffer, &team.name, team.id, &members) } {
            Ok(_) => succeed!(grbufp, group),
            Err(_) => fail!(grbufp, nix::Errno::ERANGE as libc::c_int),
        }
    }
    fail!(grbufp, libc::c_int::from(NssStatus::NotFound))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setgrent() -> libc::c_int {
    let mut list_file = match runfiles::create() {
        Ok(ret) => ret,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    let member_names = team.members.values().map(|x| x.login.as_str()).collect::<Vec<&str>>().join(" ");
    list_file.write(format!("{}\t{}\t{}\n", team.name, team.id, member_names).as_bytes())
             .unwrap();
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrent_r(grbuf: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         grbufp: *mut *mut Group)
                                         -> libc::c_int {
    let (idx, idx_file, list) = match runfiles::open() {
        Ok(ret) => ret,
        Err(_) => fail!(grbufp, libc::c_int::from(NssStatus::Unavail)),
    };
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let words: Vec<&str> = line.split("\t").collect();
        let member_names: Vec<&str> = words[2].split(" ").collect();
        let gid = words[1].parse::<u64>().unwrap();
        match unsafe { (*grbuf).pack_args(&mut buffer, words[0], gid, &member_names) } {
            Ok(_) => succeed!(grbufp, grbuf, runfiles::increment(idx, idx_file)),
            Err(_) => fail!(grbufp, nix::Errno::ERANGE as libc::c_int),
        }
    }
    fail!(grbufp, libc::c_int::from(NssStatus::Unavail))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endgrent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}
<commit_msg>Improve the consistency of variable names<commit_after>extern crate toml;
extern crate reqwest;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate glob;
extern crate nix;

#[macro_use]
extern crate lazy_static;

use std::io::{Write, BufRead};
use std::ffi::CStr;
extern crate libc;

mod structs;
use structs::Config;
mod ghclient;
mod buffer;
use buffer::Buffer;
mod cstructs;
use cstructs::{Passwd, Spwd, Group};
mod runfiles;
mod statics;
use statics::{CLIENT, CONFIG};

#[allow(dead_code)]
enum NssStatus {
    TryAgain,
    Unavail,
    NotFound,
    Success,
}

impl From<NssStatus> for libc::c_int {
    fn from(status: NssStatus) -> libc::c_int {
        match status {
            NssStatus::TryAgain => -2,
            NssStatus::Unavail => -1,
            NssStatus::NotFound => 0,
            NssStatus::Success => 1,
        }
    }
}

fn string_from(cstrptr: *const libc::c_char) -> String {
    let cstr: &CStr = unsafe { CStr::from_ptr(cstrptr) };
    String::from(cstr.to_str().unwrap())
}

macro_rules! succeed {
    ($ret_struct_p:ident,$ret_struct:ident) => {{
        unsafe { *$ret_struct_p = $ret_struct };
        return libc::c_int::from(NssStatus::Success);
    }};
    ($ret_struct_p:ident,$ret_struct:ident,$finalize:expr) => {{
        $finalize;
        unsafe { *$ret_struct_p = $ret_struct };
        return libc::c_int::from(NssStatus::Success);
    }}
}

macro_rules! fail {
    ($ret_struct_p:ident, $return_val:expr) => {{
        unsafe { *$ret_struct_p = std::ptr::null_mut() };
        return $return_val;
    }}
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwnam_r(cnameptr: *const libc::c_char,
                                         pwptr: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         pwptrp: *mut *mut Passwd)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(pwptrp, libc::c_int::from(NssStatus::NotFound)),
    };
    match team.members.get(&name) {
        Some(member) => {
            match unsafe { (*pwptr).pack_args(&mut buffer, &member.login, member.id, team.id, &CONFIG) } {
                Ok(_) => succeed!(pwptrp, pwptr),
                Err(_) => fail!(pwptrp, nix::Errno::ERANGE as libc::c_int),
            }
        }
        None => fail!(pwptrp, libc::c_int::from(NssStatus::NotFound)),
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwuid_r(uid: libc::uid_t,
                                         pwptr: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         pwptrp: *mut *mut Passwd)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(pwptrp, libc::c_int::from(NssStatus::NotFound)),
    };
    for member in team.members.values() {
        if uid == member.id as libc::uid_t {
            match unsafe { (*pwptr).pack_args(&mut buffer, &member.login, member.id, team.id, &CONFIG) } {
                Ok(_) => succeed!(pwptrp, pwptr),
                Err(_) => fail!(pwptrp, nix::Errno::ERANGE as libc::c_int),
            }
        }
    }
    fail!(pwptrp, libc::c_int::from(NssStatus::NotFound))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setpwent() -> libc::c_int {
    let mut list_file = match runfiles::create() {
        Ok(ret) => ret,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    for member in team.members.values() {
        list_file.write(format!("{}\t{}\t{}\n", member.login, member.id, team.id).as_bytes())
                 .unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwent_r(pwptr: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         pwptrp: *mut *mut Passwd)
                                         -> libc::c_int {
    let (idx, idx_file, list) = match runfiles::open() {
        Ok(ret) => ret,
        Err(_) => fail!(pwptrp, libc::c_int::from(NssStatus::Unavail)),
    };
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let words: Vec<&str> = line.split("\t").collect();
        let id = words[1].parse::<u64>().unwrap();
        let gid = words[2].parse::<u64>().unwrap();
        match unsafe { (*pwptr).pack_args(&mut buffer, words[0], id, gid, &CONFIG) } {
            Ok(_) => succeed!(pwptrp, pwptr, runfiles::increment(idx, idx_file)),
            Err(_) => fail!(pwptrp, nix::Errno::ERANGE as libc::c_int),
        }
    }
    fail!(pwptrp, libc::c_int::from(NssStatus::Unavail))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endpwent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getspnam_r(cnameptr: *const libc::c_char,
                                         spptr: *mut Spwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         spptrp: *mut *mut Spwd)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(spptrp, libc::c_int::from(NssStatus::NotFound)),
    };
    match team.members.get(&name) {
        Some(member) => {
            match unsafe { (*spptr).pack_args(&mut buffer, &member.login, &CONFIG) } {
                Ok(_) => succeed!(spptrp, spptr),
                Err(_) => fail!(spptrp, nix::Errno::ERANGE as libc::c_int),
            }
        }
        None => fail!(spptrp, libc::c_int::from(NssStatus::NotFound)),
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setspent() -> libc::c_int {
    let mut list_file = match runfiles::create() {
        Ok(ret) => ret,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    for member in team.members.values() {
        list_file.write(format!("{}\t{}\n", member.login, member.id).as_bytes())
                 .unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getspent_r(spptr: *mut Spwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         spptrp: *mut *mut Spwd)
                                         -> libc::c_int {
    let (idx, idx_file, list) = match runfiles::open() {
        Ok(ret) => ret,
        Err(_) => fail!(spptrp, libc::c_int::from(NssStatus::Unavail)),
    };
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let words: Vec<&str> = line.split("\t").collect();
        match unsafe { (*spptr).pack_args(&mut buffer, words[0], &CONFIG) } {
            Ok(_) => succeed!(spptrp, spptr, runfiles::increment(idx, idx_file)),
            Err(_) => fail!(spptrp, nix::Errno::ERANGE as libc::c_int),
        }
    }
    fail!(spptrp, libc::c_int::from(NssStatus::Unavail))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endspent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrgid_r(gid: libc::gid_t,
                                         grptr: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         grptrp: *mut *mut Group)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(grptrp, libc::c_int::from(NssStatus::NotFound)),
    };
    let members: Vec<&str> = team.members.values().map(|m| m.login.as_str()).collect();
    if gid as u64 == team.id {
        match unsafe { (*grptr).pack_args(&mut buffer, &team.name, gid as u64, &members) } {
            Ok(_) => succeed!(grptrp, grptr),
            Err(_) => fail!(grptrp, nix::Errno::ERANGE as libc::c_int),
        }
    } else {
        fail!(grptrp, libc::c_int::from(NssStatus::NotFound))
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrnam_r(cnameptr: *const libc::c_char,
                                         grptr: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         grptrp: *mut *mut Group)
                                         -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => fail!(grptrp, libc::c_int::from(NssStatus::NotFound)),
    };
    let members: Vec<&str> = team.members.values().map(|m| m.login.as_str()).collect();
    if name == team.name {
        match unsafe { (*grptr).pack_args(&mut buffer, &team.name, team.id, &members) } {
            Ok(_) => succeed!(grptrp, grptr),
            Err(_) => fail!(grptrp, nix::Errno::ERANGE as libc::c_int),
        }
    }
    fail!(grptrp, libc::c_int::from(NssStatus::NotFound))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setgrent() -> libc::c_int {
    let mut list_file = match runfiles::create() {
        Ok(ret) => ret,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    let team = match CLIENT.get_team() {
        Ok(team) => team,
        Err(_) => return libc::c_int::from(NssStatus::Success),
    };
    let member_names = team.members.values().map(|x| x.login.as_str()).collect::<Vec<&str>>().join(" ");
    list_file.write(format!("{}\t{}\t{}\n", team.name, team.id, member_names).as_bytes())
             .unwrap();
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrent_r(grptr: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         grptrp: *mut *mut Group)
                                         -> libc::c_int {
    let (idx, idx_file, list) = match runfiles::open() {
        Ok(ret) => ret,
        Err(_) => fail!(grptrp, libc::c_int::from(NssStatus::Unavail)),
    };
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let words: Vec<&str> = line.split("\t").collect();
        let member_names: Vec<&str> = words[2].split(" ").collect();
        let gid = words[1].parse::<u64>().unwrap();
        match unsafe { (*grptr).pack_args(&mut buffer, words[0], gid, &member_names) } {
            Ok(_) => succeed!(grptrp, grptr, runfiles::increment(idx, idx_file)),
            Err(_) => fail!(grptrp, nix::Errno::ERANGE as libc::c_int),
        }
    }
    fail!(grptrp, libc::c_int::from(NssStatus::Unavail))
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endgrent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}
<|endoftext|>


<commit_before>extern crate chrono;

use std::collections::HashMap;
use std::str::FromStr;
use std::fmt::{self, Write};
pub use chrono::NaiveDate as Date;

#[derive(Debug, Eq, PartialEq)]
pub enum Recurrence {
    Daily(bool, u16),
    BDaily(bool, u16),
    Monthly(bool, u16),
    Weekly(bool, u16),
    Yearly(bool, u16),
}

impl FromStr for Recurrence {
    type Err = ();
    fn from_str(s: &str) -> Result<Recurrence, ()> {
        use self::Recurrence::*;

        let hard = s.starts_with("+");

        s[hard as usize..s.len() - 1].parse::<u16>().map_err(|_| ()).and_then(|num| {
            Ok(match &s[s.len() - 1..] {
                "d" => Daily(hard, num),
                "b" => BDaily(hard, num),
                "m" => Monthly(hard, num),
                "w" => Weekly(hard, num),
                "y" => Yearly(hard, num),
                _ => return Err(()),
            })
        })
    }
}

impl fmt::Display for Recurrence {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Recurrence::*;
        try!(f.write_str("rec:"));
        match *self {
            Daily(hard, num) => {
                if hard { try!(f.write_char('+')); }
                try!(write!(f, "{}d", num));
            },
            BDaily(hard, num) => {
                if hard { try!(f.write_char('+')); }
                try!(write!(f, "{}b", num));
            },
            Monthly(hard, num) => {
                if hard { try!(f.write_char('+')); }
                try!(write!(f, "{}m", num));
            },
            Weekly(hard, num) => {
                if hard { try!(f.write_char('+')); }
                try!(write!(f, "{}w", num));
            },
            Yearly(hard, num) => {
                if hard { try!(f.write_char('+')); }
                try!(write!(f, "{}y", num));
            },
        }

        Ok(())
    }
}

#[derive(Debug, Eq, PartialEq, Default)]
pub struct Task {
    pub subject: String,
    pub priority: u8,
    pub create_date: Option<Date>,
    pub finish_date: Option<Date>,
    pub finished: bool,
    pub threshold_date: Option<Date>,
    pub due_date: Option<Date>,
    pub recurrence: Option<Recurrence>,
    pub contexts: Vec<String>,
    pub projects: Vec<String>,
    pub hashtags: Vec<String>,
    pub tags: HashMap<String, String>,
}

impl fmt::Display for Task {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.finished {
            try!(f.write_str("x "));
            if let Some(ref finish_date) = self.finish_date {
                try!(write!(f, "{} ", finish_date));
            }
        }

        if self.priority < 26 {
            try!(write!(f, "({}) ", (self.priority + b'A') as char));
        }

        if let Some(ref create_date) = self.create_date {
            try!(write!(f, "{} ", create_date));
        }

        try!(f.write_str(&self.subject));

        if let Some(ref date) = self.due_date {
            try!(write!(f, " due:{}", date));
        }
        if let Some(ref date) = self.threshold_date {
            try!(write!(f, " t:{}", date));
        }
        if let Some(ref rec) = self.recurrence {
            try!(write!(f, " rec:{}", rec));
        }
        for (tag, value) in &self.tags {
            try!(write!(f, " {}:{}", tag, value));
        }

        Ok(())
    }
}

impl FromStr for Task {
    type Err = ();
    fn from_str(mut s: &str) -> Result<Task, ()> {
        // parse finish state
        let (finished, mut finish_date) = if s.starts_with("x ") {
            s = &s[2..];
            (true, s[..10].parse::<Date>().ok())
        } else {
            (false, None)
        };

        if finish_date.is_some() {
            s = &s[11..];
        }

        // parse priority
        let priority = if s.starts_with("(") && &s[2..4] == ") " {
            match s.as_bytes()[1] {
                p @ b'A'...b'Z' => {
                    s = &s[4..];
                    p - b'A'
                }
                _ => 26,
            }
        } else {
            26
        };

        // parse creation date
        let mut create_date = match s[..10].parse::<Date>() {
            Ok(date) => {
                s = &s[11..];
                Some(date)
            }
            Err(_) => None,
        };

        // If creation date follows finished mark, it can be parsed as a finish date,
        // which is wrong. Note finish state part and creation date can be separated
        // by priority, so this confusion is possible only if no priority given.
        if priority == 26 && finish_date.is_some() && create_date.is_none() {
            create_date = finish_date;
            finish_date = None;
        }

        let buf = s;

        // Subject is the line with technical info removed (priority, creation date and finish state, tags).
        let mut subject = Vec::new();

        // FSM to parse line for tags, contexts and projects.

        #[derive(Copy, Clone, Eq, PartialEq)]
        enum St {
            Init,
            Ctx(usize),
            Prj(usize),
            Hash(usize),
            Tag0(usize),
            Tag1(usize, usize),
        }
        let mut state = St::Init;
        let mut contexts = Vec::new();
        let mut projects = Vec::new();
        let mut hashtags = Vec::new();
        let mut tags = HashMap::new();

        // Some known tags: threshold date (`t:`), due date (`due:`) and recurrence (`rec:`).
        let mut threshold_date = None;
        let mut due_date = None;
        let mut recurrence = None;

        for (i, c) in buf.bytes().enumerate() {
            let new_state = match (c, state) {
                (b'@', St::Init) => St::Ctx(i),
                (b'+', St::Init) => St::Prj(i),
                (b'#', St::Init) => St::Hash(i),
                (b'a'...b'z', St::Init) => St::Tag0(i),
                (b':', St::Tag0(j)) => St::Tag1(j, i),
                (b' ', St::Tag0(_)) => St::Init,
                (b' ', St::Ctx(j)) => {
                    if i - j > 1 {
                        contexts.push(buf[j + 1..i].to_owned());
                    }
                    St::Init
                }
                (b' ', St::Prj(j)) => {
                    if i - j > 1 {
                        projects.push(buf[j + 1..i].to_owned());
                    }
                    St::Init
                }
                (b' ', St::Hash(j)) => {
                    if i - j > 1 {
                        hashtags.push(buf[j + 1..i].to_owned());
                    }
                    St::Init
                }
                (b' ', St::Tag1(j, k)) => {
                    if i - k > 1 {
                        match &buf[j..k] {
                            "rec" => {
                                recurrence = buf[k + 1..i].parse::<Recurrence>().ok();
                            }
                            "due" => {
                                due_date = buf[k + 1..i].parse::<Date>().ok();
                            }
                            "t" => {
                                threshold_date = buf[k + 1..i].parse::<Date>().ok();
                            }
                            tag => {
                                tags.insert(tag.to_owned(), buf[k + 1..i].to_owned());
                            }
                        }
                    }
                    St::Init
                }
                _ => state,
            };

            if new_state == St::Init {
                match state {
                    St::Tag0(j) | St::Hash(j) | St::Prj(j) | St::Ctx(j) => {
                        subject.extend(&buf.as_bytes()[j..i]);
                    }
                    St::Init => subject.push(buf.as_bytes()[i]),
                    _ => {}
                }
            }

            state = new_state;
        }

        // Check final state, so tags at the end of line are also parsed.
        match state {
            St::Tag1(j, k) => {
                tags.insert(buf[j..k].to_owned(), buf[k + 1..].to_owned());
            }
            St::Prj(j) => {
                projects.push(buf[j + 1..].to_owned());
                subject.extend(&buf.as_bytes()[j..]);
            }
            St::Ctx(j) => {
                contexts.push(buf[j + 1..].to_owned());
                subject.extend(&buf.as_bytes()[j..]);
            }
            St::Hash(j) => {
                hashtags.push(buf[j + 1..].to_owned());
                subject.extend(&buf.as_bytes()[j..]);
            }
            St::Tag0(j) => {
                subject.extend(&buf.as_bytes()[j..]);
            }
            _ => {}
        }

        let subject = String::from_utf8(subject).unwrap_or_else(|_| s.to_owned());

        Ok(Task {
            subject: subject,
            priority: priority,
            create_date: create_date,
            finish_date: finish_date,
            finished: finished,
            threshold_date: threshold_date,
            due_date: due_date,
            recurrence: recurrence,
            contexts: contexts,
            projects: projects,
            hashtags: hashtags,
            tags: tags,
        })
    }
}

#[cfg(test)]
mod test {
    use super::{Date, Recurrence, Task};

    #[test]
    fn it_works() {
        let todo_item = "(A) 2016-03-24 22:00 сходить на занятие в @microfon rec:+1w \
                         due:2016-04-05 t:2016-04-05 at:20:00";
        let task = todo_item.parse::<Task>().unwrap();
        assert_eq!(task.subject, "22:00 сходить на занятие в @microfon");
        assert_eq!(task,
                   Task {
                       subject: "22:00 сходить на занятие в @microfon".to_owned(),
                       create_date: Some(Date::from_ymd(2016, 3, 24)),
                       priority: 0,
                       recurrence: Some(Recurrence::Weekly(true, 1)),
                       due_date: Some(Date::from_ymd(2016, 4, 5)),
                       threshold_date: Some(Date::from_ymd(2016, 4, 5)),
                       contexts: vec!["microfon".to_owned()],
                       tags: vec![("at".to_owned(), "20:00".to_owned())].into_iter().collect(),
                       ..Task::default()
                   });

        let todo_item = "2016-03-27 сменить загранпаспорт due:2020-08-14 t:2020-04-14 +документы";
        let task = todo_item.parse::<Task>().unwrap();
        assert_eq!(task.subject, "сменить загранпаспорт +документы");
        assert_eq!(task,
                   Task {
                       subject: "сменить загранпаспорт +документы".to_owned(),
                       create_date: Some(Date::from_ymd(2016, 3, 27)),
                       priority: 26,
                       due_date: Some(Date::from_ymd(2020, 8, 14)),
                       threshold_date: Some(Date::from_ymd(2020, 4, 14)),
                       projects: vec!["документы".to_owned()],
                       ..Task::default()
                   });

        let todo_item = "x 2016-03-27 сменить загранпаспорт due:2020-08-14 t:2020-04-14 +документы";
        let task = todo_item.parse::<Task>().unwrap();
        assert_eq!(task.subject, "сменить загранпаспорт +документы");
        assert_eq!(task,
                   Task {
                       subject: "сменить загранпаспорт +документы".to_owned(),
                       create_date: Some(Date::from_ymd(2016, 3, 27)),
                       priority: 26,
                       due_date: Some(Date::from_ymd(2020, 8, 14)),
                       threshold_date: Some(Date::from_ymd(2020, 4, 14)),
                       projects: vec!["документы".to_owned()],
                       finished: true,
                       ..Task::default()
                   });
    }
}
<commit_msg>rustfmt<commit_after>extern crate chrono;

use std::collections::HashMap;
use std::str::FromStr;
use std::fmt::{self, Write};
pub use chrono::NaiveDate as Date;

#[derive(Debug, Eq, PartialEq)]
pub enum Recurrence {
    Daily(bool, u16),
    BDaily(bool, u16),
    Monthly(bool, u16),
    Weekly(bool, u16),
    Yearly(bool, u16),
}

impl FromStr for Recurrence {
    type Err = ();
    fn from_str(s: &str) -> Result<Recurrence, ()> {
        use self::Recurrence::*;

        let hard = s.starts_with("+");

        s[hard as usize..s.len() - 1].parse::<u16>().map_err(|_| ()).and_then(|num| {
            Ok(match &s[s.len() - 1..] {
                "d" => Daily(hard, num),
                "b" => BDaily(hard, num),
                "m" => Monthly(hard, num),
                "w" => Weekly(hard, num),
                "y" => Yearly(hard, num),
                _ => return Err(()),
            })
        })
    }
}

impl fmt::Display for Recurrence {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Recurrence::*;
        try!(f.write_str("rec:"));
        match *self {
            Daily(hard, num) => {
                if hard {
                    try!(f.write_char('+'));
                }
                try!(write!(f, "{}d", num));
            }
            BDaily(hard, num) => {
                if hard {
                    try!(f.write_char('+'));
                }
                try!(write!(f, "{}b", num));
            }
            Monthly(hard, num) => {
                if hard {
                    try!(f.write_char('+'));
                }
                try!(write!(f, "{}m", num));
            }
            Weekly(hard, num) => {
                if hard {
                    try!(f.write_char('+'));
                }
                try!(write!(f, "{}w", num));
            }
            Yearly(hard, num) => {
                if hard {
                    try!(f.write_char('+'));
                }
                try!(write!(f, "{}y", num));
            }
        }

        Ok(())
    }
}

#[derive(Debug, Eq, PartialEq, Default)]
pub struct Task {
    pub subject: String,
    pub priority: u8,
    pub create_date: Option<Date>,
    pub finish_date: Option<Date>,
    pub finished: bool,
    pub threshold_date: Option<Date>,
    pub due_date: Option<Date>,
    pub recurrence: Option<Recurrence>,
    pub contexts: Vec<String>,
    pub projects: Vec<String>,
    pub hashtags: Vec<String>,
    pub tags: HashMap<String, String>,
}

impl fmt::Display for Task {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.finished {
            try!(f.write_str("x "));
            if let Some(ref finish_date) = self.finish_date {
                try!(write!(f, "{} ", finish_date));
            }
        }

        if self.priority < 26 {
            try!(write!(f, "({}) ", (self.priority + b'A') as char));
        }

        if let Some(ref create_date) = self.create_date {
            try!(write!(f, "{} ", create_date));
        }

        try!(f.write_str(&self.subject));

        if let Some(ref date) = self.due_date {
            try!(write!(f, " due:{}", date));
        }
        if let Some(ref date) = self.threshold_date {
            try!(write!(f, " t:{}", date));
        }
        if let Some(ref rec) = self.recurrence {
            try!(write!(f, " rec:{}", rec));
        }
        for (tag, value) in &self.tags {
            try!(write!(f, " {}:{}", tag, value));
        }

        Ok(())
    }
}

impl FromStr for Task {
    type Err = ();
    fn from_str(mut s: &str) -> Result<Task, ()> {
        // parse finish state
        let (finished, mut finish_date) = if s.starts_with("x ") {
            s = &s[2..];
            (true, s[..10].parse::<Date>().ok())
        } else {
            (false, None)
        };

        if finish_date.is_some() {
            s = &s[11..];
        }

        // parse priority
        let priority = if s.starts_with("(") && &s[2..4] == ") " {
            match s.as_bytes()[1] {
                p @ b'A'...b'Z' => {
                    s = &s[4..];
                    p - b'A'
                }
                _ => 26,
            }
        } else {
            26
        };

        // parse creation date
        let mut create_date = match s[..10].parse::<Date>() {
            Ok(date) => {
                s = &s[11..];
                Some(date)
            }
            Err(_) => None,
        };

        // If creation date follows finished mark, it can be parsed as a finish date,
        // which is wrong. Note finish state part and creation date can be separated
        // by priority, so this confusion is possible only if no priority given.
        if priority == 26 && finish_date.is_some() && create_date.is_none() {
            create_date = finish_date;
            finish_date = None;
        }

        let buf = s;

        // Subject is the line with technical info removed (priority, creation date and finish state, tags).
        let mut subject = Vec::new();

        // FSM to parse line for tags, contexts and projects.

        #[derive(Copy, Clone, Eq, PartialEq)]
        enum St {
            Init,
            Ctx(usize),
            Prj(usize),
            Hash(usize),
            Tag0(usize),
            Tag1(usize, usize),
        }
        let mut state = St::Init;
        let mut contexts = Vec::new();
        let mut projects = Vec::new();
        let mut hashtags = Vec::new();
        let mut tags = HashMap::new();

        // Some known tags: threshold date (`t:`), due date (`due:`) and recurrence (`rec:`).
        let mut threshold_date = None;
        let mut due_date = None;
        let mut recurrence = None;

        for (i, c) in buf.bytes().enumerate() {
            let new_state = match (c, state) {
                (b'@', St::Init) => St::Ctx(i),
                (b'+', St::Init) => St::Prj(i),
                (b'#', St::Init) => St::Hash(i),
                (b'a'...b'z', St::Init) => St::Tag0(i),
                (b':', St::Tag0(j)) => St::Tag1(j, i),
                (b' ', St::Tag0(_)) => St::Init,
                (b' ', St::Ctx(j)) => {
                    if i - j > 1 {
                        contexts.push(buf[j + 1..i].to_owned());
                    }
                    St::Init
                }
                (b' ', St::Prj(j)) => {
                    if i - j > 1 {
                        projects.push(buf[j + 1..i].to_owned());
                    }
                    St::Init
                }
                (b' ', St::Hash(j)) => {
                    if i - j > 1 {
                        hashtags.push(buf[j + 1..i].to_owned());
                    }
                    St::Init
                }
                (b' ', St::Tag1(j, k)) => {
                    if i - k > 1 {
                        match &buf[j..k] {
                            "rec" => {
                                recurrence = buf[k + 1..i].parse::<Recurrence>().ok();
                            }
                            "due" => {
                                due_date = buf[k + 1..i].parse::<Date>().ok();
                            }
                            "t" => {
                                threshold_date = buf[k + 1..i].parse::<Date>().ok();
                            }
                            tag => {
                                tags.insert(tag.to_owned(), buf[k + 1..i].to_owned());
                            }
                        }
                    }
                    St::Init
                }
                _ => state,
            };

            if new_state == St::Init {
                match state {
                    St::Tag0(j) | St::Hash(j) | St::Prj(j) | St::Ctx(j) => {
                        subject.extend(&buf.as_bytes()[j..i]);
                    }
                    St::Init => subject.push(buf.as_bytes()[i]),
                    _ => {}
                }
            }

            state = new_state;
        }

        // Check final state, so tags at the end of line are also parsed.
        match state {
            St::Tag1(j, k) => {
                tags.insert(buf[j..k].to_owned(), buf[k + 1..].to_owned());
            }
            St::Prj(j) => {
                projects.push(buf[j + 1..].to_owned());
                subject.extend(&buf.as_bytes()[j..]);
            }
            St::Ctx(j) => {
                contexts.push(buf[j + 1..].to_owned());
                subject.extend(&buf.as_bytes()[j..]);
            }
            St::Hash(j) => {
                hashtags.push(buf[j + 1..].to_owned());
                subject.extend(&buf.as_bytes()[j..]);
            }
            St::Tag0(j) => {
                subject.extend(&buf.as_bytes()[j..]);
            }
            _ => {}
        }

        let subject = String::from_utf8(subject).unwrap_or_else(|_| s.to_owned());

        Ok(Task {
            subject: subject,
            priority: priority,
            create_date: create_date,
            finish_date: finish_date,
            finished: finished,
            threshold_date: threshold_date,
            due_date: due_date,
            recurrence: recurrence,
            contexts: contexts,
            projects: projects,
            hashtags: hashtags,
            tags: tags,
        })
    }
}

#[cfg(test)]
mod test {
    use super::{Date, Recurrence, Task};

    #[test]
    fn it_works() {
        let todo_item = "(A) 2016-03-24 22:00 сходить на занятие в @microfon rec:+1w \
                         due:2016-04-05 t:2016-04-05 at:20:00";
        let task = todo_item.parse::<Task>().unwrap();
        assert_eq!(task.subject, "22:00 сходить на занятие в @microfon");
        assert_eq!(task,
                   Task {
                       subject: "22:00 сходить на занятие в @microfon".to_owned(),
                       create_date: Some(Date::from_ymd(2016, 3, 24)),
                       priority: 0,
                       recurrence: Some(Recurrence::Weekly(true, 1)),
                       due_date: Some(Date::from_ymd(2016, 4, 5)),
                       threshold_date: Some(Date::from_ymd(2016, 4, 5)),
                       contexts: vec!["microfon".to_owned()],
                       tags: vec![("at".to_owned(), "20:00".to_owned())].into_iter().collect(),
                       ..Task::default()
                   });

        let todo_item = "2016-03-27 сменить загранпаспорт due:2020-08-14 t:2020-04-14 +документы";
        let task = todo_item.parse::<Task>().unwrap();
        assert_eq!(task.subject, "сменить загранпаспорт +документы");
        assert_eq!(task,
                   Task {
                       subject: "сменить загранпаспорт +документы".to_owned(),
                       create_date: Some(Date::from_ymd(2016, 3, 27)),
                       priority: 26,
                       due_date: Some(Date::from_ymd(2020, 8, 14)),
                       threshold_date: Some(Date::from_ymd(2020, 4, 14)),
                       projects: vec!["документы".to_owned()],
                       ..Task::default()
                   });

        let todo_item = "x 2016-03-27 сменить загранпаспорт due:2020-08-14 t:2020-04-14 +документы";
        let task = todo_item.parse::<Task>().unwrap();
        assert_eq!(task.subject, "сменить загранпаспорт +документы");
        assert_eq!(task,
                   Task {
                       subject: "сменить загранпаспорт +документы".to_owned(),
                       create_date: Some(Date::from_ymd(2016, 3, 27)),
                       priority: 26,
                       due_date: Some(Date::from_ymd(2020, 8, 14)),
                       threshold_date: Some(Date::from_ymd(2020, 4, 14)),
                       projects: vec!["документы".to_owned()],
                       finished: true,
                       ..Task::default()
                   });
    }
}
<|endoftext|>


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

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

#[cfg(test)]
mod tests {
    use super::*;
    use std::mem;
    use std::ptr;
    use std::ffi::CString;
    use std::ffi::CStr;

    #[test]
    fn constants() {
        assert_eq!(
            CUPS_FORMAT_TEXT,
            CString::new("text/plain").unwrap().as_bytes_with_nul()
        );
        assert_eq!(
            CUPS_JOBID_CURRENT,
            0
        );
    }

    #[test]
    fn list_printers() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            std::slice::from_raw_parts(dests, num_dests as usize);
            cupsFreeDests(num_dests, dests);
        }
    }

    #[test]
    fn default_printer() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            cupsGetDest(ptr::null(), ptr::null(), num_dests, dests);
            cupsFreeDests(num_dests, dests);
        }
    }

    #[test]
    fn printer_info() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            let destinations = std::slice::from_raw_parts(dests, num_dests as usize);

            for destination in destinations {
                let c_printer_name = CStr::from_ptr((*destination).name);
                let printer_name = c_printer_name.to_string_lossy();

                let c_make_and_model = cupsGetOption(
                    CString::new("printer-make-and-model").unwrap().as_ptr(),
                    destination.num_options,
                    destination.options
                );
                let make_and_model = CStr::from_ptr(c_make_and_model).to_string_lossy();
                println!("{} ({})", printer_name, make_and_model);
                /*
                // This prints a real page.
                let job_id: i32 = cupsPrintFile(
                    destination.name,
                    CString::new("/System/Library/CoreServices/SystemVersion.plist")
                        .unwrap()
                        .as_ptr(),
                    CString::new("Test print")
                        .unwrap()
                        .as_ptr(),
                    destination.num_options,
                    destination.options
                );
                println!("{}", job_id);
                */

            }
            cupsFreeDests(num_dests, dests);
        }
    }
}
<commit_msg>Run `rustfmt`<commit_after>#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

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

#[cfg(test)]
mod tests {
    use super::*;
    use std::mem;
    use std::ptr;
    use std::ffi::CString;
    use std::ffi::CStr;

    #[test]
    fn constants() {
        assert_eq!(
            CUPS_FORMAT_TEXT,
            CString::new("text/plain").unwrap().as_bytes_with_nul()
        );
        assert_eq!(CUPS_JOBID_CURRENT, 0);
    }

    #[test]
    fn list_printers() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            std::slice::from_raw_parts(dests, num_dests as usize);
            cupsFreeDests(num_dests, dests);
        }
    }

    #[test]
    fn default_printer() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            cupsGetDest(ptr::null(), ptr::null(), num_dests, dests);
            cupsFreeDests(num_dests, dests);
        }
    }

    #[test]
    fn printer_info() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            let destinations = std::slice::from_raw_parts(dests, num_dests as usize);

            for destination in destinations {
                let c_printer_name = CStr::from_ptr((*destination).name);
                let printer_name = c_printer_name.to_string_lossy();

                let c_make_and_model = cupsGetOption(
                    CString::new("printer-make-and-model").unwrap().as_ptr(),
                    destination.num_options,
                    destination.options,
                );
                let make_and_model = CStr::from_ptr(c_make_and_model).to_string_lossy();
                println!("{} ({})", printer_name, make_and_model);
                /*
                // This prints a real page.
                let job_id: i32 = cupsPrintFile(
                    destination.name,
                    CString::new("/System/Library/CoreServices/SystemVersion.plist")
                        .unwrap()
                        .as_ptr(),
                    CString::new("Test print")
                        .unwrap()
                        .as_ptr(),
                    destination.num_options,
                    destination.options
                );
                println!("{}", job_id);
                */

            }
            cupsFreeDests(num_dests, dests);
        }
    }
}
<|endoftext|>


<commit_before>extern crate matrix;

pub use graph::{Graph, NodeIndex};
pub use var_store::VarIndex;

pub mod graph;
pub mod op;
pub mod var_store;
<commit_msg>reimport Operation at crate level<commit_after>extern crate matrix;

pub use graph::{Graph, NodeIndex};
pub use var_store::VarIndex;
pub use op::Operation;

pub mod graph;
pub mod op;
pub mod var_store;
<|endoftext|>


<commit_before>
extern crate crc16;
extern crate time;
extern crate serial;

mod stub;
mod protocol;

use std::io::prelude::*;
use std::io;
use time::Duration;
use serial::prelude::*;
use std::rc::Rc;
use std::cell::RefCell;

pub use protocol::ProtocolSnoop;

struct PlugwiseInner<'a, I> {
    protocol: Rc<RefCell<protocol::Protocol<'a, I>>>
}

struct CircleInner<'a, I> {
    protocol: Rc<RefCell<protocol::Protocol<'a, I>>>,
    mac: u64,
    calibration_data: protocol::ResCalibration
}

impl<'a, I: Read+Write+'a> PlugwiseInner<'a, I> {
    fn initialize(port: I) -> io::Result<PlugwiseInner<'a, I>> {
        let plugwise = PlugwiseInner {
            protocol: Rc::new(RefCell::new(protocol::Protocol::new(port)))
        };

        let result = try!(plugwise.protocol.borrow_mut().initialize());

        if !result.is_online {
            return Err(io::Error::new(io::ErrorKind::Other, "not online"));
        }

        Ok(plugwise)
    }
}

pub trait Plugwise<'a> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle + 'a>>;
    fn set_snoop(&self, snoop: ProtocolSnoop<'a>);
}

pub trait Circle {
    fn switch_on(&self) -> io::Result<()>;
    fn switch_off(&self) -> io::Result<()>;
    fn is_switched_on(&self) -> io::Result<bool>;
    fn get_actual_watt_usage(&self) -> io::Result<f64>;
    fn get_clock(&self) -> io::Result<time::Tm>;
}

impl<'a, I:Read+Write+'a> Plugwise<'a> for PlugwiseInner<'a, I> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle+ 'a>> {
        let calibration_data = try!(self.protocol.borrow_mut().calibrate(mac));
        Ok(Box::new(CircleInner {
            protocol: self.protocol.clone(),
            mac: mac,
            calibration_data: calibration_data
        }))
    }

    fn set_snoop(&self, snoop: ProtocolSnoop<'a>) {
        self.protocol.borrow_mut().set_snoop(snoop);
    }
}

impl<'a, I:Read+Write+'a> Circle for CircleInner<'a, I> {
    fn switch_on(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, true));
        Ok(())
    }

    fn switch_off(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, false));
        Ok(())
    }

    fn is_switched_on(&self) -> io::Result<bool> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        Ok(info.relay_state)
    }

    fn get_actual_watt_usage(&self) -> io::Result<f64> {
        let power_usage = try!(self.protocol.borrow_mut().get_power_usage(self.mac));
        Ok(power_usage.pulse_8s.to_watts(self.calibration_data))
    }

    fn get_clock(&self) -> io::Result<time::Tm> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        let clock = try!(self.protocol.borrow_mut().get_clock_info(self.mac));

        let mut tm = match info.datetime.to_tm() {
            Some(tm) => tm,
            None => return Err(io::Error::new(io::ErrorKind::Other, "circle returns an invalid timestamp"))
        };
        tm.tm_sec = clock.second as i32;
        tm.tm_min = clock.minute as i32;
        tm.tm_hour = clock.hour as i32;
        tm.tm_wday = (clock.day_of_week % 7) as i32;
        Ok(tm)
    }
}

pub fn plugwise_device<'a>(device: &str) -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let mut port = try!(serial::open(device));
    try!(port.configure(|settings| {
        settings.set_baud_rate(serial::Baud115200);
        settings.set_char_size(serial::Bits8);
        settings.set_parity(serial::ParityNone);
        settings.set_stop_bits(serial::Stop1);
    }));

    port.set_timeout(Duration::milliseconds(1000));
    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

pub fn plugwise_simulator<'a>() -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let port = stub::Stub::new();

    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}
<commit_msg>Set clock<commit_after>
extern crate crc16;
extern crate time;
extern crate serial;

mod stub;
mod protocol;

use std::io::prelude::*;
use std::io;
use time::Duration;
use serial::prelude::*;
use std::rc::Rc;
use std::cell::RefCell;

pub use protocol::ProtocolSnoop;

struct PlugwiseInner<'a, I> {
    protocol: Rc<RefCell<protocol::Protocol<'a, I>>>
}

struct CircleInner<'a, I> {
    protocol: Rc<RefCell<protocol::Protocol<'a, I>>>,
    mac: u64,
    calibration_data: protocol::ResCalibration
}

impl<'a, I: Read+Write+'a> PlugwiseInner<'a, I> {
    fn initialize(port: I) -> io::Result<PlugwiseInner<'a, I>> {
        let plugwise = PlugwiseInner {
            protocol: Rc::new(RefCell::new(protocol::Protocol::new(port)))
        };

        let result = try!(plugwise.protocol.borrow_mut().initialize());

        if !result.is_online {
            return Err(io::Error::new(io::ErrorKind::Other, "not online"));
        }

        Ok(plugwise)
    }
}

pub trait Plugwise<'a> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle + 'a>>;
    fn set_snoop(&self, snoop: ProtocolSnoop<'a>);
}

pub trait Circle {
    fn switch_on(&self) -> io::Result<()>;
    fn switch_off(&self) -> io::Result<()>;
    fn is_switched_on(&self) -> io::Result<bool>;
    fn get_actual_watt_usage(&self) -> io::Result<f64>;
    fn get_clock(&self) -> io::Result<time::Tm>;
    fn set_clock(&self, tm: time::Tm) -> io::Result<()>;
}

impl<'a, I:Read+Write+'a> Plugwise<'a> for PlugwiseInner<'a, I> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle+ 'a>> {
        let calibration_data = try!(self.protocol.borrow_mut().calibrate(mac));
        Ok(Box::new(CircleInner {
            protocol: self.protocol.clone(),
            mac: mac,
            calibration_data: calibration_data
        }))
    }

    fn set_snoop(&self, snoop: ProtocolSnoop<'a>) {
        self.protocol.borrow_mut().set_snoop(snoop);
    }
}

impl<'a, I:Read+Write+'a> Circle for CircleInner<'a, I> {
    fn switch_on(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, true));
        Ok(())
    }

    fn switch_off(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, false));
        Ok(())
    }

    fn is_switched_on(&self) -> io::Result<bool> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        Ok(info.relay_state)
    }

    fn get_actual_watt_usage(&self) -> io::Result<f64> {
        let power_usage = try!(self.protocol.borrow_mut().get_power_usage(self.mac));
        Ok(power_usage.pulse_8s.to_watts(self.calibration_data))
    }

    fn get_clock(&self) -> io::Result<time::Tm> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        let clock = try!(self.protocol.borrow_mut().get_clock_info(self.mac));

        let mut tm = match info.datetime.to_tm() {
            Some(tm) => tm,
            None => return Err(io::Error::new(io::ErrorKind::Other, "circle returns an invalid timestamp"))
        };
        tm.tm_sec = clock.second as i32;
        tm.tm_min = clock.minute as i32;
        tm.tm_hour = clock.hour as i32;
        tm.tm_wday = (clock.day_of_week % 7) as i32;
        Ok(tm)
    }

    fn set_clock(&self, tm: time::Tm) -> io::Result<()> {
        let clock_set = protocol::ReqClockSet::new_from_tm(tm);
        try!(self.protocol.borrow_mut().set_clock(self.mac, clock_set));
        Ok(())
    }
}

pub fn plugwise_device<'a>(device: &str) -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let mut port = try!(serial::open(device));
    try!(port.configure(|settings| {
        settings.set_baud_rate(serial::Baud115200);
        settings.set_char_size(serial::Bits8);
        settings.set_parity(serial::ParityNone);
        settings.set_stop_bits(serial::Stop1);
    }));

    port.set_timeout(Duration::milliseconds(1000));
    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

pub fn plugwise_simulator<'a>() -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let port = stub::Stub::new();

    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}
<|endoftext|>


<commit_before>
#[macro_use]
extern crate derive_new;
extern crate ndarray;
extern crate ndarray_linalg;
extern crate itertools;
extern crate num_complex;
extern crate num_traits;

pub mod traits;
pub mod explicit;
pub mod diag;
pub mod semi_implicit;
pub mod model;
pub mod lyapunov;

pub use self::traits::*;
<commit_msg>Add crate document<commit_after>//! solve ODE with rust-ndarray
//!
//! Algorithms
//! -----------
//!
//! - explicit
//!   - Euler
//!   - Heun
//!   - classical 4th order Runge-Kutta
//! - semi-implicit
//!   - stiff RK4
//!
//! Models
//! -------
//! Basic chaotic dynamics are implemented as examples in [model submodule](model/)
//!
//! - [Lorenz three-variables system](https://en.wikipedia.org/wiki/Lorenz_system): [Lorenz63](model/lorenz63/struct.Lorenz63.html)
//! - [Lorenz 96 system](https://en.wikipedia.org/wiki/Lorenz_96_system): [Lorenz96](model/lorenz96/struct.Lorenz96.html)
//! - [Roessler system](https://en.wikipedia.org/wiki/Lorenz_syste://en.wikipedia.org/wiki/R%C3%B6ssler_attractor): [Roessler](model/roessler/struct.Roessler.html)
//! - GOY shell model: [GoyShell](model/goy_shell/struct.GoyShell.html)

#[macro_use]
extern crate derive_new;
extern crate ndarray;
extern crate ndarray_linalg;
extern crate itertools;
extern crate num_complex;
extern crate num_traits;

pub mod traits;
pub mod explicit;
pub mod diag;
pub mod semi_implicit;
pub mod model;
pub mod lyapunov;

pub use self::traits::*;
<|endoftext|>