<commit_before>//! # `emit_elasticsearch`
//!
//! Log events with the [`emit`](http://emit-rs.github.io/emit/emit/index.html) structured logger to Elasticsearch.

extern crate emit;
extern crate log;
extern crate elastic_hyper as elastic;
extern crate elastic_types;
extern crate chrono;
extern crate hyper;
extern crate serde;
extern crate serde_json;

use std::io::{ Read, Write, Cursor };
use std::error::Error;
use emit::events::Event;
use emit::collectors::AcceptEvents;
use chrono::{ DateTime, UTC };
use hyper::client::Body;
use hyper::header::{ Headers, Connection, Authorization, Scheme };
use elastic::RequestParams;
use elastic_types::mapping::ElasticType;

mod events;
use events::ElasticLog;

pub const LOCAL_SERVER_URL: &'static str = "http://localhost:9200/";
pub const DEFAULT_TEMPLATE_PREFIX: &'static str = "emitlog-";
pub const DEFAULT_TEMPLATE_FORMAT: &'static str = "%Y%m%d";

/// Template for naming log indices.
///
/// The index name consists of a prefix and a date format (`chrono` compatible).
/// The default `IndexTemplate` produces index names like `'emitlog-20160501'`.
///
/// # Examples
///
/// An index template that produces index names like `'logs-2016-05-01'`:
///
/// ```
/// use emit_elasticsearch::IndexTemplate;
///
/// let template = IndexTemplate::new("emitlog-", "%Y-%m-%d");
/// ```
///
/// Use the `date_format` parameter to control how the event date is resolved to an index name.
/// For example, an index template that produces a unique index name for each month:
///
/// ```
/// # use emit_elasticsearch::IndexTemplate;
/// let template = IndexTemplate::new("emitlog-", "%Y%m");
/// ```
pub struct IndexTemplate {
    prefix: String,
    date_format: String
}

impl IndexTemplate {
    pub fn new<I>(prefix: I, date_format: I) -> IndexTemplate where
    I: Into<String> {
        IndexTemplate {
            prefix: prefix.into(),
            date_format: date_format.into()
        }
    }

    pub fn index(&self, date: &DateTime<UTC>) -> String {
        let df = date.format(&self.date_format).to_string();

        let mut fmtd = String::with_capacity(self.prefix.len() + df.len());
        fmtd.push_str(&self.prefix);
        fmtd.push_str(&df);

        fmtd
    }
}

impl Default for IndexTemplate {
    fn default() -> IndexTemplate {
        IndexTemplate {
            prefix: DEFAULT_TEMPLATE_PREFIX.into(),
            date_format: DEFAULT_TEMPLATE_FORMAT.into()
        }
    }
}

/// Log collector for Elasticsearch.
pub struct ElasticCollector {
    params: RequestParams,
    template: IndexTemplate
}

impl ElasticCollector {
    /// Create a new collector with the given node url and naming template.
    pub fn new<I>(server_url: I, index_template: IndexTemplate) -> ElasticCollector where
    I: Into<String> {
        let params = RequestParams::new(server_url, Headers::new());

        ElasticCollector {
            params: params,
            template: index_template
        }
    }

    /// Create a new collector for logging to `localhost:9200`.
    pub fn new_local(index_template: IndexTemplate) -> ElasticCollector {
        Self::new(LOCAL_SERVER_URL, index_template)
    }

    /// Supply an auth header for index requests.
    pub fn with_auth(mut self, auth: String) -> ElasticCollector {
        self.params.headers.set(Authorization(auth));

        self
    }

    fn send_batch(&self, payload: &[u8]) -> Result<(), Box<Error>> {
        let mut client = hyper::Client::new();

        let res = elastic::bulk::post(&mut client, &self.params, payload);

        match res {
            Ok(_) => Ok(()),
            Err(e) => Err(From::from(e))
        }
    }
}

fn build_batch(events: &[Event<'static>], template: &IndexTemplate) -> Vec<u8> {
    //TODO: Optimise this; pre-allocate the buffer and avoid unnecessary writes
    let mut buf = Cursor::new(Vec::new());

    for evt in events {
        let idx = template.index(&evt.timestamp());
        let es_evt = ElasticLog::new(&evt);

        //Writes a header struct of the form: {"index": {"_index":"{}","_type":"{}"}}
        buf.write_all(b"{\"index\":{\"_index\":\"");
        buf.write_all(idx.as_bytes());
        buf.write_all(b"\",\"_type\":\"");
        buf.write_all(ElasticLog::name().as_bytes());
        buf.write_all(b"\"}}");
        buf.write_all(b"\n");

        //Writes the message body to the buffer
        serde_json::to_writer(&mut buf, &es_evt);
        buf.write(b"\n");
    }

    buf.into_inner()
}

impl Default for ElasticCollector {
    fn default() -> ElasticCollector {
        ElasticCollector::new_local(IndexTemplate::default())
    }
}

impl AcceptEvents for ElasticCollector {
    fn accept_events(&self, events: &[Event<'static>])-> Result<(), Box<Error>> {
        let buf = build_batch(events, &self.template);

        self.send_batch(&buf)
    }
}

#[cfg(test)]
mod tests {
    use std::collections;
    use chrono::UTC;
    use chrono::offset::TimeZone;
    use log;
    use emit::{ events, templates };
    use super::{ IndexTemplate, ElasticCollector, build_batch };

    #[test]
    fn events_are_formatted_as_bulk() {
        let template = IndexTemplate::default();
        let timestamp = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11);

        let mut properties = collections::BTreeMap::new();
        properties.insert("number", "42".into());

        let evts = vec![
            events::Event::new(timestamp, log::LogLevel::Warn, templates::MessageTemplate::new("The number is {number}"), properties),
            events::Event::new(timestamp, log::LogLevel::Info, templates::MessageTemplate::new("The number is {number}"), collections::BTreeMap::new())
        ];

        let bulk = build_batch(&evts, &template);

        assert_eq!(bulk, b"");
    }

    #[test]
    fn dates_use_correct_index() {
        let template_y = IndexTemplate::new("testlog-", "%Y");
        let template_ym = IndexTemplate::new("testlog-", "%Y%m");
        let template_ymd = IndexTemplate::new("testlog-", "%Y%m%d");

        let date = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11);

        assert_eq!("testlog-2014", &template_y.index(&date));
        assert_eq!("testlog-201407", &template_ym.index(&date));
        assert_eq!("testlog-20140708", &template_ymd.index(&date));
    }
}
<commit_msg>add notes to impl<commit_after>//! # `emit_elasticsearch`
//!
//! Log events with the [`emit`](http://emit-rs.github.io/emit/emit/index.html) structured logger to Elasticsearch.

extern crate emit;
extern crate log;
extern crate elastic_hyper as elastic;
extern crate elastic_types;
extern crate chrono;
extern crate hyper;
extern crate serde;
extern crate serde_json;

use std::io::{ Read, Write, Cursor };
use std::error::Error;
use emit::events::Event;
use emit::collectors::AcceptEvents;
use chrono::{ DateTime, UTC };
use hyper::client::Body;
use hyper::header::{ Headers, Connection, Authorization, Scheme };
use elastic::RequestParams;
use elastic_types::mapping::ElasticType;

mod events;
use events::ElasticLog;

pub const LOCAL_SERVER_URL: &'static str = "http://localhost:9200/";
pub const DEFAULT_TEMPLATE_PREFIX: &'static str = "emitlog-";
pub const DEFAULT_TEMPLATE_FORMAT: &'static str = "%Y%m%d";

/// Template for naming log indices.
///
/// The index name consists of a prefix and a date format (`chrono` compatible).
/// The default `IndexTemplate` produces index names like `'emitlog-20160501'`.
///
/// # Examples
///
/// An index template that produces index names like `'logs-2016-05-01'`:
///
/// ```
/// use emit_elasticsearch::IndexTemplate;
///
/// let template = IndexTemplate::new("emitlog-", "%Y-%m-%d");
/// ```
///
/// Use the `date_format` parameter to control how the event date is resolved to an index name.
/// For example, an index template that produces a unique index name for each month:
///
/// ```
/// # use emit_elasticsearch::IndexTemplate;
/// let template = IndexTemplate::new("emitlog-", "%Y%m");
/// ```
pub struct IndexTemplate {
    prefix: String,
    date_format: String
}

impl IndexTemplate {
    pub fn new<I>(prefix: I, date_format: I) -> IndexTemplate where
    I: Into<String> {
        IndexTemplate {
            prefix: prefix.into(),
            date_format: date_format.into()
        }
    }

    pub fn index(&self, date: &DateTime<UTC>) -> String {
        let df = date.format(&self.date_format).to_string();

        let mut fmtd = String::with_capacity(self.prefix.len() + df.len());
        fmtd.push_str(&self.prefix);
        fmtd.push_str(&df);

        fmtd
    }
}

impl Default for IndexTemplate {
    fn default() -> IndexTemplate {
        IndexTemplate {
            prefix: DEFAULT_TEMPLATE_PREFIX.into(),
            date_format: DEFAULT_TEMPLATE_FORMAT.into()
        }
    }
}

/// Log collector for Elasticsearch.
///
/// Logs are written to an index based on their `timestamp` and the given `IndexTemplate`.
pub struct ElasticCollector {
    params: RequestParams,
    template: IndexTemplate
}

impl ElasticCollector {
    /// Create a new collector with the given node url and naming template.
    pub fn new<I>(server_url: I, index_template: IndexTemplate) -> ElasticCollector where
    I: Into<String> {
        let params = RequestParams::new(server_url, Headers::new());

        ElasticCollector {
            params: params,
            template: index_template
        }
    }

    /// Create a new collector for logging to `localhost:9200`.
    pub fn new_local(index_template: IndexTemplate) -> ElasticCollector {
        Self::new(LOCAL_SERVER_URL, index_template)
    }

    /// Supply an auth header for index requests.
    pub fn with_auth(mut self, auth: String) -> ElasticCollector {
        self.params.headers.set(Authorization(auth));

        self
    }

    /// Send a `_bulk` request represented as a byte buffer to the given node.
    fn send_batch(&self, payload: &[u8]) -> Result<(), Box<Error>> {
        let mut client = hyper::Client::new();

        let res = elastic::bulk::post(&mut client, &self.params, payload);

        match res {
            Ok(_) => Ok(()),
            Err(e) => Err(From::from(e))
        }
    }
}

