<commit_before>//! A machine learning library for supervised regression trainings
//!
//! This library wants to enable its user to write teachers
//! independent of the model trained or the cost function tried to
//! minimize.
//! Consequently its three most important traits are `Model`, `Cost`
//! and `Training`.
//!
//! # Examples
//!
//! Estimate mean
//!
//! ```
//! use vikos::{model, cost, teacher, teach_history, Model};
//! //We are only training a constant so our feature vector is empty
//! //Any other vector would be fine, too.
//! let features = ();
//! let history = [1f64, 3.0, 4.0, 7.0, 8.0, 11.0, 29.0]; //mean is 9
//! // Give each 'truth' its feature vector and stretch our history to 100 elements
//! let history = history.iter().cycle().map(|&y|((),y)).take(100);
//!
//! let cost = cost::LeastSquares{};
//! let mut model = model::Constant::new(0.0);
//!
//! let teacher = teacher::GradientDescentAl{ l0 : 0.3, t : 4.0 };
//! teach_history(&teacher, &cost, &mut model, history);
//! println!("{}", model.predict(&features));
//! ```
#![warn(missing_docs)]

extern crate num;

use std::iter::IntoIterator;
use num::Float;

/// A Model is defines how to predict a target from an input
///
/// A model usually depends on several coefficents whose values
/// are derived using a training algorithm
pub trait Model : Clone{
    /// Input features
    type Input;
    /// Target type
    type Target : Float;

    /// Predicts a target for the inputs based on the internal coefficents
    fn predict(&self, &Self::Input) -> Self::Target;

    /// The number of internal coefficents this model depends on
    fn num_coefficents(&self) -> usize;

    /// Value predict derived by the n-th `coefficent` at `input`
    fn gradient(&self, coefficent : usize, input : &Self::Input) -> Self::Target;

    /// Mutable reference to the n-th `coefficent`
    fn coefficent(& mut self, coefficent : usize) -> & mut Self::Target;
}

/// Cost functions those value is supposed be minimized by the training algorithm
pub trait Cost{

    /// Error type used by the cost function
    ///
    /// Usually `f64` or `f32`
    type Error : Float;

    /// Value of the cost function derived by the n-th coefficent at x expressed in Error(x) and dY(x)/dx
    ///
    /// This method is called by SGD based training algorithm in order to
    /// determine the delta of the coefficents
    fn gradient(&self, prediction : Self::Error, truth : Self::Error, gradient_error_by_coefficent : Self::Error) -> Self::Error;
}

/// Teaches event to a `Model`
pub trait Training{

    /// `Model` changed by this `Training`
    ///
    /// A `Training` is strictly associated with a `Model` type. One could
    /// even argue that an instance of `Training` strictly belongs to an
    /// instance of `Model`
    type Model : Model;

    /// Changes `model`s coefficents so they minimize the `cost` function (hopefully)
    fn teach_event<C>(
        &mut self, cost : &C, model : &mut Self::Model,
        features : &<Self::Model as Model>::Input,
        truth : <Self::Model as Model>::Target
    ) where
        C : Cost<Error=<Self::Model as Model>::Target>;
}

/// `Teachers` are used to train `Models`, based on events and a `Cost` function
pub trait Teacher<M : Model>{

    /// Contains state which changes during the training, but is not required by the expert
    ///
    /// Examples are the velocity of the coefficents (in SGD) or the number of events already learned.
    /// This may also be empty
    type Training : Training<Model=M>;

    /// Creates a new `Training` object to hold training state
    fn new_training(&self, model : &M) -> Self::Training;
}

/// Teaches `model` all events in `history`
pub fn teach_history<M, C, T, H>(teacher : &T, cost : &C, model : &mut M, history : H)
    where M : Model,
    C : Cost<Error=M::Target>,
    T : Teacher<M>,
    H : IntoIterator<Item=(M::Input, M::Target)>
{
    let mut training = teacher.new_training(&model);
    for (features, truth) in history{

        training.teach_event(cost, model, &features, truth);
    }
}

/// Implementations of `Model` trait
pub mod model;
/// Implementations of `Cost` trait
pub mod cost;
/// Implementations of `Training` trait
///
/// You can use `Teacher`s to create `Training` instances
pub mod training;
/// Implementatios of `Teacher` trait
pub mod teacher;
/// Defines linear algebra traits used for some model parameters
pub mod linear_algebra;

#[cfg(test)]
mod tests {

    #[test]
    fn estimate_median() {

        use model::Constant;
        use cost::LeastAbsoluteDeviation;
        use training::GradientDescentAl;
        use Training;

        let features = ();
        let history = [1.0, 3.0, 4.0, 7.0, 8.0, 11.0, 29.0]; //median is seven

        let cost = LeastAbsoluteDeviation{};
        let mut model = Constant::new(0.0);

        let mut training = GradientDescentAl{ l0 : 0.9, t : 9.0, learned_events : 0.0 };

        for &truth in history.iter().cycle().take(150){

            training.teach_event(&cost, &mut model, &features, truth);
            println!("model: {:?}, learning_rate: {:?}", model, training.learning_rate());
        }

        assert!(model.c < 7.1);
        assert!(model.c > 6.9);
    }

    #[test]
    fn estimate_mean() {

        use model::Constant;
        use cost::LeastSquares;
        use training::GradientDescentAl;
        use Training;

        let features = ();
        let history = [1f64, 3.0, 4.0, 7.0, 8.0, 11.0, 29.0]; //mean is 9

        let cost = LeastSquares{};
        let mut model = Constant::new(0.0);

        let mut training = GradientDescentAl{ l0 : 0.3, t : 4.0, learned_events : 0.0 };

        for &truth in history.iter().cycle().take(100){

            training.teach_event(&cost, &mut model, &features, truth);
            println!("model: {:?}, learning_rate: {:?}", model, training.learning_rate());
        }

        assert!(model.c < 9.1);
        assert!(model.c > 8.9);
    }

    #[test]
    fn linear_stochastic_gradient_descent() {

        use cost::LeastSquares;
        use model::Linear;
        use teacher::GradientDescent;
        use teach_history;

        let history = [(0f64, 3f64), (1.0, 4.0), (2.0, 5.0)];

        let mut model = Linear{m : 0.0, c : 0.0};

        let teacher = GradientDescent{ learning_rate : 0.2 };

        let cost = LeastSquares{};
        teach_history(&teacher, &cost, &mut model, history.iter().cycle().take(20).cloned());

        assert!(model.m < 1.1);
        assert!(model.m > 0.9);
        assert!(model.c < 3.1);
        assert!(model.c > 2.9);
    }

    #[test]
    fn linear_stochastic_gradient_descent_iter() {

        use model::Linear;
        use cost::LeastSquares;
        use teacher::GradientDescent;
        use Teacher;
        use Training;

        let history = [(0f64, 3f64), (1.0, 4.0), (2.0, 5.0)];

        let cost = LeastSquares{};
        let mut model = Linear{m : 0.0, c : 0.0};

        let teacher = GradientDescent{ learning_rate : 0.2 };
        let mut training = teacher.new_training(&model);

        for &(features, truth) in history.iter().cycle().take(20){

            training.teach_event(&cost, &mut model, &features, truth);
            println!("model: {:?}", model);
        }

        assert!(model.m < 1.1);
        assert!(model.m > 0.9);
        assert!(model.c < 3.1);
        assert!(model.c > 2.9);
    }

    #[test]
    fn linear_sgd_2d(){
        use cost::LeastSquares;
        use model::Linear;
        use teacher::Momentum;
        use teach_history;

        let history = [([0.0, 7.0], 17.0), ([1.0, 2.0], 8.0), ([2.0, -2.0], 1.0)];
        let mut model = Linear{m : [0.0, 0.0], c : 0.0};
        let cost = LeastSquares{};
        let teacher = Momentum{ l0 : 0.01, t : 10000000.0, inertia : 0.9 };

        teach_history(&teacher, &cost, &mut model, history.iter().cycle().take(15000).cloned());

        println!("{:?}", model);

        assert!(model.m[0] < 1.1);
        assert!(model.m[0] > 0.9);
        assert!(model.m[1] < 2.1);
        assert!(model.m[1] > 1.9);
        assert!(model.c < 3.1);
        assert!(model.c > 2.9);
    }

    #[test]
    fn logistic_sgd_2d_least_squares(){
        use cost::LeastSquares;
        use model::{Logicstic, Linear};
        use teacher::GradientDescent;
        use teach_history;

        use Model;

        let history = [
            ([2.7, 2.5], 0.0),
            ([1.4, 2.3], 0.0),
            ([3.3, 4.4], 0.0),
            ([1.3, 1.8], 0.0),
            ([3.0, 3.0], 0.0),
            ([7.6, 2.7], 1.0),
            ([5.3, 2.0], 1.0),
            ([6.9, 1.7], 1.0),
            ([8.6, -0.2], 1.0),
            ([7.6, 3.5], 1.0)
        ];

        let mut model = Logicstic{ linear: Linear{m : [0.0, 0.0], c : 0.0}};
        let teacher = GradientDescent{ learning_rate : 0.3 };
        let cost = LeastSquares{};

        teach_history(
            &teacher, &cost, &mut model,
            history.iter().cycle().take(40).cloned(),
        );

        println!("{:?}", model.linear);

        let classification_errors = history.iter()
            .map(|&(input, truth)| model.predict(&input).round() == truth)
            .fold(0, |errors, correct| if correct { errors } else { errors + 1 });

        assert_eq!(0, classification_errors);
    }

