<commit_before>#![feature(test)]
extern crate test;

use std::sync::atomic;
use std::sync::atomic::Ordering;
use std::cell::UnsafeCell;
use std::ptr;

const KEY_SIZE: usize = 16;
const INITIAL_SIZE: usize = 128;
const DJB2_START: usize = 5318;

type HashKey = [u8; KEY_SIZE];

#[derive(Debug)]
struct HashSlot {
    key: atomic::AtomicPtr<HashKey>,
    count: atomic::AtomicUsize,
}

/// The interior threadsafe counter struct. Everything here is atomic or
/// composed of atomics.
struct InnerCounter {
    size: usize,
    used: atomic::AtomicUsize,
    slots: Vec<HashSlot>,
    prev: atomic::AtomicPtr<Counter>,
    next: atomic:: AtomicPtr<Counter>,
}

/// The public Counter structure which contains the inner mutable state. This
/// allows us to put this straight into an Arc and safely share between threads
pub struct Counter {
    inner: UnsafeCell<InnerCounter>
}

/// An implementation of the djb2 hash using rust iterators over the contents
/// of the HashKey array.
pub fn djb2_hash(key: &HashKey) -> usize {
    key.into_iter().take_while(|c| { **c != 0 }).fold(DJB2_START, |hash, c| {
        (hash * 33) ^ (*c as usize)})
}

/// A small inlined helper function that checks if a given character is ascii
fn is_ascii(c: &u8) -> bool {
    return (*c > 47 && *c < 58) || (*c > 64 && *c < 91) || (*c > 96 && *c < 123);
}

/// Clean a given key string. This involves stripping non ascii characters out
/// and limiting the key to 16 characters. The resulting key is stack allocated
/// so be careful with it. If you need it long term put it in a Box.
pub fn clean_key(key: &str) -> HashKey {
    let mut cleaned: HashKey = [0; KEY_SIZE]; // Stack allocate a temporary key.
    for (index, v) in key.bytes().take(KEY_SIZE).filter(is_ascii).enumerate() {
        cleaned[index] = v;
    }
    return cleaned;
}

impl HashSlot {
    /// Create a new HashSlot struct with the default values. The key is
    /// initially set to null so get/incr know this slot is empty.
    pub fn new() -> HashSlot {
        HashSlot {
            key: atomic::AtomicPtr::new(ptr::null_mut()),
            count: atomic::AtomicUsize::new(0),
        }
    }
}

impl InnerCounter {
    /// Create a new InnerCounter. Sets up all the top level atomic values for
    /// keeping track of global hashmap state.
    pub fn new() -> InnerCounter {
        let mut slots: Vec<HashSlot> = Vec::with_capacity(INITIAL_SIZE);
        for _ in 0..INITIAL_SIZE {
            slots.push(HashSlot::new());
        }
        let counter: InnerCounter = InnerCounter {
            size: INITIAL_SIZE,
            used: atomic::AtomicUsize::new(0),
            slots: slots,
            prev: atomic::AtomicPtr::new(ptr::null_mut()),
            next: atomic::AtomicPtr::new(ptr::null_mut()),
        };
        return counter;
    }

    /// Debug method for printing out the contents of a HashSlot struct
    unsafe fn print_slot(&self, index: usize) {
        let slot = &self.slots[index];
        let key = slot.key.load(Ordering::Relaxed);
        println!("slot {}: {:?}", index, slot);
        if key.is_null() {
            println!("\t key=null")
        }
        else {
            println!("\t key={:?}", *key);
        }
    }

    /// Unsafe method for returning the value of a counter.
    pub unsafe fn unsafe_get(&self, key: HashKey) -> usize {
        let size = self.size;
        let mut index = djb2_hash(&key) % size;
        loop {
            let slot = &self.slots[index];
            let other_key = slot.key.load(Ordering::Relaxed);
            if other_key.is_null() {
                return 0;
            }
            else if *other_key == key{
                return slot.count.load(Ordering::Relaxed);
            }
            index = (index + 1) % size;
        }
    }

    /// Unsafe method for incrementing a counter by the given amount. Returns
    /// the previous value of the counter.
    pub unsafe fn unsafe_incr(&mut self, key: HashKey, count: usize) -> usize {
        let size = self.size;
        let mut index = djb2_hash(&key) % size;
        let mut slot: &HashSlot;
        loop {
            slot = &self.slots[index];
            let other_key = slot.key.load(Ordering::Relaxed);
            if other_key.is_null() {
                let boxed_key = Box::new(key);
                let res = slot.key.compare_exchange(other_key,
                                                    Box::into_raw(boxed_key),
                                                    Ordering::Relaxed,
                                                    Ordering::Relaxed);
                let ptr = res.unwrap_or_else(|v| { v });
                if ptr.is_null() || *ptr == key {
                    self.used.fetch_add(1, Ordering::Relaxed);
                    break;
                }
            }
            else if *other_key == key {
                break;
            }
            index = (index + 1) % size
        }
        return slot.count.fetch_add(count, Ordering::Relaxed);
    }
}

impl Drop for InnerCounter {
    /// Custom drop implementation to go through and explicitly re-box all the
    /// heap memory used to store keys within the Vec<HashSlot>. During the
    /// first call to unsafe_incr() the key is copied into a Box so it will
    /// persist.
    fn drop(&mut self) {
        for hs in &self.slots {
            let key = hs.key.load(Ordering::Relaxed);
            if !key.is_null() {
                let _: Box<HashKey> = unsafe { Box::from_raw(key) };
            }
        }
    }
}

unsafe impl Sync for Counter {}
unsafe impl Send for Counter {}

impl Counter {
    /// Create a new counter, which is has an UnsafeCell wrapping the actual
    /// counter implementation.
    pub fn new() -> Counter {
        Counter {
            inner: UnsafeCell::new(InnerCounter::new())
        }
    }

    /// Extract the mutable reference to the counter implementation and
    /// increment the given key.
    pub fn incr(&self, key: &str, count: usize) -> usize {
        return unsafe {
            (*self.inner.get()).unsafe_incr(clean_key(key), count)
        };
    }