/// Build a `_bulk` request as a byte buffer for the given slice of `Event`s.
fn build_batch(events: &[Event<'static>], template: &IndexTemplate) -> Vec<u8> {
    //TODO: Optimise this; pre-allocate the buffer and avoid unnecessary writes
    //Bench this function to determine best approaches
    let mut buf = Cursor::new(Vec::new());

    for evt in events {
        let idx = template.index(&evt.timestamp());
        let es_evt = ElasticLog::new(&evt);

        //Writes a header struct of the form: {"index":{"_index":"{}","_type":"{}"}}\n
        buf.write_all(b"{\"index\":{\"_index\":\"");
        buf.write_all(idx.as_bytes());
        buf.write_all(b"\",\"_type\":\"");
        buf.write_all(ElasticLog::name().as_bytes());
        buf.write_all(b"\"}}");
        buf.write_all(b"\n");

        //Writes the message body to the buffer
        serde_json::to_writer(&mut buf, &es_evt);
        buf.write(b"\n");
    }

    buf.into_inner()
}

impl Default for ElasticCollector {
    fn default() -> ElasticCollector {
        ElasticCollector::new_local(IndexTemplate::default())
    }
}

impl AcceptEvents for ElasticCollector {
    fn accept_events(&self, events: &[Event<'static>])-> Result<(), Box<Error>> {
        let buf = build_batch(events, &self.template);

        self.send_batch(&buf)
    }
}

#[cfg(test)]
mod tests {
    use std::collections;
    use chrono::UTC;
    use chrono::offset::TimeZone;
    use log;
    use emit::{ events, templates };
    use super::{ IndexTemplate, ElasticCollector, build_batch };

    #[test]
    fn events_are_formatted_as_bulk() {
        let template = IndexTemplate::default();
        let timestamp = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11);

        let mut properties = collections::BTreeMap::new();
        properties.insert("number", "42".into());

        let evts = vec![
            events::Event::new(timestamp, log::LogLevel::Warn, templates::MessageTemplate::new("The number is {number}"), properties),
            events::Event::new(timestamp, log::LogLevel::Info, templates::MessageTemplate::new("The number is {number}"), collections::BTreeMap::new())
        ];

        let bulk = build_batch(&evts, &template);

        assert_eq!(bulk, b"");
    }

    #[test]
    fn dates_use_correct_index() {
        let template_y = IndexTemplate::new("testlog-", "%Y");
        let template_ym = IndexTemplate::new("testlog-", "%Y%m");
        let template_ymd = IndexTemplate::new("testlog-", "%Y%m%d");

        let date = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11);

        assert_eq!("testlog-2014", &template_y.index(&date));
        assert_eq!("testlog-201407", &template_ym.index(&date));
        assert_eq!("testlog-20140708", &template_ymd.index(&date));
    }
}
<|endoftext|>


<commit_before>use fall_tree::{NodeType, INode, TextUnit, Language};
use syn_engine::BlackNode;
use lex_engine::Token;

fn is_ws(lang: &Language, token: Token) -> bool {
    lang.node_type_info(token.ty).whitespace_like
}

#[derive(Debug)]
pub struct WsNode {
    ty: Option<NodeType>,
    len: TextUnit,
    children: Vec<WsNode>,
    first: Option<usize>,
    last: Option<usize>
}

impl WsNode {
    fn push_child(&mut self, lang: &Language, child: WsNode, tokens: &[Token]) {
        match (self.last, child.first) {
            (Some(l), Some(r)) if l + 1 < r => {
                for idx in l + 1..r {
                    let t = tokens[idx];
                    assert!(is_ws(lang, t), "expected whitespace, got {:?}", t.ty);
                    self.push_child_raw(token_ws_node(idx, t))
                }
            }
            _ => {}
        }
        self.push_child_raw(child)
    }

    fn push_child_raw(&mut self, child: WsNode) {
        self.last = child.last.or(self.last);
        self.first = self.first.or(child.first);
        self.len += child.len;
        self.children.push(child);
    }

    fn attach_to_inode(self, parent: &mut INode) {
        if self.children.is_empty() {
            match self.ty {
                Some(ty) => parent.push_child(INode::new_leaf(ty, self.len)),
                None => parent.push_token_part(self.len),
            }
            return;
        }
        match self.into_inode() {
            Ok(node) => parent.push_child(node),
            Err(this) => {
                for child in this.children {
                    child.attach_to_inode(parent);
                }
            }
        }
    }

    pub fn into_inode(self) -> Result<INode, WsNode> {
        if let Some(ty) = self.ty {
            let mut node = INode::new(ty);
            for child in self.children {
                child.attach_to_inode(&mut node);
            }
            Ok(node)
        } else {
            Err(self)
        }
    }
}

fn token_ws_node(idx: usize, t: Token) -> WsNode {
    WsNode {
        ty: Some(t.ty),
        len: t.len,
        children: Vec::new(),
        first: Some(idx),
        last: Some(idx),
    }
}

pub fn to_white_node(lang: &Language, file_node: BlackNode, tokens: &[Token]) -> WsNode {
    let (ty, children) = match file_node {
        BlackNode::Composite { ty, children, .. } => (ty.unwrap(), children),
        _ => panic!("Root node must be composite")
    };
    let mut result = WsNode {
        ty: Some(ty),
        len: TextUnit::zero(),
        children: Vec::new(),
        first: None,
        last: None,
    };

    for (i, &t) in tokens.iter().enumerate() {
        if !is_ws(lang, t) {
            break
        }
        result.push_child_raw(token_ws_node(i, t))
    }

    for child in children {
        add_child(lang, &mut result, &child, tokens)
    }
    if let Some(idx) = result.last {
        for idx in idx + 1..tokens.len() {
            let t = tokens[idx];
            assert!(is_ws(lang, t));
            result.push_child_raw(token_ws_node(idx, t))
        }
    }
    result
}

fn add_child(lang: &Language, parent: &mut WsNode, node: &BlackNode, tokens: &[Token]) {
    match *node {
        BlackNode::Leaf { ty, token_idx } => {
            let mut node = token_ws_node(token_idx, tokens[token_idx]);
            node.ty = ty;
            parent.push_child(lang, node, tokens)
        }
        BlackNode::Composite { ty, ref children } => {
            let mut p = WsNode {
                ty: ty,
                len: TextUnit::zero(),
                children: Vec::new(),
                first: None,
                last: None,
            };
            for child in children {
                add_child(lang, &mut p, child, tokens);
            }
            parent.push_child(lang, p, tokens)
        }
    }
}
<commit_msg>Rename<commit_after>use fall_tree::{NodeType, INode, TextUnit, Language};
use super::BlackNode;
use lex_engine::Token;

fn is_ws(lang: &Language, token: Token) -> bool {
    lang.node_type_info(token.ty).whitespace_like
}

#[derive(Debug)]
pub struct WhiteNode {
    ty: Option<NodeType>,
    len: TextUnit,
    children: Vec<WhiteNode>,
    first: Option<usize>,
    last: Option<usize>
}

impl WhiteNode {
    fn push_child(&mut self, lang: &Language, child: WhiteNode, tokens: &[Token]) {
        match (self.last, child.first) {
            (Some(l), Some(r)) if l + 1 < r => {
                for idx in l + 1..r {
                    let t = tokens[idx];
                    assert!(is_ws(lang, t), "expected whitespace, got {:?}", t.ty);
                    self.push_child_raw(token_ws_node(idx, t))
                }
            }
            _ => {}
        }
        self.push_child_raw(child)
    }

    fn push_child_raw(&mut self, child: WhiteNode) {
        self.last = child.last.or(self.last);
        self.first = self.first.or(child.first);
        self.len += child.len;
        self.children.push(child);
    }

    fn attach_to_inode(self, parent: &mut INode) {
        if self.children.is_empty() {
            match self.ty {
                Some(ty) => parent.push_child(INode::new_leaf(ty, self.len)),
                None => parent.push_token_part(self.len),
            }
            return;
        }
        match self.into_inode() {
            Ok(node) => parent.push_child(node),
            Err(this) => {
                for child in this.children {
                    child.attach_to_inode(parent);
                }
            }
        }
    }

    pub fn into_inode(self) -> Result<INode, WhiteNode> {
        if let Some(ty) = self.ty {
            let mut node = INode::new(ty);
            for child in self.children {
                child.attach_to_inode(&mut node);
            }
            Ok(node)
        } else {
            Err(self)
        }
    }
}

fn token_ws_node(idx: usize, t: Token) -> WhiteNode {
    WhiteNode {
        ty: Some(t.ty),
        len: t.len,
        children: Vec::new(),
        first: Some(idx),
        last: Some(idx),
    }
}

pub fn to_white_node(lang: &Language, file_node: BlackNode, tokens: &[Token]) -> WhiteNode {
    let (ty, children) = match file_node {
        BlackNode::Composite { ty, children, .. } => (ty.unwrap(), children),
        _ => panic!("Root node must be composite")
    };
    let mut result = WhiteNode {
        ty: Some(ty),
        len: TextUnit::zero(),
        children: Vec::new(),
        first: None,
        last: None,
    };

    for (i, &t) in tokens.iter().enumerate() {
        if !is_ws(lang, t) {
            break
        }
        result.push_child_raw(token_ws_node(i, t))
    }

    for child in children {
        add_child(lang, &mut result, &child, tokens)
    }
    if let Some(idx) = result.last {
        for idx in idx + 1..tokens.len() {
            let t = tokens[idx];
            assert!(is_ws(lang, t));
            result.push_child_raw(token_ws_node(idx, t))
        }
    }
    result
}

fn add_child(lang: &Language, parent: &mut WhiteNode, node: &BlackNode, tokens: &[Token]) {
    match *node {
        BlackNode::Leaf { ty, token_idx } => {
            let mut node = token_ws_node(token_idx, tokens[token_idx]);
            node.ty = ty;
            parent.push_child(lang, node, tokens)
        }
        BlackNode::Composite { ty, ref children } => {
            let mut p = WhiteNode {
                ty: ty,
                len: TextUnit::zero(),
                children: Vec::new(),
                first: None,
                last: None,
            };
            for child in children {
                add_child(lang, &mut p, child, tokens);
            }
            parent.push_child(lang, p, tokens)
        }
    }
}
<|endoftext|>


<commit_before>pub mod grades;
pub mod exams;

#[derive(Debug)]
pub struct Studygroup {
    pub year: u16,
    pub course: u16,
    pub group: u16,
    pub degree: Degree,
}

impl Studygroup {
    pub fn identifier(&self) -> String {
        format!("{}/{}/{}", self.year, self.course, self.group)
    }
}

#[derive(Debug)]
pub enum Degree {
    Bachelor,
    Master,
    Diploma,
}

impl Degree {
    pub fn short(self) -> &'static str {
        match self {
            Degree::Bachelor => "B",
            Degree::Master => "M",
            Degree::Diploma => "D",
        }
    }
}
<commit_msg>add semester type<commit_after>pub mod grades;
pub mod exams;

#[derive(Debug)]
pub struct Studygroup {
    pub year: u16,
    pub course: u16,
    pub group: u16,
    pub degree: Degree,
}

impl Studygroup {
    pub fn identifier(&self) -> String {
        format!("{}/{}/{}", self.year, self.course, self.group)
    }
}

#[derive(Debug)]
pub enum Degree {
    Bachelor,
    Master,
    Diploma,
}

impl Degree {
    pub fn short(self) -> &'static str {
        match self {
            Degree::Bachelor => "B",
            Degree::Master => "M",
            Degree::Diploma => "D",
        }
    }
}

