<commit_before>//! A machine learning library for supervised regression trainings
//!
//! This library wants to enable its user to write training algorithms
//! independent of the model trained or the cost function tried to
//! minimize.
//! Consequently its two main traits are currently `Model` and `Cost`.
//! The two submodules `model` and `cost` provide ready to use
//! implementations of said traits.

#![warn(missing_docs)]

extern crate num;

use std::iter::Iterator;
use num::{Zero, One, 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;
}

/// Changes all coefficents of model based on their derivation of the cost function at features
///
/// Will not get stuck on saddle points as easily as a plain SGD and will converge quicker in general.
/// A good default for `inertia` is 0.9
pub fn inert_gradient_descent_step<C, M>(
    cost : &C,
    model : &mut M,
    features : &M::Input,
    truth : M::Target,
    learning_rate : M::Target,
    inertia : M::Target,
    velocity : & mut Vec<M::Target>
)
    where C : Cost, M : Model<Target=C::Error>
{
    let inv_inertia = M::Target::one() - inertia;
    let prediction = model.predict(&features);

    for ci in 0..model.num_coefficents(){

        velocity[ci] = inertia * velocity[ci] - inv_inertia * learning_rate * cost.gradient(prediction, truth, model.gradient(ci, features));
        *model.coefficent(ci) = *model.coefficent(ci) + velocity[ci];
    }
}

/// An SGD training step with a velocity term
///
/// Can be used to implement stochastic or batch gradient descent
pub fn gradient_descent_step<C, M>(cost : &C, model : &mut M, features : &M::Input, truth : M::Target, learning_rate : M::Target)
    where C : Cost, M : Model<Target=C::Error>
{
    let prediction = model.predict(&features);

    for ci in 0..model.num_coefficents(){
        *model.coefficent(ci) = *model.coefficent(ci) - learning_rate * cost.gradient(prediction, truth, model.gradient(ci, features));
    }
}

/// Applies a plain SGD training step to model once for every event in history using a constant learning rate
pub fn stochastic_gradient_descent<C, M, H>(cost : &C, start : M, history : H, learning_rate : M::Target) -> M
    where C : Cost,
    M : Model<Target=C::Error>,
    H : Iterator<Item=(M::Input, M::Target)>
{

    let mut next = start.clone();
    for (features, truth) in history{

        gradient_descent_step(cost, & mut next, &features, truth, learning_rate);
    }

    next
}

/// SGD tranining with constant learning rate and velocity
///
/// Velocity avoids being stuck on saddle points during optimization
/// A good default for `inertia` is 0.9
pub fn inert_stochastic_gradient_descent<C, M, H>(
    cost : &C,
    start : M,
    history : H,
    learning_rate : M::Target,
    inertia : M::Target
) -> M
    where C : Cost,
    M : Model<Target=C::Error>,
    H : Iterator<Item=(M::Input, M::Target)>
{

    let mut velocity = Vec::new();
    velocity.resize(start.num_coefficents(), M::Target::zero());
    let mut next = start.clone();
    for (features, truth) in history{

        inert_gradient_descent_step(cost, & mut next, &features, truth, learning_rate, inertia, & mut velocity);
    }

    next
}