    /// Extract the mutable reference to the counter implementation and
    /// return the current counter value for the given key.
    pub fn get(&self, key: &str) -> usize {
        return unsafe {
            (*self.inner.get()).unsafe_get(clean_key(key))
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use test::Bencher;
    use std::thread;
    use std::sync::Arc;

    #[test]
    fn increment() {
        let mut counter = Counter::new();
        let res = counter.incr("foo", 1);
        assert_eq!(res, 0);
    }

    #[test]
    fn sequential_increment() {
        let mut counter = Counter::new();
        for _ in 0..100000 {
            counter.incr("foo", 1);
        }
        assert_eq!(counter.get("foo"), 100000);
    }
    #[test]
    fn concurrent_increment() {
        let counter = Counter::new();
        let shared = Arc::new(counter);
        let nthreads = 8;
        let nincr = 10000;
        let mut children = vec![];
        for _ in 0..nthreads {
            let c = shared.clone();
            children.push(thread::spawn(move|| {
                for _ in 0..nincr {
                    c.incr("foo", 1);
                }
            }));
        }
        for t in children {
            t.join();
        }
        assert_eq!(shared.get("foo"), nincr * nthreads);
    }

    #[test]
    fn concurrent_multi_key_increment() {
        let counter = Counter::new();
        let shared = Arc::new(counter);
        let nthreads = 8;
        let nincr = 10000;
        let mut children = vec![];
        for i in 0..nthreads {
            let c = shared.clone();
            let key = format!("thread_{}", i);
            children.push(thread::spawn(move|| {
                for _ in 0..nincr {
                    c.incr(&key, 1);
                }
            }));
        }
        for t in children {
            t.join();
        }
        for i in 0..nthreads {
            let key = format!("thread_{}", i);
            assert_eq!(shared.get(&key), nincr);
        }
    }

    #[test]
    fn get() {
        let mut counter = Counter::new();
        let res = counter.incr("foo", 1);
        let count = counter.get("foo");
        assert_eq!(count, 1);
    }

    #[bench]
    fn bench_clean_key(b: &mut Bencher) {
        b.iter(|| clean_key("foobar"));
    }

    #[bench]
    fn bench_djb2_hash(b: &mut Bencher) {
        let key = clean_key("foobar");
        b.iter(|| djb2_hash(&key));
    }

    #[bench]
    fn bench_incr(b: &mut Bencher) {
        let mut counter = Counter::new();
        b.iter(|| counter.incr("foo", 1))
    }

    #[bench]
    fn bench_existing_incr(b: &mut Bencher) {
        let mut counter = Counter::new();
        counter.incr("foo", 1);
        b.iter(|| counter.incr("foo", 1));
    }

    #[bench]
    fn bench_get(b: &mut Bencher) {
        let mut counter = Counter::new();
        counter.incr("foo", 100);
        b.iter(|| counter.get("foo"))
    }

    #[bench]
    fn bench_arc_incr(b: &mut Bencher) {
        let mut counter = Counter::new();
        let shared = Arc::new(counter);
        let clone = shared.clone();
        b.iter(|| clone.incr("foo", 1))
    }

    #[bench]
    fn bench_arc_get(b: &mut Bencher) {
        let mut counter = Counter::new();
        let shared = Arc::new(counter);
        let clone = shared.clone();
        clone.incr("foo", 1);
        b.iter(|| clone.get("foo"))
    }
}
<commit_msg>Fix a bunch of warnings in the tests and benchmarks<commit_after>#![feature(test)]
extern crate test;

use std::sync::atomic;
use std::sync::atomic::Ordering;
use std::cell::UnsafeCell;
use std::ptr;

const KEY_SIZE: usize = 16;
const INITIAL_SIZE: usize = 128;
const DJB2_START: usize = 5318;

type HashKey = [u8; KEY_SIZE];

#[derive(Debug)]
struct HashSlot {
    key: atomic::AtomicPtr<HashKey>,
    count: atomic::AtomicUsize,
}

/// The interior threadsafe counter struct. Everything here is atomic or
/// composed of atomics.
struct InnerCounter {
    size: usize,
    used: atomic::AtomicUsize,
    slots: Vec<HashSlot>,
    prev: atomic::AtomicPtr<Counter>,
    next: atomic:: AtomicPtr<Counter>,
}

/// The public Counter structure which contains the inner mutable state. This
/// allows us to put this straight into an Arc and safely share between threads
pub struct Counter {
    inner: UnsafeCell<InnerCounter>
}

/// An implementation of the djb2 hash using rust iterators over the contents
/// of the HashKey array.
pub fn djb2_hash(key: &HashKey) -> usize {
    key.into_iter().take_while(|c| { **c != 0 }).fold(DJB2_START, |hash, c| {
        (hash * 33) ^ (*c as usize)})
}

/// A small inlined helper function that checks if a given character is ascii
fn is_ascii(c: &u8) -> bool {
    return (*c > 47 && *c < 58) || (*c > 64 && *c < 91) || (*c > 96 && *c < 123);
}

/// Clean a given key string. This involves stripping non ascii characters out
/// and limiting the key to 16 characters. The resulting key is stack allocated
/// so be careful with it. If you need it long term put it in a Box.
pub fn clean_key(key: &str) -> HashKey {
    let mut cleaned: HashKey = [0; KEY_SIZE]; // Stack allocate a temporary key.
    for (index, v) in key.bytes().take(KEY_SIZE).filter(is_ascii).enumerate() {
        cleaned[index] = v;
    }
    return cleaned;
}

impl HashSlot {
    /// Create a new HashSlot struct with the default values. The key is
    /// initially set to null so get/incr know this slot is empty.
    pub fn new() -> HashSlot {
        HashSlot {
            key: atomic::AtomicPtr::new(ptr::null_mut()),
            count: atomic::AtomicUsize::new(0),
        }
    }
}

impl InnerCounter {
    /// Create a new InnerCounter. Sets up all the top level atomic values for
    /// keeping track of global hashmap state.
    pub fn new() -> InnerCounter {
        let mut slots: Vec<HashSlot> = Vec::with_capacity(INITIAL_SIZE);
        for _ in 0..INITIAL_SIZE {
            slots.push(HashSlot::new());
        }
        let counter: InnerCounter = InnerCounter {
            size: INITIAL_SIZE,
            used: atomic::AtomicUsize::new(0),
            slots: slots,
            prev: atomic::AtomicPtr::new(ptr::null_mut()),
            next: atomic::AtomicPtr::new(ptr::null_mut()),
        };
        return counter;
    }

    /// Debug method for printing out the contents of a HashSlot struct
    unsafe fn print_slot(&self, index: usize) {
        let slot = &self.slots[index];
        let key = slot.key.load(Ordering::Relaxed);
        println!("slot {}: {:?}", index, slot);
        if key.is_null() {
            println!("\t key=null")
        }
        else {
            println!("\t key={:?}", *key);
        }
    }

    /// Unsafe method for returning the value of a counter.
    pub unsafe fn unsafe_get(&self, key: HashKey) -> usize {
        let size = self.size;
        let mut index = djb2_hash(&key) % size;
        loop {
            let slot = &self.slots[index];
            let other_key = slot.key.load(Ordering::Relaxed);
            if other_key.is_null() {
                return 0;
            }
            else if *other_key == key{
                return slot.count.load(Ordering::Relaxed);
            }
            index = (index + 1) % size;
        }
    }

    /// Unsafe method for incrementing a counter by the given amount. Returns
    /// the previous value of the counter.
    pub unsafe fn unsafe_incr(&mut self, key: HashKey, count: usize) -> usize {
        let size = self.size;
        let mut index = djb2_hash(&key) % size;
        let mut slot: &HashSlot;
        loop {
            slot = &self.slots[index];
            let other_key = slot.key.load(Ordering::Relaxed);
            if other_key.is_null() {
                let boxed_key = Box::new(key);
                let res = slot.key.compare_exchange(other_key,
                                                    Box::into_raw(boxed_key),
                                                    Ordering::Relaxed,
                                                    Ordering::Relaxed);
                let ptr = res.unwrap_or_else(|v| { v });
                if ptr.is_null() || *ptr == key {
                    self.used.fetch_add(1, Ordering::Relaxed);
                    break;
                }
            }
            else if *other_key == key {
                break;
            }
            index = (index + 1) % size
        }
        return slot.count.fetch_add(count, Ordering::Relaxed);
    }
}

impl Drop for InnerCounter {
    /// Custom drop implementation to go through and explicitly re-box all the
    /// heap memory used to store keys within the Vec<HashSlot>. During the
    /// first call to unsafe_incr() the key is copied into a Box so it will
    /// persist.
    fn drop(&mut self) {
        for hs in &self.slots {
            let key = hs.key.load(Ordering::Relaxed);
            if !key.is_null() {
                let _: Box<HashKey> = unsafe { Box::from_raw(key) };
            }
        }
    }
}

unsafe impl Sync for Counter {}
unsafe impl Send for Counter {}

impl Counter {
    /// Create a new counter, which is has an UnsafeCell wrapping the actual
    /// counter implementation.
    pub fn new() -> Counter {
        Counter {
            inner: UnsafeCell::new(InnerCounter::new())
        }
    }

    /// Extract the mutable reference to the counter implementation and
    /// increment the given key.
    pub fn incr(&self, key: &str, count: usize) -> usize {
        return unsafe {
            (*self.inner.get()).unsafe_incr(clean_key(key), count)
        };
    }

    /// Extract the mutable reference to the counter implementation and
    /// return the current counter value for the given key.
    pub fn get(&self, key: &str) -> usize {
        return unsafe {
            (*self.inner.get()).unsafe_get(clean_key(key))
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use test::Bencher;
    use std::thread;
    use std::sync::Arc;

    #[test]
    fn increment() {
        let counter = Counter::new();
        let res = counter.incr("foo", 1);
        assert_eq!(res, 0);
    }

    #[test]
    fn sequential_increment() {
        let counter = Counter::new();
        for _ in 0..100000 {
            counter.incr("foo", 1);
        }
        assert_eq!(counter.get("foo"), 100000);
    }
    #[test]
    fn concurrent_increment() {
        let counter = Counter::new();
        let shared = Arc::new(counter);
        let nthreads = 8;
        let nincr = 10000;
        let mut children = vec![];
        for _ in 0..nthreads {
            let c = shared.clone();
            children.push(thread::spawn(move|| {
                for _ in 0..nincr {
                    c.incr("foo", 1);
                }
            }));
        }
        for t in children {
            let _ = t.join();
        }
        assert_eq!(shared.get("foo"), nincr * nthreads);
    }

    #[test]
    fn concurrent_multi_key_increment() {
        let counter = Counter::new();
        let shared = Arc::new(counter);
        let nthreads = 8;
        let nincr = 10000;
        let mut children = vec![];
        for i in 0..nthreads {
            let c = shared.clone();
            let key = format!("thread_{}", i);
            children.push(thread::spawn(move|| {
                for _ in 0..nincr {
                    c.incr(&key, 1);
                }
            }));
        }
        for t in children {
            let _ = t.join();
        }
        for i in 0..nthreads {
            let key = format!("thread_{}", i);
            assert_eq!(shared.get(&key), nincr);
        }
    }

    #[test]
    fn get() {
        let counter = Counter::new();
        let _ = counter.incr("foo", 1);
        let count = counter.get("foo");
        assert_eq!(count, 1);
    }

    #[bench]
    fn bench_clean_key(b: &mut Bencher) {
        b.iter(|| clean_key("foobar"));
    }

    #[bench]
    fn bench_djb2_hash(b: &mut Bencher) {
        let key = clean_key("foobar");
        b.iter(|| djb2_hash(&key));
    }

    #[bench]
    fn bench_incr(b: &mut Bencher) {
        let counter = Counter::new();
        b.iter(|| counter.incr("foo", 1))
    }

    #[bench]
    fn bench_existing_incr(b: &mut Bencher) {
        let counter = Counter::new();
        counter.incr("foo", 1);
        b.iter(|| counter.incr("foo", 1));
    }

    #[bench]
    fn bench_get(b: &mut Bencher) {
        let counter = Counter::new();
        counter.incr("foo", 100);
        b.iter(|| counter.get("foo"))
    }

    #[bench]
    fn bench_arc_incr(b: &mut Bencher) {
        let counter = Counter::new();
        let shared = Arc::new(counter);
        let clone = shared.clone();
        b.iter(|| clone.incr("foo", 1))
    }

    #[bench]
    fn bench_arc_get(b: &mut Bencher) {
        let counter = Counter::new();
        let shared = Arc::new(counter);
        let clone = shared.clone();
        clone.incr("foo", 1);
        b.iter(|| clone.get("foo"))
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]

extern crate regex;
#[phase(plugin)] extern crate regex_macros;

use std::io::{BufferedReader, File, IoError, OtherIoError};
use std::os::{getenv, setenv, self_exe_path};

#[deriving(Show, Clone)]
pub struct ParseError {
	line: String
}

#[deriving(Show, Clone)]
pub enum DotenvError {
	Parse(ParseError),
	Io(IoError)
}

fn parse_line(line: String) -> Result<Option<(String, String)>, ParseError> {
	let line_regex = regex!(concat!(r"^(\s*(",
		r"#.*|", // A comment, or...
		r"\s*|", // ...an empty string, or...
		r"(export\s+)?", // ...(optionally preceded by "export")...
		r"(?P<key>[A-Za-z_][A-Za-z0-9_]*)", // ...a key,...
		r"\s*=\s*", // ...then an equal sign,...
		r"(?P<value>.+?)", // ...and then its corresponding value.
	r")\s*)[\r\n]*$"));

	line_regex.captures(line.as_slice()).map_or(
		Err(ParseError{line: line.clone()}),
		|captures| {
			let key = captures.name("key");
			let value = captures.name("value");

			if key == "" || value == "" {
				// If there's no key and value, but capturing did not fail,
				// then this means we're dealing with a comment or an empty
				// string.
				Ok(None)
			} else {
				Ok(Some((key.to_string(), value.to_string())))
			}
		}
	)
}

fn parse_line_iter<T: Iterator<String>>(lines: T) -> Result<Vec<(String, String)>, ParseError> {
	let mut parsed_lines = lines.map(parse_line);
	let failure = parsed_lines.find(|line| line.is_err());

	if failure.is_some() {
		return Err(failure.unwrap().err().unwrap());
	}

	Ok(parsed_lines.filter_map(|line| {
		line.clone().unwrap()
	}).collect())
}

fn lines_to_env(lines: Vec<(String, String)>) {
	for (key, value) in lines.into_iter() {
		if getenv(key.as_slice()).is_none() {
			setenv(key.as_slice(), value);
		}
	}
}

fn from_file(file: File) -> Result<(), DotenvError> {
	let mut reader = BufferedReader::new(file);
	let lines = reader.lines();

	parse_line_iter(lines.filter_map(|result| {
		result.ok()
	})).map(lines_to_env).map_err(|err| {
		DotenvError::Parse(err)
	})
}

pub fn from_path(path: &Path) -> Result<(), DotenvError> {
	match File::open(path) {
		Ok(file) => from_file(file),
		Err(err) => Err(DotenvError::Io(err))
	}
}

pub fn from_filename(filename: &str) -> Result<(), DotenvError> {
	self_exe_path().as_mut().map(|path| {
		path.push(filename);
		from_path(path)
	}).unwrap_or(Err(DotenvError::Io(IoError{
		kind: OtherIoError,
		desc: "Could not fetch the path of this executable",
		detail: None
	})))
}

pub fn dotenv() -> Result<(), DotenvError> {
	from_filename(".env")
}

#[test]
fn test_parse_line_env() {
	let input_iter = vec![
		"THIS_IS_KEY=hi this is value",
		"   many_spaces  =   wow a  maze   ",
		"export   SHELL_LOVER=1"
	].into_iter().map(|input| input.to_string());
	let actual_iter = input_iter.map(|input| parse_line(input));

	let expected_iter = vec![
		("THIS_IS_KEY", "hi this is value"),
		("many_spaces", "wow a  maze"),
		("SHELL_LOVER", "1")
	].into_iter().map(|(key, value)| (key.to_string(), value.to_string()));

	for (expected, actual) in expected_iter.zip(actual_iter) {
		assert!(actual.is_ok());
		assert!(actual.clone().ok().unwrap().is_some())
		assert_eq!(expected, actual.ok().unwrap().unwrap())
	}
}

#[test]
fn test_parse_line_comment() {
	let input_iter = vec![
		"# foo=bar",
		"    #    "
	].into_iter().map(|input| input.to_string());
	let mut actual_iter = input_iter.map(|input| parse_line(input));

	for actual in actual_iter {
		assert!(actual.is_ok());
		assert!(actual.ok().unwrap().is_none());
	}
}

#[test]
fn test_parse_line_invalid() {
	let input_iter = vec![
		"  invalid    ",
		"very bacon = yes indeed",
		"key=",
		"=value"
	].into_iter().map(|input| input.to_string());
	let mut actual_iter = input_iter.map(|input| parse_line(input));

	for actual in actual_iter {
		assert!(actual.is_err());
	}
}

#[test]
fn test_from_line_iter_valid() {
	let input = vec![
		"test_env_one=1",
		"# a comment",
		"test_env_two=2"
	].into_iter().map(|line| line.to_string());
	let actual = parse_line_iter(input);

	assert!(actual.is_ok())
}

#[test]
fn test_from_line_iter_invalid() {
	let input = vec![
		"test_env_one=1",
		"# a comment",
		"not valid"
	].into_iter().map(|line| line.to_string());
	let actual = parse_line_iter(input);

	assert!(actual.is_err())
}
<commit_msg>Fix awful bug on already exhausted iterators<commit_after>#![feature(phase)]

extern crate regex;
#[phase(plugin)] extern crate regex_macros;

use std::io::{BufferedReader, File, IoError, OtherIoError};
use std::os::{getenv, setenv, self_exe_path};

#[deriving(Show, Clone)]
pub struct ParseError {
	line: String
}

#[deriving(Show, Clone)]
pub enum DotenvError {
	Parse(ParseError),
	Io(IoError)
}

fn parse_line(line: String) -> Result<Option<(String, String)>, ParseError> {
	let line_regex = regex!(concat!(r"^(\s*(",
		r"#.*|", // A comment, or...
		r"\s*|", // ...an empty string, or...
		r"(export\s+)?", // ...(optionally preceded by "export")...
		r"(?P<key>[A-Za-z_][A-Za-z0-9_]*)", // ...a key,...
		r"\s*=\s*", // ...then an equal sign,...
		r"(?P<value>.+?)", // ...and then its corresponding value.
	r")\s*)[\r\n]*$"));

	line_regex.captures(line.as_slice()).map_or(
		Err(ParseError{line: line.clone()}),
		|captures| {
			let key = captures.name("key");
			let value = captures.name("value");

			if key == "" || value == "" {
				// If there's no key and value, but capturing did not fail,
				// then this means we're dealing with a comment or an empty
				// string.
				Ok(None)
			} else {
				Ok(Some((key.to_string(), value.to_string())))
			}
		}
	)
}

fn parse_line_iter<T: Iterator<String>>(lines: T) -> Result<Vec<(String, String)>, ParseError> {
	let parsed_lines: Vec<Result<Option<(String, String)>, ParseError>> = lines.map(parse_line).collect();
	let failure = parsed_lines.iter().find(|line| line.is_err());

	if failure.is_some() {
		return Err(failure.unwrap().clone().err().unwrap());
	}

	Ok(parsed_lines.iter().filter_map(|line| {
		line.clone().unwrap()
	}).collect())
}

fn lines_to_env(lines: Vec<(String, String)>) {
	for (key, value) in lines.into_iter() {
		if getenv(key.as_slice()).is_none() {
			setenv(key.as_slice(), value);
		}
	}
}

fn from_file(file: File) -> Result<(), DotenvError> {
	let mut reader = BufferedReader::new(file);
	let lines = reader.lines();

	parse_line_iter(lines.filter_map(|result| {
		result.ok()
	})).map(lines_to_env).map_err(|err| {
		DotenvError::Parse(err)
	})
}

pub fn from_path(path: &Path) -> Result<(), DotenvError> {
	match File::open(path) {
		Ok(file) => from_file(file),
		Err(err) => Err(DotenvError::Io(err))
	}
}

pub fn from_filename(filename: &str) -> Result<(), DotenvError> {
	self_exe_path().as_mut().map(|path| {
		path.push(filename);
		from_path(path)
	}).unwrap_or(Err(DotenvError::Io(IoError{
		kind: OtherIoError,
		desc: "Could not fetch the path of this executable",
		detail: None
	})))
}

pub fn dotenv() -> Result<(), DotenvError> {
	from_filename(".env")
}

#[test]
fn test_parse_line_env() {
	let input_iter = vec![
		"THIS_IS_KEY=hi this is value",
		"   many_spaces  =   wow a  maze   ",
		"export   SHELL_LOVER=1"
	].into_iter().map(|input| input.to_string());
	let actual_iter = input_iter.map(|input| parse_line(input));

	let expected_iter = vec![
		("THIS_IS_KEY", "hi this is value"),
		("many_spaces", "wow a  maze"),
		("SHELL_LOVER", "1")
	].into_iter().map(|(key, value)| (key.to_string(), value.to_string()));

	for (expected, actual) in expected_iter.zip(actual_iter) {
		assert!(actual.is_ok());
		assert!(actual.clone().ok().unwrap().is_some())
		assert_eq!(expected, actual.ok().unwrap().unwrap())
	}
}

#[test]
fn test_parse_line_comment() {
	let input_iter = vec![
		"# foo=bar",
		"    #    "
	].into_iter().map(|input| input.to_string());
	let mut actual_iter = input_iter.map(|input| parse_line(input));

	for actual in actual_iter {
		assert!(actual.is_ok());
		assert!(actual.ok().unwrap().is_none());
	}
}

#[test]
fn test_parse_line_invalid() {
	let input_iter = vec![
		"  invalid    ",
		"very bacon = yes indeed",
		"key=",
		"=value"
	].into_iter().map(|input| input.to_string());
	let mut actual_iter = input_iter.map(|input| parse_line(input));

	for actual in actual_iter {
		assert!(actual.is_err());
	}
}

#[test]
fn test_from_line_iter_valid() {
	let input = vec![
		"test_env_one=1",
		"# a comment",
		"test_env_two=2"
	].into_iter().map(|line| line.to_string());
	let actual = parse_line_iter(input);

	assert!(actual.is_ok())
}

#[test]
fn test_from_line_iter_invalid() {
	let input = vec![
		"test_env_one=1",
		"# a comment",
		"not valid"
	].into_iter().map(|line| line.to_string());
	let actual = parse_line_iter(input);

	assert!(actual.is_err())
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate downcast_rs;
extern crate uname;

use std::error::Error;
use std::fmt;

use backend::Backend;
use platform::detector::Detector;
use platform::Platform;
use resource::file::File;
use provider::Provider;

pub mod backend;
pub mod platform;
pub mod handler;
pub mod resource;
pub mod provider;

#[derive(Debug)]
pub struct Specinfra<'a> {
    pub backend: &'a Backend,
    pub platform: Box<Platform>,
    pub provider: Box<Provider>,
}

#[derive(Debug)]
pub struct SpecinfraError;

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

impl fmt::Display for SpecinfraError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "error")
    }
}

pub fn new(b: &Backend) -> Result<Specinfra, Box<Error>> {
    let mut d = Detector::new();
    let p = try!(d.detect(b).ok_or(SpecinfraError));
    let provider = p.get_provider();
    Ok(Specinfra {
        backend: b,
        platform: p,
        provider: provider,
    })
}

impl<'a> Specinfra<'a> {
    pub fn file(&self, name: &'static str) -> File {
        File::new(name, self.backend, &self.provider)
    }
}

// Wrapper functions for FFI

use backend::BackendWrapper;

#[no_mangle]
pub extern "C" fn specinfra_new<'a>(ptr: *const BackendWrapper) -> *mut Specinfra<'a> {
    let b = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    Box::into_raw(Box::new(self::new(&*b.backend).unwrap()))
}

#[no_mangle]
pub extern "C" fn specinfra_free(ptr: *mut Specinfra) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}
<commit_msg>Don't need to use *mut<commit_after>#[macro_use]
extern crate downcast_rs;
extern crate uname;

use std::error::Error;
use std::fmt;

use backend::Backend;
use platform::detector::Detector;
use platform::Platform;
use resource::file::File;
use provider::Provider;

pub mod backend;
pub mod platform;
pub mod handler;
pub mod resource;
pub mod provider;

#[derive(Debug)]
pub struct Specinfra<'a> {
    pub backend: &'a Backend,
    pub platform: Box<Platform>,
    pub provider: Box<Provider>,
}