    #[test]
    fn logistic_sgd_2d_max_likelihood(){
        use cost::MaxLikelihood;
        use model::{Logicstic, Linear};
        use teacher::GradientDescent;
        use teach_history;
        use Model;

        let history = [
            ([2.7, 2.5], 0.0),
            ([1.4, 2.3], 0.0),
            ([3.3, 4.4], 0.0),
            ([1.3, 1.8], 0.0),
            ([3.0, 3.0], 0.0),
            ([7.6, 2.7], 1.0),
            ([5.3, 2.0], 1.0),
            ([6.9, 1.7], 1.0),
            ([8.6, -0.2], 1.0),
            ([7.6, 3.5], 1.0)
        ];

        let mut model = Logicstic{ linear: Linear{m : [0.0, 0.0], c : 0.0}};
        let teacher = GradientDescent{ learning_rate : 0.3 };
        let cost = MaxLikelihood{};

        teach_history(
            &teacher, &cost, &mut model,
            history.iter().cycle().take(20).cloned(),
        );

        println!("{:?}", model.linear);

        let classification_errors = history.iter()
            .map(|&(input, truth)| model.predict(&input).round() == truth)
            .fold(0, |errors, correct| if correct { errors } else { errors + 1 });

        assert_eq!(0, classification_errors);
    }
}
<commit_msg>fixed Logistic typo in tests<commit_after>//! A machine learning library for supervised regression trainings
//!
//! This library wants to enable its user to write teachers
//! independent of the model trained or the cost function tried to
//! minimize.
//! Consequently its three most important traits are `Model`, `Cost`
//! and `Training`.
//!
//! # Examples
//!
//! Estimate mean
//!
//! ```
//! use vikos::{model, cost, teacher, teach_history, Model};
//! //We are only training a constant so our feature vector is empty
//! //Any other vector would be fine, too.
//! let features = ();
//! let history = [1f64, 3.0, 4.0, 7.0, 8.0, 11.0, 29.0]; //mean is 9
//! // Give each 'truth' its feature vector and stretch our history to 100 elements
//! let history = history.iter().cycle().map(|&y|((),y)).take(100);
//!
//! let cost = cost::LeastSquares{};
//! let mut model = model::Constant::new(0.0);
//!
//! let teacher = teacher::GradientDescentAl{ l0 : 0.3, t : 4.0 };
//! teach_history(&teacher, &cost, &mut model, history);
//! println!("{}", model.predict(&features));
//! ```
#![warn(missing_docs)]

extern crate num;

use std::iter::IntoIterator;
use num::Float;

/// A Model is defines how to predict a target from an input
///
/// A model usually depends on several coefficents whose values
/// are derived using a training algorithm
pub trait Model : Clone{
    /// Input features
    type Input;
    /// Target type
    type Target : Float;

    /// Predicts a target for the inputs based on the internal coefficents
    fn predict(&self, &Self::Input) -> Self::Target;

    /// The number of internal coefficents this model depends on
    fn num_coefficents(&self) -> usize;

    /// Value predict derived by the n-th `coefficent` at `input`
    fn gradient(&self, coefficent : usize, input : &Self::Input) -> Self::Target;

    /// Mutable reference to the n-th `coefficent`
    fn coefficent(& mut self, coefficent : usize) -> & mut Self::Target;
}

/// Cost functions those value is supposed be minimized by the training algorithm
pub trait Cost{

    /// Error type used by the cost function
    ///
    /// Usually `f64` or `f32`
    type Error : Float;

    /// Value of the cost function derived by the n-th coefficent at x expressed in Error(x) and dY(x)/dx
    ///
    /// This method is called by SGD based training algorithm in order to
    /// determine the delta of the coefficents
    fn gradient(&self, prediction : Self::Error, truth : Self::Error, gradient_error_by_coefficent : Self::Error) -> Self::Error;
}

/// Teaches event to a `Model`
pub trait Training{

    /// `Model` changed by this `Training`
    ///
    /// A `Training` is strictly associated with a `Model` type. One could
    /// even argue that an instance of `Training` strictly belongs to an
    /// instance of `Model`
    type Model : Model;

    /// Changes `model`s coefficents so they minimize the `cost` function (hopefully)
    fn teach_event<C>(
        &mut self, cost : &C, model : &mut Self::Model,
        features : &<Self::Model as Model>::Input,
        truth : <Self::Model as Model>::Target
    ) where
        C : Cost<Error=<Self::Model as Model>::Target>;
}

/// `Teachers` are used to train `Models`, based on events and a `Cost` function
pub trait Teacher<M : Model>{

    /// Contains state which changes during the training, but is not required by the expert
    ///
    /// Examples are the velocity of the coefficents (in SGD) or the number of events already learned.
    /// This may also be empty
    type Training : Training<Model=M>;

    /// Creates a new `Training` object to hold training state
    fn new_training(&self, model : &M) -> Self::Training;
}

/// Teaches `model` all events in `history`
pub fn teach_history<M, C, T, H>(teacher : &T, cost : &C, model : &mut M, history : H)
    where M : Model,
    C : Cost<Error=M::Target>,
    T : Teacher<M>,
    H : IntoIterator<Item=(M::Input, M::Target)>
{
    let mut training = teacher.new_training(&model);
    for (features, truth) in history{

        training.teach_event(cost, model, &features, truth);
    }
}

/// Implementations of `Model` trait
pub mod model;
/// Implementations of `Cost` trait
pub mod cost;
/// Implementations of `Training` trait
///
/// You can use `Teacher`s to create `Training` instances
pub mod training;
/// Implementatios of `Teacher` trait
pub mod teacher;
/// Defines linear algebra traits used for some model parameters
pub mod linear_algebra;

#[cfg(test)]
mod tests {

    #[test]
    fn estimate_median() {

        use model::Constant;
        use cost::LeastAbsoluteDeviation;
        use training::GradientDescentAl;
        use Training;

        let features = ();
        let history = [1.0, 3.0, 4.0, 7.0, 8.0, 11.0, 29.0]; //median is seven

        let cost = LeastAbsoluteDeviation{};
        let mut model = Constant::new(0.0);

        let mut training = GradientDescentAl{ l0 : 0.9, t : 9.0, learned_events : 0.0 };

        for &truth in history.iter().cycle().take(150){

            training.teach_event(&cost, &mut model, &features, truth);
            println!("model: {:?}, learning_rate: {:?}", model, training.learning_rate());
        }

        assert!(model.c < 7.1);
        assert!(model.c > 6.9);
    }

    #[test]
    fn estimate_mean() {

        use model::Constant;
        use cost::LeastSquares;
        use training::GradientDescentAl;
        use Training;

        let features = ();
        let history = [1f64, 3.0, 4.0, 7.0, 8.0, 11.0, 29.0]; //mean is 9

        let cost = LeastSquares{};
        let mut model = Constant::new(0.0);

        let mut training = GradientDescentAl{ l0 : 0.3, t : 4.0, learned_events : 0.0 };

        for &truth in history.iter().cycle().take(100){

            training.teach_event(&cost, &mut model, &features, truth);
            println!("model: {:?}, learning_rate: {:?}", model, training.learning_rate());
        }

        assert!(model.c < 9.1);
        assert!(model.c > 8.9);
    }

    #[test]
    fn linear_stochastic_gradient_descent() {

        use cost::LeastSquares;
        use model::Linear;
        use teacher::GradientDescent;
        use teach_history;

        let history = [(0f64, 3f64), (1.0, 4.0), (2.0, 5.0)];

        let mut model = Linear{m : 0.0, c : 0.0};

        let teacher = GradientDescent{ learning_rate : 0.2 };

        let cost = LeastSquares{};
        teach_history(&teacher, &cost, &mut model, history.iter().cycle().take(20).cloned());

        assert!(model.m < 1.1);
        assert!(model.m > 0.9);
        assert!(model.c < 3.1);
        assert!(model.c > 2.9);
    }

    #[test]
    fn linear_stochastic_gradient_descent_iter() {

        use model::Linear;
        use cost::LeastSquares;
        use teacher::GradientDescent;
        use Teacher;
        use Training;

        let history = [(0f64, 3f64), (1.0, 4.0), (2.0, 5.0)];

        let cost = LeastSquares{};
        let mut model = Linear{m : 0.0, c : 0.0};

        let teacher = GradientDescent{ learning_rate : 0.2 };
        let mut training = teacher.new_training(&model);

        for &(features, truth) in history.iter().cycle().take(20){

            training.teach_event(&cost, &mut model, &features, truth);
            println!("model: {:?}", model);
        }

        assert!(model.m < 1.1);
        assert!(model.m > 0.9);
        assert!(model.c < 3.1);
        assert!(model.c > 2.9);
    }

    #[test]
    fn linear_sgd_2d(){
        use cost::LeastSquares;
        use model::Linear;
        use teacher::Momentum;
        use teach_history;

        let history = [([0.0, 7.0], 17.0), ([1.0, 2.0], 8.0), ([2.0, -2.0], 1.0)];
        let mut model = Linear{m : [0.0, 0.0], c : 0.0};
        let cost = LeastSquares{};
        let teacher = Momentum{ l0 : 0.01, t : 10000000.0, inertia : 0.9 };

        teach_history(&teacher, &cost, &mut model, history.iter().cycle().take(15000).cloned());

        println!("{:?}", model);

        assert!(model.m[0] < 1.1);
        assert!(model.m[0] > 0.9);
        assert!(model.m[1] < 2.1);
        assert!(model.m[1] > 1.9);
        assert!(model.c < 3.1);
        assert!(model.c > 2.9);
    }