/// Implementations of `Model` trait
pub mod model;
/// Implementations of `Cost` trait
pub mod cost;
/// 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 gradient_descent_step;

        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 learning_rate_start = 0.4;
        let learning_rate_stop = 0.001;
        let num_steps = 200;
        let learning_rate_gradient = (learning_rate_start - learning_rate_stop) / (num_steps as f64);

        for (count_step, &truth) in history.iter().cycle().take(num_steps).enumerate(){

            let adapted_learning_rate = learning_rate_stop + learning_rate_gradient * (num_steps - count_step) as f64;
            gradient_descent_step(&cost, & mut model, &features, truth, adapted_learning_rate);
            println!("model: {:?}, learning_rate: {:?}", model, adapted_learning_rate);
        }

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

    #[test]
    fn estimate_mean() {

        use model::Constant;
        use cost::LeastSquares;
        use gradient_descent_step;

        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 learning_rate_start = 0.4;
        let learning_rate_stop = 0.001;
        let num_steps = 60;
        let learning_rate_gradient = (learning_rate_start - learning_rate_stop) / (num_steps as f64);

        for (count_step, &truth) in history.iter().cycle().take(num_steps).enumerate(){

            let adapted_learning_rate = learning_rate_stop + learning_rate_gradient * (num_steps - count_step) as f64;
            gradient_descent_step(&cost, & mut model, &features, truth, adapted_learning_rate);
            println!("model: {:?}, learning_rate: {:?}", model, adapted_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 stochastic_gradient_descent;

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

        let start = Linear{m : 0.0, c : 0.0};

        let learning_rate = 0.2;

        let cost = LeastSquares{};
        let model = stochastic_gradient_descent(&cost, start, history.iter().cycle().take(20).cloned(), learning_rate);

        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 gradient_descent_step;
        use cost::LeastSquares;

        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 learning_rate = 0.2;

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

            gradient_descent_step(&cost, & mut model, &features, truth, learning_rate);
            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 inert_stochastic_gradient_descent;

        let history = [([0.0, 7.0], 17.0), ([1.0, 2.0], 8.0), ([2.0, -2.0], 1.0)];

        let start = Linear{m : [0.0, 0.0], c : 0.0};

        let learning_rate = 0.1;

        let cost = LeastSquares{};
        let model = inert_stochastic_gradient_descent(
            &cost, start,
            history.iter().cycle().take(15000).cloned(),
            learning_rate, 0.9
        );

        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(){
        use cost::{MaxLikelihood};
        use model::{Logicstic, Linear};
        use stochastic_gradient_descent;

        use Model;

        let history = [
            ([2.7810836, 2.550537003], 0.0),
            ([1.465489372, 2.362125076], 0.0),
            ([3.396561688, 4.400293529], 0.0),
            ([1.38807019, 1.850220317], 0.0),
            ([3.06407232, 3.005305973], 0.0),
            ([7.627531214, 2.759262235], 1.0),
            ([5.332441248, 2.088626775], 1.0),
            ([6.922596716, 1.77106367], 1.0),
            ([8.675418651, -0.242068655], 1.0),
            ([7.673756466, 3.508563011], 1.0)
        ];

        let start = Logicstic{ linear: Linear{m : [0.0, 0.0], c : 0.0}};

        let learning_rate = 0.3;

        let cost = MaxLikelihood{};
        let model = stochastic_gradient_descent(
            &cost, start,
            history.iter().cycle().take(20).cloned(),
            learning_rate
        );

        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>seperate test for log reg with least squares<commit_after>//! A machine learning library for supervised regression trainings
//!
//! This library wants to enable its user to write training algorithms
//! independent of the model trained or the cost function tried to
//! minimize.
//! Consequently its two main traits are currently `Model` and `Cost`.
//! The two submodules `model` and `cost` provide ready to use
//! implementations of said traits.

#![warn(missing_docs)]

extern crate num;

use std::iter::Iterator;
use num::{Zero, One, 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;
}

/// Changes all coefficents of model based on their derivation of the cost function at features
///
/// Will not get stuck on saddle points as easily as a plain SGD and will converge quicker in general.
/// A good default for `inertia` is 0.9
pub fn inert_gradient_descent_step<C, M>(
    cost : &C,
    model : &mut M,
    features : &M::Input,
    truth : M::Target,
    learning_rate : M::Target,
    inertia : M::Target,
    velocity : & mut Vec<M::Target>
)
    where C : Cost, M : Model<Target=C::Error>
{
    let inv_inertia = M::Target::one() - inertia;
    let prediction = model.predict(&features);

    for ci in 0..model.num_coefficents(){

        velocity[ci] = inertia * velocity[ci] - inv_inertia * learning_rate * cost.gradient(prediction, truth, model.gradient(ci, features));
        *model.coefficent(ci) = *model.coefficent(ci) + velocity[ci];
    }
}

/// An SGD training step with a velocity term
///
/// Can be used to implement stochastic or batch gradient descent
pub fn gradient_descent_step<C, M>(cost : &C, model : &mut M, features : &M::Input, truth : M::Target, learning_rate : M::Target)
    where C : Cost, M : Model<Target=C::Error>
{
    let prediction = model.predict(&features);

    for ci in 0..model.num_coefficents(){
        *model.coefficent(ci) = *model.coefficent(ci) - learning_rate * cost.gradient(prediction, truth, model.gradient(ci, features));
    }
}

/// Applies a plain SGD training step to model once for every event in history using a constant learning rate
pub fn stochastic_gradient_descent<C, M, H>(cost : &C, start : M, history : H, learning_rate : M::Target) -> M
    where C : Cost,
    M : Model<Target=C::Error>,
    H : Iterator<Item=(M::Input, M::Target)>
{

    let mut next = start.clone();
    for (features, truth) in history{

        gradient_descent_step(cost, & mut next, &features, truth, learning_rate);
    }

    next
}

/// SGD tranining with constant learning rate and velocity
///
/// Velocity avoids being stuck on saddle points during optimization
/// A good default for `inertia` is 0.9
pub fn inert_stochastic_gradient_descent<C, M, H>(
    cost : &C,
    start : M,
    history : H,
    learning_rate : M::Target,
    inertia : M::Target
) -> M
    where C : Cost,
    M : Model<Target=C::Error>,
    H : Iterator<Item=(M::Input, M::Target)>
{

    let mut velocity = Vec::new();
    velocity.resize(start.num_coefficents(), M::Target::zero());
    let mut next = start.clone();
    for (features, truth) in history{

        inert_gradient_descent_step(cost, & mut next, &features, truth, learning_rate, inertia, & mut velocity);
    }

    next
}

/// Implementations of `Model` trait
pub mod model;
/// Implementations of `Cost` trait
pub mod cost;
/// 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 gradient_descent_step;

        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 learning_rate_start = 0.4;
        let learning_rate_stop = 0.001;
        let num_steps = 200;
        let learning_rate_gradient = (learning_rate_start - learning_rate_stop) / (num_steps as f64);

        for (count_step, &truth) in history.iter().cycle().take(num_steps).enumerate(){

            let adapted_learning_rate = learning_rate_stop + learning_rate_gradient * (num_steps - count_step) as f64;
            gradient_descent_step(&cost, & mut model, &features, truth, adapted_learning_rate);
            println!("model: {:?}, learning_rate: {:?}", model, adapted_learning_rate);
        }

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

    #[test]
    fn estimate_mean() {

        use model::Constant;
        use cost::LeastSquares;
        use gradient_descent_step;

        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 learning_rate_start = 0.4;
        let learning_rate_stop = 0.001;
        let num_steps = 60;
        let learning_rate_gradient = (learning_rate_start - learning_rate_stop) / (num_steps as f64);

        for (count_step, &truth) in history.iter().cycle().take(num_steps).enumerate(){

            let adapted_learning_rate = learning_rate_stop + learning_rate_gradient * (num_steps - count_step) as f64;
            gradient_descent_step(&cost, & mut model, &features, truth, adapted_learning_rate);
            println!("model: {:?}, learning_rate: {:?}", model, adapted_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 stochastic_gradient_descent;

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

        let start = Linear{m : 0.0, c : 0.0};

        let learning_rate = 0.2;

        let cost = LeastSquares{};
        let model = stochastic_gradient_descent(&cost, start, history.iter().cycle().take(20).cloned(), learning_rate);

        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 gradient_descent_step;
        use cost::LeastSquares;

        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 learning_rate = 0.2;

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

            gradient_descent_step(&cost, & mut model, &features, truth, learning_rate);
            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 inert_stochastic_gradient_descent;

        let history = [([0.0, 7.0], 17.0), ([1.0, 2.0], 8.0), ([2.0, -2.0], 1.0)];

        let start = Linear{m : [0.0, 0.0], c : 0.0};

        let learning_rate = 0.1;

        let cost = LeastSquares{};
        let model = inert_stochastic_gradient_descent(
            &cost, start,
            history.iter().cycle().take(15000).cloned(),
            learning_rate, 0.9
        );

        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_squaers(){
        use cost::LeastSquares;
        use model::{Logicstic, Linear};
        use stochastic_gradient_descent;

        use Model;

        let history = [
            ([2.7810836, 2.550537003], 0.0),
            ([1.465489372, 2.362125076], 0.0),
            ([3.396561688, 4.400293529], 0.0),
            ([1.38807019, 1.850220317], 0.0),
            ([3.06407232, 3.005305973], 0.0),
            ([7.627531214, 2.759262235], 1.0),
            ([5.332441248, 2.088626775], 1.0),
            ([6.922596716, 1.77106367], 1.0),
            ([8.675418651, -0.242068655], 1.0),
            ([7.673756466, 3.508563011], 1.0)
        ];

        let start = Logicstic{ linear: Linear{m : [0.0, 0.0], c : 0.0}};

        let learning_rate = 0.3;

        let cost = LeastSquares{};
        let model = stochastic_gradient_descent(
            &cost, start,
            history.iter().cycle().take(40).cloned(),
            learning_rate
        );

        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 stochastic_gradient_descent;

        use Model;

        let history = [
            ([2.7810836, 2.550537003], 0.0),
            ([1.465489372, 2.362125076], 0.0),
            ([3.396561688, 4.400293529], 0.0),
            ([1.38807019, 1.850220317], 0.0),
            ([3.06407232, 3.005305973], 0.0),
            ([7.627531214, 2.759262235], 1.0),
            ([5.332441248, 2.088626775], 1.0),
            ([6.922596716, 1.77106367], 1.0),
            ([8.675418651, -0.242068655], 1.0),
            ([7.673756466, 3.508563011], 1.0)
        ];

        let start = Logicstic{ linear: Linear{m : [0.0, 0.0], c : 0.0}};

        let learning_rate = 0.3;

        let cost = MaxLikelihood{};
        let model = stochastic_gradient_descent(
            &cost, start,
            history.iter().cycle().take(20).cloned(),
            learning_rate
        );

        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>extern crate unicode_segmentation;

pub fn normalize_tag(tag: &str) -> Result<String, &'static str> {
    if tag.is_empty() {
        Err("Tag is empty")
    } else {
        Ok(tag.to_lowercase())
    }
}

#[test]
fn test_tag_normalize() {
    assert!(normalize_tag("").is_err());
    assert_eq!(Ok("abc".to_string()), normalize_tag("ABC"));
}
<commit_msg>Split unicode words<commit_after>#![feature(slice_concat_ext)]

use unicode_segmentation::UnicodeSegmentation;
use std::slice::SliceConcatExt;

extern crate unicode_segmentation;

pub fn normalize_tag(tag: &str) -> Result<String, &'static str> {

    if tag.is_empty() {
        Err("Tag is empty")
    } else {
        let lc = tag.to_lowercase();
        let words: Vec<&str> = lc.unicode_words().collect();
        let joined = words.join("-");
        Ok(joined)
    }
}

#[test]
fn test_tag_normalize() {
    assert!(normalize_tag("").is_err());
    assert_eq!(Ok("abc".to_string()), normalize_tag("ABC"));
    assert_eq!(Ok("abc-this-is-a-tag".to_string()), normalize_tag("ABC this is a tag\""));
    assert_eq!(Ok("abc-this-is-a-tag".to_string()), normalize_tag("   ABC this     is a tag\""));
    assert_eq!(Ok("abc-this-is-a-tag".to_string()), normalize_tag("--   ABC this     is a tag--\""));
}
<|endoftext|>


<commit_before>//! A toolkit for building command-line programs in Rust.
//!
//! `cli` aims to
//!
//! - reduce boilerplate
//! - canonicalize output formatting
//! - encourage adherence to the conventions of standard streams
//!   (e.g. when to print to stderr vs stdout)
//! - encourage appropriate use of exit statuses
//!
//! Concretely, `cli` is a collection of simple functions for formatting
//! command-line output, defining flags, and (eventually) things like
//! integrating with sysexits.
//!
//! It uses [getopts](http://doc.rust-lang.org/getopts/) for argument parsing
//! and thus currently inherits `getopt`'s `experimental` stability level.
//!
//! ## Usage
//!
//! The following example shows how to integrate `cli` functions into a program's
//! `main` function in order to avoid `getopts` boilerplate. Sane defaults are
//! provided for help and version output without forcing consuming code to buy into
//! an application framework.
//!
//!     extern crate cli;
//!     extern crate getopts;
//!
//!     fn main() {
//!         let opts = &[
//!             cli::helpopt(),
//!             cli::versionopt(),
//!             getopts::optopt("o", "", "Set output file name", "FILENAME"),
//!         ];
//!
//!         let matches = cli::parse_args(opts);
//!         if matches.opt_present("h") {
//!             println!("{}", cli::usage_string(opts));
//!             return;
//!         }
//!         if matches.opt_present("version") {
//!             println!("{}", cli::version_string("0.0.1"));
//!             return;
//!         }
//!
//!         ...
//!     }
//!
//! When compiled to a binary named `foo`, this program emits the following output.
//!
//!     $ foo -h
//!     Usage: foo [-h] [--version] [-o FILENAME]
//!
//!     Options:
//!         -h --help           Print this help menu
//!         --version           Print the version of target/cli being run
//!         -o FILENAME         Set output file name
//!
//!     $ foo --version
//!     foo version 0.0.1

#![crate_name = "cli"]
#![experimental]
#![crate_type="rlib"]

extern crate getopts;
use getopts::{Matches, OptGroup, getopts, optflag, short_usage, usage};
use std::{io, os};
use std::io::fs;

mod test;

/// The file path of the executed program.
///
/// Typically the file path of the invoked executable. If the invocation target
/// is a symlink, then it will be resolved to the executable it links to.
pub fn exec_path() -> Path {
    let path = Path::new(os::args()[0].clone());
    fs::readlink(&path).unwrap_or(path)
}

/// Construct a canonical usage string from a collection of `OptGroup`s.
///
/// Usage string format:
///
///```ignore
///     Usage: <program name> [option synopsis]...
///
///     Options:
///         [option description]...
///```
pub fn usage_string(opts: &[OptGroup]) -> String {
    let exec_path = exec_path();
    let exec_path = exec_path.as_str().unwrap_or_else(|| "");
    format!("{}", usage(short_usage(exec_path, opts).as_slice(), opts))
}

/// Construct a version string.
///
/// Intended for use as output in response to `--version` as defined by
/// `cli::versionopt`.
///
/// Version string format:
///
///```ignore
///     <program name> version <version>
///```
pub fn version_string(version: &str) -> String {
    format!("{} version {}", exec_path().display(), version)
}

/// Parse the command-line arguments with which the program was executed
/// according to a collection of `OptGroup`s.
///
/// Any flag parsing failure results in task panic. The program's usage string
/// is printed to stderr prior to panic. Panic is induced in order to avoid
/// program execution with undefined configuration. In such cases, the presence
/// of unrecognized flags or invalid flag values implies confusion on the part
/// of the executor. While perhaps overbearing, it is preferable to halt
/// execution abruptly than to continue with the risk of unwanted behavior.
pub fn parse_args(opts: &[OptGroup]) -> Matches {
    match getopts(os::args().tail(), opts) {
        Ok(matches) => matches,
        Err(getopts_error) => {
            // Write usage string to stderr, then panic.
            match io::stderr().write_str(usage_string(opts).as_slice()) {
                Ok(()) => panic!(getopts_error.to_string()),
                Err(write_error) =>
                    // Write to stderr failed -- panic with both error messages.
                    panic!("{}\n{}", getopts_error.to_string(), write_error.to_string())
            }
        }
    }
}

/// Create a help flag `OptGroup`.
///
/// The returned `OptGroup` is an optional long option for the input `-h`
/// and `--help`.
pub fn helpopt() -> OptGroup {
    optflag("h", "help", "Print this help menu")
}

/// Create a version flag `OptGroup`.
///
/// The returned `OptGroup` is an optional long option for the input
/// `--version`. `-v` and `-V` are avoided in order to prevent confusion in the
/// event when a flag is needed for enabling verbose output.
pub fn versionopt() -> OptGroup {
    optflag(
        "",
        "version",
        format!("Print the version of {} being run", exec_path().display()).as_slice()
    )
}
<commit_msg>Add exit code constants derived from sysexits.h.<commit_after>//! A toolkit for building command-line programs in Rust.
//!
//! `cli` aims to
//!
//! - reduce boilerplate
//! - canonicalize output formatting
//! - encourage adherence to the conventions of standard streams
//!   (e.g. when to print to stderr vs stdout)
//! - encourage appropriate use of exit statuses
//!
//! Concretely, `cli` is a collection of simple functions for formatting
//! command-line output, defining flags, and (eventually) things like
//! integrating with sysexits.
//!
//! It uses [getopts](http://doc.rust-lang.org/getopts/) for argument parsing
//! and thus currently inherits `getopt`'s `experimental` stability level.
//!
//! ## Usage
//!
//! The following example shows how to integrate `cli` functions into a program's
//! `main` function in order to avoid `getopts` boilerplate. Sane defaults are
//! provided for help and version output without forcing consuming code to buy into
//! an application framework.
//!
//!     extern crate cli;
//!     extern crate getopts;
//!
//!     fn main() {
//!         let opts = &[
//!             cli::helpopt(),
//!             cli::versionopt(),
//!             getopts::optopt("o", "", "Set output file name", "FILENAME"),
//!         ];
//!
//!         let matches = cli::parse_args(opts);
//!         if matches.opt_present("h") {
//!             println!("{}", cli::usage_string(opts));
//!             return;
//!         }
//!         if matches.opt_present("version") {
//!             println!("{}", cli::version_string("0.0.1"));
//!             return;
//!         }
//!
//!         ...
//!     }
//!
//! When compiled to a binary named `foo`, this program emits the following output.
//!
//!     $ foo -h
//!     Usage: foo [-h] [--version] [-o FILENAME]
//!
//!     Options:
//!         -h --help           Print this help menu
//!         --version           Print the version of target/cli being run
//!         -o FILENAME         Set output file name
//!
//!     $ foo --version
//!     foo version 0.0.1

#![crate_name = "cli"]
#![experimental]
#![crate_type="rlib"]

extern crate getopts;
use getopts::{Matches, OptGroup, getopts, optflag, short_usage, usage};
use std::{io, os};
use std::io::fs;

mod test;

/// A collection of predefined exit codes cribbed from
/// [sysexits.h](http://www.freebsd.org/cgi/man.cgi?query=sysexits).
///
/// From `sysexits.h`:
///
/// > According to style(9), it is not a good practice to call exit(3) with
/// > arbitrary values to indicate a failure condition when ending a
/// > program.  Instead, the pre-defined exit codes from sysexits should be
/// > used, so the caller of the process can get a rough estimation about
/// > the failure class without looking up the source code.
///
/// Intended for use with `std::os::set_exit_status` prior to process exit.
///
/// # Example
///
/// ```
/// fn main() {
///   ...
///   os::set_exit_status(cli::sysexits::USAGE);
///   return
/// ```
pub mod sysexits {
    /// Successful termination
    pub const OK: int = 0;
    /// Command line usage error
    pub const USAGE: int = 64;
    /// Data format error
    pub const DATA_ERR: int = 65;
    /// Cannot open input
    pub const NO_INPUT: int = 66;
    /// Addressee unknown
    pub const NO_USER: int = 67;
    /// Host name unknown
    pub const NO_HOST: int = 68;
    /// Service unavailable
    pub const UNAVAILABLE: int = 6;
    /// Internal software error
    pub const SOFTWARE_ERR: int = 70;
    /// System error (e.g. can't fork)
    pub const OS_ERR: int = 71;
    /// Critical OS file missing
    pub const  OS_FILE: int = 72;
    /// Can't create (user) output file
    pub const CANT_CREAT: int = 73;
    /// Input/output error
    pub const IO_ERR: int = 74;
    /// Temp failure; user is invited to retry
    pub const TEMP_FAIL: int = 75;
    /// Remote error in protocol
    pub const PROTOCOL: int = 76;
    /// Permission denied
    pub const NO_PERM: int = 77;
    /// Configuration error
    pub const CONFIG: int = 78;
}

/// The file path of the executed program.
///
/// Typically the file path of the invoked executable. If the invocation target
/// is a symlink, then it will be resolved to the executable it links to.
pub fn exec_path() -> Path {
    let path = Path::new(os::args()[0].clone());
    fs::readlink(&path).unwrap_or(path)
}

/// Construct a canonical usage string from a collection of `OptGroup`s.
///
/// Usage string format:
///
/// ```ignore
/// Usage: <program name> [option synopsis]...
///
/// Options:
///     [option description]...
/// ```
pub fn usage_string(opts: &[OptGroup]) -> String {
    let exec_path = exec_path();
    let exec_path = exec_path.as_str().unwrap_or_else(|| "");
    format!("{}", usage(short_usage(exec_path, opts).as_slice(), opts))
}

/// Construct a version string.
///
/// Intended for use as output in response to `--version` as defined by
/// `cli::versionopt`.
///
/// Version string format:
///
/// ```ignore
/// <program name> version <version>
/// ```
pub fn version_string(version: &str) -> String {
    format!("{} version {}", exec_path().display(), version)
}

/// Parse the command-line arguments with which the program was executed
/// according to a collection of `OptGroup`s.
///
/// Any flag parsing failure results in task panic. The program's usage string
/// is printed to stderr prior to panic. Panic is induced in order to avoid
/// program execution with undefined configuration. In such cases, the presence
/// of unrecognized flags or invalid flag values implies confusion on the part
/// of the executor. While perhaps overbearing, it is preferable to halt
/// execution abruptly than to continue with the risk of unwanted behavior.
pub fn parse_args(opts: &[OptGroup]) -> Matches {
    match getopts(os::args().tail(), opts) {
        Ok(matches) => matches,
        Err(getopts_error) => {
            // Write usage string to stderr, then panic.
            match io::stderr().write_str(usage_string(opts).as_slice()) {
                Ok(()) => panic!(getopts_error.to_string()),
                Err(write_error) =>
                    // Write to stderr failed -- panic with both error messages.
                    panic!("{}\n{}", getopts_error.to_string(), write_error.to_string())
            }
        }
    }
}

/// Create a help flag `OptGroup`.
///
/// The returned `OptGroup` is an optional long option for the input `-h`
/// and `--help`.
pub fn helpopt() -> OptGroup {
    optflag("h", "help", "Print this help menu")
}

/// Create a version flag `OptGroup`.
///
/// The returned `OptGroup` is an optional long option for the input
/// `--version`. `-v` and `-V` are avoided in order to prevent confusion in the
/// event when a flag is needed for enabling verbose output.
pub fn versionopt() -> OptGroup {
    optflag(
        "",
        "version",
        format!("Print the version of {} being run", exec_path().display()).as_slice()
    )
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate url;
extern crate serde_json;

use std::result;
use std::io;
use std::io::Read;

use hyper::{Client, Url};
use hyper::header::UserAgent;

const LANGUAGE_URL_MARKER:&'static str = "{language}";

macro_rules! results {
    ($data: expr, $query_field: expr) => {
        // There has to be a better way to write the following code
        try!(
            $data.as_object()
            .and_then(|x| x.get("query"))
            .and_then(|x| x.as_object())
            .and_then(|x| x.get($query_field))
            .and_then(|x| x.as_array())
            .ok_or(Error::JSONPathError)
            ).into_iter().filter_map(|i|
                i.as_object()
                .and_then(|i| i.get("title"))
                .and_then(|s| s.as_string().map(|s| s.to_owned()))
                ).collect()
    }
}

#[derive(Debug)]
pub enum Error {
    UrlError(url::ParseError),
    HyperError(hyper::error::Error),
    HTTPError(hyper::client::response::Response),
    IOError(io::Error),
    JSONError(serde_json::error::Error),
    JSONPathError,
    InvalidParameter(String),
}

impl std::convert::From<url::ParseError> for Error {
    fn from(e: url::ParseError) -> Self {
        Error::UrlError(e)
    }
}

impl std::convert::From<hyper::error::Error> for Error {
    fn from(e: hyper::error::Error) -> Self {
        Error::HyperError(e)
    }
}

impl std::convert::From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::IOError(e)
    }
}

impl std::convert::From<serde_json::error::Error> for Error {
    fn from(e: serde_json::error::Error) -> Self {
        Error::JSONError(e)
    }
}

pub type Result<T> = result::Result<T, Error>;

pub struct Wikipedia {
    pub pre_language_url:String,
    pub post_language_url:String,
    pub user_agent:String,
    pub language:String,
    pub search_results:u32,
}

impl Default for Wikipedia {
    fn default() -> Self {
        Wikipedia {
            pre_language_url: "https://".to_owned(),
            post_language_url: ".wikipedia.org/w/api.php".to_owned(),
            user_agent: "wikipedia (https://github.com/seppo0010/wikipedia-rs)".to_owned(),
            language: "en".to_owned(),
            search_results: 10,
        }
    }
}

impl Wikipedia {
    pub fn base_url(&self) -> String {
        format!("{}{}{}", self.pre_language_url, self.language, self.post_language_url)
    }

    pub fn set_base_url(&mut self, base_url: &str) {
        let index = match base_url.find(LANGUAGE_URL_MARKER) {
            Some(i) => i,
            None => {
                self.pre_language_url = base_url.to_owned();
                self.language = "".to_owned();
                self.post_language_url = "".to_owned();
                return;
            }
        };
        self.pre_language_url = base_url[0..index].to_owned();
        self.post_language_url = base_url[index+LANGUAGE_URL_MARKER.len()..].to_owned();
    }

    fn query(&self, url: Url) -> Result<serde_json::Value> {
        let client = Client::new();
        let mut response = try!(client.get(url)
            .header(UserAgent(self.user_agent.clone()))
            .send());

        if !response.status.is_success() {
            return Err(Error::HTTPError(response));
        }

        let mut response_str = String::new();
        try!(response.read_to_string(&mut response_str));

        Ok(try!(serde_json::from_str(&*response_str)))
    }

    fn search_url(&self, query: &str) -> Result<Url> {
        let mut url = try!(Url::parse(&*self.base_url()));
        let results = &*format!("{}", self.search_results);
        url.set_query_from_pairs(vec![
                ("list", "search"),
                ("srprop", ""),
                ("srlimit", results),
                ("srsearch", query),
                ("continue", ""),
                ("format", "json"),
                ("action", "query"),
                ].into_iter());
        Ok(url)
    }

    pub fn search(&self, query: &str) -> Result<Vec<String>> {
        let url = try!(self.search_url(query));
        let data = try!(self.query(url));

        Ok(results!(data, "search"))
    }

    fn geosearch_url(&self, latitude: f64, longitude: f64, radius: u16) -> Result<Url> {
        if latitude < -90.0 || latitude > 90.0 {
            return Err(Error::InvalidParameter("latitude".to_string()))
        }
        if longitude < -180.0 || longitude > 180.0 {
            return Err(Error::InvalidParameter("longitude".to_string()))
        }
        if radius < 10 || radius > 10000 {
            return Err(Error::InvalidParameter("radius".to_string()))
        }
        let mut url = try!(Url::parse(&*self.base_url()));
        let results = &*format!("{}", self.search_results);
        url.set_query_from_pairs(vec![
                ("list", "geosearch"),
                ("gsradius", &*format!("{}", radius)),
                ("gscoord", &*format!("{}|{}", latitude, longitude)),
                ("gslimit", results),
                ("format", "json"),
                ("action", "query"),
                ].into_iter());
        Ok(url)
    }

    pub fn geosearch(&self, latitude: f64, longitude: f64, radius: u16) -> Result<Vec<String>> {
        let url = try!(self.geosearch_url(latitude, longitude, radius));
        let data = try!(self.query(url));

        Ok(results!(data, "geosearch"))
    }

    fn random_url(&self, count: u8) -> Result<Url> {
        let mut url = try!(Url::parse(&*self.base_url()));
        url.set_query_from_pairs(vec![
                ("list", "random"),
                ("rnnamespace", "0"),
                ("rnlimit", &*format!("{}", count)),
                ("continue", ""),
                ("format", "json"),
                ("action", "query"),
                ].into_iter());
        Ok(url)
    }

    pub fn random(&self) -> Result<Option<String>> {
        let url = try!(self.random_url(1));
        let data = try!(self.query(url));
        let r:Vec<String> = results!(data, "random");
        Ok(r.into_iter().next())
    }

    pub fn random_count(&self, count: u8) -> Result<Vec<String>> {
        let url = try!(self.random_url(count));
        let data = try!(self.query(url));
        Ok(results!(data, "random"))
    }
}

#[test]
fn base_url() {
    let mut wikipedia = Wikipedia::default();
    assert_eq!(wikipedia.base_url(), "https://en.wikipedia.org/w/api.php");
    wikipedia.language = "es".to_owned();
    assert_eq!(wikipedia.base_url(), "https://es.wikipedia.org/w/api.php");

    wikipedia.set_base_url("https://hello.{language}.world/");
    assert_eq!(wikipedia.base_url(), "https://hello.es.world/");

    wikipedia.set_base_url("https://hello.world/");
    assert_eq!(wikipedia.base_url(), "https://hello.world/");
}

#[test]
fn search_url() {
    let wikipedia = Wikipedia::default();
    assert_eq!(&*format!("{}", wikipedia.search_url("hello world").unwrap()),
            "https://en.wikipedia.org/w/api.php?list=search&srprop=&srlimit=10&srsearch=hello+world&continue=&format=json&action=query");
}

#[test]
fn search() {
    let wikipedia = Wikipedia::default();
    let results = wikipedia.search("hello world").unwrap();
    assert!(results.len() > 0);
    assert!(results.contains(&"\"Hello, World!\" program".to_owned()));
}

#[test]
fn geosearch_url() {
    let wikipedia = Wikipedia::default();
    assert_eq!(&*format!("{}", wikipedia.geosearch_url(-34.603333, -58.381667, 10).unwrap()),
            "https://en.wikipedia.org/w/api.php?list=geosearch&gsradius=10&gscoord=-34.603333%7C-58.381667&gslimit=10&format=json&action=query");
}

#[test]
fn geosearch() {
    let wikipedia = Wikipedia::default();
    let results = wikipedia.geosearch(-34.603333, -58.381667, 10).unwrap();
    assert!(results.len() > 0);
    assert!(results.contains(&"Buenos Aires".to_owned()));
}

#[test]
fn random_url() {
    let wikipedia = Wikipedia::default();
    assert_eq!(&*format!("{}", wikipedia.random_url(10).unwrap()),
            "https://en.wikipedia.org/w/api.php?list=random&rnnamespace=0&rnlimit=10&continue=&format=json&action=query");
}

#[test]
fn random() {
    let wikipedia = Wikipedia::default();
    wikipedia.random().unwrap().unwrap();
}

#[test]
fn random_count() {
    let wikipedia = Wikipedia::default();
    assert_eq!(wikipedia.random_count(3).unwrap().len(), 3);
}
<commit_msg>Add Page<commit_after>extern crate hyper;
extern crate url;
extern crate serde_json;

use std::result;
use std::io;
use std::io::Read;
use std::cmp::PartialEq;

use hyper::{Client, Url};
use hyper::header::UserAgent;

const LANGUAGE_URL_MARKER:&'static str = "{language}";

macro_rules! results {
    ($data: expr, $query_field: expr) => {
        // There has to be a better way to write the following code
        try!(
            $data.as_object()
            .and_then(|x| x.get("query"))
            .and_then(|x| x.as_object())
            .and_then(|x| x.get($query_field))
            .and_then(|x| x.as_array())
            .ok_or(Error::JSONPathError)
            ).into_iter().filter_map(|i|
                i.as_object()
                .and_then(|i| i.get("title"))
                .and_then(|s| s.as_string().map(|s| s.to_owned()))
                ).collect()
    }
}

#[derive(Debug)]
pub enum Error {
    UrlError(url::ParseError),
    HyperError(hyper::error::Error),
    HTTPError(hyper::client::response::Response),
    IOError(io::Error),
    JSONError(serde_json::error::Error),
    JSONPathError,
    InvalidParameter(String),
}

impl std::convert::From<url::ParseError> for Error {
    fn from(e: url::ParseError) -> Self {
        Error::UrlError(e)
    }
}

impl std::convert::From<hyper::error::Error> for Error {
    fn from(e: hyper::error::Error) -> Self {
        Error::HyperError(e)
    }
}

impl std::convert::From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::IOError(e)
    }
}

impl std::convert::From<serde_json::error::Error> for Error {
    fn from(e: serde_json::error::Error) -> Self {
        Error::JSONError(e)
    }
}

pub type Result<T> = result::Result<T, Error>;

pub struct Wikipedia {
    pub pre_language_url:String,
    pub post_language_url:String,
    pub user_agent:String,
    pub language:String,
    pub search_results:u32,
}

impl Default for Wikipedia {
    fn default() -> Self {
        Wikipedia {
            pre_language_url: "https://".to_owned(),
            post_language_url: ".wikipedia.org/w/api.php".to_owned(),
            user_agent: "wikipedia (https://github.com/seppo0010/wikipedia-rs)".to_owned(),
            language: "en".to_owned(),
            search_results: 10,
        }
    }
}

impl Wikipedia {
    pub fn base_url(&self) -> String {
        format!("{}{}{}", self.pre_language_url, self.language, self.post_language_url)
    }

    pub fn set_base_url(&mut self, base_url: &str) {
        let index = match base_url.find(LANGUAGE_URL_MARKER) {
            Some(i) => i,
            None => {
                self.pre_language_url = base_url.to_owned();
                self.language = "".to_owned();
                self.post_language_url = "".to_owned();
                return;
            }
        };
        self.pre_language_url = base_url[0..index].to_owned();
        self.post_language_url = base_url[index+LANGUAGE_URL_MARKER.len()..].to_owned();
    }

    fn query(&self, url: Url) -> Result<serde_json::Value> {
        let client = Client::new();
        let mut response = try!(client.get(url)
            .header(UserAgent(self.user_agent.clone()))
            .send());

        if !response.status.is_success() {
            return Err(Error::HTTPError(response));
        }

        let mut response_str = String::new();
        try!(response.read_to_string(&mut response_str));

        Ok(try!(serde_json::from_str(&*response_str)))
    }

    fn search_url(&self, query: &str) -> Result<Url> {
        let mut url = try!(Url::parse(&*self.base_url()));
        let results = &*format!("{}", self.search_results);
        url.set_query_from_pairs(vec![
                ("list", "search"),
                ("srprop", ""),
                ("srlimit", results),
                ("srsearch", query),
                ("continue", ""),
                ("format", "json"),
                ("action", "query"),
                ].into_iter());
        Ok(url)
    }

    pub fn search(&self, query: &str) -> Result<Vec<String>> {
        let url = try!(self.search_url(query));
        let data = try!(self.query(url));

        Ok(results!(data, "search"))
    }

    fn geosearch_url(&self, latitude: f64, longitude: f64, radius: u16) -> Result<Url> {
        if latitude < -90.0 || latitude > 90.0 {
            return Err(Error::InvalidParameter("latitude".to_string()))
        }
        if longitude < -180.0 || longitude > 180.0 {
            return Err(Error::InvalidParameter("longitude".to_string()))
        }
        if radius < 10 || radius > 10000 {
            return Err(Error::InvalidParameter("radius".to_string()))
        }
        let mut url = try!(Url::parse(&*self.base_url()));
        let results = &*format!("{}", self.search_results);
        url.set_query_from_pairs(vec![
                ("list", "geosearch"),
                ("gsradius", &*format!("{}", radius)),
                ("gscoord", &*format!("{}|{}", latitude, longitude)),
                ("gslimit", results),
                ("format", "json"),
                ("action", "query"),
                ].into_iter());
        Ok(url)
    }

    pub fn geosearch(&self, latitude: f64, longitude: f64, radius: u16) -> Result<Vec<String>> {
        let url = try!(self.geosearch_url(latitude, longitude, radius));
        let data = try!(self.query(url));

        Ok(results!(data, "geosearch"))
    }

    fn random_url(&self, count: u8) -> Result<Url> {
        let mut url = try!(Url::parse(&*self.base_url()));
        url.set_query_from_pairs(vec![
                ("list", "random"),
                ("rnnamespace", "0"),
                ("rnlimit", &*format!("{}", count)),
                ("continue", ""),
                ("format", "json"),
                ("action", "query"),
                ].into_iter());
        Ok(url)
    }

    pub fn random(&self) -> Result<Option<String>> {
        let url = try!(self.random_url(1));
        let data = try!(self.query(url));
        let r:Vec<String> = results!(data, "random");
        Ok(r.into_iter().next())
    }

    pub fn random_count(&self, count: u8) -> Result<Vec<String>> {
        let url = try!(self.random_url(count));
        let data = try!(self.query(url));
        Ok(results!(data, "random"))
    }
}

#[derive(Debug)]
pub struct Page {
    title: Option<String>,
    pageid: Option<String>,
}

impl Page {
    pub fn from_title(title: String) -> Page {
        Page { title: Some(title), pageid: None, }
    }

    pub fn from_pageid(pageid: String) -> Page {
        Page { title: None, pageid: Some(pageid), }
    }
}

impl PartialEq<Page> for Page {
    fn eq(&self, other: &Page) -> bool {
        if self.title.is_some() && other.title.is_some() {
            return self.title == other.title;
        }
        if self.pageid.is_some() && other.pageid.is_some() {
            return self.title == other.title;
        }
        return false;
    }
}

#[test]
fn base_url() {
    let mut wikipedia = Wikipedia::default();
    assert_eq!(wikipedia.base_url(), "https://en.wikipedia.org/w/api.php");
    wikipedia.language = "es".to_owned();
    assert_eq!(wikipedia.base_url(), "https://es.wikipedia.org/w/api.php");

    wikipedia.set_base_url("https://hello.{language}.world/");
    assert_eq!(wikipedia.base_url(), "https://hello.es.world/");

    wikipedia.set_base_url("https://hello.world/");
    assert_eq!(wikipedia.base_url(), "https://hello.world/");
}

#[test]
fn search_url() {
    let wikipedia = Wikipedia::default();
    assert_eq!(&*format!("{}", wikipedia.search_url("hello world").unwrap()),
            "https://en.wikipedia.org/w/api.php?list=search&srprop=&srlimit=10&srsearch=hello+world&continue=&format=json&action=query");
}

#[test]
fn search() {
    let wikipedia = Wikipedia::default();
    let results = wikipedia.search("hello world").unwrap();
    assert!(results.len() > 0);
    assert!(results.contains(&"\"Hello, World!\" program".to_owned()));
}

#[test]
fn geosearch_url() {
    let wikipedia = Wikipedia::default();
    assert_eq!(&*format!("{}", wikipedia.geosearch_url(-34.603333, -58.381667, 10).unwrap()),
            "https://en.wikipedia.org/w/api.php?list=geosearch&gsradius=10&gscoord=-34.603333%7C-58.381667&gslimit=10&format=json&action=query");
}

#[test]
fn geosearch() {
    let wikipedia = Wikipedia::default();
    let results = wikipedia.geosearch(-34.603333, -58.381667, 10).unwrap();
    assert!(results.len() > 0);
    assert!(results.contains(&"Buenos Aires".to_owned()));
}

#[test]
fn random_url() {
    let wikipedia = Wikipedia::default();
    assert_eq!(&*format!("{}", wikipedia.random_url(10).unwrap()),
            "https://en.wikipedia.org/w/api.php?list=random&rnnamespace=0&rnlimit=10&continue=&format=json&action=query");
}

#[test]
fn random() {
    let wikipedia = Wikipedia::default();
    wikipedia.random().unwrap().unwrap();
}

#[test]
fn random_count() {
    let wikipedia = Wikipedia::default();
    assert_eq!(wikipedia.random_count(3).unwrap().len(), 3);
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]

//! A generic library for textures.
//!
//! This library is used in Piston for generic code when working with textures.
//!
//! The `ImageSize` trait is used for passing textures around for rendering.
//! For more information, see
//! [Piston-Graphics](https://github.com/pistondevelopers/graphics).

pub mod ops;

/// Implemented by all images to be used with generic algorithms.
pub trait ImageSize {
    /// Get the image size.
    fn get_size(&self) -> (u32, u32);

    /// Gets the image width.
    #[inline(always)]
    fn get_width(&self) -> u32 {
        let (w, _) = self.get_size();
        w
    }

    /// Gets the image height.
    #[inline(always)]
    fn get_height(&self) -> u32 {
        let (_, h) = self.get_size();
        h
    }
}

/// Texture creation parameters.
#[derive(Clone, Copy)]
pub struct TextureSettings {
    // Whether to convert gamma, treated as sRGB color space
    convert_gamma: bool,
    // Compress on GPU.
    compress: bool,
    // Generate mipmap chain.
    generate_mipmap: bool,
    // Filtering Mode for Minifying
    min: Filter,
    // Filtering Mode for Magnifying
    mag: Filter,
    // Filtering Mode for Minify Mipmapping
    mipmap: Filter,
    // Wrapping mode for s coordinate
    wrap_u: Wrap,
    // Wrapping mode for t coordinate
    wrap_v: Wrap,
    // Border Color if ClampToBorder is specified as wrap mode
    border_color: [f32; 4],
}

impl TextureSettings {
    /// Create default settings.
    pub fn new() -> TextureSettings {
        TextureSettings {
            convert_gamma: false,
            compress: false,
            generate_mipmap: false,
            min: Filter::Linear,
            mag: Filter::Linear,
            mipmap: Filter::Linear,
            wrap_u: Wrap::ClampToEdge,
            wrap_v: Wrap::ClampToEdge,
            border_color: [0.0, 0.0, 0.0, 1.0],
        }
    }

    /// Gets whether to convert gamma, treated as sRGB color space.
    pub fn get_convert_gamma(&self) -> bool { self.convert_gamma }
    /// Sets convert gamma.
    pub fn set_convert_gamma(&mut self, val: bool) { self.convert_gamma = val; }
    /// Sets convert gamma.
    pub fn convert_gamma(mut self, val: bool) -> Self {
        self.set_convert_gamma(val);
        self
    }

    /// Gets wheter compress on the GPU.
    pub fn get_compress(&self) -> bool { self.compress }
    /// Sets compress.
    pub fn set_compress(&mut self, val: bool) { self.compress = val; }
    /// Sets compress.
    pub fn compress(mut self, val: bool) -> Self {
        self.set_compress(val);
        self
    }

    /// Gets generate mipmap.
    pub fn get_generate_mipmap(&self) -> bool { self.generate_mipmap }
    /// Sets generate mipmap.
    pub fn set_generate_mipmap(&mut self, val: bool) {
        self.generate_mipmap = val;
    }
    /// Sets generate mipmap.
    pub fn generate_mipmap(mut self, val: bool) -> Self {
        self.set_generate_mipmap(val);
        self
    }

    /// Gets minify filter.
    pub fn get_min(&self) -> Filter { self.min }
    /// Sets minify filter.
    pub fn set_min(&mut self, val: Filter) {
        self.min = val
    }
    /// Sets minify filter.
    pub fn min(mut self, val: Filter) -> Self {
        self.set_min(val);
        self
    }

    /// Gets magnify filter
    pub fn get_mag(&self) -> Filter { self.mag }
    /// Sets magnify filter
    pub fn set_mag(&mut self, val: Filter) {
        self.mag = val;
    }
    /// Sets magnify filter
    pub fn mag(mut self, val: Filter) -> Self {
        self.set_mag(val);
        self
    }

    /// Gets minify mipmap filter
    pub fn get_mipmap(&self) -> Filter { self.mipmap }
    /// Sets magnify mipmap filter, and sets generate_mipmap to true.
    pub fn set_mipmap(&mut self, val: Filter) {
        self.set_generate_mipmap(true);
        self.mag = val;
    }
    /// Sets magnify mipmap filter, and sets generate_mipmap to true
    pub fn mipmap(mut self, val: Filter) -> Self {
        self.set_mag(val);
        self
    }

    /// Returns the min and mag filter
    pub fn get_filter(&self) -> (Filter, Filter) { (self.min, self.mag) }
    /// Sets the min and mag filter
    pub fn set_filter(&mut self, val: Filter) {
        self.set_min(val);
        self.set_mag(val);
    }

    /// Sets the min and mag filter
    pub fn filter(mut self, val: Filter) -> Self {
        self.set_filter(val);
        self
    }

    /// Gets the wrapping mode for the u coordinate
    pub fn get_wrap_u(&self) -> Wrap {
        self.wrap_u
    }
    /// Sets the wrapping mode for the u coordinate
    pub fn set_wrap_u(& mut self, val: Wrap) {
        self.wrap_u = val
    }
    /// Sets the wrapping mode for the u coordinate
    pub fn wrap_u(mut self, val: Wrap) -> Self {
        self.set_wrap_u(val);
        self
    }

    /// Gets the wrapping mode for the v coordinate
    pub fn get_wrap_v(&self) -> Wrap {
        self.wrap_v
    }
    /// Sets the wrapping mode for the v coordinate
    pub fn set_wrap_v(& mut self, val: Wrap) {
        self.wrap_v = val
    }
    /// Sets the wrapping mode for the v coordinate
    pub fn wrap_v(mut self, val: Wrap) -> Self {
        self.set_wrap_v(val);
        self
    }

    /// Gets the border color
    pub fn get_border_color(&self) -> [f32; 4] {
        self.border_color
    }
    /// Sets the border color
    pub fn set_border_color(&mut self, val: [f32; 4]) {
        self.border_color = val
    }
    /// Sets the border color
    pub fn border_color(mut self, val: [f32; 4]) -> Self {
        self.set_border_color(val);
        self
    }

}

/// Texture format.
#[derive(Copy, Clone, Debug)]
pub enum Format {
    /// `(red, green, blue, alpha)` with values 0-255.
    Rgba8,
}

/// Implemented by texture operations.
pub trait TextureOp<F> {
    /// The error when performing an operation.
    type Error;
}

/// Implemented by textures for creation.
pub trait CreateTexture<F>: TextureOp<F> + ImageSize + Sized {
    /// Create texture from memory.
    fn create<S: Into<[u32; 2]>>(
        factory: &mut F,
        format: Format,
        memory: &[u8],
        size: S,
        settings: &TextureSettings
    ) -> Result<Self, Self::Error>;
}

/// Implemented by textures for updating.
pub trait UpdateTexture<F>: TextureOp<F> + ImageSize + Sized {
    /// Update the texture.
    ///
    /// The `offset` and `size` arguments represent the position and dimensions of the sub-section
    /// of the texture that is to be updated with the given `memory`.
    fn update<O, S>(
        &mut self,
        factory: &mut F,
        format: Format,
        memory: &[u8],
        offset: O,
        size: S,
    ) -> Result<(), Self::Error>
        where O: Into<[u32; 2]>,
              S: Into<[u32; 2]>;
}

/// Sampling filter
#[derive(Copy, Clone, Debug)]
pub enum Filter {
    /// A Weighted Linear Blend
    Linear,
    /// Nearest Texel
    Nearest
}

/// Wrap mode
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Wrap {
    /// Repeats the texture by ignoring the integer part of the coordinate.
    Repeat,
    /// Repeats the texture and mirrors it, when the integer part of the coordinate is odd.
    MirroredRepeat,
    /// The coordinate will be clamped between 0 and 1.
    ClampToEdge,
    /// Coordinates outside the range [0.0, 1.0] will be given a border color.
    ClampToBorder,
}
<commit_msg>Made `TextureOp::Error` require `Debug`<commit_after>#![deny(missing_docs)]

//! A generic library for textures.
//!
//! This library is used in Piston for generic code when working with textures.
//!
//! The `ImageSize` trait is used for passing textures around for rendering.
//! For more information, see
//! [Piston-Graphics](https://github.com/pistondevelopers/graphics).

pub mod ops;

/// Implemented by all images to be used with generic algorithms.
pub trait ImageSize {
    /// Get the image size.
    fn get_size(&self) -> (u32, u32);

    /// Gets the image width.
    #[inline(always)]
    fn get_width(&self) -> u32 {
        let (w, _) = self.get_size();
        w
    }

    /// Gets the image height.
    #[inline(always)]
    fn get_height(&self) -> u32 {
        let (_, h) = self.get_size();
        h
    }
}

/// Texture creation parameters.
#[derive(Clone, Copy)]
pub struct TextureSettings {
    // Whether to convert gamma, treated as sRGB color space
    convert_gamma: bool,
    // Compress on GPU.
    compress: bool,
    // Generate mipmap chain.
    generate_mipmap: bool,
    // Filtering Mode for Minifying
    min: Filter,
    // Filtering Mode for Magnifying
    mag: Filter,
    // Filtering Mode for Minify Mipmapping
    mipmap: Filter,
    // Wrapping mode for s coordinate
    wrap_u: Wrap,
    // Wrapping mode for t coordinate
    wrap_v: Wrap,
    // Border Color if ClampToBorder is specified as wrap mode
    border_color: [f32; 4],
}

impl TextureSettings {
    /// Create default settings.
    pub fn new() -> TextureSettings {
        TextureSettings {
            convert_gamma: false,
            compress: false,
            generate_mipmap: false,
            min: Filter::Linear,
            mag: Filter::Linear,
            mipmap: Filter::Linear,
            wrap_u: Wrap::ClampToEdge,
            wrap_v: Wrap::ClampToEdge,
            border_color: [0.0, 0.0, 0.0, 1.0],
        }
    }

    /// Gets whether to convert gamma, treated as sRGB color space.
    pub fn get_convert_gamma(&self) -> bool { self.convert_gamma }
    /// Sets convert gamma.
    pub fn set_convert_gamma(&mut self, val: bool) { self.convert_gamma = val; }
    /// Sets convert gamma.
    pub fn convert_gamma(mut self, val: bool) -> Self {
        self.set_convert_gamma(val);
        self
    }

    /// Gets wheter compress on the GPU.
    pub fn get_compress(&self) -> bool { self.compress }
    /// Sets compress.
    pub fn set_compress(&mut self, val: bool) { self.compress = val; }
    /// Sets compress.
    pub fn compress(mut self, val: bool) -> Self {
        self.set_compress(val);
        self
    }

    /// Gets generate mipmap.
    pub fn get_generate_mipmap(&self) -> bool { self.generate_mipmap }
    /// Sets generate mipmap.
    pub fn set_generate_mipmap(&mut self, val: bool) {
        self.generate_mipmap = val;
    }
    /// Sets generate mipmap.
    pub fn generate_mipmap(mut self, val: bool) -> Self {
        self.set_generate_mipmap(val);
        self
    }

    /// Gets minify filter.
    pub fn get_min(&self) -> Filter { self.min }
    /// Sets minify filter.
    pub fn set_min(&mut self, val: Filter) {
        self.min = val
    }
    /// Sets minify filter.
    pub fn min(mut self, val: Filter) -> Self {
        self.set_min(val);
        self
    }

    /// Gets magnify filter
    pub fn get_mag(&self) -> Filter { self.mag }
    /// Sets magnify filter
    pub fn set_mag(&mut self, val: Filter) {
        self.mag = val;
    }
    /// Sets magnify filter
    pub fn mag(mut self, val: Filter) -> Self {
        self.set_mag(val);
        self
    }

    /// Gets minify mipmap filter
    pub fn get_mipmap(&self) -> Filter { self.mipmap }
    /// Sets magnify mipmap filter, and sets generate_mipmap to true.
    pub fn set_mipmap(&mut self, val: Filter) {
        self.set_generate_mipmap(true);
        self.mag = val;
    }
    /// Sets magnify mipmap filter, and sets generate_mipmap to true
    pub fn mipmap(mut self, val: Filter) -> Self {
        self.set_mag(val);
        self
    }

    /// Returns the min and mag filter
    pub fn get_filter(&self) -> (Filter, Filter) { (self.min, self.mag) }
    /// Sets the min and mag filter
    pub fn set_filter(&mut self, val: Filter) {
        self.set_min(val);
        self.set_mag(val);
    }

    /// Sets the min and mag filter
    pub fn filter(mut self, val: Filter) -> Self {
        self.set_filter(val);
        self
    }

    /// Gets the wrapping mode for the u coordinate
    pub fn get_wrap_u(&self) -> Wrap {
        self.wrap_u
    }
    /// Sets the wrapping mode for the u coordinate
    pub fn set_wrap_u(& mut self, val: Wrap) {
        self.wrap_u = val
    }
    /// Sets the wrapping mode for the u coordinate
    pub fn wrap_u(mut self, val: Wrap) -> Self {
        self.set_wrap_u(val);
        self
    }

    /// Gets the wrapping mode for the v coordinate
    pub fn get_wrap_v(&self) -> Wrap {
        self.wrap_v
    }
    /// Sets the wrapping mode for the v coordinate
    pub fn set_wrap_v(& mut self, val: Wrap) {
        self.wrap_v = val
    }
    /// Sets the wrapping mode for the v coordinate
    pub fn wrap_v(mut self, val: Wrap) -> Self {
        self.set_wrap_v(val);
        self
    }

    /// Gets the border color
    pub fn get_border_color(&self) -> [f32; 4] {
        self.border_color
    }
    /// Sets the border color
    pub fn set_border_color(&mut self, val: [f32; 4]) {
        self.border_color = val
    }
    /// Sets the border color
    pub fn border_color(mut self, val: [f32; 4]) -> Self {
        self.set_border_color(val);
        self
    }

}

/// Texture format.
#[derive(Copy, Clone, Debug)]
pub enum Format {
    /// `(red, green, blue, alpha)` with values 0-255.
    Rgba8,
}

/// Implemented by texture operations.
pub trait TextureOp<F> {
    /// The error when performing an operation.
    type Error: core::fmt::Debug;
}

/// Implemented by textures for creation.
pub trait CreateTexture<F>: TextureOp<F> + ImageSize + Sized {
    /// Create texture from memory.
    fn create<S: Into<[u32; 2]>>(
        factory: &mut F,
        format: Format,
        memory: &[u8],
        size: S,
        settings: &TextureSettings
    ) -> Result<Self, Self::Error>;
}

/// Implemented by textures for updating.
pub trait UpdateTexture<F>: TextureOp<F> + ImageSize + Sized {
    /// Update the texture.
    ///
    /// The `offset` and `size` arguments represent the position and dimensions of the sub-section
    /// of the texture that is to be updated with the given `memory`.
    fn update<O, S>(
        &mut self,
        factory: &mut F,
        format: Format,
        memory: &[u8],
        offset: O,
        size: S,
    ) -> Result<(), Self::Error>
        where O: Into<[u32; 2]>,
              S: Into<[u32; 2]>;
}

/// Sampling filter
#[derive(Copy, Clone, Debug)]
pub enum Filter {
    /// A Weighted Linear Blend
    Linear,
    /// Nearest Texel
    Nearest
}

/// Wrap mode
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Wrap {
    /// Repeats the texture by ignoring the integer part of the coordinate.
    Repeat,
    /// Repeats the texture and mirrors it, when the integer part of the coordinate is odd.
    MirroredRepeat,
    /// The coordinate will be clamped between 0 and 1.
    ClampToEdge,
    /// Coordinates outside the range [0.0, 1.0] will be given a border color.
    ClampToBorder,
}
<|endoftext|>


<commit_before>#![feature(libc)]

extern crate libc;

use libc::{c_void, c_int, c_char};
use std::ffi::CString;
use std::slice;

pub type Voidptr = *mut c_void;

#[link(name = "sophia", kind="static")]
extern {
    fn sp_env() -> Voidptr;
    fn sp_object(a: Voidptr) -> Voidptr;
    fn sp_open(a: Voidptr) -> c_int;
    fn sp_drop(a: Voidptr) -> c_int;
    fn sp_destroy(a: Voidptr) -> c_int;
    fn sp_error(a: Voidptr) -> c_int;
    fn sp_asynchronous(a: Voidptr) -> Voidptr;
    fn sp_poll(a: Voidptr) -> Voidptr;
    fn sp_setobject(a: Voidptr, b: *const c_char, c: Voidptr) -> c_int;
    fn sp_setstring(a: Voidptr, b: *const c_char, c: *const c_void, d: c_int) -> c_int;
    fn sp_setint(a: Voidptr, b: *const c_char, c: i64) -> c_int;

    fn sp_getobject(a: Voidptr, b: *const u8) -> Voidptr;
    fn sp_getstring(a: Voidptr, b: *const u8, c: *mut c_int) -> Voidptr;
    fn sp_getint(a: Voidptr, b: *mut u8) -> i64;
    fn sp_set(a: Voidptr, b: Voidptr) -> c_int;
    fn sp_update(a: Voidptr, b: Voidptr) -> c_int;
    fn sp_delete(a: Voidptr, b: Voidptr) -> c_int;
    fn sp_get(a: Voidptr, b: Voidptr) -> Voidptr;
    fn sp_cursor(a: Voidptr, b: Voidptr) -> Voidptr;
    fn sp_begin(a: Voidptr) -> Voidptr;
    fn sp_prepare(a: Voidptr) -> c_int;
    fn sp_commit(a: Voidptr) -> c_int;
}

pub struct Env {
    env: Voidptr
}

pub struct Db {
    db: Voidptr
}

unsafe impl Sync for Db {}
unsafe impl Send for Db {}

unsafe impl Sync for Env {}
unsafe impl Send for Env {}

impl Env {
    pub fn new() -> Env {
        Env {env: unsafe{sp_env()}}
    }

    pub fn destroy(self) {
        unsafe{sp_destroy(self.env)};
    }

    pub fn db(&mut self, dbname: &str) {
        let dbname = CString::new(dbname).unwrap();
        unsafe {sp_setstring(self.env, "db\0".as_ptr() as *const c_char, dbname.as_ptr() as *const c_void, 0) };
    }

    pub fn open(&mut self) {
        unsafe {sp_open(self.env)};
    }

    pub fn get_object(&mut self, attr: &str) -> Voidptr {
        let attr = CString::new(attr).unwrap();
        unsafe {sp_getobject(self.env, attr.as_ptr() as *const u8)}
    }

    pub fn get_db(&mut self, dbname: &str) -> Option<Db> {
        let dbstr = "db.".to_string() + dbname;
        let attr = CString::new(&dbstr[..]).unwrap();
        unsafe {
            let obj = sp_getobject(self.env, attr.as_ptr() as *const u8);
            if obj.is_null() {
                None
            }
            else {
                Some(Db {db: obj})
            }
        }
    }

    pub fn setattr(&mut self, key: &str, val: &str) {
        let key = CString::new(key).unwrap();
        let val = CString::new(val).unwrap();
        unsafe {sp_setstring(self.env, key.as_ptr(), val.as_ptr() as *const c_void, 0) };
    }

    pub fn setintattr(&mut self, key: &str, val: i64) {
        let key = CString::new(key).unwrap();
        unsafe {sp_setint(self.env, key.as_ptr(), val) };
    }

}

pub struct Object {
    obj: Voidptr
}

impl Object {
    pub fn get_value<'a>(&'a mut self) -> Option<&'a[u8]> {
        unsafe {
            if self.obj.is_null() {
                return None;
            }
            let mut sz = 0;
            let valptr = sp_getstring(self.obj, "value\0".as_ptr(), &mut sz);

            // XXX: what if we stored an empty value?
            if valptr.is_null() {
                return None;
            }

            let s = slice::from_raw_parts(valptr as *const u8, sz as usize);
            return Some(s);
        }
    }
}

impl Drop for Object {
    fn drop(&mut self) {
        unsafe {sp_destroy(self.obj);}
    }
}

impl Db {
    pub fn set(&mut self, key: &[u8], value: &[u8]) {
        unsafe {
            let o = sp_object(self.db);
            sp_setstring(o, "key\0".as_ptr() as *const c_char, key.as_ptr() as Voidptr, key.len() as i32);
            sp_setstring(o, "value\0".as_ptr() as *const c_char, value.as_ptr() as Voidptr, value.len() as i32);
            sp_set(self.db, o);
            sp_destroy(o);
        }
    }

    pub fn get(&self, key: &[u8]) -> Object {
        unsafe {
            let o = sp_object(self.db);
            sp_setstring(o, "key\0".as_ptr() as *const c_char, key.as_ptr() as Voidptr, key.len() as i32);
            let o2 = sp_get(self.db, o);
            return Object{obj: o2};
        }
    }
}
<commit_msg>Take &self instead of &mut self in get_db()<commit_after>#![feature(libc)]

extern crate libc;

use libc::{c_void, c_int, c_char};
use std::ffi::CString;
use std::slice;

pub type Voidptr = *mut c_void;

#[link(name = "sophia", kind="static")]
extern {
    fn sp_env() -> Voidptr;
    fn sp_object(a: Voidptr) -> Voidptr;
    fn sp_open(a: Voidptr) -> c_int;
    fn sp_drop(a: Voidptr) -> c_int;
    fn sp_destroy(a: Voidptr) -> c_int;
    fn sp_error(a: Voidptr) -> c_int;
    fn sp_asynchronous(a: Voidptr) -> Voidptr;
    fn sp_poll(a: Voidptr) -> Voidptr;
    fn sp_setobject(a: Voidptr, b: *const c_char, c: Voidptr) -> c_int;
    fn sp_setstring(a: Voidptr, b: *const c_char, c: *const c_void, d: c_int) -> c_int;
    fn sp_setint(a: Voidptr, b: *const c_char, c: i64) -> c_int;

    fn sp_getobject(a: Voidptr, b: *const u8) -> Voidptr;
    fn sp_getstring(a: Voidptr, b: *const u8, c: *mut c_int) -> Voidptr;
    fn sp_getint(a: Voidptr, b: *mut u8) -> i64;
    fn sp_set(a: Voidptr, b: Voidptr) -> c_int;
    fn sp_update(a: Voidptr, b: Voidptr) -> c_int;
    fn sp_delete(a: Voidptr, b: Voidptr) -> c_int;
    fn sp_get(a: Voidptr, b: Voidptr) -> Voidptr;
    fn sp_cursor(a: Voidptr, b: Voidptr) -> Voidptr;
    fn sp_begin(a: Voidptr) -> Voidptr;
    fn sp_prepare(a: Voidptr) -> c_int;
    fn sp_commit(a: Voidptr) -> c_int;
}

pub struct Env {
    env: Voidptr
}

pub struct Db {
    db: Voidptr
}

unsafe impl Sync for Db {}
unsafe impl Send for Db {}

unsafe impl Sync for Env {}
unsafe impl Send for Env {}

impl Env {
    pub fn new() -> Env {
        Env {env: unsafe{sp_env()}}
    }

    pub fn destroy(self) {
        unsafe{sp_destroy(self.env)};
    }

    pub fn db(&mut self, dbname: &str) {
        let dbname = CString::new(dbname).unwrap();
        unsafe {sp_setstring(self.env, "db\0".as_ptr() as *const c_char, dbname.as_ptr() as *const c_void, 0) };
    }

    pub fn open(&mut self) {
        unsafe {sp_open(self.env)};
    }

    pub fn get_object(&mut self, attr: &str) -> Voidptr {
        let attr = CString::new(attr).unwrap();
        unsafe {sp_getobject(self.env, attr.as_ptr() as *const u8)}
    }

    pub fn get_db(&self, dbname: &str) -> Option<Db> {
        let dbstr = "db.".to_string() + dbname;
        let attr = CString::new(&dbstr[..]).unwrap();
        unsafe {
            let obj = sp_getobject(self.env, attr.as_ptr() as *const u8);
            if obj.is_null() {
                None
            }
            else {
                Some(Db {db: obj})
            }
        }
    }

    pub fn setattr(&mut self, key: &str, val: &str) {
        let key = CString::new(key).unwrap();
        let val = CString::new(val).unwrap();
        unsafe {sp_setstring(self.env, key.as_ptr(), val.as_ptr() as *const c_void, 0) };
    }

    pub fn setintattr(&mut self, key: &str, val: i64) {
        let key = CString::new(key).unwrap();
        unsafe {sp_setint(self.env, key.as_ptr(), val) };
    }

}

pub struct Object {
    obj: Voidptr
}

impl Object {
    pub fn get_value<'a>(&'a mut self) -> Option<&'a[u8]> {
        unsafe {
            if self.obj.is_null() {
                return None;
            }
            let mut sz = 0;
            let valptr = sp_getstring(self.obj, "value\0".as_ptr(), &mut sz);

            // XXX: what if we stored an empty value?
            if valptr.is_null() {
                return None;
            }

            let s = slice::from_raw_parts(valptr as *const u8, sz as usize);
            return Some(s);
        }
    }
}