#[derive(Debug)]
pub enum Semester {
    Winter(u16),
    Summer(u16),
}
<|endoftext|>


<commit_before>extern crate crossbeam;

use crossbeam::mem::epoch::{Atomic, Owned, pin};
use std::sync::atomic::Ordering::*;

use std::ops::Deref;

pub struct Pinboard<T: Clone>(Atomic<T>);

impl<T: Clone> Pinboard<T> {
    pub fn new() -> Pinboard<T> {
        Pinboard(Atomic::null())
    }

    pub fn set(&self, t: T) {
        let guard = pin();
        let t = Owned::new(t);
        if let Some(t) = self.0.swap(Some(t), Release, &guard) {
            unsafe { guard.unlinked(t); }
        }
    }

    pub fn clear(&self) {
        let guard = pin();
        if let Some(t) = self.0.swap(None, Release, &guard) {
            unsafe { guard.unlinked(t); }
        }
    }

    pub fn read(&self) -> Option<T> {
        let guard = pin();
        let t = self.0.load(Acquire, &guard);
        t.map(|t| -> &T { t.deref() }).cloned()
    }
}

impl<T: Clone> Drop for Pinboard<T> {
    fn drop(&mut self) {
        // Make sure any stored data is marked for deletion
        self.clear();
    }
}

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

    fn consume<T: Clone + Display>(t: &Pinboard<T>) {
        loop {
            match t.read() {
                Some(_) => {},
                None => break,
            }
            std::thread::sleep(std::time::Duration::from_millis(1));
        }
    }

    fn produce(t: &Pinboard<u32>) {
        for i in 1..100 {
            t.set(i);
            std::thread::sleep(std::time::Duration::from_millis(2));
        }
        t.clear();
    }

    #[test]
    fn it_works() {
        let t = Pinboard::<u32>::new();
        assert_eq!(None, t.read());
        t.set(3);
        assert_eq!(Some(3), t.read());
        t.clear();
        assert_eq!(None, t.read());
    }

    #[test]
    fn single_producer_single_consumer() {
        let t = Pinboard::<u32>::new();
        t.set(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn multi_producer_single_consumer() {
        let t = Pinboard::<u32>::new();
        t.set(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn single_producer_multi_consumer() {
        let t = Pinboard::<u32>::new();
        t.set(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn multi_producer_multi_consumer() {
        let t = Pinboard::<u32>::new();
        t.set(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
        })
    }
}
<commit_msg>More idiomatic creation APIs<commit_after>extern crate crossbeam;

use crossbeam::mem::epoch::{Atomic, Owned, pin};
use std::sync::atomic::Ordering::*;

use std::ops::Deref;

pub struct Pinboard<T: Clone>(Atomic<T>);

impl<T: Clone> Pinboard<T> {
    pub fn new(t: T) -> Pinboard<T> {
        let t = Owned::new(t);
        let p = Pinboard::default();
        p.0.store(Some(t), Release);
        p
    }

    pub fn set(&self, t: T) {
        let guard = pin();
        let t = Owned::new(t);
        if let Some(t) = self.0.swap(Some(t), Release, &guard) {
            unsafe { guard.unlinked(t); }
        }
    }

    pub fn clear(&self) {
        let guard = pin();
        if let Some(t) = self.0.swap(None, Release, &guard) {
            unsafe { guard.unlinked(t); }
        }
    }

    pub fn read(&self) -> Option<T> {
        let guard = pin();
        let t = self.0.load(Acquire, &guard);
        t.map(|t| -> &T { t.deref() }).cloned()
    }
}

impl<T: Clone> Default for Pinboard<T> {
    fn default() -> Pinboard<T> {
        Pinboard(Atomic::null())
    }
}

impl<T: Clone> Drop for Pinboard<T> {
    fn drop(&mut self) {
        // Make sure any stored data is marked for deletion
        self.clear();
    }
}

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

    fn consume<T: Clone + Display>(t: &Pinboard<T>) {
        loop {
            match t.read() {
                Some(_) => {},
                None => break,
            }
            std::thread::sleep(std::time::Duration::from_millis(1));
        }
    }

    fn produce(t: &Pinboard<u32>) {
        for i in 1..100 {
            t.set(i);
            std::thread::sleep(std::time::Duration::from_millis(2));
        }
        t.clear();
    }

    #[test]
    fn it_works() {
        let t = Pinboard::<u32>::default();
        assert_eq!(None, t.read());
        t.set(3);
        assert_eq!(Some(3), t.read());
        t.clear();
        assert_eq!(None, t.read());
    }

    #[test]
    fn single_producer_single_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn multi_producer_single_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn single_producer_multi_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn multi_producer_multi_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
        })
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Terminal formatting library.
//!
//! This crate provides the `Terminal` trait, which abstracts over an [ANSI
//! Terminal][ansi] to provide color printing, among other things. There are two implementations,
//! the `TerminfoTerminal`, which uses control characters from a
//! [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
//! API][win].
//!
//! # Examples
//!
//! ```no_run
//! extern crate term;
//!
//! fn main() {
//!     let mut t = term::stdout().unwrap();
//!
//!     t.fg(term::color::GREEN).unwrap();
//!     (write!(t, "hello, ")).unwrap();
//!
//!     t.fg(term::color::RED).unwrap();
//!     (writeln!(t, "world!")).unwrap();
//!
//!     t.reset().unwrap();
//! }
//! ```
//!
//! [ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
//! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
//! [ti]: https://en.wikipedia.org/wiki/Terminfo

#![crate_name = "term"]
#![experimental]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/nightly/",
       html_playground_url = "http://play.rust-lang.org/")]

#![allow(unknown_features)]
#![feature(macro_rules, phase, slicing_syntax, globs)]

#![deny(missing_docs)]

#[phase(plugin, link)] extern crate log;

pub use terminfo::TerminfoTerminal;
#[cfg(windows)]
pub use win::WinConsole;

use std::io::IoResult;

pub mod terminfo;

#[cfg(windows)]
mod win;

/// A hack to work around the fact that `Box<Writer + Send>` does not
/// currently implement `Writer`.
pub struct WriterWrapper {
    wrapped: Box<Writer + Send>,
}

impl Writer for WriterWrapper {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        self.wrapped.write(buf)
    }

    #[inline]
    fn flush(&mut self) -> IoResult<()> {
        self.wrapped.flush()
    }
}

#[cfg(not(windows))]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<Terminal<WriterWrapper> + Send>> {
    TerminfoTerminal::new(WriterWrapper {
        wrapped: box std::io::stdout() as Box<Writer + Send>,
    })
}

#[cfg(windows)]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<Terminal<WriterWrapper> + Send>> {
    let boxed = box std::io::stdio() as Box<Writer + Send>;
    let wrapped = WriterWrapper {wrapper: boxed};

    TerminfoTerminal::new(wrapped).or(WinConsole::new(wrapped));
}

#[cfg(not(windows))]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<Terminal<WriterWrapper> + Send>> {
    TerminfoTerminal::new(WriterWrapper {
        wrapped: box std::io::stderr() as Box<Writer + Send>,
    })
}

#[cfg(windows)]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<Terminal<WriterWrapper> + Send>> {
    let boxed = box std::io::stderr() as Box<Writer + Send>;
    let wrapped = WriterWrapper {wrapper: boxed};

    TerminfoTerminal::new(wrapped).or(WinConsole::new(wrapped));
}

/// Terminal color definitions
pub mod color {
    /// Number for a terminal color
    pub type Color = u16;

    pub const BLACK:   Color = 0u16;
    pub const RED:     Color = 1u16;
    pub const GREEN:   Color = 2u16;
    pub const YELLOW:  Color = 3u16;
    pub const BLUE:    Color = 4u16;
    pub const MAGENTA: Color = 5u16;
    pub const CYAN:    Color = 6u16;
    pub const WHITE:   Color = 7u16;

    pub const BRIGHT_BLACK:   Color = 8u16;
    pub const BRIGHT_RED:     Color = 9u16;
    pub const BRIGHT_GREEN:   Color = 10u16;
    pub const BRIGHT_YELLOW:  Color = 11u16;
    pub const BRIGHT_BLUE:    Color = 12u16;
    pub const BRIGHT_MAGENTA: Color = 13u16;
    pub const BRIGHT_CYAN:    Color = 14u16;
    pub const BRIGHT_WHITE:   Color = 15u16;
}

/// Terminal attributes for use with term.attr().
///
/// Most attributes can only be turned on and must be turned off with term.reset().
/// The ones that can be turned off explicitly take a boolean value.
/// Color is also represented as an attribute for convenience.
#[derive(Copy)]
pub enum Attr {
    /// Bold (or possibly bright) mode
    Bold,
    /// Dim mode, also called faint or half-bright. Often not supported
    Dim,
    /// Italics mode. Often not supported
    Italic(bool),
    /// Underline mode
    Underline(bool),
    /// Blink mode
    Blink,
    /// Standout mode. Often implemented as Reverse, sometimes coupled with Bold
    Standout(bool),
    /// Reverse mode, inverts the foreground and background colors
    Reverse,
    /// Secure mode, also called invis mode. Hides the printed text
    Secure,
    /// Convenience attribute to set the foreground color
    ForegroundColor(color::Color),
    /// Convenience attribute to set the background color
    BackgroundColor(color::Color)
}

/// A terminal with similar capabilities to an ANSI Terminal
/// (foreground/background colors etc).
pub trait Terminal<T: Writer>: Writer {
    /// Sets the foreground color to the given color.
    ///
    /// If the color is a bright color, but the terminal only supports 8 colors,
    /// the corresponding normal color will be used instead.
    ///
    /// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
    /// if there was an I/O error.
    fn fg(&mut self, color: color::Color) -> IoResult<bool>;

    /// Sets the background color to the given color.
    ///
    /// If the color is a bright color, but the terminal only supports 8 colors,
    /// the corresponding normal color will be used instead.
    ///
    /// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
    /// if there was an I/O error.
    fn bg(&mut self, color: color::Color) -> IoResult<bool>;

    /// Sets the given terminal attribute, if supported.  Returns `Ok(true)`
    /// if the attribute was supported, `Ok(false)` otherwise, and `Err(e)` if
    /// there was an I/O error.
    fn attr(&mut self, attr: Attr) -> IoResult<bool>;

    /// Returns whether the given terminal attribute is supported.
    fn supports_attr(&self, attr: Attr) -> bool;

    /// Resets all terminal attributes and color to the default.
    /// Returns `Ok()`.
    fn reset(&mut self) -> IoResult<()>;

    /// Gets an immutable reference to the stream inside
    fn get_ref<'a>(&'a self) -> &'a T;

    /// Gets a mutable reference to the stream inside
    fn get_mut<'a>(&'a mut self) -> &'a mut T;
}