#[derive(Debug)]
pub struct SpecinfraError;

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

impl fmt::Display for SpecinfraError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "error")
    }
}

pub fn new(b: &Backend) -> Result<Specinfra, Box<Error>> {
    let mut d = Detector::new();
    let p = try!(d.detect(b).ok_or(SpecinfraError));
    let provider = p.get_provider();
    Ok(Specinfra {
        backend: b,
        platform: p,
        provider: provider,
    })
}

impl<'a> Specinfra<'a> {
    pub fn file(&self, name: &'static str) -> File {
        File::new(name, self.backend, &self.provider)
    }
}

// Wrapper functions for FFI

use backend::BackendWrapper;

#[no_mangle]
pub extern "C" fn specinfra_new<'a>(ptr: *const BackendWrapper) -> *const Specinfra<'a> {
    let b = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    Box::into_raw(Box::new(self::new(&*b.backend).unwrap()))
}

#[no_mangle]
pub extern "C" fn specinfra_free(ptr: *mut Specinfra) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}
<|endoftext|>


<commit_before>//! RethinkDB protocol implementation in Rust

extern crate protobuf;
extern crate serde_json;

pub mod proto;

use serde_json::value::{Value, ToJson};
use protobuf::repeated::RepeatedField;
use protobuf::ProtobufEnum;
use proto::{
    Term, Datum,
    Datum_DatumType as DT,
    Term_TermType as TT,
};