impl Drop for Object {
    fn drop(&mut self) {
        unsafe {sp_destroy(self.obj);}
    }
}

impl Db {
    pub fn set(&mut self, key: &[u8], value: &[u8]) {
        unsafe {
            let o = sp_object(self.db);
            sp_setstring(o, "key\0".as_ptr() as *const c_char, key.as_ptr() as Voidptr, key.len() as i32);
            sp_setstring(o, "value\0".as_ptr() as *const c_char, value.as_ptr() as Voidptr, value.len() as i32);
            sp_set(self.db, o);
            sp_destroy(o);
        }
    }

    pub fn get(&self, key: &[u8]) -> Object {
        unsafe {
            let o = sp_object(self.db);
            sp_setstring(o, "key\0".as_ptr() as *const c_char, key.as_ptr() as Voidptr, key.len() as i32);
            let o2 = sp_get(self.db, o);
            return Object{obj: o2};
        }
    }
}
<|endoftext|>


<commit_before>use base::fnv::FnvMap;
use base::kind::{ArcKind, Kind, KindEnv};
use base::symbol::{Symbol, SymbolRef};
use base::types::{Alias, ArcType, TypeEnv, Type};

pub use self::Instruction::*;

pub type VmIndex = u32;
pub type VmTag = u32;
pub type VmInt = isize;