/// A terminal which can be unwrapped.
pub trait UnwrappableTerminal<T: Writer>: Terminal<T> {
    /// Returns the contained stream, destroying the `Terminal`
    fn unwrap(self) -> T;
}
<commit_msg>Remove unnecessary int type suffix<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Terminal formatting library.
//!
//! This crate provides the `Terminal` trait, which abstracts over an [ANSI
//! Terminal][ansi] to provide color printing, among other things. There are two implementations,
//! the `TerminfoTerminal`, which uses control characters from a
//! [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
//! API][win].
//!
//! # Examples
//!
//! ```no_run
//! extern crate term;
//!
//! fn main() {
//!     let mut t = term::stdout().unwrap();
//!
//!     t.fg(term::color::GREEN).unwrap();
//!     (write!(t, "hello, ")).unwrap();
//!
//!     t.fg(term::color::RED).unwrap();
//!     (writeln!(t, "world!")).unwrap();
//!
//!     t.reset().unwrap();
//! }
//! ```
//!
//! [ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
//! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
//! [ti]: https://en.wikipedia.org/wiki/Terminfo

#![crate_name = "term"]
#![experimental]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/nightly/",
       html_playground_url = "http://play.rust-lang.org/")]

#![allow(unknown_features)]
#![feature(macro_rules, phase, slicing_syntax, globs)]

#![deny(missing_docs)]

#[phase(plugin, link)] extern crate log;

pub use terminfo::TerminfoTerminal;
#[cfg(windows)]
pub use win::WinConsole;

use std::io::IoResult;

pub mod terminfo;

#[cfg(windows)]
mod win;

/// A hack to work around the fact that `Box<Writer + Send>` does not
/// currently implement `Writer`.
pub struct WriterWrapper {
    wrapped: Box<Writer + Send>,
}

impl Writer for WriterWrapper {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        self.wrapped.write(buf)
    }

    #[inline]
    fn flush(&mut self) -> IoResult<()> {
        self.wrapped.flush()
    }
}

#[cfg(not(windows))]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<Terminal<WriterWrapper> + Send>> {
    TerminfoTerminal::new(WriterWrapper {
        wrapped: box std::io::stdout() as Box<Writer + Send>,
    })
}

#[cfg(windows)]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<Terminal<WriterWrapper> + Send>> {
    let boxed = box std::io::stdio() as Box<Writer + Send>;
    let wrapped = WriterWrapper {wrapper: boxed};

    TerminfoTerminal::new(wrapped).or(WinConsole::new(wrapped));
}

#[cfg(not(windows))]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<Terminal<WriterWrapper> + Send>> {
    TerminfoTerminal::new(WriterWrapper {
        wrapped: box std::io::stderr() as Box<Writer + Send>,
    })
}

#[cfg(windows)]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<Terminal<WriterWrapper> + Send>> {
    let boxed = box std::io::stderr() as Box<Writer + Send>;
    let wrapped = WriterWrapper {wrapper: boxed};

    TerminfoTerminal::new(wrapped).or(WinConsole::new(wrapped));
}

/// Terminal color definitions
pub mod color {
    /// Number for a terminal color
    pub type Color = u16;

    pub const BLACK:   Color = 0;
    pub const RED:     Color = 1;
    pub const GREEN:   Color = 2;
    pub const YELLOW:  Color = 3;
    pub const BLUE:    Color = 4;
    pub const MAGENTA: Color = 5;
    pub const CYAN:    Color = 6;
    pub const WHITE:   Color = 7;

    pub const BRIGHT_BLACK:   Color = 8;
    pub const BRIGHT_RED:     Color = 9;
    pub const BRIGHT_GREEN:   Color = 10;
    pub const BRIGHT_YELLOW:  Color = 11;
    pub const BRIGHT_BLUE:    Color = 12;
    pub const BRIGHT_MAGENTA: Color = 13;
    pub const BRIGHT_CYAN:    Color = 14;
    pub const BRIGHT_WHITE:   Color = 15;
}

/// Terminal attributes for use with term.attr().
///
/// Most attributes can only be turned on and must be turned off with term.reset().
/// The ones that can be turned off explicitly take a boolean value.
/// Color is also represented as an attribute for convenience.
#[derive(Copy)]
pub enum Attr {
    /// Bold (or possibly bright) mode
    Bold,
    /// Dim mode, also called faint or half-bright. Often not supported
    Dim,
    /// Italics mode. Often not supported
    Italic(bool),
    /// Underline mode
    Underline(bool),
    /// Blink mode
    Blink,
    /// Standout mode. Often implemented as Reverse, sometimes coupled with Bold
    Standout(bool),
    /// Reverse mode, inverts the foreground and background colors
    Reverse,
    /// Secure mode, also called invis mode. Hides the printed text
    Secure,
    /// Convenience attribute to set the foreground color
    ForegroundColor(color::Color),
    /// Convenience attribute to set the background color
    BackgroundColor(color::Color)
}

/// A terminal with similar capabilities to an ANSI Terminal
/// (foreground/background colors etc).
pub trait Terminal<T: Writer>: Writer {
    /// Sets the foreground color to the given color.
    ///
    /// If the color is a bright color, but the terminal only supports 8 colors,
    /// the corresponding normal color will be used instead.
    ///
    /// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
    /// if there was an I/O error.
    fn fg(&mut self, color: color::Color) -> IoResult<bool>;

    /// Sets the background color to the given color.
    ///
    /// If the color is a bright color, but the terminal only supports 8 colors,
    /// the corresponding normal color will be used instead.
    ///
    /// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
    /// if there was an I/O error.
    fn bg(&mut self, color: color::Color) -> IoResult<bool>;

    /// Sets the given terminal attribute, if supported.  Returns `Ok(true)`
    /// if the attribute was supported, `Ok(false)` otherwise, and `Err(e)` if
    /// there was an I/O error.
    fn attr(&mut self, attr: Attr) -> IoResult<bool>;

    /// Returns whether the given terminal attribute is supported.
    fn supports_attr(&self, attr: Attr) -> bool;

    /// Resets all terminal attributes and color to the default.
    /// Returns `Ok()`.
    fn reset(&mut self) -> IoResult<()>;

    /// Gets an immutable reference to the stream inside
    fn get_ref<'a>(&'a self) -> &'a T;

    /// Gets a mutable reference to the stream inside
    fn get_mut<'a>(&'a mut self) -> &'a mut T;
}

/// A terminal which can be unwrapped.
pub trait UnwrappableTerminal<T: Writer>: Terminal<T> {
    /// Returns the contained stream, destroying the `Terminal`
    fn unwrap(self) -> T;
}
<|endoftext|>


<commit_before>extern crate itertools;

use itertools::Itertools;

use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use std::vec;

#[derive(Debug, Clone)]
pub enum EvalError {
    StackUnderflow,
    NameNotFound(String),
    TypeMismatch,
}

impl fmt::Display for EvalError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            EvalError::StackUnderflow => write!(f, "stack underflow"),
            EvalError::NameNotFound(ref name) => write!(f, "could not find name: {}", name),
            EvalError::TypeMismatch => write!(f, "type mismatch"),
        }
    }
}

pub type EvalResult<T> = Result<T, EvalError>;

// This wrapper struct is needed to allow for the derivation of traits on `Term`
#[derive(Copy)]
pub struct Prim {
    pub f: fn(&mut Evaluator) -> EvalResult<()>,
}

impl fmt::Debug for Prim {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Prim(<prim>)")
    }
}

impl Clone for Prim {
    fn clone(&self) -> Prim { *self }
}