    #[test]
    fn logistic_sgd_2d_least_squares(){
        use cost::LeastSquares;
        use model::{Logistic, Linear};
        use teacher::GradientDescent;
        use teach_history;

        use Model;

        let history = [
            ([2.7, 2.5], 0.0),
            ([1.4, 2.3], 0.0),
            ([3.3, 4.4], 0.0),
            ([1.3, 1.8], 0.0),
            ([3.0, 3.0], 0.0),
            ([7.6, 2.7], 1.0),
            ([5.3, 2.0], 1.0),
            ([6.9, 1.7], 1.0),
            ([8.6, -0.2], 1.0),
            ([7.6, 3.5], 1.0)
        ];

        let mut model = Logistic{ linear: Linear{m : [0.0, 0.0], c : 0.0}};
        let teacher = GradientDescent{ learning_rate : 0.3 };
        let cost = LeastSquares{};

        teach_history(
            &teacher, &cost, &mut model,
            history.iter().cycle().take(40).cloned(),
        );

        println!("{:?}", model.linear);

        let classification_errors = history.iter()
            .map(|&(input, truth)| model.predict(&input).round() == truth)
            .fold(0, |errors, correct| if correct { errors } else { errors + 1 });

        assert_eq!(0, classification_errors);
    }

    #[test]
    fn logistic_sgd_2d_max_likelihood(){
        use cost::MaxLikelihood;
        use model::{Logistic, Linear};
        use teacher::GradientDescent;
        use teach_history;
        use Model;

        let history = [
            ([2.7, 2.5], 0.0),
            ([1.4, 2.3], 0.0),
            ([3.3, 4.4], 0.0),
            ([1.3, 1.8], 0.0),
            ([3.0, 3.0], 0.0),
            ([7.6, 2.7], 1.0),
            ([5.3, 2.0], 1.0),
            ([6.9, 1.7], 1.0),
            ([8.6, -0.2], 1.0),
            ([7.6, 3.5], 1.0)
        ];

        let mut model = Logistic{ linear: Linear{m : [0.0, 0.0], c : 0.0}};
        let teacher = GradientDescent{ learning_rate : 0.3 };
        let cost = MaxLikelihood{};

        teach_history(
            &teacher, &cost, &mut model,
            history.iter().cycle().take(20).cloned(),
        );

        println!("{:?}", model.linear);

        let classification_errors = history.iter()
            .map(|&(input, truth)| model.predict(&input).round() == truth)
            .fold(0, |errors, correct| if correct { errors } else { errors + 1 });

        assert_eq!(0, classification_errors);
    }
}
<|endoftext|>


<commit_before>#![feature(custom_derive, quote, plugin, plugin_registrar, rustc_private)]

#[macro_use]
extern crate rustc;
#[macro_use]
extern crate syntax;

use syntax::ext::base::MultiDecorator;
use syntax::parse::token;
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::ptr::P;
use rustc::plugin::Registry;

fn expand_derive_tojson(ct: &mut ExtCtxt, span: Span, _: &ast::MetaItem,
                        item: &Annotatable, push: &mut FnMut(Annotatable)) {
    if let Annotatable::Item(ref item) = *item {
        if let ast::ItemStruct(ref struct_def, _) = item.node {
            let struct_name = item.ident;
            let conv_body: Vec<P<ast::Expr>> = struct_def.fields.iter().map(|field| {
                if let ast::NamedField(name, _) = field.node.kind {
                    let name_str = name.name.as_str();
                    quote_expr!(ct, {
                        __container.insert($name_str.to_string(), self.$name.to_json());
                    })
                } else {
                    ct.span_fatal(span, "#[derive(ToJson)] doesn't support simple struct for now");
                }
            }).collect();
            let impl_item = quote_item!(ct,
                                        #[automatically_derived]
                                        impl ::rustc_serialize::json::ToJson for $struct_name {
                                            fn to_json(&self) -> ::rustc_serialize::json::Json {
                                                let mut __container = ::std::collections::BTreeMap::new();
                                                $conv_body;
                                                ::rustc_serialize::json::Json::Object(__container)
                                            }
                                        }).unwrap();

            push(Annotatable::Item(impl_item));
        } else {
            ct.span_err(span, "#[derive(ToJson)] is only valid for struct");
        }
    } else {
        ct.span_err(span, "#[derive(ToJson)] is only valid for struct");
    }

}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
        token::intern("derive_ToJson"),
        MultiDecorator(Box::new(expand_derive_tojson)));
}
<commit_msg>Update for upstream syntax<commit_after>#![feature(custom_derive, quote, plugin, plugin_registrar, rustc_private)]

#[macro_use]
extern crate rustc;
#[macro_use]
extern crate syntax;

use syntax::ext::base::MultiDecorator;
use syntax::parse::token;
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::ptr::P;
use rustc::plugin::Registry;

fn expand_derive_tojson(ct: &mut ExtCtxt, span: Span, _: &ast::MetaItem,
                        item: &Annotatable, push: &mut FnMut(Annotatable)) {
    if let Annotatable::Item(ref item) = *item {
        if let ast::ItemStruct(ref struct_def, _) = item.node {
            let struct_name = item.ident;
            let conv_body: Vec<P<ast::Expr>> = struct_def.fields().map(|field| {
                if let ast::NamedField(name, _) = field.node.kind {
                    let name_str = name.name.as_str();
                    quote_expr!(ct, {
                        __container.insert($name_str.to_string(), self.$name.to_json());
                    })
                } else {
                    ct.span_fatal(span, "#[derive(ToJson)] doesn't support simple struct for now");
                }
            }).collect();
            let impl_item = quote_item!(ct,
                                        #[automatically_derived]
                                        impl ::rustc_serialize::json::ToJson for $struct_name {
                                            fn to_json(&self) -> ::rustc_serialize::json::Json {
                                                let mut __container = ::std::collections::BTreeMap::new();
                                                $conv_body;
                                                ::rustc_serialize::json::Json::Object(__container)
                                            }
                                        }).unwrap();

            push(Annotatable::Item(impl_item));
        } else {
            ct.span_err(span, "#[derive(ToJson)] is only valid for struct");
        }
    } else {
        ct.span_err(span, "#[derive(ToJson)] is only valid for struct");
    }

}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
        token::intern("derive_ToJson"),
        MultiDecorator(Box::new(expand_derive_tojson)));
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate nix;
#[macro_use]
extern crate serde;
extern crate serde_json;
extern crate toml;

mod applog;
mod buffer;
mod connection;
mod cstructs;
mod error;
mod message;
mod statics;
mod structs;

use buffer::Buffer;
use connection::Connection;
use cstructs::{Group, Passwd, Spwd};
use message::ClientMessage as CMsg;
use message::*;
use nix::errno::Errno;
use std::ffi::CStr;
use std::process;
use std::string::String;

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

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

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

macro_rules! succeed {
    () => {{
        log::debug!("Success!");
        return libc::c_int::from(NssStatus::Success);
    }};
}

macro_rules! fail {
    ($err_no_p:ident, $err_no:expr, $return_val:expr) => {{
        *$err_no_p = $err_no as libc::c_int;
        log::debug!("Faill!");
        return libc::c_int::from($return_val);
    }};
}