/// Enum which represent the instructions executed by the virtual machine.
///
/// The binary arithmetic instructions pop two values of the stack and then push the result.
#[derive(Copy, Clone, Debug)]
pub enum Instruction {
    /// Push an integer to the stack
    PushInt(isize),
    /// Push a byte to the stack
    PushByte(u8),
    /// Push a float to the stack
    PushFloat(f64),
    /// Push a string to the stack by loading the string at `index` in the currently executing
    /// function
    PushString(VmIndex),
    /// Push a variable to the stack by loading the upvariable at `index` from the currently
    /// executing function
    PushUpVar(VmIndex),
    /// Push the value at `index`
    Push(VmIndex),
    /// Push the value at `index`
    PushGlobal(VmIndex),
    /// Call a function by passing it `args` number of arguments. The function is at the index in
    /// the stack just before the arguments. After the call is all arguments are removed and the
    /// function is replaced by the result of the call.
    Call(VmIndex),
    /// Tailcalls a function, removing the current stack frame before calling it.
    /// See `Call`.
    TailCall(VmIndex),
    /// Constructs a data value tagged by `tag` by taking the top `args` values of the stack.
    Construct {
        /// The tag of the data
        tag: VmIndex,
        /// How many arguments that is taken from the stack to construct the data.
        args: VmIndex,
    },
    ConstructRecord {
        /// Index to the specification describing which fields this record contains
        record: VmIndex,
        /// How many arguments that is taken from the stack to construct the data.
        args: VmIndex,
    },
    /// Constructs an array containing `args` values.
    ConstructArray(VmIndex),
    /// Retrieves the field at `offset` of an object at the top of the stack. The result of the
    /// field access replaces the object on the stack.
    GetOffset(VmIndex),
    /// Retrieves the field of a polymorphic record by retrieving the string constant at `index`
    /// and using that to retrieve lookup the field. The result of the
    /// field access replaces the object on the stack.
    GetField(VmIndex),
    /// Splits a object, pushing all contained values to the stack.
    Split,
    /// Tests if the value at the top of the stack is tagged with `tag`. Pushes `True` if the tag
    /// matches, otherwise `False`
    TestTag(VmTag),
    /// Jumps to the instruction at `index` in the currently executing function.
    Jump(VmIndex),
    /// Jumps to the instruction at `index` in the currently executing function if `True` is at the
    /// top of the stack and pops that value.
    CJump(VmIndex),
    /// Pops the top `n` values from the stack.
    Pop(VmIndex),
    /// Pops the top value from the stack, then pops `n` more values, finally the first value is
    /// pushed back to the stack.
    Slide(VmIndex),