impl IsEmpty for Term {
    fn is_empty(&self) -> bool {
        *self == Term::new()
    }
}

pub trait IsDatum {
    fn is_datum(&self) -> bool;
}

impl IsDatum for Term {
    fn is_datum(&self) -> bool {
        self.get_field_type() == TT::DATUM
    }
}

impl Encode for Datum {
    fn encode(&self) -> String {
        match self.get_field_type() {
            DT::R_NULL => {
                unimplemented!();
            },
            DT::R_BOOL => {
                if self.has_r_bool() {
                    format!("{:?}", self.get_r_bool())
                } else {
                    unimplemented!();
                }
            },
            DT::R_NUM => {
                if self.has_r_num() {
                    format!("{}", self.get_r_num())
                } else {
                    unimplemented!();
                }
            },
            DT::R_STR => {
                if self.has_r_str() {
                    format!("{:?}", self.get_r_str())
                } else {
                    unimplemented!();
                }
            },
            DT::R_ARRAY => {
                let mut args = format!("[{},[", TT::MAKE_ARRAY.value());
                for term in self.get_r_array() {
                    args.push_str(&format!("{},", term.encode()));
                }
                args = args.trim_right_matches(",").to_string();
                args.push_str("]]");
                args
            },
            DT::R_OBJECT => {
                unimplemented!();
            },
            DT::R_JSON => {
                unimplemented!();
            },
        }
    }
}

impl Encode for Term {
    fn encode(&self) -> String {
        let mut res = String::new();
        if !self.is_datum() {
            res.push_str(&format!("[{},", self.get_field_type().value()));
        }
        let terms = self.get_args();
        if !terms.is_empty() {
            let mut args = String::from("[");
            for term in terms {
                args.push_str(&format!("{},", term.encode()));
            }
            args = args.trim_right_matches(",").to_string();
            args.push_str("]");
            res.push_str(&args);
        }
        if self.has_datum() {
            let datum = self.get_datum();
            res.push_str(&datum.encode());
        }
        if !self.is_datum() {
            res.push_str("]");
        }
        res
    }
}

pub trait IsEmpty {
    fn is_empty(&self) -> bool;
}

pub trait Encode {
    fn encode(&self) -> String;
}

pub trait ToTerm {
    fn to_term(&self) -> Term;
}

impl<T: ToJson> ToTerm for T {
    fn to_term(&self) -> Term {
        // Datum
        let mut datum = Datum::new();
        match self.to_json() {
            Value::String(val) => {
                datum.set_field_type(DT::R_STR);
                datum.set_r_str(val);
            },
            Value::Bool(val) => {
                datum.set_field_type(DT::R_BOOL);
                datum.set_r_bool(val);
            },
            Value::I64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val as f64);
            },
            Value::U64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val as f64);
            },
            Value::F64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val);
            },
            Value::Array(_val) => {
                unimplemented!();
            },
            Value::Object(_val) => {
                unimplemented!();
            },
            Value::Null => {
                datum.set_field_type(DT::R_NULL);
            },
        }
        // Term
        let mut term = Term::new();
        term.set_field_type(TT::DATUM);
        term.set_datum(datum);
        term
    }
}

impl ToTerm for Term {
    fn to_term(&self) -> Term {
        self.clone()
    }
}

macro_rules! command {
    ($T:expr, $cmd:expr, $args:expr, $opts:expr) => {{
        let mut term = Term::new();
        term.set_field_type($T);
        let mut args = Vec::new();
        let cmd = $cmd.into();
        if !cmd.is_empty() {
            args.push(cmd);
        }
        if let Some(list) = $args {
            args.extend(list);
        }
        /*
           if let Some(_opt) = $opts {
           unimplemented!();
           }
           */
        if !args.is_empty() {
            let args = RepeatedField::from_vec(args);
            term.set_args(args);
        }
        From::from(term)
    }}
}

macro_rules! closure_par {
    () => {{
        // ID
        let mut id = Datum::new();
        id.set_field_type(DT::R_NUM);
        id.set_r_num(1.0);
        // DATUM
        let mut datum = Term::new();
        datum.set_field_type(TT::DATUM);
        datum.set_datum(id);
        // VAR
        let mut var = Term::new();
        var.set_field_type(TT::VAR);
        let args = RepeatedField::from_vec(vec![datum]);
        var.set_args(args);
        From::from(var)
    }}
}

macro_rules! closure_arg {
    ($res:expr) => {{
        // ID
        let mut id = Datum::new();
        id.set_field_type(DT::R_NUM);
        id.set_r_num(1.0);
        // ARRAY
        let mut array = Datum::new();
        array.set_field_type(DT::R_ARRAY);
        let args = RepeatedField::from_vec(vec![id]);
        array.set_r_array(args);
        // DATUM
        let mut datum = Term::new();
        datum.set_field_type(TT::DATUM);
        datum.set_datum(array);
        // FUNC
        let mut func = Term::new();
        func.set_field_type(TT::FUNC);
        let args = RepeatedField::from_vec(vec![datum, $res.into()]);
        func.set_args(args);
        func
    }}
}

pub trait Command
where Self: Sized + From<Term> + Into<Term>
{
    fn db<T: ToTerm>(self, arg: T) -> Self {
        let arg = arg.to_term();
        command!(TT::DB, self, Some(vec![arg]), None)
    }

    fn table<T: ToTerm>(self, arg: T) -> Self {
        let arg = arg.to_term();
        command!(TT::TABLE, self, Some(vec![arg]), None)
    }

    fn get_field<T: ToTerm>(self, arg: T) -> Self {
        let arg = arg.to_term();
        command!(TT::GET_FIELD, self, Some(vec![arg]), None)
    }

    fn map<F>(self, func: F) -> Self
        where F: Fn(Self) -> Self
        {
            let res = func(closure_par!());
            let arg = closure_arg!(res);
            command!(TT::MAP, self, Some(vec![arg]), None)
        }

    fn array(self, arg: Vec<&ToTerm>) -> Self {
        let args: Vec<Term> = arg.iter()
            .map(|a| a.to_term())
            .collect();
        command!(TT::MAKE_ARRAY, self, Some(args), None)
    }
}

#[test]
fn test_commands_can_be_chained() {
    impl Command for Term { }
    let r = Term::new();
    let term = r.db("heroes").table("marvel").map(|row| row.get_field("first_appearance"));
    let term: Term = term.into();
    panic!(format!("{:?}\n\n{}", term, term.encode()));
}
<commit_msg>Refactor commands<commit_after>//! RethinkDB protocol implementation in Rust

extern crate protobuf;
extern crate serde_json;