impl PartialEq for Prim {
    fn eq(&self, _: &Prim) -> bool {
        unimplemented!() // TODO: figure out function equality
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Term {
    Number(i32),
    Quote(Stack),
    Name(String),
    Prim(Prim),
}

impl Term {
    pub fn prim(f: fn(&mut Evaluator) -> EvalResult<()>) -> Term {
        Term::Prim(Prim { f: f })
    }
}

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Term::Number(x) => write!(f, "{}", x),
            Term::Quote(ref stack) => write!(f, "[ {} ]", stack),
            Term::Name(ref name) => write!(f, "{}", name),
            Term::Prim(_) => write!(f, "<prim>"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Words {
    defs: HashMap<String, Term>,
}

impl Words {
    pub fn empty() -> Words {
        Words {
            defs: HashMap::new(),
        }
    }

    pub fn standard() -> Words {
        let mut words = Words::empty();

        words.define("dup", Term::prim(prim::dup)); // (A b -> A b b)
        words.define("pop", Term::prim(prim::pop)); // (A b -> A)
        words.define("swap", Term::prim(prim::swap)); // (A b c -> A c b)
        words.define("apply", Term::prim(prim::apply)); // (A (A -> B) -> B)
        words.define("quote", Term::prim(prim::quote)); // (A b -> A (C -> C b))
        words.define("compose", Term::prim(prim::compose)); // (A (B -> C) (C -> D) -> A (B -> D)))

        words.define("words", Term::prim(prim::words)); // (A ~> A)

        words.define("+", Term::prim(prim::add)); // (A num num -> A num)
        words.define("-", Term::prim(prim::sub)); // (A num num -> A num)
        words.define("*", Term::prim(prim::mul)); // (A num num -> A num)
        words.define("/", Term::prim(prim::div)); // (A num num -> A num)
        words.define("%", Term::prim(prim::rem)); // (A num num -> A num)

        words
    }

    pub fn define<S: Into<String>>(&mut self, name: S, x: Term) {
        self.defs.insert(name.into(), x);
    }

    pub fn lookup(&self, name: &str) -> Option<&Term> {
        self.defs.get(name)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Stack {
    terms: Vec<Term>,
}

impl Stack {
    pub fn new(terms: Vec<Term>) -> Stack {
        Stack { terms: terms }
    }

    fn push(&mut self, term: Term) {
        self.terms.push(term);
    }

    fn pop(&mut self) -> EvalResult<Term> {
        match self.terms.pop() {
            Some(term) => Ok(term),
            None => Err(EvalError::StackUnderflow),
        }
    }

    fn pop_number(&mut self) -> EvalResult<i32> {
        match try!(self.pop()) {
            Term::Number(x) => Ok(x),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn pop_quote(&mut self) -> EvalResult<Stack> {
        match try!(self.pop()) {
            Term::Quote(stack) => Ok(stack),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn peek(&mut self) -> EvalResult<&Term> {
        match self.terms.last() {
            Some(term) => Ok(term),
            None => Err(EvalError::StackUnderflow),
        }
    }
}

impl FromStr for Stack {
    type Err = String;

    fn from_str(src: &str) -> Result<Stack, String> {
        fn parse_tokens<'a, I>(tokens: &mut I, in_quote: bool) -> Result<Stack, String> where
            I: Iterator<Item = &'a str>,
        {
            let mut terms = vec![];

            while let Some(token) = tokens.next() {
                match token {
                    "[" => {
                        terms.push(Term::Quote(
                            try!(parse_tokens(tokens, true))
                        ))
                    },
                    "]" => {
                        if in_quote {
                            return Ok(Stack::new(terms));
                        } else {
                            return Err("found unexpected `]`".to_string());
                        }
                    },
                    token => match token.parse() {
                        Ok(x) => terms.push(Term::Number(x)),
                        Err(_) => terms.push(Term::Name(token.to_string())),
                    },
                }
            }
            if in_quote {
                Err("expected closing `]`".to_string())
            } else {
                Ok(Stack::new(terms))
            }
        }

        parse_tokens(&mut src.split_whitespace(), false)
    }
}

pub type Terms = vec::IntoIter<Term>;

impl IntoIterator for Stack {
    type Item = Term;
    type IntoIter = Terms;

    fn into_iter(self) -> Terms {
        self.terms.into_iter()
    }
}

impl fmt::Display for Stack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.terms.iter().format(" ", |t, f| f(t)))
    }
}

pub struct Evaluator<'a> {
    words: &'a Words,
    stack: Stack,
    quote: Terms,
}

impl<'a> Evaluator<'a> {
    pub fn new(words: &Words, quote: Stack) -> Evaluator {
        Evaluator {
            words: words,
            stack: Stack::new(vec![]),
            quote: quote.into_iter(),

        }
    }

    fn eval_name(&mut self, name: String) -> EvalResult<()> {
        match self.words.lookup(&name) {
            Some(term) => self.eval_term(term.clone()),
            None => Err(EvalError::NameNotFound(name)),
        }
    }

    fn eval_term(&mut self, term: Term) -> EvalResult<()> {
        match term {
            Term::Number(x) => { self.stack.push(Term::Number(x)); Ok(()) },
            Term::Quote(stack) => { self.stack.push(Term::Quote(stack)); Ok(()) },
            Term::Name(name) => self.eval_name(name),
            Term::Prim(prim) => (prim.f)(self),
        }
    }

    fn eval_quote(&mut self, quote: Stack) -> EvalResult<()> {
        let mut quote = quote.into_iter();
        while let Some(term) = quote.next() {
            try!(self.eval_term(term))
        }
        Ok(())
    }

    fn eval_stack(&mut self) -> EvalResult<()> {
        while let Some(term) = self.quote.next() {
            try!(self.eval_term(term))
        }
        Ok(())
    }

    pub fn eval(mut self) -> EvalResult<Stack> {
        try!(self.eval_stack());
        Ok(self.stack)
    }
}

mod prim {
    use std::ops::*;
    use super::*;

    // dup : (A b -> A b b)
    pub fn dup(ev: &mut Evaluator) -> EvalResult<()> {
        let term = try!(ev.stack.peek()).clone();
        ev.stack.push(term);
        Ok(())
    }

    // pop : (A b -> A)
    pub fn pop(ev: &mut Evaluator) -> EvalResult<()> {
        ev.stack.pop().map(|_| ())
    }

    // swap : (A b c -> A c b)
    pub fn swap(ev: &mut Evaluator) -> EvalResult<()> {
        let term_a = try!(ev.stack.pop());
        let term_b = try!(ev.stack.pop());

        ev.stack.push(term_a);
        ev.stack.push(term_b);

        Ok(())
    }

    // apply : (A (A -> B) -> B)
    pub fn apply(ev: &mut Evaluator) -> EvalResult<()> {
        let quote = try!(ev.stack.pop_quote());

        ev.eval_quote(quote)
    }

    // quote : (A b -> A (C -> C b))
    pub fn quote(ev: &mut Evaluator) -> EvalResult<()> {
        let term = try!(ev.stack.pop());
        let quoted = Term::Quote(Stack::new(vec![term]));

        ev.stack.push(quoted);

        Ok(())
    }

    // compose : (A (B -> C) (C -> D) -> A (B -> D)))
    pub fn compose(ev: &mut Evaluator) -> EvalResult<()> {
        let stack_a = try!(ev.stack.pop_quote());
        let mut stack_b = try!(ev.stack.pop_quote());

        stack_b.terms.extend(stack_a.terms);
        ev.stack.push(Term::Quote(stack_b));

        Ok(())
    }

    // words : (A ~> A)
    pub fn words(ev: &mut Evaluator) -> EvalResult<()> {
        for name in ev.words.defs.keys() {
            println!("{}", name);
        }

        Ok(())
    }

    fn apply_binop<F: Fn(i32, i32) -> i32>(stack: &mut Stack, f: F) -> EvalResult<()> {
        let x = try!(stack.pop_number());
        let y = try!(stack.pop_number());

        stack.push(Term::Number(f(x, y)));

        Ok(())
    }

    // add : (A num num -> A num)
    pub fn add(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::add)
    }

    // sub : (A num num -> A num)
    pub fn sub(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::sub)
    }

    // mul : (A num num -> A num)
    pub fn mul(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::mul)
    }

    // div : (A num num -> A num)
    pub fn div(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::div)
    }

    // rem : (A num num -> A num)
    pub fn rem(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::rem)
    }
}
<commit_msg>Add parse tests<commit_after>extern crate itertools;

use itertools::Itertools;

use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use std::vec;

#[derive(Debug, Clone)]
pub enum EvalError {
    StackUnderflow,
    NameNotFound(String),
    TypeMismatch,
}

impl fmt::Display for EvalError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            EvalError::StackUnderflow => write!(f, "stack underflow"),
            EvalError::NameNotFound(ref name) => write!(f, "could not find name: {}", name),
            EvalError::TypeMismatch => write!(f, "type mismatch"),
        }
    }
}

pub type EvalResult<T> = Result<T, EvalError>;

// This wrapper struct is needed to allow for the derivation of traits on `Term`
#[derive(Copy)]
pub struct Prim {
    pub f: fn(&mut Evaluator) -> EvalResult<()>,
}

impl fmt::Debug for Prim {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Prim(<prim>)")
    }
}

impl Clone for Prim {
    fn clone(&self) -> Prim { *self }
}

impl PartialEq for Prim {
    fn eq(&self, _: &Prim) -> bool {
        unimplemented!() // TODO: figure out function equality
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Term {
    Number(i32),
    Quote(Stack),
    Name(String),
    Prim(Prim),
}

impl Term {
    pub fn prim(f: fn(&mut Evaluator) -> EvalResult<()>) -> Term {
        Term::Prim(Prim { f: f })
    }
}

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Term::Number(x) => write!(f, "{}", x),
            Term::Quote(ref stack) => write!(f, "[ {} ]", stack),
            Term::Name(ref name) => write!(f, "{}", name),
            Term::Prim(_) => write!(f, "<prim>"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Words {
    defs: HashMap<String, Term>,
}

impl Words {
    pub fn empty() -> Words {
        Words {
            defs: HashMap::new(),
        }
    }

    pub fn standard() -> Words {
        let mut words = Words::empty();

        words.define("dup", Term::prim(prim::dup)); // (A b -> A b b)
        words.define("pop", Term::prim(prim::pop)); // (A b -> A)
        words.define("swap", Term::prim(prim::swap)); // (A b c -> A c b)
        words.define("apply", Term::prim(prim::apply)); // (A (A -> B) -> B)
        words.define("quote", Term::prim(prim::quote)); // (A b -> A (C -> C b))
        words.define("compose", Term::prim(prim::compose)); // (A (B -> C) (C -> D) -> A (B -> D)))

        words.define("words", Term::prim(prim::words)); // (A ~> A)

        words.define("+", Term::prim(prim::add)); // (A num num -> A num)
        words.define("-", Term::prim(prim::sub)); // (A num num -> A num)
        words.define("*", Term::prim(prim::mul)); // (A num num -> A num)
        words.define("/", Term::prim(prim::div)); // (A num num -> A num)
        words.define("%", Term::prim(prim::rem)); // (A num num -> A num)

        words
    }

    pub fn define<S: Into<String>>(&mut self, name: S, x: Term) {
        self.defs.insert(name.into(), x);
    }

    pub fn lookup(&self, name: &str) -> Option<&Term> {
        self.defs.get(name)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Stack {
    terms: Vec<Term>,
}

impl Stack {
    pub fn new(terms: Vec<Term>) -> Stack {
        Stack { terms: terms }
    }

    fn push(&mut self, term: Term) {
        self.terms.push(term);
    }

    fn pop(&mut self) -> EvalResult<Term> {
        match self.terms.pop() {
            Some(term) => Ok(term),
            None => Err(EvalError::StackUnderflow),
        }
    }

    fn pop_number(&mut self) -> EvalResult<i32> {
        match try!(self.pop()) {
            Term::Number(x) => Ok(x),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn pop_quote(&mut self) -> EvalResult<Stack> {
        match try!(self.pop()) {
            Term::Quote(stack) => Ok(stack),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn peek(&mut self) -> EvalResult<&Term> {
        match self.terms.last() {
            Some(term) => Ok(term),
            None => Err(EvalError::StackUnderflow),
        }
    }
}

impl FromStr for Stack {
    type Err = String;

    fn from_str(src: &str) -> Result<Stack, String> {
        fn parse_tokens<'a, I>(tokens: &mut I, in_quote: bool) -> Result<Stack, String> where
            I: Iterator<Item = &'a str>,
        {
            let mut terms = vec![];

            while let Some(token) = tokens.next() {
                match token {
                    "[" => {
                        terms.push(Term::Quote(
                            try!(parse_tokens(tokens, true))
                        ))
                    },
                    "]" => {
                        if in_quote {
                            return Ok(Stack::new(terms));
                        } else {
                            return Err("found unexpected `]`".to_string());
                        }
                    },
                    token => match token.parse() {
                        Ok(x) => terms.push(Term::Number(x)),
                        Err(_) => terms.push(Term::Name(token.to_string())),
                    },
                }
            }
            if in_quote {
                Err("expected closing `]`".to_string())
            } else {
                Ok(Stack::new(terms))
            }
        }

        parse_tokens(&mut src.split_whitespace(), false)
    }
}

pub type Terms = vec::IntoIter<Term>;

impl IntoIterator for Stack {
    type Item = Term;
    type IntoIter = Terms;

    fn into_iter(self) -> Terms {
        self.terms.into_iter()
    }
}

impl fmt::Display for Stack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.terms.iter().format(" ", |t, f| f(t)))
    }
}

pub struct Evaluator<'a> {
    words: &'a Words,
    stack: Stack,
    quote: Terms,
}

impl<'a> Evaluator<'a> {
    pub fn new(words: &Words, quote: Stack) -> Evaluator {
        Evaluator {
            words: words,
            stack: Stack::new(vec![]),
            quote: quote.into_iter(),

        }
    }

    fn eval_name(&mut self, name: String) -> EvalResult<()> {
        match self.words.lookup(&name) {
            Some(term) => self.eval_term(term.clone()),
            None => Err(EvalError::NameNotFound(name)),
        }
    }

    fn eval_term(&mut self, term: Term) -> EvalResult<()> {
        match term {
            Term::Number(x) => { self.stack.push(Term::Number(x)); Ok(()) },
            Term::Quote(stack) => { self.stack.push(Term::Quote(stack)); Ok(()) },
            Term::Name(name) => self.eval_name(name),
            Term::Prim(prim) => (prim.f)(self),
        }
    }

    fn eval_quote(&mut self, quote: Stack) -> EvalResult<()> {
        let mut quote = quote.into_iter();
        while let Some(term) = quote.next() {
            try!(self.eval_term(term))
        }
        Ok(())
    }

    fn eval_stack(&mut self) -> EvalResult<()> {
        while let Some(term) = self.quote.next() {
            try!(self.eval_term(term))
        }
        Ok(())
    }

    pub fn eval(mut self) -> EvalResult<Stack> {
        try!(self.eval_stack());
        Ok(self.stack)
    }
}

mod prim {
    use std::ops::*;
    use super::*;

    // dup : (A b -> A b b)
    pub fn dup(ev: &mut Evaluator) -> EvalResult<()> {
        let term = try!(ev.stack.peek()).clone();
        ev.stack.push(term);
        Ok(())
    }

    // pop : (A b -> A)
    pub fn pop(ev: &mut Evaluator) -> EvalResult<()> {
        ev.stack.pop().map(|_| ())
    }

    // swap : (A b c -> A c b)
    pub fn swap(ev: &mut Evaluator) -> EvalResult<()> {
        let term_a = try!(ev.stack.pop());
        let term_b = try!(ev.stack.pop());

        ev.stack.push(term_a);
        ev.stack.push(term_b);

        Ok(())
    }

    // apply : (A (A -> B) -> B)
    pub fn apply(ev: &mut Evaluator) -> EvalResult<()> {
        let quote = try!(ev.stack.pop_quote());

        ev.eval_quote(quote)
    }

    // quote : (A b -> A (C -> C b))
    pub fn quote(ev: &mut Evaluator) -> EvalResult<()> {
        let term = try!(ev.stack.pop());
        let quoted = Term::Quote(Stack::new(vec![term]));

        ev.stack.push(quoted);

        Ok(())
    }

    // compose : (A (B -> C) (C -> D) -> A (B -> D)))
    pub fn compose(ev: &mut Evaluator) -> EvalResult<()> {
        let stack_a = try!(ev.stack.pop_quote());
        let mut stack_b = try!(ev.stack.pop_quote());

        stack_b.terms.extend(stack_a.terms);
        ev.stack.push(Term::Quote(stack_b));

        Ok(())
    }

    // words : (A ~> A)
    pub fn words(ev: &mut Evaluator) -> EvalResult<()> {
        for name in ev.words.defs.keys() {
            println!("{}", name);
        }

        Ok(())
    }

    fn apply_binop<F: Fn(i32, i32) -> i32>(stack: &mut Stack, f: F) -> EvalResult<()> {
        let x = try!(stack.pop_number());
        let y = try!(stack.pop_number());

        stack.push(Term::Number(f(x, y)));

        Ok(())
    }

    // add : (A num num -> A num)
    pub fn add(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::add)
    }

    // sub : (A num num -> A num)
    pub fn sub(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::sub)
    }

    // mul : (A num num -> A num)
    pub fn mul(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::mul)
    }

    // div : (A num num -> A num)
    pub fn div(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::div)
    }

    // rem : (A num num -> A num)
    pub fn rem(ev: &mut Evaluator) -> EvalResult<()> {
        apply_binop(&mut ev.stack, i32::rem)
    }
}

#[cfg(test)]
mod tests {
    mod stack {
        mod parse {
            use Stack;
            use Term::*;