    /// Creates a closure with the function at `function_index` of the currently executing function
    /// and `upvars` upvariables popped from the top of the stack.
    MakeClosure {
        /// The index in the currently executing function which the function data is located at
        function_index: VmIndex,
        /// How many upvariables the closure contains
        upvars: VmIndex,
    },
    /// Creates a closure with the function at `function_index` of the currently executing
    /// function. The closure has room for `upvars` upvariables but these are not filled until the
    /// matching call to `ClosureClosure` is executed.
    NewClosure {
        /// The index in the currently executing function which the function data is located at
        function_index: VmIndex,
        /// How many upvariables the closure contains
        upvars: VmIndex,
    },
    /// Fills the previously allocated closure with `n` upvariables.
    CloseClosure(VmIndex),

    AddInt,
    SubtractInt,
    MultiplyInt,
    DivideInt,
    IntLT,
    IntEQ,

    AddByte,
    SubtractByte,
    MultiplyByte,
    DivideByte,
    ByteLT,
    ByteEQ,

    AddFloat,
    SubtractFloat,
    MultiplyFloat,
    DivideFloat,
    FloatLT,
    FloatEQ,
}

impl Instruction {
    /// Returns by how much the stack is adjusted when executing the instruction `self`.
    pub fn adjust(&self) -> i32 {
        match *self {
            PushInt(_) | PushByte(_) | PushFloat(_) | PushString(_) | Push(_) | PushGlobal(_) => 1,
            Call(n) => -(n as i32),
            TailCall(n) => -(n as i32),
            Construct { args, .. } |
            ConstructRecord { args, .. } |
            ConstructArray(args) => 1 - args as i32,
            GetField(_) | GetOffset(_) => 0,
            // The number of added stack slots are handled separately as the type is needed to
            // calculate the number of slots needed
            Split => -1,
            TestTag(_) => 1,
            Jump(_) => 0,
            CJump(_) => -1,
            Pop(n) => -(n as i32),
            Slide(n) => -(n as i32),
            MakeClosure { .. } => 1,
            NewClosure { .. } => 1,
            CloseClosure(_) => -1,
            PushUpVar(_) => 1,
            AddInt | SubtractInt | MultiplyInt | DivideInt | IntLT | IntEQ | AddFloat |
            AddByte | SubtractByte | MultiplyByte | DivideByte | ByteLT | ByteEQ |
            SubtractFloat | MultiplyFloat | DivideFloat | FloatLT | FloatEQ => -1,
        }
    }
}