macro_rules! try_unwrap {
    ($getter:expr) => {{
        match $getter {
            Ok(ret) => {
                log::debug!("Ok: {:?}", ret);
                ret
            }
            Err(e) => {
                log::debug!("failed (will retry): {:?}", e);
                return libc::c_int::from(NssStatus::TryAgain);
            }
        }
    }};
    ($getter:expr, $err_no_p:ident) => {{
        match $getter {
            Ok(ret) => ret,
            Err(e) => {
                log::debug!("failed (will retry): {:?}", e);
                *$err_no_p = Errno::EAGAIN as libc::c_int;
                return libc::c_int::from(NssStatus::TryAgain);
            }
        }
    }};
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwnam_r(cnameptr: *const libc::c_char, pwptr: *mut Passwd,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getpwnam_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Nam(string_from(cnameptr)))), errnop);
    if let DaemonMessage::Pw { login,
                               uid,
                               gid,
                               home,
                               sh, } = msg
    {
        match { (*pwptr).pack_args(&mut buffer, &login, uid, gid, &home, &sh) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwuid_r(uid: libc::uid_t, pwptr: *mut Passwd, buf: *mut libc::c_char,
                                                 buflen: libc::size_t, errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getpwuid_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Uid(uid as u64))), errnop);
    if let DaemonMessage::Pw { login,
                               uid,
                               gid,
                               home,
                               sh, } = msg
    {
        match { (*pwptr).pack_args(&mut buffer, &login, uid, gid, &home, &sh) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setpwent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_setpwent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Ent(Ent::Set(process::id())))));
    if let DaemonMessage::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::TryAgain)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwent_r(pwptr: *mut Passwd, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getpwent_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Ent(Ent::Get(process::id())))), errnop);
    if let DaemonMessage::Pw { login,
                               uid,
                               gid,
                               home,
                               sh, } = msg
    {
        match { (*pwptr).pack_args(&mut buffer, &login, uid, gid, &home, &sh) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endpwent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_endpwent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Ent(Ent::End(process::id())))));
    if let DaemonMessage::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::TryAgain)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getspnam_r(cnameptr: *const libc::c_char, spptr: *mut Spwd,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getspnam_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Sp(Sp::Nam(string_from(cnameptr)))), errnop);
    if let DaemonMessage::Sp { login, pass } = msg {
        match { (*spptr).pack_args(&mut buffer, &login, &pass) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setspent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_setspent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Sp(Sp::Ent(Ent::Set(process::id())))));
    if let DaemonMessage::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getspent_r(spptr: *mut Spwd, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getspent_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Sp(Sp::Ent(Ent::Get(process::id())))), errnop);
    if let DaemonMessage::Sp { login, pass } = msg {
        match { (*spptr).pack_args(&mut buffer, &login, &pass) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endspent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_endspent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Sp(Sp::Ent(Ent::End(process::id())))));
    if let DaemonMessage::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::TryAgain)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrgid_r(gid: libc::gid_t, grptr: *mut Group, buf: *mut libc::c_char,
                                                 buflen: libc::size_t, errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getgrgid_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Gid(gid as u64))), errnop);
    if let DaemonMessage::Gr { sector } = msg {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        match { (*grptr).pack_args(&mut buffer, &sector.get_group(), u64::from(gid), &members) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrnam_r(cnameptr: *const libc::c_char, grptr: *mut Group,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getgrnam_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Nam(string_from(cnameptr)))), errnop);
    if let DaemonMessage::Gr { sector } = msg {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        match { (*grptr).pack_args(&mut buffer, &sector.get_group(), sector.get_gid(), &members) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setgrent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_setgrent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Ent(Ent::Set(process::id())))));
    if let DaemonMessage::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrent_r(grptr: *mut Group, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getgrent_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Ent(Ent::Get(process::id())))), errnop);
    if let DaemonMessage::Gr { sector } = msg {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        match { (*grptr).pack_args(&mut buffer, &sector.get_group(), sector.get_gid(), &members) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endgrent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_endgrent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Ent(Ent::End(process::id())))));
    if let DaemonMessage::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::TryAgain)
}
<commit_msg>Tweak<commit_after>#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate nix;
#[macro_use]
extern crate serde;
extern crate serde_json;
extern crate toml;

mod applog;
mod buffer;
mod connection;
mod cstructs;
mod error;
mod message;
mod statics;
mod structs;

use buffer::Buffer;
use connection::Connection;
use cstructs::{Group, Passwd, Spwd};
use message::{ClientMessage as CMsg, DaemonMessage as DMsg, Ent, Gr, Pw, Sp};
use nix::errno::Errno;
use std::ffi::CStr;
use std::process;
use std::string::String;

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

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

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

macro_rules! succeed {
    () => {{
        log::debug!("Success!");
        return libc::c_int::from(NssStatus::Success);
    }};
}

macro_rules! fail {
    ($err_no_p:ident, $err_no:expr, $return_val:expr) => {{
        *$err_no_p = $err_no as libc::c_int;
        log::debug!("Faill!");
        return libc::c_int::from($return_val);
    }};
}

macro_rules! try_unwrap {
    ($getter:expr) => {{
        match $getter {
            Ok(ret) => {
                log::debug!("Ok: {:?}", ret);
                ret
            }
            Err(e) => {
                log::debug!("failed (will retry): {:?}", e);
                return libc::c_int::from(NssStatus::TryAgain);
            }
        }
    }};
    ($getter:expr, $err_no_p:ident) => {{
        match $getter {
            Ok(ret) => ret,
            Err(e) => {
                log::debug!("failed (will retry): {:?}", e);
                *$err_no_p = Errno::EAGAIN as libc::c_int;
                return libc::c_int::from(NssStatus::TryAgain);
            }
        }
    }};
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwnam_r(cnameptr: *const libc::c_char, pwptr: *mut Passwd,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getpwnam_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Nam(string_from(cnameptr)))), errnop);
    if let DMsg::Pw { login,
                      uid,
                      gid,
                      home,
                      sh, } = msg
    {
        match { (*pwptr).pack_args(&mut buffer, &login, uid, gid, &home, &sh) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwuid_r(uid: libc::uid_t, pwptr: *mut Passwd, buf: *mut libc::c_char,
                                                 buflen: libc::size_t, errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getpwuid_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Uid(uid as u64))), errnop);
    if let DMsg::Pw { login,
                      uid,
                      gid,
                      home,
                      sh, } = msg
    {
        match { (*pwptr).pack_args(&mut buffer, &login, uid, gid, &home, &sh) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setpwent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_setpwent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Ent(Ent::Set(process::id())))));
    if let DMsg::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::TryAgain)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwent_r(pwptr: *mut Passwd, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getpwent_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Ent(Ent::Get(process::id())))), errnop);
    if let DMsg::Pw { login,
                      uid,
                      gid,
                      home,
                      sh, } = msg
    {
        match { (*pwptr).pack_args(&mut buffer, &login, uid, gid, &home, &sh) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endpwent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_endpwent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Pw(Pw::Ent(Ent::End(process::id())))));
    if let DMsg::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::TryAgain)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getspnam_r(cnameptr: *const libc::c_char, spptr: *mut Spwd,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getspnam_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Sp(Sp::Nam(string_from(cnameptr)))), errnop);
    if let DMsg::Sp { login, pass } = msg {
        match { (*spptr).pack_args(&mut buffer, &login, &pass) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setspent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_setspent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Sp(Sp::Ent(Ent::Set(process::id())))));
    if let DMsg::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getspent_r(spptr: *mut Spwd, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getspent_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Sp(Sp::Ent(Ent::Get(process::id())))), errnop);
    if let DMsg::Sp { login, pass } = msg {
        match { (*spptr).pack_args(&mut buffer, &login, &pass) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endspent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_endspent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Sp(Sp::Ent(Ent::End(process::id())))));
    if let DMsg::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::TryAgain)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrgid_r(gid: libc::gid_t, grptr: *mut Group, buf: *mut libc::c_char,
                                                 buflen: libc::size_t, errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getgrgid_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Gid(gid as u64))), errnop);
    if let DMsg::Gr { sector } = msg {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        match { (*grptr).pack_args(&mut buffer, &sector.get_group(), u64::from(gid), &members) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrnam_r(cnameptr: *const libc::c_char, grptr: *mut Group,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getgrnam_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Nam(string_from(cnameptr)))), errnop);
    if let DMsg::Gr { sector } = msg {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        match { (*grptr).pack_args(&mut buffer, &sector.get_group(), sector.get_gid(), &members) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setgrent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_setgrent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Ent(Ent::Set(process::id())))));
    if let DMsg::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrent_r(grptr: *mut Group, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let conn = try_unwrap!(Connection::new("_nss_sectora_getgrent_r"), errnop);
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Ent(Ent::Get(process::id())))), errnop);
    if let DMsg::Gr { sector } = msg {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        match { (*grptr).pack_args(&mut buffer, &sector.get_group(), sector.get_gid(), &members) } {
            Ok(_) => succeed!(),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endgrent() -> libc::c_int {
    let conn = try_unwrap!(Connection::new("_nss_sectora_endgrent"));
    let msg = try_unwrap!(conn.communicate(CMsg::Gr(Gr::Ent(Ent::End(process::id())))));
    if let DMsg::Success = msg {
        return libc::c_int::from(NssStatus::Success);
    }
    libc::c_int::from(NssStatus::TryAgain)
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]

extern crate time;
extern crate conduit;
extern crate middleware = "conduit-middleware";

use std::str::{MaybeOwned, Slice, Owned};
use std::fmt::Show;
use std::io::util::NullReader;
use time::{Tm, strptime};
use conduit::Request;
use middleware::Middleware;

type Response = Result<conduit::Response, Box<Show + 'static>>;

pub struct ConditionalGet;

impl Middleware for ConditionalGet {
    fn after(&self, req: &mut Request, res: Response) -> Response {
        let mut res = try!(res);

        match req.method() {
            conduit::Get | conduit::Head => {
                if is_ok(&res) && is_fresh(req, &res) {
                    res.status = (304, "Not Modified");
                    res.headers.pop_equiv(&"Content-Type");
                    res.headers.pop_equiv(&"Content-Length");
                    res.body = box NullReader as Box<Reader + Send>;
                }
            },
            _ => ()
        }

        Ok(res)
    }
}

fn is_ok(response: &conduit::Response) -> bool {
    match *response {
        conduit::Response { status: (200, _), .. } => true,
        _ => false
    }
}

fn is_fresh(req: &Request, res: &conduit::Response) -> bool {
    let modified_since = req.headers().find("If-Modified-Since").map(header_val);
    let none_match     = req.headers().find("If-None-Match").map(header_val);

    if modified_since.as_ref().or_else(|| none_match.as_ref()).is_none() {
        return false;
    }

    let mut success = true;

    modified_since.and_then(|modified_since| {
        parse_http_date(modified_since).map_err(|_| { success = false; }).ok()
    }).map(|parsed| {
        success = success && is_modified_since(parsed, res);
    });

    none_match.map(|none_match| {
        success = success && etag_matches(none_match, res);
    });

    success
}

fn etag_matches<S: Str>(none_match: S, res: &conduit::Response) -> bool {
    res.headers.find_equiv(&"ETag").map(|etag| {
        res_header_val(etag).as_slice() == none_match.as_slice()
    }).unwrap_or(false)
}

fn is_modified_since(modified_since: Tm, res: &conduit::Response) -> bool {
    res.headers.find_equiv(&"Last-Modified").and_then(|last_modified| {
        parse_http_date(res_header_val(last_modified).as_slice()).ok()
    }).map(|last_modified| {
        modified_since.to_timespec() >= last_modified.to_timespec()
    }).unwrap_or(false)
}

fn header_val<'a>(header: Vec<&'a str>) -> MaybeOwned<'a> {
    if header.len() == 1 {
        Slice(header[0])
    } else {
        Owned(header.concat())
    }
}