pub mod proto;

use serde_json::value::{Value, ToJson};
use protobuf::repeated::RepeatedField;
use protobuf::ProtobufEnum;
use proto::{
    Term, Datum,
    Datum_DatumType as DT,
    Term_TermType as TT,
};

pub trait IsDatum {
    fn is_datum(&self) -> bool;
}

pub trait IsEmpty {
    fn is_empty(&self) -> bool;
}

pub trait Encode {
    fn encode(&self) -> String;
}

pub trait ToTerm {
    fn to_term(&self) -> Term;
}

impl IsDatum for Term {
    fn is_datum(&self) -> bool {
        self.get_field_type() == TT::DATUM
    }
}

impl IsEmpty for Term {
    fn is_empty(&self) -> bool {
        *self == Term::new()
    }
}

impl Encode for Datum {
    fn encode(&self) -> String {
        match self.get_field_type() {
            DT::R_NULL => {
                unimplemented!();
            },
            DT::R_BOOL => {
                if self.has_r_bool() {
                    format!("{:?}", self.get_r_bool())
                } else {
                    unimplemented!();
                }
            },
            DT::R_NUM => {
                if self.has_r_num() {
                    format!("{}", self.get_r_num())
                } else {
                    unimplemented!();
                }
            },
            DT::R_STR => {
                if self.has_r_str() {
                    format!("{:?}", self.get_r_str())
                } else {
                    unimplemented!();
                }
            },
            DT::R_ARRAY => {
                let mut args = format!("[{},[", TT::MAKE_ARRAY.value());
                for term in self.get_r_array() {
                    args.push_str(&format!("{},", term.encode()));
                }
                args = args.trim_right_matches(",").to_string();
                args.push_str("]]");
                args
            },
            DT::R_OBJECT => {
                unimplemented!();
            },
            DT::R_JSON => {
                unimplemented!();
            },
        }
    }
}

impl Encode for Term {
    fn encode(&self) -> String {
        let mut res = String::new();
        if !self.is_datum() {
            res.push_str(&format!("[{},", self.get_field_type().value()));
        }
        let terms = self.get_args();
        if !terms.is_empty() {
            let mut args = String::from("[");
            for term in terms {
                args.push_str(&format!("{},", term.encode()));
            }
            args = args.trim_right_matches(",").to_string();
            args.push_str("]");
            res.push_str(&args);
        }
        if self.has_datum() {
            let datum = self.get_datum();
            res.push_str(&datum.encode());
        }
        if !self.is_datum() {
            res.push_str("]");
        }
        res
    }
}

impl<T: ToJson> ToTerm for T {
    fn to_term(&self) -> Term {
        // Datum
        let mut datum = Datum::new();
        match self.to_json() {
            Value::String(val) => {
                datum.set_field_type(DT::R_STR);
                datum.set_r_str(val);
            },
            Value::Bool(val) => {
                datum.set_field_type(DT::R_BOOL);
                datum.set_r_bool(val);
            },
            Value::I64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val as f64);
            },
            Value::U64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val as f64);
            },
            Value::F64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val);
            },
            Value::Array(_val) => {
                unimplemented!();
            },
            Value::Object(_val) => {
                unimplemented!();
            },
            Value::Null => {
                datum.set_field_type(DT::R_NULL);
            },
        }
        // Term
        let mut term = Term::new();
        term.set_field_type(TT::DATUM);
        term.set_datum(datum);
        term
    }
}

impl ToTerm for Term {
    fn to_term(&self) -> Term {
        self.clone()
    }
}

macro_rules! command {
    ($T:expr, $cmd:expr, $args:expr, $opts:expr) => {{
        let mut term = Term::new();
        term.set_field_type($T);
        let mut args = Vec::new();
        let cmd = $cmd.into();
        if !cmd.is_empty() {
            args.push(cmd);
        }
        if let Some(list) = $args {
            args.extend(list);
        }
        /*
           if let Some(_opt) = $opts {
           unimplemented!();
           }
           */
        if !args.is_empty() {
            let args = RepeatedField::from_vec(args);
            term.set_args(args);
        }
        From::from(term)
    }}
}

macro_rules! closure_par {
    () => {{
        // ID
        let mut id = Datum::new();
        id.set_field_type(DT::R_NUM);
        id.set_r_num(1.0);
        // DATUM
        let mut datum = Term::new();
        datum.set_field_type(TT::DATUM);
        datum.set_datum(id);
        // VAR
        let mut var = Term::new();
        var.set_field_type(TT::VAR);
        let args = RepeatedField::from_vec(vec![datum]);
        var.set_args(args);
        From::from(var)
    }}
}

macro_rules! closure_arg {
    ($res:expr) => {{
        // ID
        let mut id = Datum::new();
        id.set_field_type(DT::R_NUM);
        id.set_r_num(1.0);
        // ARRAY
        let mut array = Datum::new();
        array.set_field_type(DT::R_ARRAY);
        let args = RepeatedField::from_vec(vec![id]);
        array.set_r_array(args);
        // DATUM
        let mut datum = Term::new();
        datum.set_field_type(TT::DATUM);
        datum.set_datum(array);
        // FUNC
        let mut func = Term::new();
        func.set_field_type(TT::FUNC);
        let args = RepeatedField::from_vec(vec![datum, $res.into()]);
        func.set_args(args);
        func
    }}
}

pub trait Command
where Self: Sized + From<Term> + Into<Term>
{
    fn db<T: ToTerm>(self, arg: T) -> Self {
        let arg = arg.to_term();
        command!(TT::DB, self, Some(vec![arg]), None)
    }

    fn table<T: ToTerm>(self, arg: T) -> Self {
        let arg = arg.to_term();
        command!(TT::TABLE, self, Some(vec![arg]), None)
    }

    fn get_field<T: ToTerm>(self, arg: T) -> Self {
        let arg = arg.to_term();
        command!(TT::GET_FIELD, self, Some(vec![arg]), None)
    }

    fn map<F>(self, func: F) -> Self
        where F: Fn(Self) -> Self
        {
            let res = func(closure_par!());
            let arg = closure_arg!(res);
            command!(TT::MAP, self, Some(vec![arg]), None)
        }

    fn array(self, arg: Vec<&ToTerm>) -> Self {
        let args: Vec<Term> = arg.iter()
            .map(|a| a.to_term())
            .collect();
        command!(TT::MAKE_ARRAY, self, Some(args), None)
    }
}

#[test]
fn test_commands_can_be_chained() {
    impl Command for Term { }
    let r = Term::new();
    let term = r.db("heroes").table("marvel").map(|row| row.get_field("first_appearance"));
    let term: Term = term.into();
    panic!(format!("{:?}\n\n{}", term, term.encode()));
}
<|endoftext|>


<commit_before>#![warn(missing_docs, trivial_numeric_casts, unused_extern_crates, unused_import_braces, unused_qualifications,
        unused_results)]

//! # Pascal strings in Rust.
//!
//! A `PascalString`, or `ShortString` is a String which stores its data on the stack. Because of this, it has
//! a fixed maximum size, which cannot be changed. Traditionally, the size of a `PascalString` is 256 bytes -
//! the first byte stores the length, which means that each remaining byte is indexable using only that byte.
//!
//! This is a very niche string type - generally, you are better off using `std::string::String`, or the
//! `AsciiString` type from the `ascii` crate if you need an ascii string. They have no upper size limit, and
//! are cheaper to pass around as they are only 64 bytes on the stack. Generally, you should only use `PascalString` if:
//!
//! * You know that you absolutely, certainly cannot do without heap allocation.
//! * You need to store your string data inline into your `struct` type - for example if you will allocate a bunch
//!   into a pool allocator, and cannot afford the heap fragmentation.
//! * You will keep, allocate, and deallocate a *lot* of short strings in your program.

extern crate ascii;

mod pascal_str;
mod pascal_string;

pub use pascal_str::{Chars, CharsMut, PascalStr};
pub use pascal_string::{IntoChars, PascalString, PascalStringAppendError, PascalStringCreateError, AsciiError};
use std::u8;

const PASCAL_STRING_BUF_SIZE: usize = u8::MAX as usize;

#[cfg(test)]
mod tests {
    use super::*;
    use ascii::*;
    use std::borrow::Cow;
    use std::ffi::{CStr, CString};

    #[test]
    fn test_string_creation() {
        let test = "Hello, my world!".to_owned();
        let test_pascal = PascalString::from(&test).unwrap();
        assert_eq!(&test, test_pascal.as_str());

        let too_many_bytes = vec![12u8; 256];
        assert!(match PascalString::from(&too_many_bytes) {
            Err(PascalStringCreateError::InputTooLong) => true,
            _ => false
        });
    }

    #[test]
    fn test_character_append() {
        let mut string = PascalString::new();
        assert!(string.try_push('h').is_ok());
        string.push('e');
        string.push(76u8);
        string.push('l');
        string.push(AsciiChar::L);
        string.push('o');
        string.push(AsciiChar::Null);

        assert_eq!(string.as_str(), "heLlLo\0");
    }

    #[test]
    fn test_string_append() {
        let mut string = PascalString::new();
        string.push_str("Hola, ");
        string.push_str("Senor!");
        assert_eq!(string.as_str(), "Hola, Senor!");
    }

    #[test]
    fn test_string_indexing_and_char_iteration() {
        {
            let string = PascalString::from("q").unwrap();
            assert_eq!(string[0u8], AsciiChar::q);
        }

        {
            let string2 = PascalString::from("WASD").unwrap();
            let mut iter = string2.chars();
            assert_eq!(iter.next(), Some(&AsciiChar::W));
            assert_eq!(iter.next(), Some(&AsciiChar::A));
            assert_eq!(iter.next(), Some(&AsciiChar::S));
            assert_eq!(iter.next(), Some(&AsciiChar::D));
        }
    }