#[derive(Debug)]
pub struct TypeInfos {
    pub id_to_type: FnvMap<String, Alias<Symbol, ArcType>>,
    pub type_to_id: FnvMap<ArcType, ArcType>,
}

impl KindEnv for TypeInfos {
    fn find_kind(&self, type_name: &SymbolRef) -> Option<ArcKind> {
        let type_name = AsRef::<str>::as_ref(type_name);
        self.id_to_type
            .get(type_name)
            .map(|alias| {
                alias.args.iter().rev().fold(Kind::typ(),
                                             |acc, arg| Kind::function(arg.kind.clone(), acc))
            })
    }
}

impl TypeEnv for TypeInfos {
    fn find_type(&self, id: &SymbolRef) -> Option<&ArcType> {
        let id = AsRef::<str>::as_ref(id);
        self.id_to_type
            .iter()
            .filter_map(|(_, ref alias)| {
                match **alias.unresolved_type() {
                    Type::Variant(ref row) => {
                        row.row_iter().find(|field| field.name.as_ref() == id)
                    }
                    _ => None,
                }
            })
            .next()
            .map(|field| &field.typ)
    }

    fn find_type_info(&self, id: &SymbolRef) -> Option<&Alias<Symbol, ArcType>> {
        let id = AsRef::<str>::as_ref(id);
        self.id_to_type
            .get(id)
    }