fn res_header_val<'a>(header: &'a Vec<String>) -> MaybeOwned<'a> {
    if header.len() == 1 {
        Slice(header[0].as_slice())
    } else {
        Owned(header.concat())
    }
}

fn parse_http_date<S: Str>(string: S) -> Result<Tm, ()> {
    let string = string.as_slice();

    parse_rfc1123(string)
        .or_else(|_| parse_rfc850(string))
        .or_else(|_| parse_asctime(string))
        .map_err(|_| ())
}

fn parse_rfc1123(string: &str) -> Result<Tm, String> {
    time::strptime(string, "%a, %d %b %Y %T GMT")
}

fn parse_rfc850(string: &str) -> Result<Tm, String> {
    time::strptime(string, "%a, %d-%m-%y %T GMT")
}

fn parse_asctime(string: &str) -> Result<Tm, String> {
    time::strptime(string, "%a %m%t%d %T %Y")
}

#[cfg(test)]
mod tests {
    extern crate test = "conduit-test";

    use std::fmt::Show;
    use std::io::MemReader;
    use std::collections::HashMap;
    use time;
    use time::Tm;
    use conduit;
    use conduit::{Request, Response, Handler};
    use middleware::MiddlewareBuilder;

    use super::ConditionalGet;

    macro_rules! returning(
        ($code:expr, $($header:expr => $value:expr),+) => ({
            let mut headers = HashMap::new();
            $(headers.insert($header.to_string(), vec!($value.to_string()));)+
            let handler = SimpleHandler::new(headers, $code, "hello");
            let mut stack = MiddlewareBuilder::new(handler);
            stack.add(ConditionalGet);
            stack
        });
        ($($header:expr => $value:expr),+) => ({
            returning!((200, "OK"), $($header => $value),+)
        })
    )

    macro_rules! request(
        ($($header:expr => $value:expr),+) => ({
            let mut req = test::MockRequest::new(conduit::Get, "/");
            $(req.header($header, $value.to_string());)+
            req
        })
    )