    #[test]
    fn test_as_cstr() {
        {
            let msg = "I am your favourite cookie monster >:-)\0";
            let pstr = PascalString::from(&msg).unwrap();
            let cstr = CStr::from_bytes_with_nul(msg.as_bytes()).unwrap();
            let pstr_as_cstr = pstr.as_cstr().unwrap();
            assert!(match pstr_as_cstr {
                Cow::Borrowed(_) => true,
                _ => false
            });
            assert_eq!(&*pstr_as_cstr, cstr);
        }

        {
            let oversized = ['l'; 255];
            let string_oversized = {
                let mut s = String::new();
                for i in 0..oversized.len() {
                    s.push(oversized[i]);
                }
                s
            };
            let pstr_oversized = PascalString::from_fixed_ascii_array(255, oversized).unwrap();
            let cstr_from_pstr_oversized = pstr_oversized.as_cstr().unwrap();
            let cstr_from_string_oversized = CString::new(string_oversized).unwrap();
            assert!(match cstr_from_pstr_oversized {
                Cow::Owned(_) => true,
                _ => false
            });
            assert_eq!(cstr_from_pstr_oversized.into_owned(), cstr_from_string_oversized);
        }

        {
            let has_interior_null = "lol\0hi";
            let pstr = PascalString::from(&has_interior_null).unwrap();
            let err = match pstr.as_cstr() {
                Err(err) => err,
                _ => panic!("incorrect result")
            };
            assert_eq!(err.interior_null_index(), 3);
        }
    }
}
<commit_msg>Add an extra test case to test_string_indexing_and_char_iteration for iterator exhaustion<commit_after>#![warn(missing_docs, trivial_numeric_casts, unused_extern_crates, unused_import_braces, unused_qualifications,
        unused_results)]

//! # Pascal strings in Rust.
//!
//! A `PascalString`, or `ShortString` is a String which stores its data on the stack. Because of this, it has
//! a fixed maximum size, which cannot be changed. Traditionally, the size of a `PascalString` is 256 bytes -
//! the first byte stores the length, which means that each remaining byte is indexable using only that byte.
//!
//! This is a very niche string type - generally, you are better off using `std::string::String`, or the
//! `AsciiString` type from the `ascii` crate if you need an ascii string. They have no upper size limit, and
//! are cheaper to pass around as they are only 64 bytes on the stack. Generally, you should only use `PascalString` if:
//!
//! * You know that you absolutely, certainly cannot do without heap allocation.
//! * You need to store your string data inline into your `struct` type - for example if you will allocate a bunch
//!   into a pool allocator, and cannot afford the heap fragmentation.
//! * You will keep, allocate, and deallocate a *lot* of short strings in your program.

extern crate ascii;

mod pascal_str;
mod pascal_string;

pub use pascal_str::{Chars, CharsMut, PascalStr};
pub use pascal_string::{IntoChars, PascalString, PascalStringAppendError, PascalStringCreateError, AsciiError};
use std::u8;

const PASCAL_STRING_BUF_SIZE: usize = u8::MAX as usize;

#[cfg(test)]
mod tests {
    use super::*;
    use ascii::*;
    use std::borrow::Cow;
    use std::ffi::{CStr, CString};

    #[test]
    fn test_string_creation() {
        let test = "Hello, my world!".to_owned();
        let test_pascal = PascalString::from(&test).unwrap();
        assert_eq!(&test, test_pascal.as_str());

        let too_many_bytes = vec![12u8; 256];
        assert!(match PascalString::from(&too_many_bytes) {
            Err(PascalStringCreateError::InputTooLong) => true,
            _ => false
        });
    }

    #[test]
    fn test_character_append() {
        let mut string = PascalString::new();
        assert!(string.try_push('h').is_ok());
        string.push('e');
        string.push(76u8);
        string.push('l');
        string.push(AsciiChar::L);
        string.push('o');
        string.push(AsciiChar::Null);

        assert_eq!(string.as_str(), "heLlLo\0");
    }

    #[test]
    fn test_string_append() {
        let mut string = PascalString::new();
        string.push_str("Hola, ");
        string.push_str("Senor!");
        assert_eq!(string.as_str(), "Hola, Senor!");
    }

    #[test]
    fn test_string_indexing_and_char_iteration() {
        {
            let string = PascalString::from("q").unwrap();
            assert_eq!(string[0u8], AsciiChar::q);
        }

        {
            let string2 = PascalString::from("WASD").unwrap();
            let mut iter = string2.chars();
            assert_eq!(iter.next(), Some(&AsciiChar::W));
            assert_eq!(iter.next(), Some(&AsciiChar::A));
            assert_eq!(iter.next(), Some(&AsciiChar::S));
            assert_eq!(iter.next(), Some(&AsciiChar::D));
            assert_eq!(iter.next(), None);
        }
    }

    #[test]
    fn test_as_cstr() {
        {
            let msg = "I am your favourite cookie monster >:-)\0";
            let pstr = PascalString::from(&msg).unwrap();
            let cstr = CStr::from_bytes_with_nul(msg.as_bytes()).unwrap();
            let pstr_as_cstr = pstr.as_cstr().unwrap();
            assert!(match pstr_as_cstr {
                Cow::Borrowed(_) => true,
                _ => false
            });
            assert_eq!(&*pstr_as_cstr, cstr);
        }

        {
            let oversized = ['l'; 255];
            let string_oversized = {
                let mut s = String::new();
                for i in 0..oversized.len() {
                    s.push(oversized[i]);
                }
                s
            };
            let pstr_oversized = PascalString::from_fixed_ascii_array(255, oversized).unwrap();
            let cstr_from_pstr_oversized = pstr_oversized.as_cstr().unwrap();
            let cstr_from_string_oversized = CString::new(string_oversized).unwrap();
            assert!(match cstr_from_pstr_oversized {
                Cow::Owned(_) => true,
                _ => false
            });
            assert_eq!(cstr_from_pstr_oversized.into_owned(), cstr_from_string_oversized);
        }

        {
            let has_interior_null = "lol\0hi";
            let pstr = PascalString::from(&has_interior_null).unwrap();
            let err = match pstr.as_cstr() {
                Err(err) => err,
                _ => panic!("incorrect result")
            };
            assert_eq!(err.interior_null_index(), 3);
        }
    }
}
<|endoftext|>


<commit_before>//! An experimental functional reactive programming library
//!
//! `carboxyl` provides primitives for functional reactive programming in Rust.
//! It is heavily influenced by the
//! [Sodium](https://github.com/SodiumFRP/sodium/) libraries.
//!
//! Functional reactive programming (FRP) is a paradigm that effectively fixes
//! the issues present in the traditional observer pattern approach to event
//! handling. It uses a set of compositional primitives to model the dependency
//! graph of a reactive system. If you want to learn more about FRP, check out
//! [the Sodium blog](http://blog.reactiveprogramming.org).
//!
//! This library provides two basic types: `Stream` and `Cell`. A stream is a
//! discrete sequence of events, a cell is a container for values that change
//! (discretely) over time.
//!
//! The FRP primitive functions are mostly implemented as methods of the basic
//! types to ease method chaining, except for `lift2` which does not really
//! belong to any type in particular.
//!
//! In addition, the `Sink` type allows one to create a stream of events by
//! dumping values into it. It is the only way to create an event from scratch,
//! i.e. without using any of the other primitives.
//!
//! Here is a simple example of what you can do with streams and cells:
//!
//! ```
//! # // NOTE: If you change this example, please update the README.md
//! # // accordingly, so that they remain in sync!
//! use carboxyl::Sink;
//!
//! // A new sink with a stream
//! let sink = Sink::new();
//! let stream = sink.stream();
//!
//! // Make a cell by holding the last event in a stream
//! let cell = stream.hold(3);
//! assert_eq!(cell.sample(), 3);
//!
//! // Send a value into the sink
//! sink.send(5);
//!
//! // The cell gets updated accordingly
//! assert_eq!(cell.sample(), 5);
//!
//! // Now map it to something else
//! let squares = stream.map(|x| x * x).hold(0);
//! sink.send(4);
//! assert_eq!(squares.sample(), 16);
//!
//! // Or filter it
//! let negatives = stream.filter(|&x| x < 0).hold(0);
//! sink.send(4); // This won't arrive
//! assert_eq!(negatives.sample(), 0);
//! sink.send(-3); // but this will!
//! assert_eq!(negatives.sample(), -3);
//! ```
//!
//! There are some other methods on streams and cells, that you can find in
//! their respective APIs.
//!
//! Note that all these objects are `Send + Sync + Clone`. This means you can
//! easily pass them around in your code, make clones, give them to another
//! thread, and they will still be updated correctly.
//!
//! As you can see, these functional reactive primitives essentially provide a
//! type- and thread-safe, compositional abstraction around mutable state in
//! your application to avoid the pitfalls associated with it.

#![feature(unboxed_closures)]
#![allow(unstable)]
#![warn(missing_docs)]

#[cfg(test)]
extern crate test;

pub use primitives::{Stream, Cell, Sink, lift2};