    fn find_record(&self, fields: &[Symbol]) -> Option<(&ArcType, &ArcType)> {
        self.id_to_type
            .iter()
            .find(|&(_, alias)| {
                match **alias.unresolved_type() {
                    Type::Record(ref row) => {
                        fields.iter()
                            .all(|name| row.row_iter().any(|f| f.name.as_ref() == name.as_ref()))
                    }
                    _ => false,
                }
            })
            .and_then(|t| {
                // FIXME Don't use unresolved type
                let typ = t.1.unresolved_type();
                self.type_to_id.get(typ).map(|id_type| (id_type, typ))
            })
    }
}

impl TypeInfos {
    pub fn new() -> TypeInfos {
        TypeInfos {
            id_to_type: FnvMap::default(),
            type_to_id: FnvMap::default(),
        }
    }

    pub fn extend(&mut self, other: TypeInfos) {
        let TypeInfos { id_to_type, type_to_id } = other;
        self.id_to_type.extend(id_to_type);
        self.type_to_id.extend(type_to_id);
    }
}
<commit_msg>refactor(vm): Remove the type_to_id field of TypeInfos<commit_after>use base::fnv::FnvMap;
use base::kind::{ArcKind, Kind, KindEnv};
use base::symbol::{Symbol, SymbolRef};
use base::types::{Alias, ArcType, TypeEnv, Type};