            #[test]
            fn test_number() {
                assert_eq!("123".parse(), Ok(Stack::new(vec![Number(123)])));
                assert_eq!(" 34 ".parse(), Ok(Stack::new(vec![Number(34)])));
            }

            #[test]
            fn test_name() {
                assert_eq!("foo".parse(), Ok(Stack::new(vec![Name("foo".to_string())])));
                assert_eq!(" * ".parse(), Ok(Stack::new(vec![Name("*".to_string())])));
            }

            #[test]
            fn test_quote() {
                assert_eq!("[ foo ]".parse(),
                    Ok(Stack::new(vec![
                        Quote(Stack::new(vec![Name("foo".to_string())]))
                    ])));

                assert_eq!("[ 1 2 * + ]".parse(),
                    Ok(Stack::new(vec![
                        Quote(Stack::new(vec![
                            Number(1),
                            Number(2),
                            Name("*".to_string()),
                            Name("+".to_string()),
                        ])),
                    ])));
            }

            #[test]
            fn test_compose() {
                assert_eq!(" 1 2 [ foo ]  * +".parse(),
                    Ok(Stack::new(vec![
                        Number(1),
                        Number(2),
                        Quote(Stack::new(vec![Name("foo".to_string())])),
                        Name("*".to_string()),
                        Name("+".to_string()),
                    ])));
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Florian Wilkens
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Crate dependencies
extern crate libc;
extern crate pam_sys as pam;
extern crate users;

// Modules
mod ffi;
mod simple;

// Re-Exports
pub use simple::*;

// Usings
use pam::{PamConversation, PamFlag, PamHandle, PamReturnCode};

/// Main struct to authenticate a user
pub struct Authenticator<'a> {
    handle:         *mut PamHandle,
    credentials:    [&'a str; 2]
}

impl <'a> Authenticator<'a> {
    /// Creates a new Authenticator with a given service name
    pub fn new(service: &str) -> Option<Authenticator> {
        use std::ffi::CString;
        use std::ptr;

        let creds = [""; 2];
        let conv = PamConversation {
            conv:       Some(ffi::converse),
            data_ptr:   creds.as_ptr() as *mut ::libc::c_void
        };
        let mut handle: *mut PamHandle = ptr::null_mut();

        match unsafe {
            pam::start(CString::new(service).unwrap().as_ptr(), ptr::null(), &conv, &mut handle)
        } {
            PamReturnCode::SUCCESS => Some(Authenticator {
                handle:         handle,
                credentials:    creds
            }),
            _   => None
        }
    }

    /// Set the credentials which should be used in the authentication process
    pub fn set_credentials(&mut self, user: &'a str, password: &'a str) {
        self.credentials[0] = user;
        self.credentials[1] = password;
    }

    /// Perform the authentication with the provided credentials
    pub fn authenticate(&self) -> Result<(), PamReturnCode> {
        let success = PamReturnCode::SUCCESS;

        let mut res = unsafe { pam::authenticate(self.handle, PamFlag::NONE) };
        if res != success {
            return self.fail(res);
        }

        res = unsafe { pam::acct_mgmt(self.handle, PamFlag::NONE) };
        if res != success {
            return self.fail(res);
        }

        res = unsafe { pam::setcred(self.handle, PamFlag::ESTABLISH_CRED) };
        if res != success {
            return self.fail(res);
        }
        Ok(())
    }

    /// Open a session for a previously authenticated user and
    /// initialize the environment appropriately (in PAM and regular enviroment variables).
    ///
    /// Does not currently check for authentication and just calls the ffi method,
    /// but clients should not rely on that.
    pub fn open_session(&self) -> Result<(), PamReturnCode> {
        let res = unsafe { pam::open_session(self.handle, PamFlag::NONE) };
        if res != PamReturnCode::SUCCESS {
            return self.fail(res);
        }
        self.initialize_environment();
        Ok(())
    }

    // Initialize the client environment with common variables.
    // Currently always called from Authenticator.open_session()
    fn initialize_environment(&self) {
        let user = users::get_user_by_name(self.credentials[0])
            .expect("Could not get user by name!");

        self.set_env("USER", &user.name);
        self.set_env("LOGNAME", &user.name);
        self.set_env("HOME", &user.home_dir);
        self.set_env("PWD", &user.home_dir);
        self.set_env("SHELL", &user.shell);
        // Taken from https://github.com/gsingh93/display-manager/blob/master/pam.c
        // Should be a better way to get this. Revisit later.
        self.set_env("PATH", "/usr/local/sbin:/usr/local/bin:/usr/bin");
    }

    // Utility function to set an environment variable in PAM and the process
    fn set_env(&self, key: &str, value: &str) {
        use std::env;
        use std::ffi::CString;

        // Set regular environment variable
        env::set_var(key, value);

        // Set pam environment variable
        let name_value = CString::new(format!("{}={}", key, value)).unwrap();
        match unsafe { pam::putenv(self.handle, name_value.as_ptr()) } {
            PamReturnCode::SUCCESS => (),
            _   => panic!("set_env failed!")    //TODO: add proper error handling (through results?)
        }
    }

    // Utility function to properly clean up pam
    // TODO: Move or copy this into Authenticator.drop()?
    fn fail(&self, code: PamReturnCode) -> Result<(), PamReturnCode> {
        unsafe {
            pam::setcred(self.handle, pam::PamFlag::DELETE_CRED);
            pam::end(self.handle, 0);
        }
        Err(code)
    }
}
<commit_msg>Changed Authenticator to use a box for the credentials<commit_after>// Copyright (C) 2015 Florian Wilkens
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Crate dependencies
extern crate libc;
extern crate pam_sys as pam;
extern crate users;

// Modules
mod ffi;
mod simple;

// Re-Exports
pub use simple::*;

// Usings
use pam::{PamConversation, PamFlag, PamHandle, PamReturnCode};

/// Main struct to authenticate a user
pub struct Authenticator<'a> {
    handle:         *mut PamHandle,
    credentials:    Box<[&'a str; 2]>
}

impl <'a> Authenticator<'a> {
    /// Creates a new Authenticator with a given service name
    pub fn new(service: &str) -> Option<Authenticator> {
        use std::ffi::CString;
        use std::ptr;

        let creds = Box::new([""; 2]);
        let conv = PamConversation {
            conv:       Some(ffi::converse),
            data_ptr:   creds.as_ptr() as *mut ::libc::c_void
        };
        let mut handle: *mut PamHandle = ptr::null_mut();

        match unsafe {
            pam::start(CString::new(service).unwrap().as_ptr(), ptr::null(), &conv, &mut handle)
        } {
            PamReturnCode::SUCCESS => Some(Authenticator {
                handle:         handle,
                credentials:    creds
            }),
            _   => None
        }
    }

    /// Set the credentials which should be used in the authentication process
    pub fn set_credentials(&mut self, user: &'a str, password: &'a str) {
        self.credentials[0] = user;
        self.credentials[1] = password;
    }

    /// Perform the authentication with the provided credentials
    pub fn authenticate(&self) -> Result<(), PamReturnCode> {
        let success = PamReturnCode::SUCCESS;

        let mut res = unsafe { pam::authenticate(self.handle, PamFlag::NONE) };
        if res != success {
            return self.fail(res);
        }

        res = unsafe { pam::acct_mgmt(self.handle, PamFlag::NONE) };
        if res != success {
            return self.fail(res);
        }

        res = unsafe { pam::setcred(self.handle, PamFlag::ESTABLISH_CRED) };
        if res != success {
            return self.fail(res);
        }
        Ok(())
    }

    /// Open a session for a previously authenticated user and
    /// initialize the environment appropriately (in PAM and regular enviroment variables).
    ///
    /// Does not currently check for authentication and just calls the ffi method,
    /// but clients should not rely on that.
    pub fn open_session(&self) -> Result<(), PamReturnCode> {
        let res = unsafe { pam::open_session(self.handle, PamFlag::NONE) };
        if res != PamReturnCode::SUCCESS {
            return self.fail(res);
        }
        self.initialize_environment();
        Ok(())
    }

    // Initialize the client environment with common variables.
    // Currently always called from Authenticator.open_session()
    fn initialize_environment(&self) {
        let user = users::get_user_by_name(self.credentials[0])
            .expect("Could not get user by name!");

        self.set_env("USER", &user.name);
        self.set_env("LOGNAME", &user.name);
        self.set_env("HOME", &user.home_dir);
        self.set_env("PWD", &user.home_dir);
        self.set_env("SHELL", &user.shell);
        // Taken from https://github.com/gsingh93/display-manager/blob/master/pam.c
        // Should be a better way to get this. Revisit later.
        self.set_env("PATH", "/usr/local/sbin:/usr/local/bin:/usr/bin");
    }

    // Utility function to set an environment variable in PAM and the process
    fn set_env(&self, key: &str, value: &str) {
        use std::env;
        use std::ffi::CString;

        // Set regular environment variable
        env::set_var(key, value);

        // Set pam environment variable
        let name_value = CString::new(format!("{}={}", key, value)).unwrap();
        match unsafe { pam::putenv(self.handle, name_value.as_ptr()) } {
            PamReturnCode::SUCCESS => (),
            _   => panic!("set_env failed!")    //TODO: add proper error handling (through results?)
        }
    }

    // Utility function to properly clean up pam
    // TODO: Move or copy this into Authenticator.drop()?
    fn fail(&self, code: PamReturnCode) -> Result<(), PamReturnCode> {
        unsafe {
            pam::setcred(self.handle, pam::PamFlag::DELETE_CRED);
            pam::end(self.handle, 0);
        }
        Err(code)
    }
}
<|endoftext|>


<commit_before>//! Support for concurrent and parallel programming.
//!
//! This crate is an early work in progress. The focus for the moment is
//! concurrency:
//!
//! - **Non-blocking data structures**. These data structures allow for high
//! performance, highly-concurrent access, much superior to wrapping with a
//! `Mutex`. Ultimately the goal is to include stacks, queues, deques, bags,
//! sets and maps. These live in the `sync` module.
//!
//! - **Memory management**. Because non-blocking data structures avoid global
//! synchronization, it is not easy to tell when internal data can be safely
//! freed. The `mem` module provides generic, easy to use, and high-performance
//! APIs for managing memory in these cases. These live in the `mem` module.
//!
//! - **Synchronization**. The standard library provides a few synchronization
//! primitives (locks, barriers, etc) but this crate seeks to expand that set
//! to include more advanced/niche primitives, as well as userspace
//! alternatives. These live in the `sync` module.
//!
//! - **Scoped thread API**. Finally, the crate provides a "scoped" thread API,
//! making it possible to spawn threads that share stack data with their
//! parents. This functionality is exported at the top-level.

//#![deny(missing_docs)]

pub extern crate crossbeam_epoch as epoch;
pub extern crate crossbeam_utils as utils;

pub mod sync;
pub use utils::cache_padded::CachePadded;
pub use utils::scoped::{scope, Scope, ScopedJoinHandle};
<commit_msg>Expose ScopedThreadBuilder<commit_after>//! Support for concurrent and parallel programming.
//!
//! This crate is an early work in progress. The focus for the moment is
//! concurrency:
//!
//! - **Non-blocking data structures**. These data structures allow for high
//! performance, highly-concurrent access, much superior to wrapping with a
//! `Mutex`. Ultimately the goal is to include stacks, queues, deques, bags,
//! sets and maps. These live in the `sync` module.
//!
//! - **Memory management**. Because non-blocking data structures avoid global
//! synchronization, it is not easy to tell when internal data can be safely
//! freed. The `mem` module provides generic, easy to use, and high-performance
//! APIs for managing memory in these cases. These live in the `mem` module.
//!
//! - **Synchronization**. The standard library provides a few synchronization
//! primitives (locks, barriers, etc) but this crate seeks to expand that set
//! to include more advanced/niche primitives, as well as userspace
//! alternatives. These live in the `sync` module.
//!
//! - **Scoped thread API**. Finally, the crate provides a "scoped" thread API,
//! making it possible to spawn threads that share stack data with their
//! parents. This functionality is exported at the top-level.

//#![deny(missing_docs)]

pub extern crate crossbeam_epoch as epoch;
pub extern crate crossbeam_utils as utils;

pub mod sync;
pub use utils::cache_padded::CachePadded;
pub use utils::scoped::{scope, Scope, ScopedJoinHandle, ScopedThreadBuilder};
<|endoftext|>


<commit_before>use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::io::{Cursor, SeekFrom, Take};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::vec::Vec;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fs2::FileExt;
use time;
use regex::Regex;

use data::{Entry, Hint};
use util::{crc32, Crc32, get_file_handle};

const DATA_FILE_EXTENSION: &'static str = "cask.data";
const HINT_FILE_EXTENSION: &'static str = "cask.hint";
const LOCK_FILE_NAME: &'static str = "cask.lock";

const DEFAULT_SIZE_THRESHOLD: usize = 100 * 1024 * 1024;

pub struct Log {
    path: PathBuf,
    sync: bool,
    size_threshold: usize,
    lock_file: File,
    current_file_id: u32,
    active_data_file: File,
    active_hint_file: File,
    active_hint_file_digest: Crc32,
}

pub struct Entries<'a> {
    data_file: Take<File>,
    data_file_pos: u64,
    phantom: PhantomData<&'a ()>,
}

impl<'a> Iterator for Entries<'a> {
    type Item = (u64, Entry<'a>);

    fn next(&mut self) -> Option<(u64, Entry<'a>)> {
        if self.data_file.limit() == 0 {
            None
        } else {
            let entry = Entry::from_read(&mut self.data_file);
            let entry_pos = self.data_file_pos;

            self.data_file_pos += entry.size();

            Some((entry_pos, entry))
        }
    }
}

pub struct Hints<'a> {
    hint_file: Take<File>,
    phantom: PhantomData<&'a ()>,
}

impl<'a> Iterator for Hints<'a> {
    type Item = Hint<'a>;