mod subject;
mod primitives;
<commit_msg>Mention pure functions in crate docs<commit_after>//! An experimental functional reactive programming library
//!
//! *Carboxyl* provides primitives for functional reactive programming in Rust.
//! It is heavily influenced by the
//! [Sodium](https://github.com/SodiumFRP/sodium/) libraries.
//!
//! Functional reactive programming (FRP) is a paradigm that effectively fixes
//! the issues present in the traditional observer pattern approach to event
//! handling. It uses a set of compositional primitives to model the dependency
//! graph of a reactive system. These primitives essentially provide a type- and
//! thread-safe, compositional abstraction around mutable state in your
//! application to avoid the pitfalls normally associated with it.
//!
//! If you want to learn more about FRP in general, check out [the Sodium
//! blog](http://blog.reactiveprogramming.org).
//!
//!
//! # Functional reactive primitives
//!
//! This library provides two basic types: `Stream` and `Cell`. A stream is a
//! discrete sequence of events, a cell is a container for values that change
//! (discretely) over time.
//!
//! The FRP primitive functions are mostly implemented as methods of the basic
//! types to ease method chaining, except for `lift2` which does not really
//! belong to any type in particular.
//!
//! In addition, the `Sink` type allows one to create a stream of events by
//! dumping values into it. It is the only way to create an event from scratch,
//! i.e. without using any of the other primitives.
//!
//!
//! # Example
//!
//! Here is some code that demonstrates what you can do with streams and cells:
//!
//! ```
//! # // NOTE: If you change this example, please update the README.md
//! # // accordingly, so that they remain in sync!
//! use carboxyl::Sink;
//!
//! // A new sink with a stream
//! let sink = Sink::new();
//! let stream = sink.stream();
//!
//! // Make a cell by holding the last event in a stream
//! let cell = stream.hold(3);
//! assert_eq!(cell.sample(), 3);
//!
//! // Send a value into the sink
//! sink.send(5);
//!
//! // The cell gets updated accordingly
//! assert_eq!(cell.sample(), 5);
//!
//! // Now map it to something else
//! let squares = stream.map(|x| x * x).hold(0);
//! sink.send(4);
//! assert_eq!(squares.sample(), 16);
//!
//! // Or filter it
//! let negatives = stream.filter(|&x| x < 0).hold(0);
//! sink.send(4); // This won't arrive
//! assert_eq!(negatives.sample(), 0);
//! sink.send(-3); // but this will!
//! assert_eq!(negatives.sample(), -3);
//! ```
//!
//! There are some other methods on streams and cells, that you can find in
//! their respective APIs.
//!
//! Note that all these objects are `Send + Sync + Clone`. This means you can
//! easily pass them around in your code, make clones, give them to another
//! thread, and they will still be updated correctly.
//!
//! You may have noticed that certain primitives take a function as an argument.
//! There is a limitation on what kind of functions can and should be used here.
//! In general, as FRP provides an abstraction around mutable state, they should
//! be pure functions (i.e. free of side effects).
//!
//! For the most part this is guaranteed by Rust's type system. A static
//! function with a matching signature always works. A closure though is very
//! restricted: it must not borrow its environment, as it is impossible to
//! satisfy the lifetime requirements for that. So you can only move stuff into
//! it from the environment. However, the moved contents of the closure may also
//! not be altered, which is guaranteed by the `Fn(…) -> …)` trait bound.
//!
//! However, both closures and functions could still have side effects such as
//! I/O, changing shared mutable state via `Arc` pointers, etc. While Rust's
//! type system cannot prevent this, you should generally not pass such
//! functions to the FRP primitives, as they break the benefits you get from
//! using FRP. (Except temporary print statements for debugging.)

#![feature(unboxed_closures)]
#![allow(unstable)]
#![warn(missing_docs)]

#[cfg(test)]
extern crate test;

pub use primitives::{Stream, Cell, Sink, lift2};

mod subject;
mod primitives;
<|endoftext|>


<commit_before>#![crate_name="postgres_macros"]
#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private, libc)]

extern crate libc;
extern crate rustc;
extern crate syntax;

use rustc::plugin::Registry;
use std::ffi::{CStr, CString};
use std::mem;
use std::str;
use syntax::ast::{TokenTree, ExprLit, LitStr, Expr, Ident};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
use syntax::parse::token;
use syntax::parse::token::{InternedString, Comma, Eof};
use syntax::parse;
use syntax::parse::parser::Parser;
use syntax::ptr::P;

mod ffi {
    use libc::{c_char, c_int};

    #[repr(C)]
    pub struct ParseResult {
        pub success: c_int,
        pub error_message: *const c_char,
        pub index: c_int,
        pub num_params: c_int,
    }

    extern {
        pub fn init_parser();
        pub fn parse_query(query: *const c_char, result: *mut ParseResult);
    }
}

struct ParseInfo {
    num_params: usize,
}

struct ParseError {
    message: String,
    index: usize,
}

#[plugin_registrar]
#[doc(hidden)]
pub fn registrar(reg: &mut Registry) {
    reg.register_macro("sql", expand_sql);
    reg.register_macro("execute", expand_execute);
    unsafe { ffi::init_parser() }
}

fn expand_sql(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
              -> Box<MacResult+'static> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_vec());

    let query_expr = cx.expander().fold_expr(parser.parse_expr());
    let query = match parse_str_lit(cx, &*query_expr) {
        Some(query) => query,
        None => return DummyResult::expr(sp)
    };

    match parse(&query) {
        Ok(_) => {}
        Err(err) => parse_error(cx, query_expr.span, err),
    }

    MacEager::expr(query_expr)
}

fn expand_execute(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
                  -> Box<MacResult+'static> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_vec());

    let conn = parser.parse_expr();

    if !parser.eat(&Comma).ok().unwrap() {
        cx.span_err(parser.span, "expected `,`");
        return DummyResult::expr(sp);
    }

    let query_expr = cx.expander().fold_expr(parser.parse_expr());
    let query = match parse_str_lit(cx, &*query_expr) {
        Some(query) => query,
        None => return DummyResult::expr(sp),
    };

    if !parser.eat(&Comma).ok().unwrap() {
        cx.span_err(parser.span, "expected `,`");
        return DummyResult::expr(sp);
    }

    let args = match parse_args(cx, &mut parser) {
        Some(args) => args,
        None => return DummyResult::expr(sp),
    };

    match parse(&query) {
        Ok(ref info) if info.num_params != args.len() =>
            cx.span_err(sp, &format!("Expected {} query parameters but got {}",
                                     info.num_params, args.len())),
        Ok(_) => {}
        Err(err) => parse_error(cx, query_expr.span, err),
    }

    let ident = Ident::new(token::intern("execute"));
    let args = cx.expr_vec(sp, args);
    MacEager::expr(cx.expr_method_call(sp, conn, ident, vec![query_expr, args]))
}

fn parse_error(cx: &mut ExtCtxt, sp: Span, err: ParseError) {
    cx.span_err(sp, &format!("Invalid syntax at position {}: {}", err.index, err.message));
}

fn parse_str_lit(cx: &mut ExtCtxt, e: &Expr) -> Option<InternedString> {
    match e.node {
        ExprLit(ref lit) => {
            match lit.node {
                LitStr(ref s, _) => Some(s.clone()),
                _ => {
                    cx.span_err(e.span, "expected string literal");
                    None
                }
            }
        }
        _ => {
            cx.span_err(e.span, "expected string literal");
            None
        }
    }
}