pub use self::Instruction::*;

pub type VmIndex = u32;
pub type VmTag = u32;
pub type VmInt = isize;

/// Enum which represent the instructions executed by the virtual machine.
///
/// The binary arithmetic instructions pop two values of the stack and then push the result.
#[derive(Copy, Clone, Debug)]
pub enum Instruction {
    /// Push an integer to the stack
    PushInt(isize),
    /// Push a byte to the stack
    PushByte(u8),
    /// Push a float to the stack
    PushFloat(f64),
    /// Push a string to the stack by loading the string at `index` in the currently executing
    /// function
    PushString(VmIndex),
    /// Push a variable to the stack by loading the upvariable at `index` from the currently
    /// executing function
    PushUpVar(VmIndex),
    /// Push the value at `index`
    Push(VmIndex),
    /// Push the value at `index`
    PushGlobal(VmIndex),
    /// Call a function by passing it `args` number of arguments. The function is at the index in
    /// the stack just before the arguments. After the call is all arguments are removed and the
    /// function is replaced by the result of the call.
    Call(VmIndex),
    /// Tailcalls a function, removing the current stack frame before calling it.
    /// See `Call`.
    TailCall(VmIndex),
    /// Constructs a data value tagged by `tag` by taking the top `args` values of the stack.
    Construct {
        /// The tag of the data
        tag: VmIndex,
        /// How many arguments that is taken from the stack to construct the data.
        args: VmIndex,
    },
    ConstructRecord {
        /// Index to the specification describing which fields this record contains
        record: VmIndex,
        /// How many arguments that is taken from the stack to construct the data.
        args: VmIndex,
    },
    /// Constructs an array containing `args` values.
    ConstructArray(VmIndex),
    /// Retrieves the field at `offset` of an object at the top of the stack. The result of the
    /// field access replaces the object on the stack.
    GetOffset(VmIndex),
    /// Retrieves the field of a polymorphic record by retrieving the string constant at `index`
    /// and using that to retrieve lookup the field. The result of the
    /// field access replaces the object on the stack.
    GetField(VmIndex),
    /// Splits a object, pushing all contained values to the stack.
    Split,
    /// Tests if the value at the top of the stack is tagged with `tag`. Pushes `True` if the tag
    /// matches, otherwise `False`
    TestTag(VmTag),
    /// Jumps to the instruction at `index` in the currently executing function.
    Jump(VmIndex),
    /// Jumps to the instruction at `index` in the currently executing function if `True` is at the
    /// top of the stack and pops that value.
    CJump(VmIndex),
    /// Pops the top `n` values from the stack.
    Pop(VmIndex),
    /// Pops the top value from the stack, then pops `n` more values, finally the first value is
    /// pushed back to the stack.
    Slide(VmIndex),

    /// Creates a closure with the function at `function_index` of the currently executing function
    /// and `upvars` upvariables popped from the top of the stack.
    MakeClosure {
        /// The index in the currently executing function which the function data is located at
        function_index: VmIndex,
        /// How many upvariables the closure contains
        upvars: VmIndex,
    },
    /// Creates a closure with the function at `function_index` of the currently executing
    /// function. The closure has room for `upvars` upvariables but these are not filled until the
    /// matching call to `ClosureClosure` is executed.
    NewClosure {
        /// The index in the currently executing function which the function data is located at
        function_index: VmIndex,
        /// How many upvariables the closure contains
        upvars: VmIndex,
    },
    /// Fills the previously allocated closure with `n` upvariables.
    CloseClosure(VmIndex),

    AddInt,
    SubtractInt,
    MultiplyInt,
    DivideInt,
    IntLT,
    IntEQ,

    AddByte,
    SubtractByte,
    MultiplyByte,
    DivideByte,
    ByteLT,
    ByteEQ,

    AddFloat,
    SubtractFloat,
    MultiplyFloat,
    DivideFloat,
    FloatLT,
    FloatEQ,
}

impl Instruction {
    /// Returns by how much the stack is adjusted when executing the instruction `self`.
    pub fn adjust(&self) -> i32 {
        match *self {
            PushInt(_) | PushByte(_) | PushFloat(_) | PushString(_) | Push(_) | PushGlobal(_) => 1,
            Call(n) => -(n as i32),
            TailCall(n) => -(n as i32),
            Construct { args, .. } |
            ConstructRecord { args, .. } |
            ConstructArray(args) => 1 - args as i32,
            GetField(_) | GetOffset(_) => 0,
            // The number of added stack slots are handled separately as the type is needed to
            // calculate the number of slots needed
            Split => -1,
            TestTag(_) => 1,
            Jump(_) => 0,
            CJump(_) => -1,
            Pop(n) => -(n as i32),
            Slide(n) => -(n as i32),
            MakeClosure { .. } => 1,
            NewClosure { .. } => 1,
            CloseClosure(_) => -1,
            PushUpVar(_) => 1,
            AddInt | SubtractInt | MultiplyInt | DivideInt | IntLT | IntEQ | AddFloat |
            AddByte | SubtractByte | MultiplyByte | DivideByte | ByteLT | ByteEQ |
            SubtractFloat | MultiplyFloat | DivideFloat | FloatLT | FloatEQ => -1,
        }
    }
}

#[derive(Debug)]
pub struct TypeInfos {
    pub id_to_type: FnvMap<String, Alias<Symbol, ArcType>>,
}

impl KindEnv for TypeInfos {
    fn find_kind(&self, type_name: &SymbolRef) -> Option<ArcKind> {
        let type_name = AsRef::<str>::as_ref(type_name);
        self.id_to_type
            .get(type_name)
            .map(|alias| {
                alias.args.iter().rev().fold(Kind::typ(),
                                             |acc, arg| Kind::function(arg.kind.clone(), acc))
            })
    }
}

impl TypeEnv for TypeInfos {
    fn find_type(&self, id: &SymbolRef) -> Option<&ArcType> {
        let id = AsRef::<str>::as_ref(id);
        self.id_to_type
            .iter()
            .filter_map(|(_, ref alias)| {
                match **alias.unresolved_type() {
                    Type::Variant(ref row) => {
                        row.row_iter().find(|field| field.name.as_ref() == id)
                    }
                    _ => None,
                }
            })
            .next()
            .map(|field| &field.typ)
    }

    fn find_type_info(&self, id: &SymbolRef) -> Option<&Alias<Symbol, ArcType>> {
        let id = AsRef::<str>::as_ref(id);
        self.id_to_type
            .get(id)
    }

    fn find_record(&self, fields: &[Symbol]) -> Option<(&ArcType, &ArcType)> {
        None
    }
}

impl TypeInfos {
    pub fn new() -> TypeInfos {
        TypeInfos { id_to_type: FnvMap::default() }
    }

    pub fn extend(&mut self, other: TypeInfos) {
        let TypeInfos { id_to_type } = other;
        self.id_to_type.extend(id_to_type);
    }
}
<|endoftext|>