    #[test]
    fn test_sends_304() {
        let handler = returning!("Last-Modified" => httpdate(time::now()));
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_304_if_older_than_now() {
        let handler = returning!("Last-Modified" => before_now());
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_304_with_etag() {
        let handler = returning!("ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-None-Match" => "1234"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_200_with_fresh_time_but_not_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "4321"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_200_with_fresh_etag_but_not_time() {
        let handler = returning!("Last-Modified" => now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now(),
            "If-None-Match" => "1234"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_200_with_fresh_etag() {
        let handler = returning!("ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-None-Match" => "4321"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_200_with_fresh_time() {
        let handler = returning!("Last-Modified" => now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now()
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_304_with_fresh_time_and_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_does_not_affect_non_200() {
        let code = (302, "Found");
        let handler = returning!(code, "Last-Modified" => before_now(), "ETag" => "1234");
        expect(code, handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_does_not_affect_malformed_timestamp() {
        let bad_stamp = time::now().strftime("%Y-%m-%d %H:%M:%S %z");
        let handler = returning!("Last-Modified" => before_now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => bad_stamp
        ) as &mut conduit::Request));
    }

    fn expect_304(response: Result<Response, Box<Show>>) {
        let mut response = response.ok().expect("No response");
        let body = response.body.read_to_string().ok().expect("No body");

        assert_eq!(response.status, (304, "Not Modified"));
        assert_eq!(body.as_slice(), "");
    }

    fn expect_200(response: Result<Response, Box<Show>>) {
        expect((200, "OK"), response);
    }

    fn expect(status: (uint, &'static str), response: Result<Response, Box<Show>>) {
        let mut response = response.ok().expect("No response");
        let body = response.body.read_to_string().ok().expect("No body");

        assert_eq!(response.status, status);
        assert_eq!(body.as_slice(), "hello");
    }

    struct SimpleHandler {
        map: HashMap<String, Vec<String>>,
        status: Status,
        body: &'static str
    }

    type Status = (uint, &'static str);

    impl SimpleHandler {
        fn new(map: HashMap<String, Vec<String>>, status: Status, body: &'static str) -> SimpleHandler {
            SimpleHandler { map: map, status: status, body: body }
        }
    }

    impl Handler for SimpleHandler {
        fn call(&self, _: &mut Request) -> Result<Response, Box<Show + 'static>> {
            Ok(Response {
                status: self.status,
                headers: self.map.clone(),
                body: box MemReader::new(self.body.to_string().into_bytes()) as Box<Reader + Send>
            })
        }
    }

    fn before_now() -> String {
        let mut now = time::now();
        now.tm_year -= 1;
        httpdate(now)
    }

    fn now() -> String {
        httpdate(time::now())
    }

    fn httpdate(time: Tm) -> String {
        time.strftime("%a, %d-%m-%y %T GMT")
    }
}
<commit_msg>Update to master<commit_after>#![feature(macro_rules)]

extern crate time;
extern crate conduit;
extern crate "conduit-middleware" as middleware;

use std::str::{MaybeOwned, Slice, Owned};
use std::fmt::Show;
use std::io::util::NullReader;
use time::{Tm, strptime};
use conduit::Request;
use middleware::Middleware;

type Response = Result<conduit::Response, Box<Show + 'static>>;

pub struct ConditionalGet;

impl Middleware for ConditionalGet {
    fn after(&self, req: &mut Request, res: Response) -> Response {
        let mut res = try!(res);

        match req.method() {
            conduit::Get | conduit::Head => {
                if is_ok(&res) && is_fresh(req, &res) {
                    res.status = (304, "Not Modified");
                    res.headers.pop_equiv(&"Content-Type");
                    res.headers.pop_equiv(&"Content-Length");
                    res.body = box NullReader as Box<Reader + Send>;
                }
            },
            _ => ()
        }

        Ok(res)
    }
}

fn is_ok(response: &conduit::Response) -> bool {
    match *response {
        conduit::Response { status: (200, _), .. } => true,
        _ => false
    }
}

fn is_fresh(req: &Request, res: &conduit::Response) -> bool {
    let modified_since = req.headers().find("If-Modified-Since").map(header_val);
    let none_match     = req.headers().find("If-None-Match").map(header_val);

    if modified_since.as_ref().or_else(|| none_match.as_ref()).is_none() {
        return false;
    }

    let mut success = true;

    modified_since.and_then(|modified_since| {
        parse_http_date(modified_since).map_err(|_| { success = false; }).ok()
    }).map(|parsed| {
        success = success && is_modified_since(parsed, res);
    });

    none_match.map(|none_match| {
        success = success && etag_matches(none_match, res);
    });

    success
}

fn etag_matches<S: Str>(none_match: S, res: &conduit::Response) -> bool {
    res.headers.find_equiv(&"ETag").map(|etag| {
        res_header_val(etag).as_slice() == none_match.as_slice()
    }).unwrap_or(false)
}

fn is_modified_since(modified_since: Tm, res: &conduit::Response) -> bool {
    res.headers.find_equiv(&"Last-Modified").and_then(|last_modified| {
        parse_http_date(res_header_val(last_modified).as_slice()).ok()
    }).map(|last_modified| {
        modified_since.to_timespec() >= last_modified.to_timespec()
    }).unwrap_or(false)
}

fn header_val<'a>(header: Vec<&'a str>) -> MaybeOwned<'a> {
    if header.len() == 1 {
        Slice(header[0])
    } else {
        Owned(header.concat())
    }
}

fn res_header_val<'a>(header: &'a Vec<String>) -> MaybeOwned<'a> {
    if header.len() == 1 {
        Slice(header[0].as_slice())
    } else {
        Owned(header.concat())
    }
}

fn parse_http_date<S: Str>(string: S) -> Result<Tm, ()> {
    let string = string.as_slice();

    parse_rfc1123(string)
        .or_else(|_| parse_rfc850(string))
        .or_else(|_| parse_asctime(string))
        .map_err(|_| ())
}

fn parse_rfc1123(string: &str) -> Result<Tm, String> {
    time::strptime(string, "%a, %d %b %Y %T GMT")
}

fn parse_rfc850(string: &str) -> Result<Tm, String> {
    time::strptime(string, "%a, %d-%m-%y %T GMT")
}

fn parse_asctime(string: &str) -> Result<Tm, String> {
    time::strptime(string, "%a %m%t%d %T %Y")
}

#[cfg(test)]
mod tests {
    extern crate "conduit-test" as test;

    use std::fmt::Show;
    use std::io::MemReader;
    use std::collections::HashMap;
    use time;
    use time::Tm;
    use conduit;
    use conduit::{Request, Response, Handler};
    use middleware::MiddlewareBuilder;

    use super::ConditionalGet;

    macro_rules! returning(
        ($code:expr, $($header:expr => $value:expr),+) => ({
            let mut headers = HashMap::new();
            $(headers.insert($header.to_string(), vec!($value.to_string()));)+
            let handler = SimpleHandler::new(headers, $code, "hello");
            let mut stack = MiddlewareBuilder::new(handler);
            stack.add(ConditionalGet);
            stack
        });
        ($($header:expr => $value:expr),+) => ({
            returning!((200, "OK"), $($header => $value),+)
        })
    )

    macro_rules! request(
        ($($header:expr => $value:expr),+) => ({
            let mut req = test::MockRequest::new(conduit::Get, "/");
            $(req.header($header, $value.to_string());)+
            req
        })
    )

    #[test]
    fn test_sends_304() {
        let handler = returning!("Last-Modified" => httpdate(time::now()));
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_304_if_older_than_now() {
        let handler = returning!("Last-Modified" => before_now());
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_304_with_etag() {
        let handler = returning!("ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-None-Match" => "1234"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_200_with_fresh_time_but_not_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "4321"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_200_with_fresh_etag_but_not_time() {
        let handler = returning!("Last-Modified" => now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now(),
            "If-None-Match" => "1234"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_200_with_fresh_etag() {
        let handler = returning!("ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-None-Match" => "4321"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_200_with_fresh_time() {
        let handler = returning!("Last-Modified" => now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now()
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_sends_304_with_fresh_time_and_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_does_not_affect_non_200() {
        let code = (302, "Found");
        let handler = returning!(code, "Last-Modified" => before_now(), "ETag" => "1234");
        expect(code, handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        ) as &mut conduit::Request));
    }

    #[test]
    fn test_does_not_affect_malformed_timestamp() {
        let bad_stamp = time::now().strftime("%Y-%m-%d %H:%M:%S %z");
        let handler = returning!("Last-Modified" => before_now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => bad_stamp
        ) as &mut conduit::Request));
    }

    fn expect_304(response: Result<Response, Box<Show>>) {
        let mut response = response.ok().expect("No response");
        let body = response.body.read_to_string().ok().expect("No body");

        assert_eq!(response.status, (304, "Not Modified"));
        assert_eq!(body.as_slice(), "");
    }

    fn expect_200(response: Result<Response, Box<Show>>) {
        expect((200, "OK"), response);
    }

    fn expect(status: (uint, &'static str), response: Result<Response, Box<Show>>) {
        let mut response = response.ok().expect("No response");
        let body = response.body.read_to_string().ok().expect("No body");

        assert_eq!(response.status, status);
        assert_eq!(body.as_slice(), "hello");
    }

    struct SimpleHandler {
        map: HashMap<String, Vec<String>>,
        status: Status,
        body: &'static str
    }

    type Status = (uint, &'static str);

    impl SimpleHandler {
        fn new(map: HashMap<String, Vec<String>>, status: Status, body: &'static str) -> SimpleHandler {
            SimpleHandler { map: map, status: status, body: body }
        }
    }

    impl Handler for SimpleHandler {
        fn call(&self, _: &mut Request) -> Result<Response, Box<Show + 'static>> {
            Ok(Response {
                status: self.status,
                headers: self.map.clone(),
                body: box MemReader::new(self.body.to_string().into_bytes()) as Box<Reader + Send>
            })
        }
    }

    fn before_now() -> String {
        let mut now = time::now();
        now.tm_year -= 1;
        httpdate(now)
    }

    fn now() -> String {
        httpdate(time::now())
    }

    fn httpdate(time: Tm) -> String {
        time.strftime("%a, %d-%m-%y %T GMT")
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::convert::From;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use std::result;

use libc::{c_char, c_short, c_int};

/// The error type for SPC operations; either an io::Error, or an error
/// originating within the underlying snes_spc library. The latter case
/// is just exposed as a string containing the error message.
#[derive(Debug)]
pub enum SpcError {
    Io(io::Error),
    Internal(String)
}

impl fmt::Display for SpcError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SpcError::Io(ref e) => write!(f, "IO error: {}", e),
            SpcError::Internal(ref e) => write!(f, "Internal SPC error: {}", e),
        }
    }
}

impl error::Error for SpcError {
    fn description(&self) -> &str {
        match *self {
            SpcError::Io(ref e) => e.description(),
            SpcError::Internal(ref e) => &e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SpcError::Io(ref e) => Some(e),
            SpcError::Internal(_) => None
        }
    }
}

impl From<io::Error> for SpcError {
    fn from(err: io::Error) -> SpcError {
        SpcError::Io(err)
    }
}

impl From<String> for SpcError {
    fn from(err: String) -> SpcError {
        SpcError::Internal(err)
    }
}

/// Convert an ffi C-string into our error type. Deal with the rigmarole
/// of cloning into an owned string.
impl From<*const c_char> for SpcError {
    fn from(err: *const c_char) -> SpcError {
        unsafe {
            SpcError::Internal(String::from_utf8_lossy(
                CStr::from_ptr(err).to_bytes()).into_owned())
        }
    }
}

/// The result type for SPC operations that may fail.
pub type Result<T> = result::Result<T, SpcError>;

/// Convert a possibly null ffi C-string into our Result<()> type.
/// N.B.: this is not an implementation of `std::convert::From` because
/// Rust's coherency rules prohibit us from defining external traits for
/// `snes_spc::Result`, which is just an alias of `std::result::Result`.
/// This makes tiny kittens sad, but they try their best to understand.
fn result_from(err: *const c_char) -> Result<()> {
    if err.is_null() {
        Ok(())
    } else {
        Err(SpcError::from(err))
    }
}

/// A type for the internal library's opaque struct SNES_SPC
enum SpcHandle {}

#[link(name = "snes_spc")]
extern {
    // Instantiate a new SPC emulator and return a handle to it.
    fn spc_new() -> *mut SpcHandle;

    // Free an SPC emulator given its handle.
    fn spc_delete(spc: *mut SpcHandle);

    // Load SPC data into emulator.
    fn spc_load_spc(spc: *mut SpcHandle, data: *const u8, len: c_int)
        -> *const c_char;

    // Clear SPC echo buffer.
    fn spc_clear_echo(spc: *mut SpcHandle);

    // Emulate SPC output, recording `count` 16-bit samples to `out`
    fn spc_play(spc: *mut SpcHandle, count: c_int, out: *mut c_short)
        -> *const c_char;
}

/// A simple, safe wrapper around an opaque SNES_SPC handle.
pub struct SnesSpc {
    handle: *mut SpcHandle
}

/// SnesSpc's internal handle is never exposed except through its safe
/// implementation, so it should be save to move to another thread.
unsafe impl Send for SnesSpc {}

impl SnesSpc {
    pub fn new() -> SnesSpc {
        unsafe {
            SnesSpc {
                handle: spc_new()
            }
        }
    }

    /// Creates a new `SnesSpc` using a byte slice as its initial SPC data.
    pub fn from_data(data: &[u8]) -> Result<SnesSpc> {
        let mut spc = SnesSpc::new();
        try!(spc.load_spc(data));
        Ok(spc)
    }

    /// Creates a new SPC emulator initially loaded with the contents of a
    /// file at the given path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<SnesSpc> {
        let mut data = Vec::new();
        let mut file = try!(File::open(path));
        try!(file.read_to_end(&mut data));
        SnesSpc::from_data(&data)
    }

    /// Loads data into the SPC emulator.
    pub fn load_spc(&mut self, data: &[u8]) -> Result<()> {
        unsafe {
            result_from(spc_load_spc(
                self.handle, data.as_ptr(), data.len() as c_int))
        }
    }

    /// Clears the SPC emulator's echo buffer. This can be useful if loading
    /// SPC files that have garbage in the initial echo buffer.
    pub fn clear_echo(&mut self) {
        unsafe {
            spc_clear_echo(self.handle);
        }
    }

    /// Emulate a specified number of samples, storing them in a given buffer.
    pub fn play(&mut self, count: u32, out: &mut [i16]) -> Result<()> {
        unsafe {
            // TODO: check that out is big enough :)
            result_from(spc_play(self.handle, count as c_int, out.as_mut_ptr()))
        }
    }
}

impl Drop for SnesSpc {
    fn drop(&mut self) {
        unsafe {
            spc_delete(self.handle);
        }
    }
}

#[test]
fn can_call_unsafe_api_without_exploding() {
    unsafe {
        let spc = spc_new();
        spc_delete(spc);
    }
}

#[test]
fn can_use_safe_wrapper_without_being_stabbed() {
    let mut spc = SnesSpc::new();
    spc.clear_echo();
    // Drop it!
}
<commit_msg>Remove unnecessary count param from play(); add OddBuffSize error<commit_after>extern crate libc;

use std::convert::From;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use std::result;

use libc::{c_char, c_short, c_int};

/// The error type for SPC operations. Most of the wrapped C `snes_spc`
/// functions have the possibility of returning an error string from the
/// underlying library; in those cases the returned error is just an
/// uninterpreted copy of that string. Other members of the enumeration
/// represent errors arising from the wrapper functions themselves.
#[derive(Debug)]
pub enum SpcError {
    Internal(String),
    Io(io::Error),
    OddSizeBuffer,
}

impl fmt::Display for SpcError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SpcError::Io(ref e) => write!(f, "IO error: {}", e),
            SpcError::Internal(ref e) => write!(f, "Internal SPC error: {}", e),
            SpcError::OddSizeBuffer => write!(f, "SPC error: {}", self)
        }
    }
}

impl error::Error for SpcError {
    fn description(&self) -> &str {
        match *self {
            SpcError::Internal(ref e) => &e,
            SpcError::Io(ref e) => e.description(),
            SpcError::OddSizeBuffer => "sample buffer size not multiple of two",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SpcError::Internal(_) => None,
            SpcError::Io(ref e) => Some(e),
            SpcError::OddSizeBuffer => None,
        }
    }
}

impl From<io::Error> for SpcError {
    fn from(err: io::Error) -> SpcError {
        SpcError::Io(err)
    }
}

impl From<String> for SpcError {
    fn from(err: String) -> SpcError {
        SpcError::Internal(err)
    }
}

/// Convert an ffi C-string into our error type. Deal with the rigmarole
/// of cloning into an owned string.
impl From<*const c_char> for SpcError {
    fn from(err: *const c_char) -> SpcError {
        unsafe {
            SpcError::Internal(String::from_utf8_lossy(
                CStr::from_ptr(err).to_bytes()).into_owned())
        }
    }
}

/// The result type for SPC operations that may fail.
pub type Result<T> = result::Result<T, SpcError>;

/// Convert a possibly null ffi C-string into our Result<()> type.
/// N.B.: this is not an implementation of `std::convert::From` because
/// Rust's coherency rules prohibit us from defining external traits for
/// `snes_spc::Result`, which is just an alias of `std::result::Result`.
/// This makes tiny kittens sad, but they try their best to understand.
fn result_from(err: *const c_char) -> Result<()> {
    if err.is_null() {
        Ok(())
    } else {
        Err(SpcError::from(err))
    }
}

/// A type for the internal library's opaque struct SNES_SPC
enum SpcHandle {}

#[link(name = "snes_spc")]
extern {
    // Instantiate a new SPC emulator and return a handle to it.
    fn spc_new() -> *mut SpcHandle;

    // Free an SPC emulator given its handle.
    fn spc_delete(spc: *mut SpcHandle);

    // Load SPC data into emulator.
    fn spc_load_spc(spc: *mut SpcHandle, data: *const u8, len: c_int)
        -> *const c_char;

    // Clear SPC echo buffer.
    fn spc_clear_echo(spc: *mut SpcHandle);

    // Emulate SPC output, recording `count` 16-bit samples to `out`
    fn spc_play(spc: *mut SpcHandle, count: c_int, out: *mut c_short)
        -> *const c_char;
}

/// A simple, safe wrapper around an opaque SNES_SPC handle.
pub struct SnesSpc {
    handle: *mut SpcHandle
}

/// SnesSpc's internal handle is never exposed except through its safe
/// implementation, so it should be save to move to another thread.
unsafe impl Send for SnesSpc {}

impl SnesSpc {
    pub fn new() -> SnesSpc {
        unsafe {
            SnesSpc {
                handle: spc_new()
            }
        }
    }

    /// Creates a new `SnesSpc` using a byte slice as its initial SPC data.
    pub fn from_data(data: &[u8]) -> Result<SnesSpc> {
        let mut spc = SnesSpc::new();
        try!(spc.load_spc(data));
        Ok(spc)
    }

    /// Creates a new SPC emulator initially loaded with the contents of a
    /// file at the given path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<SnesSpc> {
        let mut data = Vec::new();
        let mut file = try!(File::open(path));
        try!(file.read_to_end(&mut data));
        SnesSpc::from_data(&data)
    }

    /// Loads data into the SPC emulator.
    pub fn load_spc(&mut self, data: &[u8]) -> Result<()> {
        unsafe {
            result_from(spc_load_spc(
                self.handle, data.as_ptr(), data.len() as c_int))
        }
    }

    /// Clears the SPC emulator's echo buffer. This can be useful if loading
    /// SPC files that have garbage in the initial echo buffer.
    pub fn clear_echo(&mut self) {
        unsafe {
            spc_clear_echo(self.handle);
        }
    }

    /// Emulate samples into the specified buffer. The number of samples
    /// emulated will be equal to the size of the buffer, which must be a
    /// multiple of two (to accommodate stereo channels).
    pub fn play(&mut self, out: &mut [i16]) -> Result<()> {
        unsafe {
            let len = out.len() as c_int;
            if len % 2 != 0 {
                Err(SpcError::OddSizeBuffer)
            } else {
                result_from(spc_play(self.handle, len, out.as_mut_ptr()))
            }
        }
    }
}

impl Drop for SnesSpc {
    fn drop(&mut self) {
        unsafe {
            spc_delete(self.handle);
        }
    }
}

#[test]
fn can_call_unsafe_api_without_exploding() {
    unsafe {
        let spc = spc_new();
        spc_delete(spc);
    }
}

#[test]
fn can_use_safe_wrapper_without_being_stabbed() {
    let mut spc = SnesSpc::new();
    spc.clear_echo();
    // Drop it!
}

#[test]
fn from_file_with_bad_path_produces_io_error() {
    match SnesSpc::from_file("") {
        Err(SpcError::Io(_)) => {},
        _ => { panic!("Expected io::Error!") }
    }
}

#[test]
fn odd_size_buffer_produces_playback_error() {
    let mut spc = SnesSpc::new();
    let mut buf = vec![0;101];
    match spc.play(&mut buf) {
        Err(SpcError::OddSizeBuffer) => {},
        _ => { panic!("Expected OddSizeBuffer error!") }
    }
}
<|endoftext|>


<commit_before>macro_rules! FSM {
    (@inner next $new_state:ident{$($new_el:ident:$new_el_val:expr),*}) => (
        $new_state{$($new_el:$new_el_val),*}
    );
    (@inner next $new_state:ident) => (
        $new_state{}
    );

    (@inner command $cur:ident;$callback:block;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
        {
            $callback;
            Some(States::$new_state{context: FSM!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
        }
    );
    (@inner command $cur:ident;$callback:block;) => ({$callback; Some(States::__SameState__)});
    (@inner command $cur:ident; ;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => ({
        Some(States::$new_state{context: FSM!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
    });
    (@inner command $cur:ident ; ;) => (Some(States::__SameState__));

    (@inner >> $sel:ident $income:block) => (
            fn enter(&self) -> Result<(), ()> {
                let $sel = self;
                $income
                Ok(())
            }
    );
    (@inner << $sel:ident $outcome:block) => (
            fn leave(&self) -> Result<(), ()> {
                let $sel = self;
                $outcome
                Ok(())
            }
    );
    (@inner >> $sel:ident) => (
            fn enter(&self) -> Result<(), ()> {
                Ok(())
            }
    );
    (@inner << $sel:ident) => (
            fn leave(&self) -> Result<(), ()> {
                Ok(())
            }
    );
    (@inner params $state:ident {$($el:ident:$typ:ty);*}) => (
        #[derive(Debug)]
        #[derive(PartialEq)]
        struct $state {$($el:$typ),*}
    );
    (@inner params $state:ident) => (
        #[derive(Debug)]
        #[derive(PartialEq)]
        struct $state {}
    );
    (@inner initial $initial:ident{$($init_field:ident:$init_val:expr),*}) => ($initial{$($init_field: $init_val),*});
    (@inner initial $initial:ident) => ($initial{});
(
    $machine:ident ($initial:ident$({$($init_field:ident:$init_val:expr),*})*)
    states[$($states:ident),*]
    commands[$($commands:ident),*]

    $(($state:ident $sel:ident$({$($el:ident:$typ:ty);*})*
        $(>> $income:block)*
        $(<< $outcome:block)*
        $($cmd:ident $($callback:block)* => $($new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*)*;)*
    ))*
) => (
    mod $machine {
    use super::*;
        trait CanDoJob {
            fn do_job(&mut self, cmd: &Commands) -> Option<States>;
            fn leave(&self) -> Result<(), ()>;
            fn enter(&self) -> Result<(), ()>;
        }

        $(
        FSM!(@inner params $state $({$($el:$typ);*})*);

        impl CanDoJob for $state {
            fn do_job(&mut self, cmd: & Commands) -> Option<States> {
                let $sel = self;
                match *cmd {
                    $(Commands::$cmd => {FSM!(@inner command self;$($callback)*;$($new_state$({$($new_el:$new_el_val),*})*)*)})*
                    _ => None
                }
            }

            FSM!(@inner >> $sel $($income)*);
            FSM!(@inner << $sel $($outcome)*);
        }
        )*

        #[derive(Debug)]
        #[derive(PartialEq)]
        enum States {
            __SameState__,
            $($states {context: $states}),*
        }

        #[derive(Debug)]
        #[derive(PartialEq)]
        pub enum Commands {
            $($commands),*
        }

        pub struct Machine {
            state: States
        }
        pub fn new() -> Machine {
            let context = FSM!(@inner initial $initial $({$($init_field: $init_val),*})*);
            context.enter().unwrap();
            Machine{state: States::$initial{context: context}}
        }

        impl Machine {
            pub fn execute(&mut self, cmd: & Commands) -> Result<(),()>{
                match {
                    match self.state {
                        States::__SameState__ => None,
                        $(States::$state{ ref mut context } => context.do_job(cmd)),*
                    }
                } {
                    Some(x) => {
                        match x {
                            States::__SameState__ => {},
                            _ => {
                                self.change_state(x)
                            }
                        };Ok(())
                    },
                    None => {println!("Wrong operation {:?} for {:?} state!", cmd, self.state); Err(())}
                }
            }
            fn change_state(&mut self, new_state: States) {
                match self.state {
                    States::__SameState__ => Ok(()),
                    $(States::$state{ ref mut context } => context.leave()),*
                }.unwrap();
                self.state = new_state;
                match self.state {
                    States::__SameState__ => Ok(()),
                    $(States::$state{ ref mut context } => context.enter()),*
                }.unwrap();
            }
        }
    }
)
}

#[cfg(test)]
mod tests {
    fn tes(x:i16) {
        println!("x:{}",x);
    }

    FSM!(
    Mach1 (New{x:0})

    states[New,InConfig,Operational]
    commands[Configure, ConfigureDone, Drop]

    ( New context{x: i16}
        >> {println!("Enter {:?}", context)}
        << {println!("Leave {:?}", context)}
        Configure     {println!("In New with context val: {}", context.x);} =>     InConfig{x:context.x+1, y:0};
        ConfigureDone => New{x:0};
    )
    ( InConfig context{x:i16; y:i16}
        >> {println!("Enter {:?}", context)}
        << {println!("Leave {:?}", context)}
        ConfigureDone {tes(context.x)}=> Operational;
    )
    ( Operational context
        >> {println!("Enter {:?}", context)}
        << {println!("Leave {:?}", context)}
        ConfigureDone =>;
        Drop => New{x:0};
    )
    );

    FSM!(
    Mach2 (State1)

    states[State1,State2,State3]
    commands[ToState1, ToState2, ToState3]

    ( State1 context
        ToState2 => State2;
    )
    ( State2 context
        ToState3 => State3;
    )
    ( State3 context
        ToState1 => State1;
    )
    );

    #[test]
    fn test1() {
        let mut m = Mach1::new();
        m.execute(&Mach1::Commands::Configure);
        m.execute(&Mach1::Commands::ConfigureDone);
        m.execute(&Mach1::Commands::Configure);
        m.execute(&Mach1::Commands::Drop);
        m.execute(&Mach1::Commands::Configure);
        m.execute(&Mach1::Commands::ConfigureDone);
        m.execute(&Mach1::Commands::ConfigureDone);
        m.execute(&Mach1::Commands::ConfigureDone);
    }

    #[test]
    #[should_panic]
    fn test2() {
        let mut m = Mach2::new();
        m.execute(&Mach2::Commands::ToState2).unwrap();
        m.execute(&Mach2::Commands::ToState3).unwrap();
        m.execute(&Mach2::Commands::ToState1).unwrap();
        m.execute(&Mach2::Commands::ToState3).unwrap();
    }
}
<commit_msg>Context binder is now optional too<commit_after>macro_rules! FSM {
    (@inner next $new_state:ident{$($new_el:ident:$new_el_val:expr),*}) => (
        $new_state{$($new_el:$new_el_val),*}
    );
    (@inner next $new_state:ident) => (
        $new_state{}
    );

    (@inner command $cur:ident;$callback:block;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
        {
            $callback;
            Some(States::$new_state{context: FSM!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
        }
    );
    (@inner command $cur:ident;$callback:block;) => ({$callback; Some(States::__SameState__)});
    (@inner command $cur:ident; ;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => ({
        Some(States::$new_state{context: FSM!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
    });
    (@inner command $cur:ident ; ;) => (Some(States::__SameState__));

    (@inner context $ss:ident $sel:ident)=>(let $sel = $ss;);
    (@inner context $ss:ident )=>();

    (@inner >> $($sel:ident)* $income:block) => (
            fn enter(&self) -> Result<(), ()> {
                FSM!(@inner context self $($sel)*);
                $income
                Ok(())
            }
    );
    (@inner << $($sel:ident)* $outcome:block) => (
            fn leave(&self) -> Result<(), ()> {
                FSM!(@inner context self $($sel)*);
                $outcome
                Ok(())
            }
    );
    (@inner >> $($sel:ident)*) => (
            fn enter(&self) -> Result<(), ()> {
                Ok(())
            }
    );
    (@inner << $($sel:ident)*) => (
            fn leave(&self) -> Result<(), ()> {
                Ok(())
            }
    );

    (@inner params $state:ident {$($el:ident:$typ:ty);*}) => (
        #[derive(Debug)]
        #[derive(PartialEq)]
        struct $state {$($el:$typ),*}
    );
    (@inner params $state:ident) => (
        #[derive(Debug)]
        #[derive(PartialEq)]
        struct $state {}
    );
    (@inner initial $initial:ident{$($init_field:ident:$init_val:expr),*}) => ($initial{$($init_field: $init_val),*});
    (@inner initial $initial:ident) => ($initial{});

(
    $machine:ident ($initial:ident$({$($init_field:ident:$init_val:expr),*})*)
    states[$($states:ident),*]
    commands[$($commands:ident),*]

    $(($state:ident $($sel:ident)*$({$($el:ident:$typ:ty);*})*:
        $(>> $income:block)*
        $(<< $outcome:block)*
        $($cmd:ident $($callback:block)* => $($new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*)*;)*
    ))*
) => (
    mod $machine {
        use super::*;
        trait CanDoJob {
            fn do_job(&mut self, cmd: &Commands) -> Option<States>;
            fn leave(&self) -> Result<(), ()>;
            fn enter(&self) -> Result<(), ()>;
        }

        $(
        FSM!(@inner params $state $({$($el:$typ);*})*);

        impl CanDoJob for $state {
            fn do_job(&mut self, cmd: & Commands) -> Option<States> {
                FSM!(@inner context self $($sel)*);
                match *cmd {
                    $(Commands::$cmd => {FSM!(@inner command self;$($callback)*;$($new_state$({$($new_el:$new_el_val),*})*)*)})*
                    _ => None
                }
            }

            FSM!(@inner >> $($sel)* $($income)*);
            FSM!(@inner << $($sel)* $($outcome)*);
        }
        )*

        #[derive(Debug)]
        #[derive(PartialEq)]
        enum States {
            __SameState__,
            $($states {context: $states}),*
        }

        #[derive(Debug)]
        #[derive(PartialEq)]
        pub enum Commands {
            $($commands),*
        }

        pub struct Machine {
            state: States
        }
        pub fn new() -> Machine {
            let context = FSM!(@inner initial $initial $({$($init_field: $init_val),*})*);
            context.enter().unwrap();
            Machine{state: States::$initial{context: context}}
        }

        impl Machine {
            pub fn execute(&mut self, cmd: & Commands) -> Result<(),()>{
                match {
                    match self.state {
                        States::__SameState__ => None,
                        $(States::$state{ ref mut context } => context.do_job(cmd)),*
                    }
                } {
                    Some(x) => {
                        match x {
                            States::__SameState__ => {},
                            _ => {
                                self.change_state(x)
                            }
                        };Ok(())
                    },
                    None => {println!("Wrong operation {:?} for {:?} state!", cmd, self.state); Err(())}
                }
            }
            fn change_state(&mut self, new_state: States) {
                match self.state {
                    States::__SameState__ => Ok(()),
                    $(States::$state{ ref mut context } => context.leave()),*
                }.unwrap();
                self.state = new_state;
                match self.state {
                    States::__SameState__ => Ok(()),
                    $(States::$state{ ref mut context } => context.enter()),*
                }.unwrap();
            }
        }
    }
)
}

#[cfg(test)]
mod tests {
    fn tes(x:i16) {
        println!("x:{}",x);
    }

    FSM!(
    Mach1 (New{x:0})

    states[New,InConfig,Operational]
    commands[Configure, ConfigureDone, Drop]

    ( New context{x: i16}:
        >> {println!("Enter {:?}", context)}
        << {println!("Leave {:?}", context)}
        Configure     {println!("In New with context val: {}", context.x);} =>     InConfig{x:context.x+1, y:0};
        ConfigureDone => New{x:0};
    )
    ( InConfig context{x:i16; y:i16}:
        >> {println!("Enter {:?}", context)}
        << {println!("Leave {:?}", context)}
        ConfigureDone {tes(context.x)}=> Operational;
    )
    ( Operational context:
        >> {println!("Enter {:?}", context)}
        << {println!("Leave {:?}", context)}
        ConfigureDone =>;
        Drop => New{x:0};
    )
    );

    FSM!(
    Mach2 (State1)

    states[State1,State2,State3]
    commands[ToState1, ToState2, ToState3]

    ( State1 :
        ToState2 => State2;
    )
    ( State2 :
        ToState3 => State3;
    )
    ( State3 :
        ToState1 => State1;
    )
    );

    #[test]
    fn test1() {
        let mut m = Mach1::new();
        m.execute(&Mach1::Commands::Configure);
        m.execute(&Mach1::Commands::ConfigureDone);
        m.execute(&Mach1::Commands::Configure);
        m.execute(&Mach1::Commands::Drop);
        m.execute(&Mach1::Commands::Configure);
        m.execute(&Mach1::Commands::ConfigureDone);
        m.execute(&Mach1::Commands::ConfigureDone);
        m.execute(&Mach1::Commands::ConfigureDone);
    }

    #[test]
    #[should_panic]
    fn test2() {
        let mut m = Mach2::new();
        m.execute(&Mach2::Commands::ToState2).unwrap();
        m.execute(&Mach2::Commands::ToState3).unwrap();
        m.execute(&Mach2::Commands::ToState1).unwrap();
        m.execute(&Mach2::Commands::ToState3).unwrap();
    }
}
<|endoftext|>