    fn next(&mut self) -> Option<Hint<'a>> {
        if self.hint_file.limit() == 0 {
            None
        } else {
            Some(Hint::from_read(&mut self.hint_file))
        }
    }
}

pub struct RecreateHints<'a> {
    hint_file: File,
    hint_file_digest: Crc32,
    entries: Entries<'a>,
}

impl<'a> Iterator for RecreateHints<'a> {
    type Item = Hint<'a>;

    fn next(&mut self) -> Option<Hint<'a>> {
        self.entries.next().map(|e| {
            let (entry_pos, entry) = e;
            let hint = Hint::from(entry, entry_pos);
            hint.write_bytes(&mut self.hint_file);
            hint.write_bytes(&mut self.hint_file_digest);
            hint
        })
    }
}

impl<'a> Drop for RecreateHints<'a> {
    fn drop(&mut self) {
        while self.next().is_some() {}
        self.hint_file
            .write_u32::<LittleEndian>(self.hint_file_digest.sum32())
            .unwrap();
    }
}

impl Log {
    pub fn open(path: &str, sync: bool) -> Log {
        let path = PathBuf::from(path);

        if path.exists() {
            assert!(path.is_dir());
        } else {
            fs::create_dir(&path).unwrap();
        }

        let lock_file = File::create(path.join(LOCK_FILE_NAME)).unwrap();
        lock_file.try_lock_exclusive().unwrap();

        let current_file_id = time::now().to_timespec().sec as u32;
        let active_data_file = get_file_handle(&get_data_file_path(&path, current_file_id), true);
        let active_hint_file = get_file_handle(&get_hint_file_path(&path, current_file_id), true);
        let active_hint_file_digest = Crc32::new();

        Log {
            path: path,
            sync: sync,
            size_threshold: DEFAULT_SIZE_THRESHOLD,
            lock_file: lock_file,
            current_file_id: current_file_id,
            active_data_file: active_data_file,
            active_hint_file: active_hint_file,
            active_hint_file_digest: active_hint_file_digest,
        }
    }

    pub fn find_files(&self) -> Vec<u32> {
        let files = fs::read_dir(&self.path).unwrap();

        lazy_static! {
            static ref RE: Regex =
                Regex::new(&format!("(\\d+).{}$", DATA_FILE_EXTENSION)).unwrap();
        }

        let mut files: Vec<u32> = files.flat_map(|f| {
                let file = f.unwrap();
                let file_metadata = file.metadata().unwrap();

                if file_metadata.is_file() {
                    let file_name = file.file_name();
                    let captures = RE.captures(file_name.to_str().unwrap());
                    captures.and_then(|c| c.at(1).and_then(|n| n.parse::<u32>().ok()))
                        .and_then(|id| if id != self.current_file_id {
                            Some(id)
                        } else {
                            None
                        })
                } else {
                    None
                }
            })
            .collect();

        files.sort();

        files
    }

    pub fn entries<'a>(&self, file_id: u32) -> Entries<'a> {
        let data_file_path = get_data_file_path(&self.path, file_id);
        info!("Loading data file: {:?}", data_file_path);
        let data_file = get_file_handle(&data_file_path, false);
        let data_file_size = data_file.metadata().unwrap().len();

        Entries {
            data_file: data_file.take(data_file_size),
            data_file_pos: 0,
            phantom: PhantomData,
        }
    }

    pub fn hints<'a>(&self, file_id: u32) -> Option<Hints<'a>> {
        let hint_file_path = get_hint_file_path(&self.path, file_id);
        if is_valid_hint_file(&hint_file_path) {
            info!("Loading hint file: {:?}", hint_file_path);
            let hint_file = get_file_handle(&hint_file_path, false);
            let hint_file_size = hint_file.metadata().unwrap().len();

            Some(Hints {
                hint_file: hint_file.take(hint_file_size - 4),
                phantom: PhantomData,
            })
        } else {
            None
        }
    }

    pub fn recreate_hints<'a>(&mut self, file_id: u32) -> RecreateHints<'a> {
        let hint_file_path = get_hint_file_path(&self.path, file_id);
        info!("Re-creating hint file: {:?}", hint_file_path);
        let hint_file = get_file_handle(&hint_file_path, true);
        let entries = self.entries(file_id);

        RecreateHints {
            hint_file: hint_file,
            hint_file_digest: Crc32::new(),
            entries: entries,
        }
    }

    pub fn read_entry<'a>(&self, file_id: u32, entry_pos: u64) -> Entry<'a> {
        let mut data_file = get_file_handle(&get_data_file_path(&self.path, file_id), false);
        data_file.seek(SeekFrom::Start(entry_pos)).unwrap();
        Entry::from_read(&mut data_file)
    }