fn parse_args(cx: &mut ExtCtxt, parser: &mut Parser) -> Option<Vec<P<Expr>>> {
    let mut args = Vec::new();

    while parser.token != Eof {
        args.push(parser.parse_expr());

        if !parser.eat(&Comma).ok().unwrap() && parser.token != Eof {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    Some(args)
}

fn parse(query: &str) -> Result<ParseInfo, ParseError> {
    unsafe {
        let mut result = mem::uninitialized();
        let query = CString::new(query.as_bytes()).unwrap();
        ffi::parse_query(query.as_ptr(), &mut result);
        if result.success != 0 {
            Ok(ParseInfo {
                num_params: result.num_params as usize,
            })
        } else {
            let bytes = CStr::from_ptr(result.error_message).to_bytes();
            Err(ParseError {
                message: str::from_utf8(bytes).unwrap().to_string(),
                index: result.index as usize,
            })
        }
    }
}
<commit_msg>Update for new syntax API<commit_after>#![crate_name="postgres_macros"]
#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private, libc)]

extern crate libc;
extern crate rustc;
extern crate syntax;

use rustc::plugin::Registry;
use std::ffi::{CStr, CString};
use std::mem;
use std::str;
use syntax::ast::{TokenTree, ExprLit, LitStr, Expr, Ident};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
use syntax::parse::token;
use syntax::parse::token::{InternedString, Comma, Eof};
use syntax::parse;
use syntax::parse::parser::Parser;
use syntax::ptr::P;

mod ffi {
    use libc::{c_char, c_int};

    #[repr(C)]
    pub struct ParseResult {
        pub success: c_int,
        pub error_message: *const c_char,
        pub index: c_int,
        pub num_params: c_int,
    }

    extern {
        pub fn init_parser();
        pub fn parse_query(query: *const c_char, result: *mut ParseResult);
    }
}

struct ParseInfo {
    num_params: usize,
}

struct ParseError {
    message: String,
    index: usize,
}

#[plugin_registrar]
#[doc(hidden)]
pub fn registrar(reg: &mut Registry) {
    reg.register_macro("sql", expand_sql);
    reg.register_macro("execute", expand_execute);
    unsafe { ffi::init_parser() }
}

fn expand_sql(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
              -> Box<MacResult+'static> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_vec());

    let query_expr = cx.expander().fold_expr(parser.parse_expr());
    let query = match parse_str_lit(cx, &*query_expr) {
        Some(query) => query,
        None => return DummyResult::expr(sp)
    };

    match parse(&query) {
        Ok(_) => {}
        Err(err) => parse_error(cx, query_expr.span, err),
    }

    MacEager::expr(query_expr)
}

fn expand_execute(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
                  -> Box<MacResult+'static> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_vec());

    let conn = parser.parse_expr();

    if !parser.eat(&Comma).ok().unwrap() {
        cx.span_err(parser.span, "expected `,`");
        return DummyResult::expr(sp);
    }

    let query_expr = cx.expander().fold_expr(parser.parse_expr());
    let query = match parse_str_lit(cx, &*query_expr) {
        Some(query) => query,
        None => return DummyResult::expr(sp),
    };

    if !parser.eat(&Comma).ok().unwrap() {
        cx.span_err(parser.span, "expected `,`");
        return DummyResult::expr(sp);
    }

    let args = match parse_args(cx, &mut parser) {
        Some(args) => args,
        None => return DummyResult::expr(sp),
    };

    match parse(&query) {
        Ok(ref info) if info.num_params != args.len() =>
            cx.span_err(sp, &format!("Expected {} query parameters but got {}",
                                     info.num_params, args.len())),
        Ok(_) => {}
        Err(err) => parse_error(cx, query_expr.span, err),
    }

    let ident = Ident::with_empty_ctxt(token::intern("execute"));
    let args = cx.expr_vec(sp, args);
    MacEager::expr(cx.expr_method_call(sp, conn, ident, vec![query_expr, args]))
}

fn parse_error(cx: &mut ExtCtxt, sp: Span, err: ParseError) {
    cx.span_err(sp, &format!("Invalid syntax at position {}: {}", err.index, err.message));
}

fn parse_str_lit(cx: &mut ExtCtxt, e: &Expr) -> Option<InternedString> {
    match e.node {
        ExprLit(ref lit) => {
            match lit.node {
                LitStr(ref s, _) => Some(s.clone()),
                _ => {
                    cx.span_err(e.span, "expected string literal");
                    None
                }
            }
        }
        _ => {
            cx.span_err(e.span, "expected string literal");
            None
        }
    }
}

fn parse_args(cx: &mut ExtCtxt, parser: &mut Parser) -> Option<Vec<P<Expr>>> {
    let mut args = Vec::new();

    while parser.token != Eof {
        args.push(parser.parse_expr());

        if !parser.eat(&Comma).ok().unwrap() && parser.token != Eof {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    Some(args)
}

fn parse(query: &str) -> Result<ParseInfo, ParseError> {
    unsafe {
        let mut result = mem::uninitialized();
        let query = CString::new(query.as_bytes()).unwrap();
        ffi::parse_query(query.as_ptr(), &mut result);
        if result.success != 0 {
            Ok(ParseInfo {
                num_params: result.num_params as usize,
            })
        } else {
            let bytes = CStr::from_ptr(result.error_message).to_bytes();
            Err(ParseError {
                message: str::from_utf8(bytes).unwrap().to_string(),
                index: result.index as usize,
            })
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

pub struct Keypad {
    layout: HashMap<u8, Key>,
    current_value: u8,
}

impl Keypad {
    fn new() -> Keypad {
        let mut layout = HashMap::new();

        layout.insert(1, Key {
           value: 1,
           left: None,
           up: None,
           right: Some(2),
           down: Some(4),
        });

        layout.insert(2, Key {
           value: 2,
           left: Some(1),
           up: None,
           right: Some(3),
           down: Some(5),
        });

        layout.insert(3, Key {
           value: 3,
           left: Some(2),
           up: None,
           right: None,
           down: Some(6),
        });

        layout.insert(4, Key {
           value: 4,
           left: None,
           up: Some(1),
           right: Some(5),
           down: Some(7),
        });

        layout.insert(5, Key {
           value: 5,
           left: Some(4),
           up: Some(2),
           right: Some(6),
           down: Some(8),
        });

        layout.insert(6, Key {
           value: 6,
           left: Some(5),
           up: Some(3),
           right: None,
           down: Some(9),
        });

        layout.insert(7, Key {
           value: 7,
           left: None,
           up: Some(4),
           right: Some(8),
           down: None,
        });

        layout.insert(8, Key {
           value: 1,
           left: Some(7),
           up: Some(5),
           right: Some(9),
           down: None,
        });

        layout.insert(9, Key {
           value: 9,
           left: Some(8),
           up: Some(6),
           right: None,
           down: None,
        });

        Keypad {
            layout: layout,
            current_value: 5,
        }
    }

    fn move_a_key(&mut self, instruction: char) {
        let current_key = self.layout.get(&self.current_value)
                                     .expect("impossible missing key");

        let next_key_value = match instruction {
            'U' => current_key.up.unwrap_or(current_key.value),
            'D' => current_key.down.unwrap_or(current_key.value),
            'L' => current_key.left.unwrap_or(current_key.value),
            'R' => current_key.right.unwrap_or(current_key.value),
            other => panic!("Don't know how to move ({})!", other),
        };

        self.current_value = next_key_value;
    }
}

struct Key {
    value: u8,
    left: Option<u8>,
    right: Option<u8>,
    up: Option<u8>,
    down: Option<u8>,
}

pub fn puzzle(input: &str) -> u8 {
    let keypad = Keypad::new();
    1
}

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

    #[test]
    fn regular_move() {
        let mut keypad = Keypad::new();

        keypad.move_a_key('U');
        assert_eq!(keypad.current_value, 2);
    }

    #[test]
    fn ignored_move() {
        let mut keypad = Keypad::new();

        keypad.move_a_key('U');
        keypad.move_a_key('U');
        assert_eq!(keypad.current_value, 2);
    }
}
<commit_msg>Implement walking through input and accumulating output<commit_after>use std::collections::HashMap;

pub struct Keypad {
    layout: HashMap<u8, Key>,
    current_value: u8,
}

impl Keypad {
    fn new() -> Keypad {
        let mut layout = HashMap::new();

        layout.insert(1, Key {
           value: 1,
           left: None,
           up: None,
           right: Some(2),
           down: Some(4),
        });

        layout.insert(2, Key {
           value: 2,
           left: Some(1),
           up: None,
           right: Some(3),
           down: Some(5),
        });

        layout.insert(3, Key {
           value: 3,
           left: Some(2),
           up: None,
           right: None,
           down: Some(6),
        });

        layout.insert(4, Key {
           value: 4,
           left: None,
           up: Some(1),
           right: Some(5),
           down: Some(7),
        });

        layout.insert(5, Key {
           value: 5,
           left: Some(4),
           up: Some(2),
           right: Some(6),
           down: Some(8),
        });

        layout.insert(6, Key {
           value: 6,
           left: Some(5),
           up: Some(3),
           right: None,
           down: Some(9),
        });

        layout.insert(7, Key {
           value: 7,
           left: None,
           up: Some(4),
           right: Some(8),
           down: None,
        });

        layout.insert(8, Key {
           value: 1,
           left: Some(7),
           up: Some(5),
           right: Some(9),
           down: None,
        });

        layout.insert(9, Key {
           value: 9,
           left: Some(8),
           up: Some(6),
           right: None,
           down: None,
        });

        Keypad {
            layout: layout,
            current_value: 5,
        }
    }

    fn move_a_key(&mut self, instruction: char) {
        let current_key = self.layout.get(&self.current_value)
                                     .expect("impossible missing key");

        let next_key_value = match instruction {
            'U' => current_key.up.unwrap_or(current_key.value),
            'D' => current_key.down.unwrap_or(current_key.value),
            'L' => current_key.left.unwrap_or(current_key.value),
            'R' => current_key.right.unwrap_or(current_key.value),
            other => panic!("Don't know how to move ({})!", other),
        };

        self.current_value = next_key_value;
    }
}

struct Key {
    value: u8,
    left: Option<u8>,
    right: Option<u8>,
    up: Option<u8>,
    down: Option<u8>,
}

pub fn puzzle(input: &str) -> String {
    let mut keypad = Keypad::new();
    let mut answer = String::new();

    for line in input.lines() {
        for c in line.chars() {
            keypad.move_a_key(c);
        }
        answer.push_str(&format!("{}", keypad.current_value));
    }
    answer
}

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

    #[test]
    fn regular_move() {
        let mut keypad = Keypad::new();

        keypad.move_a_key('U');
        assert_eq!(keypad.current_value, 2);
    }

    #[test]
    fn ignored_move() {
        let mut keypad = Keypad::new();

        keypad.move_a_key('U');
        keypad.move_a_key('U');
        assert_eq!(keypad.current_value, 2);
    }

    #[test]
    fn overall_puzzle() {
        let input = "ULL\nRRDDD\nLURDL\nUUUUD\n";
        let answer = puzzle(input);
        assert_eq!(String::from("1985"), answer);
    }
}
<|endoftext|>


<commit_before>/// Utility functions for my scripts.

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

extern crate test;

use std::hash;
use std::collections::hash_map;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

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

#[test]
fn test_counter() {
	let my_list : Vec<&str> = vec!();
	let count : HashMap<&str, u32> = counter(my_list.into_iter());
	assert_eq!(count.get(&"a"), None);

	let my_list = vec!("a", "b", "cd", "a", "a", "b");
	let count : HashMap<&str, u32> = counter(my_list.into_iter());

	assert_eq!(count.get(&"a"), Some(&3u32));
	assert_eq!(count.get(&"b"), Some(&2u32));
	assert_eq!(count.get(&"cd"), Some(&1u32));
	assert_eq!(count.get(&"e"), None);
}
<commit_msg>Updated to newest Rust<commit_after>/// Utility functions for my scripts.

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

extern crate test;

use std::hash;
use std::collections::hash_map;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

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

#[test]
fn test_counter() {
	let my_list : Vec<&str> = vec!();
	let count : HashMap<&str, u32> = counter(my_list.into_iter());
	assert_eq!(count.get(&"a"), None);

	let my_list = vec!("a", "b", "cd", "a", "a", "b");
	let count : HashMap<&str, u32> = counter(my_list.into_iter());

	assert_eq!(count.get(&"a"), Some(&3u32));
	assert_eq!(count.get(&"b"), Some(&2u32));
	assert_eq!(count.get(&"cd"), Some(&1u32));
	assert_eq!(count.get(&"e"), None);
}
<|endoftext|>