    pub fn write_entry<'a>(&mut self, entry: &Entry<'a>) -> (u32, u64) {
        let mut active_data_file_pos = self.active_data_file.seek(SeekFrom::Current(0)).unwrap();

        if active_data_file_pos + entry.size() > self.size_threshold as u64 {
            if self.sync {
                self.active_data_file.sync_data().unwrap();
            }

            self.current_file_id = time::now().to_timespec().sec as u32;

            self.active_data_file =
                get_file_handle(&get_data_file_path(&self.path, self.current_file_id), true);

            // FIXME: hint file checksum is not being appended
            self.active_hint_file =
                get_file_handle(&get_hint_file_path(&self.path, self.current_file_id), true);
            self.active_hint_file_digest = Crc32::new();

            active_data_file_pos = 0
        }

        let hint = Hint::new(&entry, active_data_file_pos);

        entry.write_bytes(&mut self.active_data_file);
        hint.write_bytes(&mut self.active_hint_file);
        hint.write_bytes(&mut self.active_hint_file_digest);

        if self.sync {
            self.active_data_file.sync_data().unwrap();
        }

        (self.current_file_id, active_data_file_pos)
    }
}

impl Drop for Log {
    fn drop(&mut self) {
        self.active_hint_file
            .write_u32::<LittleEndian>(self.active_hint_file_digest.sum32())
            .unwrap();

        self.lock_file.unlock().unwrap();
    }
}

fn get_data_file_path(path: &Path, file_id: u32) -> PathBuf {
    path.join(file_id.to_string()).with_extension(DATA_FILE_EXTENSION)
}

fn get_hint_file_path(path: &Path, file_id: u32) -> PathBuf {
    path.join(file_id.to_string()).with_extension(HINT_FILE_EXTENSION)
}

fn is_valid_hint_file(path: &Path) -> bool {
    path.is_file() &&
    {
        let mut hint_file = get_file_handle(path, false);

        // FIXME: avoid reading the whole hint file into memory;
        let mut buf = Vec::new();
        hint_file.read_to_end(&mut buf).unwrap();

        buf.len() >= 4 &&
        {
            let crc = crc32(&buf[..buf.len() - 4]);

            let mut cursor = Cursor::new(&buf[buf.len() - 4..]);
            let checksum = cursor.read_u32::<LittleEndian>().unwrap();

            let valid = crc == checksum;

            if !valid {
                warn!("Found corrupt hint file: {:?}", &path);
            }

            valid
        }
    }
}
<commit_msg>fix hint file checksum update<commit_after>use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::io::{Cursor, SeekFrom, Take};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::vec::Vec;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fs2::FileExt;
use time;
use regex::Regex;

use data::{Entry, Hint};
use util::{crc32, Crc32, get_file_handle};

const DATA_FILE_EXTENSION: &'static str = "cask.data";
const HINT_FILE_EXTENSION: &'static str = "cask.hint";
const LOCK_FILE_NAME: &'static str = "cask.lock";

const DEFAULT_SIZE_THRESHOLD: usize = 100 * 1024 * 1024;

pub struct Log {
    path: PathBuf,
    sync: bool,
    size_threshold: usize,
    lock_file: File,
    current_file_id: u32,
    active_data_file: File,
    active_hint_file: File,
    active_hint_file_digest: Crc32,
}

pub struct Entries<'a> {
    data_file: Take<File>,
    data_file_pos: u64,
    phantom: PhantomData<&'a ()>,
}

impl<'a> Iterator for Entries<'a> {
    type Item = (u64, Entry<'a>);

    fn next(&mut self) -> Option<(u64, Entry<'a>)> {
        if self.data_file.limit() == 0 {
            None
        } else {
            let entry = Entry::from_read(&mut self.data_file);
            let entry_pos = self.data_file_pos;

            self.data_file_pos += entry.size();

            Some((entry_pos, entry))
        }
    }
}

pub struct Hints<'a> {
    hint_file: Take<File>,
    phantom: PhantomData<&'a ()>,
}

impl<'a> Iterator for Hints<'a> {
    type Item = Hint<'a>;

    fn next(&mut self) -> Option<Hint<'a>> {
        if self.hint_file.limit() == 0 {
            None
        } else {
            Some(Hint::from_read(&mut self.hint_file))
        }
    }
}

pub struct RecreateHints<'a> {
    hint_file: File,
    hint_file_digest: Crc32,
    entries: Entries<'a>,
}

impl<'a> Iterator for RecreateHints<'a> {
    type Item = Hint<'a>;

    fn next(&mut self) -> Option<Hint<'a>> {
        self.entries.next().map(|e| {
            let (entry_pos, entry) = e;
            let hint = Hint::from(entry, entry_pos);
            hint.write_bytes(&mut self.hint_file);
            hint.write_bytes(&mut self.hint_file_digest);
            hint
        })
    }
}

impl<'a> Drop for RecreateHints<'a> {
    fn drop(&mut self) {
        while self.next().is_some() {}
        self.hint_file
            .write_u32::<LittleEndian>(self.hint_file_digest.sum32())
            .unwrap();
    }
}

impl Log {
    pub fn open(path: &str, sync: bool) -> Log {
        let path = PathBuf::from(path);

        if path.exists() {
            assert!(path.is_dir());
        } else {
            fs::create_dir(&path).unwrap();
        }

        let lock_file = File::create(path.join(LOCK_FILE_NAME)).unwrap();
        lock_file.try_lock_exclusive().unwrap();

        let current_file_id = time::now().to_timespec().sec as u32;
        let active_data_file = get_file_handle(&get_data_file_path(&path, current_file_id), true);
        let active_hint_file = get_file_handle(&get_hint_file_path(&path, current_file_id), true);
        let active_hint_file_digest = Crc32::new();

        Log {
            path: path,
            sync: sync,
            size_threshold: DEFAULT_SIZE_THRESHOLD,
            lock_file: lock_file,
            current_file_id: current_file_id,
            active_data_file: active_data_file,
            active_hint_file: active_hint_file,
            active_hint_file_digest: active_hint_file_digest,
        }
    }

    pub fn find_files(&self) -> Vec<u32> {
        let files = fs::read_dir(&self.path).unwrap();

        lazy_static! {
            static ref RE: Regex =
                Regex::new(&format!("(\\d+).{}$", DATA_FILE_EXTENSION)).unwrap();
        }

        let mut files: Vec<u32> = files.flat_map(|f| {
                let file = f.unwrap();
                let file_metadata = file.metadata().unwrap();

                if file_metadata.is_file() {
                    let file_name = file.file_name();
                    let captures = RE.captures(file_name.to_str().unwrap());
                    captures.and_then(|c| c.at(1).and_then(|n| n.parse::<u32>().ok()))
                        .and_then(|id| if id != self.current_file_id {
                            Some(id)
                        } else {
                            None
                        })
                } else {
                    None
                }
            })
            .collect();

        files.sort();

        files
    }

    pub fn entries<'a>(&self, file_id: u32) -> Entries<'a> {
        let data_file_path = get_data_file_path(&self.path, file_id);
        info!("Loading data file: {:?}", data_file_path);
        let data_file = get_file_handle(&data_file_path, false);
        let data_file_size = data_file.metadata().unwrap().len();

        Entries {
            data_file: data_file.take(data_file_size),
            data_file_pos: 0,
            phantom: PhantomData,
        }
    }

    pub fn hints<'a>(&self, file_id: u32) -> Option<Hints<'a>> {
        let hint_file_path = get_hint_file_path(&self.path, file_id);
        if is_valid_hint_file(&hint_file_path) {
            info!("Loading hint file: {:?}", hint_file_path);
            let hint_file = get_file_handle(&hint_file_path, false);
            let hint_file_size = hint_file.metadata().unwrap().len();

            Some(Hints {
                hint_file: hint_file.take(hint_file_size - 4),
                phantom: PhantomData,
            })
        } else {
            None
        }
    }

    pub fn recreate_hints<'a>(&mut self, file_id: u32) -> RecreateHints<'a> {
        let hint_file_path = get_hint_file_path(&self.path, file_id);
        info!("Re-creating hint file: {:?}", hint_file_path);
        let hint_file = get_file_handle(&hint_file_path, true);
        let entries = self.entries(file_id);

        RecreateHints {
            hint_file: hint_file,
            hint_file_digest: Crc32::new(),
            entries: entries,
        }
    }

    pub fn read_entry<'a>(&self, file_id: u32, entry_pos: u64) -> Entry<'a> {
        let mut data_file = get_file_handle(&get_data_file_path(&self.path, file_id), false);
        data_file.seek(SeekFrom::Start(entry_pos)).unwrap();
        Entry::from_read(&mut data_file)
    }

    pub fn write_entry<'a>(&mut self, entry: &Entry<'a>) -> (u32, u64) {
        let mut active_data_file_pos = self.active_data_file.seek(SeekFrom::Current(0)).unwrap();

        if active_data_file_pos + entry.size() > self.size_threshold as u64 {
            self.new_active_file();
            active_data_file_pos = 0;
        }

        let hint = Hint::new(&entry, active_data_file_pos);

        entry.write_bytes(&mut self.active_data_file);
        hint.write_bytes(&mut self.active_hint_file);
        hint.write_bytes(&mut self.active_hint_file_digest);

        if self.sync {
            self.active_data_file.sync_data().unwrap();
        }

        (self.current_file_id, active_data_file_pos)
    }

    fn new_active_file(&mut self) {
        if self.sync {
            self.active_data_file.sync_data().unwrap();
        }

        self.active_hint_file
            .write_u32::<LittleEndian>(self.active_hint_file_digest.sum32())
            .unwrap();

        self.current_file_id = time::now().to_timespec().sec as u32;

        self.active_data_file =
            get_file_handle(&get_data_file_path(&self.path, self.current_file_id), true);

        self.active_hint_file =
            get_file_handle(&get_hint_file_path(&self.path, self.current_file_id), true);
        self.active_hint_file_digest = Crc32::new();
    }
}

impl Drop for Log {
    fn drop(&mut self) {
        self.active_hint_file
            .write_u32::<LittleEndian>(self.active_hint_file_digest.sum32())
            .unwrap();

        self.lock_file.unlock().unwrap();
    }
}

fn get_data_file_path(path: &Path, file_id: u32) -> PathBuf {
    path.join(file_id.to_string()).with_extension(DATA_FILE_EXTENSION)
}

fn get_hint_file_path(path: &Path, file_id: u32) -> PathBuf {
    path.join(file_id.to_string()).with_extension(HINT_FILE_EXTENSION)
}

fn is_valid_hint_file(path: &Path) -> bool {
    path.is_file() &&
    {
        let mut hint_file = get_file_handle(path, false);

        // FIXME: avoid reading the whole hint file into memory;
        let mut buf = Vec::new();
        hint_file.read_to_end(&mut buf).unwrap();

        buf.len() >= 4 &&
        {
            let crc = crc32(&buf[..buf.len() - 4]);

            let mut cursor = Cursor::new(&buf[buf.len() - 4..]);
            let checksum = cursor.read_u32::<LittleEndian>().unwrap();

            let valid = crc == checksum;

            if !valid {
                warn!("Found corrupt hint file: {:?}", &path);
            }

            valid
        }
    }
}
<|endoftext|>