<commit_before>use gfx;
use gfx::traits::FactoryExt;

pub static VERTEX_SRC: &'static [u8] = b"
    #version 150 core

    layout (std140) uniform cb_VertexArgs {
        uniform mat4 u_Proj;
        uniform mat4 u_View;
        uniform mat4 u_Model;
    };

    in vec3 a_Pos;
    in vec3 a_Normal;
    in vec2 a_TexCoord;

    out VertexData {
        vec4 Position;
        vec3 Normal;
        vec2 TexCoord;
    } v_Out;

    void main() {
        v_Out.Position = u_Model * vec4(a_Pos, 1.0);
        v_Out.Normal = mat3(u_Model) * a_Normal;
        v_Out.TexCoord = a_TexCoord;
        gl_Position = u_Proj * u_View * v_Out.Position;
    }
";

pub static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core
    #define MAX_NUM_TOTAL_LIGHTS 512

    layout (std140) uniform cb_FragmentArgs {
        int u_LightCount;
    };

    struct Light {
        vec4 propagation;
        vec4 center;
        vec4 color;
    };

    layout (std140) uniform u_Lights {
        Light light[MAX_NUM_TOTAL_LIGHTS];
    };

    uniform sampler2D t_Ka;
    uniform sampler2D t_Kd;

    in VertexData {
        vec4 Position;
        vec3 Normal;
        vec2 TexCoord;
    } v_In;

    out vec4 o_Color;

    void main() {
        vec4 kd = texture(t_Kd, v_In.TexCoord);
        vec4 color = texture(t_Ka, v_In.TexCoord);
        for (int i = 0; i < u_LightCount; i++) {
            vec4 delta = light[i].center - v_In.Position;
            float dist = length(delta);
            float inv_dist = 1. / dist;
            vec4 light_to_point_normal = delta * inv_dist;
            float intensity = dot(light[i].propagation.xyz, vec3(1., inv_dist, inv_dist * inv_dist));
            color += kd * light[i].color * intensity * max(0, dot(light_to_point_normal, vec4(v_In.Normal, 0.)));
        }
        o_Color = color;
    }
";

/// Placeholder Color format
pub type ColorFormat = gfx::format::Rgba8;
/// Placeholder Depth Format
pub type DepthFormat = gfx::format::DepthStencil;

// placeholder
gfx_vertex_struct!(VertexPosNormal {
	pos: [f32; 3] = "a_Pos",
	normal: [f32; 3] = "a_Normal",
	tex_coord: [f32; 2] = "a_TexCoord",
});

/// holds a 1x1 texture that can be used to store constant colors
pub struct ConstantColorTexture<R: gfx::Resources> {
	texture: gfx::handle::Texture<R, gfx::format::R8_G8_B8_A8>,
	view: gfx::handle::ShaderResourceView<R, [f32; 4]>
}

impl<R: gfx::Resources> ConstantColorTexture<R> {
	/// Create a texture buffer
	pub fn new<F>(factory: &mut F) -> ConstantColorTexture<R>
		where F: gfx::Factory<R> {
		let kind = gfx::tex::Kind::D2(1, 1, gfx::tex::AaMode::Single);
		let tex = factory.create_texture::<gfx::format::R8_G8_B8_A8>(
            kind,
            1,
            gfx::SHADER_RESOURCE,
            gfx::Usage::Dynamic,
            Some(gfx::format::ChannelType::Unorm)
        ).unwrap();
		let levels = (0, tex.get_info().levels - 1);
		let view = factory.view_texture_as_shader_resource::<gfx::format::Rgba8>(&tex, levels, gfx::format::Swizzle::new()).unwrap();
		ConstantColorTexture {
			texture: tex,
			view: view
		}
	}
}

pub struct ColorBuffer<R: gfx::Resources> {
	pub color: gfx::handle::RenderTargetView<R, ColorFormat>,
	pub output_depth: gfx::handle::DepthStencilView<R, DepthFormat>
}

pub type GFormat = [f32; 4];

gfx_defines!(
    constant PointLight {
        propagation: [f32; 4] = "propagation",
        center: [f32; 4] = "center",
        color: [f32; 4] = "color",
    }

    constant VertexArgs {
        proj: [[f32; 4]; 4] = "u_Proj",
      view: [[f32; 4]; 4] = "u_View",
        model: [[f32; 4]; 4] = "u_Model",
    }

    constant FragmentArgs {
        light_count: i32 = "u_LightCount",
    }

    pipeline shaded {
        vbuf: gfx::VertexBuffer<VertexPosNormal> = (),
        vertex_args: gfx::ConstantBuffer<VertexArgs> = "cb_VertexArgs",
        fragment_args: gfx::ConstantBuffer<FragmentArgs> = "cb_FragmentArgs",
        lights: gfx::ConstantBuffer<PointLight> = "u_Lights",
        out_ka: gfx::RenderTarget<gfx::format::Rgba8> = "o_Color",
        out_depth: gfx::DepthTarget<gfx::format::DepthStencil> = gfx::preset::depth::LESS_EQUAL_WRITE,
        ka: gfx::TextureSampler<[f32; 4]> = "t_Ka",
        kd: gfx::TextureSampler<[f32; 4]> = "t_Kd",
    }
);

pub struct DrawShaded<R: gfx::Resources> {
	vertex: gfx::handle::Buffer<R, VertexArgs>,
	fragment: gfx::handle::Buffer<R, FragmentArgs>,
	lights: gfx::handle::Buffer<R, PointLight>,
	pso: gfx::pso::PipelineState<R, shaded::Meta>,
	sampler: gfx::handle::Sampler<R>,
	ka: ConstantColorTexture<R>,
	kd: ConstantColorTexture<R>
}

impl<R: gfx::Resources> DrawShaded<R> {
	pub fn new<F>(factory: &mut F) -> DrawShaded<R>
		where R: gfx::Resources,
		      F: gfx::Factory<R> {
		let lights = factory.create_constant_buffer(512);
		let vertex = factory.create_constant_buffer(1);
		let fragment = factory.create_constant_buffer(1);
		let pso = factory.create_pipeline_simple(VERTEX_SRC, FRAGMENT_SRC, shaded::new())
		                 .unwrap();

		let sampler =
			factory.create_sampler(gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Scale,
			                                                  gfx::tex::WrapMode::Clamp));

		DrawShaded {
			vertex: vertex,
			fragment: fragment,
			lights: lights,
			pso: pso,
			ka: ConstantColorTexture::new(factory),
			kd: ConstantColorTexture::new(factory),
			sampler: sampler
		}
	}

	fn draw<C: gfx::CommandBuffer<R>>(self: &mut DrawShaded<R>,
	                                  target: &ColorBuffer<R>,
	                                  encoder: &mut gfx::Encoder<R, C>,
	                                  lights: &Vec<PointLight>) {

		let count = lights.len();
		while lights.len() < 512 {
			lights.push(PointLight {
				propagation: [0., 0., 0., 0.],
				color: [0., 0., 0., 0.],
				center: [0., 0., 0., 0.]
			})
		}
		encoder.update_buffer(&self.lights, &lights[..], 0).unwrap();

		encoder.update_constant_buffer(&self.vertex,
		                               &VertexArgs {
			                               proj: projection,
			                               view: view,
			                               model: transform
		                               });

		encoder.update_constant_buffer(&self.fragment, &FragmentArgs { light_count: count as i32 });

		let ka = e.ka.to_view(&self.ka, encoder);
		let kd = e.kd.to_view(&self.kd, encoder);

		encoder.draw(&e.slice,
		             &self.pso,
		             &shaded::Data {
			             vbuf: e.buffer.clone(),
			             fragment_args: self.fragment.clone(),
			             vertex_args: self.vertex.clone(),
			             lights: self.lights.clone(),
			             out_ka: target.color.clone(),
			             out_depth: target.output_depth.clone(),
			             ka: (ka, self.sampler.clone()),
			             kd: (kd, self.sampler.clone())
		             });
	}
}

fn pad(x: [f32; 3]) -> [f32; 4] {
	[x[0], x[1], x[2], 0.]
}
<commit_msg>Still does not compile<commit_after>use gfx;
use gfx::traits::FactoryExt;

pub static VERTEX_SRC: &'static [u8] = b"
    #version 150 core

    layout (std140) uniform cb_VertexArgs {
        uniform mat4 u_Proj;
        uniform mat4 u_View;
        uniform mat4 u_Model;
    };

    in vec3 a_Pos;
    in vec3 a_Normal;
    in vec2 a_TexCoord;

    out VertexData {
        vec4 Position;
        vec3 Normal;
        vec2 TexCoord;
    } v_Out;

    void main() {
        v_Out.Position = u_Model * vec4(a_Pos, 1.0);
        v_Out.Normal = mat3(u_Model) * a_Normal;
        v_Out.TexCoord = a_TexCoord;
        gl_Position = u_Proj * u_View * v_Out.Position;
    }
";

pub static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core
    #define MAX_NUM_TOTAL_LIGHTS 512

    layout (std140) uniform cb_FragmentArgs {
        int u_LightCount;
    };

    struct Light {
        vec4 propagation;
        vec4 center;
        vec4 color;
    };

    layout (std140) uniform u_Lights {
        Light light[MAX_NUM_TOTAL_LIGHTS];
    };

    uniform sampler2D t_Ka;
    uniform sampler2D t_Kd;

    in VertexData {
        vec4 Position;
        vec3 Normal;
        vec2 TexCoord;
    } v_In;

    out vec4 o_Color;

    void main() {
        vec4 kd = texture(t_Kd, v_In.TexCoord);
        vec4 color = texture(t_Ka, v_In.TexCoord);
        for (int i = 0; i < u_LightCount; i++) {
            vec4 delta = light[i].center - v_In.Position;
            float dist = length(delta);
            float inv_dist = 1. / dist;
            vec4 light_to_point_normal = delta * inv_dist;
            float intensity = dot(light[i].propagation.xyz, vec3(1., inv_dist, inv_dist * inv_dist));
            color += kd * light[i].color * intensity * max(0, dot(light_to_point_normal, vec4(v_In.Normal, 0.)));
        }
        o_Color = color;
    }
";

/// Placeholder Color format
pub type ColorFormat = gfx::format::Rgba8;
/// Placeholder Depth Format
pub type DepthFormat = gfx::format::DepthStencil;

// placeholder
gfx_vertex_struct!( VertexPosNormal {
	pos: [f32; 3] = "a_Pos",
	normal: [f32; 3] = "a_Normal",
	tex_coord: [f32; 2] = "a_TexCoord",
});

/// holds a 1x1 texture that can be used to store constant colors
pub struct ConstantColorTexture<R: gfx::Resources> {
	texture: gfx::handle::Texture<R, gfx::format::R8_G8_B8_A8>,
	view: gfx::handle::ShaderResourceView<R, [f32; 4]>
}

impl<R: gfx::Resources> ConstantColorTexture<R> {
	/// Create a texture buffer
	pub fn new<F>(factory: &mut F) -> ConstantColorTexture<R>
		where F: gfx::Factory<R> {
		let kind = gfx::tex::Kind::D2(1, 1, gfx::tex::AaMode::Single);
		let tex = factory.create_texture::<gfx::format::R8_G8_B8_A8>(
            kind,
            1,
            gfx::SHADER_RESOURCE,
            gfx::Usage::Dynamic,
            Some(gfx::format::ChannelType::Unorm)
        ).unwrap();
		let levels = (0, tex.get_info().levels - 1);
		let view = factory.view_texture_as_shader_resource::<gfx::format::Rgba8>(&tex, levels, gfx::format::Swizzle::new()).unwrap();
		ConstantColorTexture {
			texture: tex,
			view: view
		}
	}
}

pub struct ColorBuffer<R: gfx::Resources> {
	pub color: gfx::handle::RenderTargetView<R, ColorFormat>,
	pub output_depth: gfx::handle::DepthStencilView<R, DepthFormat>
}

pub type GFormat = [f32; 4];

gfx_defines!(
    constant PointLight {
        propagation: [f32; 4] = "propagation",
        center: [f32; 4] = "center",
        color: [f32; 4] = "color",
    }

    constant VertexArgs {
        proj: [[f32; 4]; 4] = "u_Proj",
        view: [[f32; 4]; 4] = "u_View",
        model: [[f32; 4]; 4] = "u_Model",
    }

    constant FragmentArgs {
        light_count: i32 = "u_LightCount",
    }

    pipeline shaded {
        vbuf: gfx::VertexBuffer<VertexPosNormal> = (),
        vertex_args: gfx::ConstantBuffer<VertexArgs> = "cb_VertexArgs",
        fragment_args: gfx::ConstantBuffer<FragmentArgs> = "cb_FragmentArgs",
        lights: gfx::ConstantBuffer<PointLight> = "u_Lights",
        out_ka: gfx::RenderTarget<gfx::format::Rgba8> = "o_Color",
        out_depth: gfx::DepthTarget<gfx::format::DepthStencil> = gfx::preset::depth::LESS_EQUAL_WRITE,
        ka: gfx::TextureSampler<[f32; 4]> = "t_Ka",
        kd: gfx::TextureSampler<[f32; 4]> = "t_Kd",
    }
);

pub struct DrawShaded<R: gfx::Resources> {
	vertex: gfx::handle::Buffer<R, VertexArgs>,
	fragment: gfx::handle::Buffer<R, FragmentArgs>,
	lights: gfx::handle::Buffer<R, PointLight>,
	pso: gfx::pso::PipelineState<R, shaded::Meta>,
	sampler: gfx::handle::Sampler<R>,
	ka: ConstantColorTexture<R>,
	kd: ConstantColorTexture<R>
}

impl<R: gfx::Resources> DrawShaded<R> {
	pub fn new<F>(factory: &mut F) -> DrawShaded<R>
		where R: gfx::Resources,
		      F: gfx::Factory<R> {
		let lights = factory.create_constant_buffer(512);
		let vertex = factory.create_constant_buffer(1);
		let fragment = factory.create_constant_buffer(1);
		let pso = factory.create_pipeline_simple(VERTEX_SRC, FRAGMENT_SRC, shaded::new())
		                 .unwrap();

		let sampler =
			factory.create_sampler(gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Scale,
			                                                  gfx::tex::WrapMode::Clamp));

		DrawShaded {
			vertex: vertex,
			fragment: fragment,
			lights: lights,
			pso: pso,
			ka: ConstantColorTexture::new(factory),
			kd: ConstantColorTexture::new(factory),
			sampler: sampler
		}
	}

	fn draw<C: gfx::CommandBuffer<R>>(&self,
	                                  target: &ColorBuffer<R>,
	                                  encoder: &mut gfx::Encoder<R, C>,
	                                  lights: &Vec<PointLight>) {

		let count = lights.len();
		while lights.len() < 512 {
			lights.push(PointLight {
				propagation: [0., 0., 0., 0.],
				color: [0., 0., 0., 0.],
				center: [0., 0., 0., 0.]
			})
		}
		encoder.update_buffer(&self.lights, &lights[..], 0).unwrap();

		encoder.update_constant_buffer(&self.vertex,
		                               &VertexArgs {
			                               proj: projection,
			                               view: view,
			                               model: transform
		                               });

		encoder.update_constant_buffer(&self.fragment, &FragmentArgs { light_count: count as i32 });

		let ka = e.ka.to_view(&self.ka, encoder);
		let kd = e.kd.to_view(&self.kd, encoder);

		encoder.draw(&e.slice,
		             &self.pso,
		             &shaded::Data {
			             vbuf: e.buffer.clone(),
			             fragment_args: self.fragment.clone(),
			             vertex_args: self.vertex.clone(),
			             lights: self.lights.clone(),
			             out_ka: target.color.clone(),
			             out_depth: target.output_depth.clone(),
			             ka: (ka, self.sampler.clone()),
			             kd: (kd, self.sampler.clone())
		             });
	}
}

fn pad(x: [f32; 3]) -> [f32; 4] {
	[x[0], x[1], x[2], 0.]
}
<|endoftext|>


<commit_before>use std::error;
use std::fmt::{self, Display, Formatter};
use std::result;

use Class;

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

#[derive(Debug, PartialEq)]
pub enum Error {
    ArgumentError(String),
    TypeError(String),
}

impl Error {
    /// Converts error to an exception class.
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::result::Error;
    /// use ruru::{Class, VM};
    ///
    /// # VM::init();
    /// let argument_error = Error::ArgumentError("Argument is missing".to_string());
    /// let type_error = Error::TypeError("Wrong type".to_string());
    ///
    /// assert_eq!(argument_error.to_exception(), Class::from_existing("ArgumentError"));
    /// assert_eq!(type_error.to_exception(), Class::from_existing("TypeError"));
    /// ```
    pub fn to_exception(&self) -> Class {
        let class_name = match *self {
            Error::ArgumentError(_) => "ArgumentError",
            Error::TypeError(_) => "TypeError",
        };

        Class::from_existing(class_name)
    }
}

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

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::ArgumentError(ref message) | Error::TypeError(ref message) => message,
        }
    }
}
<commit_msg>Remove unnecessary referencing of Result<commit_after>use std::error;
use std::fmt::{self, Display, Formatter};
use std::result;

use Class;

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

#[derive(Debug, PartialEq)]
pub enum Error {
    ArgumentError(String),
    TypeError(String),
}

impl Error {
    /// Converts error to an exception class.
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::result::Error;
    /// use ruru::{Class, VM};
    ///
    /// # VM::init();
    /// let argument_error = Error::ArgumentError("Argument is missing".to_string());
    /// let type_error = Error::TypeError("Wrong type".to_string());
    ///
    /// assert_eq!(argument_error.to_exception(), Class::from_existing("ArgumentError"));
    /// assert_eq!(type_error.to_exception(), Class::from_existing("TypeError"));
    /// ```
    pub fn to_exception(&self) -> Class {
        let class_name = match *self {
            Error::ArgumentError(_) => "ArgumentError",
            Error::TypeError(_) => "TypeError",
        };

        Class::from_existing(class_name)
    }
}

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

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::ArgumentError(ref message) | Error::TypeError(ref message) => message,
        }
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use http::{Method, Options, Get, Post, Put, Delete, Patch};
use iron::{Ingot, Request, Response, Alloy};
use iron::ingot::{Status, Continue, Unwind};

pub type Handler = |&mut Rq, &mut Rs, &mut A| -> ()

#[deriving(Clone)]
pub struct Router {
    options: Vec<Method>,
    routes: Vec<Route>
}

#[deriving(Clone)]
struct Route {
    method: Method,
    matches: Regex,
    handler: |&mut Rq, &mut Rs, &mut A| -> ()
}

impl Router {
    fn new() -> Router { Router { options: Vec::new(), routes: Vec::new() } }
    fn addRoute(&mut self, route: Route) {
        if !self.options.contains(route.method) {
            self.options.push(route.method)
        }
        self.routes.push(route);
    }
    method!(get, Get)
    method!(post, Post)
    method!(put, Put)
    method!(patch, Patch)
    method!(delete, Delete)
}

macro_rules! method {
    ($name:ident, $method:ident) => {
        fn $name(&mut self, matches: Regex, handler: Handler) {
            self.addRoute(Route {
                method: $method
                matches: matches,
                handler: handler
            });
        }
    }
}

impl<Rq: Request, Rs: Response> Ingot<Rq, Rs> for Router {
    fn enter(&mut self, req: &mut Rq, res: &mut Rs, alloy: &mut Alloy) -> Status {
        if *req.method() == Options {
            res.write(self.options.iter().map(|p| p.show()).collect().join(" "));
            return Unwind;
        }
        for route in self.routes.iter() {
            if route.matches.is_match(req.reques_uri()) {
                route.handler(req, res, alloy);
                return Unwind;
            }
        }
        Continue
    }
}

<commit_msg>(refactor) Refactored method-routing to use a single method.<commit_after>use regex::Regex;
use http::method::{Method, Options};
use http::status::{InternalServerError};
use iron::{Ingot, Request, Response, Alloy};
use iron::ingot::{Status, Continue, Unwind};

pub type Handler = |&mut Rq, &mut Rs, &mut A| -> ()

#[deriving(Clone)]
pub struct Router {
    options: Vec<Method>,
    routes: Vec<Route>
}

#[deriving(Clone)]
struct Route {
    method: Method,
    glob: String,
    matches: Regex,
    handler: Handler<Rq, Rs>,
    params: Vec<String>
}

impl Router {
    fn new() -> Router { Router { options: Vec::new(), routes: Vec::new() } }
    fn addRoute(&mut self, route: Route) {
        if !self.options.contains(route.method) {
            self.options.push(route.method)
        }
        self.routes.push(route);
    }
}

impl<Rq, Rs> Router<Rq, Rs> {
    pub fn new() -> Router<Rq, Rs> { Router { options: Vec::new(), routes: Vec::new() } }
    pub fn route(&mut self, method: Method, glob: String,
                 params: Vec<String>, handler: Handler<Rq, Rs>) {
        self.add_route(Route {
            method: method,
            matches: deglob(glob.clone()),
            params: params,
            handler: handler,
            glob: glob
        });
    }

    fn add_route(&mut self, route: Route<Rq, Rs>) {
        if !self.options.contains(&route.method) {
            self.options.push(route.method.clone())
        }
        self.routes.push(route);
    }
}

impl<Rq: Request, Rs: Response> Ingot<Rq, Rs> for Router {
    fn enter(&mut self, req: &mut Rq, res: &mut Rs, alloy: &mut Alloy) -> Status {
        if *req.method() == Options {
            res.write(self.options.iter().map(|p| p.show()).collect().join(" "));
            return Unwind;
        }
        for route in self.routes.iter() {
            if route.matches.is_match(req.reques_uri()) {
                route.handler(req, res, alloy);
                return Unwind;
            }
        }
        Continue
    }
}

<|endoftext|>


<commit_before>//! # runner
//!
//! Runs the requested tasks.

//! The flow is as follows:
//!
//! * Load env variables
//! * Create an execution plan based on the requested task and its dependencies
//! * Run all tasks defined in the execution plan
//!

#[cfg(test)]
#[path = "./runner_test.rs"]
mod runner_test;

use command;
use condition;
use environment;
use installer;
use logger;
use rsscript;
use std::collections::HashMap;
use std::collections::HashSet;
use std::time::SystemTime;
use types::{Config, CrateInfo, EnvInfo, ExecutionPlan, FlowInfo, Step, Task};

fn invoke_script_runner(step: &Step) -> bool {
    match step.config.script_runner {
        Some(ref script_runner) => {
            match step.config.script {
                Some(ref script) => {
                    debug!("Checking script runner: {}", script_runner);

                    if script_runner == "@rust" {
                        debug!("Rust script detected.");
                        rsscript::execute(script);

                        true
                    } else {
                        false
                    }
                }
                None => false,
            }
        }
        None => false,
    }
}

fn validate_condition(
    flow_info: &FlowInfo,
    step: &Step,
) -> bool {
    condition::validate_condition(&flow_info, &step)
}

fn run_sub_task(
    flow_info: &FlowInfo,
    sub_task: &str,
) {
    let mut sub_flow_info = flow_info.clone();
    sub_flow_info.task = sub_task.to_string();

    run_flow(&sub_flow_info);
}

fn run_task(
    flow_info: &FlowInfo,
    step: &Step,
) {
    info!("Running Task: {}", &step.name);

    if validate_condition(&flow_info, &step) {
        if !step.config.is_valid() {
            error!("Invalid task, contains multiple actions.\n{:#?}", &step.config);
        }

        let env = match step.config.env {
            Some(ref env) => env.clone(),
            None => HashMap::new(),
        };
        environment::set_env(env);

        installer::install(&step.config);

        match step.config.run_task {
            Some(ref sub_task) => run_sub_task(&flow_info, sub_task),
            None => {
                let revert_directory = match step.config.cwd {
                    Some(ref cwd) => {
                        let directory = environment::get_env("CARGO_MAKE_WORKING_DIRECTORY", "");

                        environment::setup_cwd(Some(cwd));

                        directory
                    }
                    None => "".to_string(),
                };

                // try to invoke it as a none OS script
                let script_runner_done = invoke_script_runner(&step);

                // run as command or OS script
                if !script_runner_done {
                    command::run(&step)
                };

                // revert to original cwd
                match step.config.cwd {
                    Some(_) => environment::setup_cwd(Some(&revert_directory)),
                    _ => (),
                };
            }
        };
    } else {
        debug!("Task: {} disabled", &step.name);
    }
}

fn run_task_flow(
    flow_info: &FlowInfo,
    execution_plan: &ExecutionPlan,
) {
    for step in &execution_plan.steps {
        run_task(&flow_info, &step);
    }
}

/// Returns the actual task name to invoke as tasks may have aliases
fn get_task_name(
    config: &Config,
    name: &str,
) -> String {
    match config.tasks.get(name) {
        Some(task_config) => {
            let alias = task_config.get_alias();

            match alias {
                Some(ref alias) => get_task_name(config, alias),
                _ => name.to_string(),
            }
        }
        None => {
            // This will actually panic
            error!("Task not found: {}", &name);

            name.to_string()
        }
    }
}

/// Creates an execution plan for the given step based on existing execution plan data
fn create_execution_plan_for_step(
    config: &Config,
    task: &str,
    steps: &mut Vec<Step>,
    task_names: &mut HashSet<String>,
    root: bool,
) {
    let actual_task = get_task_name(config, task);

    match config.tasks.get(&actual_task) {
        Some(task_config) => {
            match task_config.dependencies {
                Some(ref dependencies) => {
                    for dependency in dependencies {
                        create_execution_plan_for_step(&config, &dependency, steps, task_names, false);
                    }
                }
                _ => debug!("No dependencies found for task: {}", &task),
            };

            if !task_names.contains(task) {
                let mut clone_task = task_config.clone();
                let normalized_task = clone_task.get_normalized_task();
                let add = !normalized_task.disabled.unwrap_or(false);

                if add {
                    steps.push(Step { name: task.to_string(), config: normalized_task });
                    task_names.insert(task.to_string());
                }
            } else if root {
                error!("Circular reference found for task: {}", &task);
            }
        }
        None => error!("Task not found: {}", &task),
    }
}

fn create_workspace_task(
    crate_info: CrateInfo,
    task: &str,
) -> Task {
    let workspace = crate_info.workspace.unwrap();
    let members = workspace.members.unwrap_or(vec![]);

    let log_level = logger::get_log_level();

    let mut script_lines = vec![];
    for member in &members {
        let mut cd_line = "cd ./".to_string();
        cd_line.push_str(&member);
        script_lines.push(cd_line);

        let mut make_line = "cargo make --disable-check-for-updates --loglevel=".to_string();
        make_line.push_str(&log_level);
        make_line.push_str(" ");
        make_line.push_str(&task);
        script_lines.push(make_line);

        if cfg!(windows) {
            script_lines.push("cd %CARGO_MAKE_WORKING_DIRECTORY%".to_string());
        } else {
            script_lines.push("cd ${CARGO_MAKE_WORKING_DIRECTORY}".to_string());
        };
    }

    let mut workspace_task = Task::new();
    workspace_task.script = Some(script_lines);

    workspace_task
}

/// Creates the full execution plan
fn create_execution_plan(
    config: &Config,
    task: &str,
    disable_workspace: bool,
) -> ExecutionPlan {
    let mut task_names = HashSet::new();
    let mut steps = Vec::new();

    match config.config.init_task {
        Some(ref task) => {
            match config.tasks.get(task) {
                Some(task_config) => {
                    let mut clone_task = task_config.clone();
                    let normalized_task = clone_task.get_normalized_task();
                    let add = !normalized_task.disabled.unwrap_or(false);

                    if add {
                        steps.push(Step { name: task.to_string(), config: normalized_task });
                    }
                }
                None => error!("Task not found: {}", &task),
            }
        }
        None => debug!("Init task not defined."),
    };

    // load crate info and look for workspace info
    let crate_info = environment::crateinfo::load();

    if disable_workspace || crate_info.workspace.is_none() {
        create_execution_plan_for_step(&config, &task, &mut steps, &mut task_names, true);
    } else {
        let workspace_task = create_workspace_task(crate_info, task);

        steps.push(Step { name: "workspace".to_string(), config: workspace_task });
    }

    // always add end task even if already executed due to some depedency
    match config.config.end_task {
        Some(ref task) => {
            match config.tasks.get(task) {
                Some(task_config) => {
                    let mut clone_task = task_config.clone();
                    let normalized_task = clone_task.get_normalized_task();
                    let add = !normalized_task.disabled.unwrap_or(false);

                    if add {
                        steps.push(Step { name: task.to_string(), config: normalized_task });
                    }
                }
                None => error!("Task not found: {}", &task),
            }
        }
        None => debug!("End task not defined."),
    };

    ExecutionPlan { steps }
}

fn run_flow(flow_info: &FlowInfo) {
    let execution_plan = create_execution_plan(&flow_info.config, &flow_info.task, flow_info.disable_workspace);
    debug!("Created execution plan: {:#?}", &execution_plan);

    run_task_flow(&flow_info, &execution_plan);
}

/// Runs the requested tasks.

/// The flow is as follows:
///
/// * Create an execution plan based on the requested task and its dependencies
/// * Run all tasks defined in the execution plan
pub fn run(
    config: Config,
    task: &str,
    env_info: EnvInfo,
    disable_workspace: bool,
) {
    let start_time = SystemTime::now();

    let flow_info = FlowInfo { config, task: task.to_string(), env_info, disable_workspace };

    run_flow(&flow_info);

    let time_string = match start_time.elapsed() {
        Ok(elapsed) => {
            let mut string = " in ".to_string();
            string.push_str(&elapsed.as_secs().to_string());
            string.push_str(" seconds");

            string
        }
        _ => "".to_string(),
    };

    info!("Build Done {}.", &time_string);
}

/// Only prints the execution plan
pub fn print(
    config: &Config,
    task: &str,
    disable_workspace: bool,
) {
    let execution_plan = create_execution_plan(&config, &task, disable_workspace);
    debug!("Created execution plan: {:#?}", &execution_plan);

    println!("{:#?}", &execution_plan);
}
<commit_msg>formatting<commit_after>//! # runner
//!
//! Runs the requested tasks.

//! The flow is as follows:
//!
//! * Load env variables
//! * Create an execution plan based on the requested task and its dependencies
//! * Run all tasks defined in the execution plan
//!

#[cfg(test)]
#[path = "./runner_test.rs"]
mod runner_test;

use command;
use condition;
use environment;
use installer;
use logger;
use rsscript;
use std::collections::HashMap;
use std::collections::HashSet;
use std::time::SystemTime;
use types::{Config, CrateInfo, EnvInfo, ExecutionPlan, FlowInfo, Step, Task};

fn invoke_script_runner(step: &Step) -> bool {
    match step.config.script_runner {
        Some(ref script_runner) => {
            match step.config.script {
                Some(ref script) => {
                    debug!("Checking script runner: {}", script_runner);

                    if script_runner == "@rust" {
                        debug!("Rust script detected.");
                        rsscript::execute(script);

                        true
                    } else {
                        false
                    }
                }
                None => false,
            }
        }
        None => false,
    }
}

fn validate_condition(
    flow_info: &FlowInfo,
    step: &Step,
) -> bool {
    condition::validate_condition(&flow_info, &step)
}

fn run_sub_task(
    flow_info: &FlowInfo,
    sub_task: &str,
) {
    let mut sub_flow_info = flow_info.clone();
    sub_flow_info.task = sub_task.to_string();

    run_flow(&sub_flow_info);
}

fn run_task(
    flow_info: &FlowInfo,
    step: &Step,
) {
    info!("Running Task: {}", &step.name);

    if validate_condition(&flow_info, &step) {
        if !step.config.is_valid() {
            error!("Invalid task, contains multiple actions.\n{:#?}", &step.config);
        }

        let env = match step.config.env {
            Some(ref env) => env.clone(),
            None => HashMap::new(),
        };
        environment::set_env(env);

        installer::install(&step.config);

        match step.config.run_task {
            Some(ref sub_task) => run_sub_task(&flow_info, sub_task),
            None => {
                let revert_directory = match step.config.cwd {
                    Some(ref cwd) => {
                        let directory = environment::get_env("CARGO_MAKE_WORKING_DIRECTORY", "");

                        environment::setup_cwd(Some(cwd));

                        directory
                    }
                    None => "".to_string(),
                };

                // try to invoke it as a none OS script
                let script_runner_done = invoke_script_runner(&step);

                // run as command or OS script
                if !script_runner_done {
                    command::run(&step)
                };

                // revert to original cwd
                match step.config.cwd {
                    Some(_) => environment::setup_cwd(Some(&revert_directory)),
                    _ => (),
                };
            }
        };
    } else {
        debug!("Task: {} disabled", &step.name);
    }
}

fn run_task_flow(
    flow_info: &FlowInfo,
    execution_plan: &ExecutionPlan,
) {
    for step in &execution_plan.steps {
        run_task(&flow_info, &step);
    }
}

/// Returns the actual task name to invoke as tasks may have aliases
fn get_task_name(
    config: &Config,
    name: &str,
) -> String {
    match config.tasks.get(name) {
        Some(task_config) => {
            let alias = task_config.get_alias();

            match alias {
                Some(ref alias) => get_task_name(config, alias),
                _ => name.to_string(),
            }
        }
        None => {
            // This will actually panic
            error!("Task not found: {}", &name);

            name.to_string()
        }
    }
}

/// Creates an execution plan for the given step based on existing execution plan data
fn create_execution_plan_for_step(
    config: &Config,
    task: &str,
    steps: &mut Vec<Step>,
    task_names: &mut HashSet<String>,
    root: bool,
) {
    let actual_task = get_task_name(config, task);

    match config.tasks.get(&actual_task) {
        Some(task_config) => {
            match task_config.dependencies {
                Some(ref dependencies) => {
                    for dependency in dependencies {
                        create_execution_plan_for_step(&config, &dependency, steps, task_names, false);
                    }
                }
                _ => debug!("No dependencies found for task: {}", &task),
            };

            if !task_names.contains(task) {
                let mut clone_task = task_config.clone();
                let normalized_task = clone_task.get_normalized_task();
                let add = !normalized_task.disabled.unwrap_or(false);

                if add {
                    steps.push(Step { name: task.to_string(), config: normalized_task });
                    task_names.insert(task.to_string());
                }
            } else if root {
                error!("Circular reference found for task: {}", &task);
            }
        }
        None => error!("Task not found: {}", &task),
    }
}

fn create_workspace_task(
    crate_info: CrateInfo,
    task: &str,
) -> Task {
    let workspace = crate_info.workspace.unwrap();
    let members = workspace.members.unwrap_or(vec![]);

    let log_level = logger::get_log_level();

    let mut script_lines = vec![];
    for member in &members {
        let mut cd_line = "cd ./".to_string();
        cd_line.push_str(&member);
        script_lines.push(cd_line);

        let mut make_line = "cargo make --disable-check-for-updates --loglevel=".to_string();
        make_line.push_str(&log_level);
        make_line.push_str(" ");
        make_line.push_str(&task);
        script_lines.push(make_line);

        if cfg!(windows) {
            script_lines.push("cd %CARGO_MAKE_WORKING_DIRECTORY%".to_string());
        } else {
            script_lines.push("cd ${CARGO_MAKE_WORKING_DIRECTORY}".to_string());
        };
    }

    let mut workspace_task = Task::new();
    workspace_task.script = Some(script_lines);

    workspace_task
}

/// Creates the full execution plan
fn create_execution_plan(
    config: &Config,
    task: &str,
    disable_workspace: bool,
) -> ExecutionPlan {
    let mut task_names = HashSet::new();
    let mut steps = Vec::new();

    match config.config.init_task {
        Some(ref task) => {
            match config.tasks.get(task) {
                Some(task_config) => {
                    let mut clone_task = task_config.clone();
                    let normalized_task = clone_task.get_normalized_task();
                    let add = !normalized_task.disabled.unwrap_or(false);

                    if add {
                        steps.push(Step { name: task.to_string(), config: normalized_task });
                    }
                }
                None => error!("Task not found: {}", &task),
            }
        }
        None => debug!("Init task not defined."),
    };

    // load crate info and look for workspace info
    let crate_info = environment::crateinfo::load();

    if disable_workspace || crate_info.workspace.is_none() {
        create_execution_plan_for_step(&config, &task, &mut steps, &mut task_names, true);
    } else {
        let workspace_task = create_workspace_task(crate_info, task);

        steps.push(Step { name: "workspace".to_string(), config: workspace_task });
    }

    // always add end task even if already executed due to some depedency
    match config.config.end_task {
        Some(ref task) => {
            match config.tasks.get(task) {
                Some(task_config) => {
                    let mut clone_task = task_config.clone();
                    let normalized_task = clone_task.get_normalized_task();
                    let add = !normalized_task.disabled.unwrap_or(false);

                    if add {
                        steps.push(Step { name: task.to_string(), config: normalized_task });
                    }
                }
                None => error!("Task not found: {}", &task),
            }
        }
        None => debug!("End task not defined."),
    };

    ExecutionPlan { steps }
}

fn run_flow(flow_info: &FlowInfo) {
    let execution_plan = create_execution_plan(&flow_info.config, &flow_info.task, flow_info.disable_workspace);
    debug!("Created execution plan: {:#?}", &execution_plan);

    run_task_flow(&flow_info, &execution_plan);
}

/// Runs the requested tasks.

/// The flow is as follows:
///
/// * Create an execution plan based on the requested task and its dependencies
/// * Run all tasks defined in the execution plan
pub fn run(
    config: Config,
    task: &str,
    env_info: EnvInfo,
    disable_workspace: bool,
) {
    let start_time = SystemTime::now();

    let flow_info = FlowInfo { config, task: task.to_string(), env_info, disable_workspace };

    run_flow(&flow_info);

    let time_string = match start_time.elapsed() {
        Ok(elapsed) => {
            let mut string = " in ".to_string();
            string.push_str(&elapsed.as_secs().to_string());
            string.push_str(" seconds");

            string
        }
        _ => "".to_string(),
    };

    info!("Build Done {}.", &time_string);
}

/// Only prints the execution plan
pub fn print(
    config: &Config,
    task: &str,
    disable_workspace: bool,
) {
    let execution_plan = create_execution_plan(&config, &task, disable_workspace);
    debug!("Created execution plan: {:#?}", &execution_plan);

    println!("{:#?}", &execution_plan);
}
<|endoftext|>


<commit_before>// A port of fzy's scoring algorithm.
// fzy (c) 2014 John Hawthorn
// Licensed under the MIT license
// https://github.com/jhawthorn/fzy

use consts::*;
use matcher::eq;
use matrix::Matrix;

pub fn score(needle: &str, haystack: &str) -> f64 {
    let needle_length = needle.chars().count();

    // empty needle
    if needle_length == 0 {
        return SCORE_MIN;
    }

    let haystack_length = haystack.chars().count();

    // perfect match
    if needle_length == haystack_length {
        return SCORE_MAX;
    }

    // unreasonably large haystack
    if haystack_length > 1024 {
        return SCORE_MIN;
    }

    let bonus = compute_bonus(haystack);

    let mut m = Matrix::new(needle_length, haystack_length);
    let mut d = Matrix::new(needle_length, haystack_length);

    for (i, n) in needle.chars().enumerate() {
        let mut prev_score = SCORE_MIN;
        let gap_score = if i == needle_length - 1 { SCORE_GAP_TRAILING } else { SCORE_GAP_INNER };

        for (j, h) in haystack.chars().enumerate() {
            if eq(n, h) {
                let bonus_score = bonus[j];

                let score = match i {
                    0 => ((j as f64) * SCORE_GAP_LEADING) + bonus_score,
                    _ if j > 0 => {
                        let m = m.get(i - 1, j - 1);
                        let d = d.get(i - 1, j - 1);

                        let m = m + bonus_score;
                        let d = d + SCORE_MATCH_CONSECUTIVE;

                        (m).max(d)
                    },
                    _ => SCORE_MIN
                };

                prev_score = score.max(prev_score + gap_score);

                d.set(i, j, score);
                m.set(i, j, prev_score);
            } else {
                prev_score += gap_score;

                d.set(i, j, SCORE_MIN);
                m.set(i, j, prev_score);
            }
        }
    }

    *m.get(needle_length - 1, haystack_length - 1)
}

pub fn compute_bonus(haystack: &str) -> Vec<f64> {
    let mut last_char = '/';
    let len = haystack.chars().count();

    haystack.chars().fold(Vec::with_capacity(len), |mut vec, ch| {
        vec.push(bonus_for_char(last_char, ch));
        last_char = ch;
        vec
    })
}

fn bonus_for_char(prev: char, current: char) -> f64 {
    match current {
        'a' ... 'z' | '0' ... '9' => bonus_for_prev(prev),
        'A' ... 'Z' => {
            match prev {
                'a' ... 'z' => SCORE_MATCH_CAPITAL,
                _ => bonus_for_prev(prev)
            }
        }
        _ => 0.0
    }
}

fn bonus_for_prev(ch: char) -> f64 {
    match ch {
        '/' => SCORE_MATCH_SLASH,
        '-' | '_' | ' ' => SCORE_MATCH_WORD,
        '.' => SCORE_MATCH_DOT,
        _ => 0.0
    }
}

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

    #[test]
    fn test_score_basic() {
        assert_eq!(score("", "asdf"), SCORE_MIN);
        assert_eq!(score("asdf", "asdf"), SCORE_MAX);

        let huge_string = "X".repeat(1025);
        assert_eq!(score("asdf", &huge_string), SCORE_MIN);
    }

    #[test]
    fn relative_scores() {
        // App/Models/Order is better than App/MOdels/zRder
        assert!(score("amor", "app/models/order") > score("amor", "app/models/zrder"));

        // App/MOdels/foo is better than App/M/fOo
        assert!(score("amo", "app/m/foo") < score("amo", "app/models/foo"));

        // GEMFIle.Lock < GEMFILe
        assert!(score("gemfil", "Gemfile.lock") < score("gemfil", "Gemfile"));

        // GEMFIle.Lock < GEMFILe
        assert!(score("gemfil", "Gemfile.lock") < score("gemfil", "Gemfile"));

        // Prefer shorter scorees
        assert!(score("abce", "abcdef") > score("abce", "abc de"));

        // Prefer shorter candidates
        assert!(score("test", "tests") > score("test", "testing"));

        // Scores first letter highly
        assert!(score("test", "testing") > score("test", "/testing"));

        // Prefer shorter scorees
        assert!(score("abc", "    a b c ") > score("abc", " a  b  c "));
        assert!(score("abc", " a b c    ") > score("abc", " a  b  c "));
    }

    #[test]
    fn score_utf8() {
        assert_eq!(score("ß", "öäßéè"), -0.02);
    }

    #[test]
    fn test_compute_bonus() {
        assert_eq!(compute_bonus("a/b/c/d"), vec![0.9, 0.0, 0.9, 0.0, 0.9, 0.0, 0.9]);
        assert_eq!(compute_bonus("aTestString"), vec![0.9, 0.7, 0.0, 0.0, 0.0, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0]);
    }

    #[test]
    fn test_for_char() {
        assert_eq!(bonus_for_char('*', '*'), 0.0);
        assert_eq!(bonus_for_char('a', 'a'), 0.0);

        assert_eq!(bonus_for_char('/', 'a'), SCORE_MATCH_SLASH);
        assert_eq!(bonus_for_char('/', 'A'), SCORE_MATCH_SLASH);
        assert_eq!(bonus_for_char('/', '0'), SCORE_MATCH_SLASH);

        assert_eq!(bonus_for_char('-', 'a'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char('-', 'A'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char('-', '0'), SCORE_MATCH_WORD);

        assert_eq!(bonus_for_char('_', 'a'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char('_', 'A'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char('_', '0'), SCORE_MATCH_WORD);

        assert_eq!(bonus_for_char(' ', 'a'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char(' ', 'A'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char(' ', '0'), SCORE_MATCH_WORD);

        assert_eq!(bonus_for_char('.', 'a'), SCORE_MATCH_DOT);
        assert_eq!(bonus_for_char('.', 'A'), SCORE_MATCH_DOT);
        assert_eq!(bonus_for_char('.', '0'), SCORE_MATCH_DOT);

        assert_eq!(bonus_for_char('a', 'A'), SCORE_MATCH_CAPITAL);
    }
}
<commit_msg>Use Iterator#size_hint() when allocating bonus vector<commit_after>// A port of fzy's scoring algorithm.
// fzy (c) 2014 John Hawthorn
// Licensed under the MIT license
// https://github.com/jhawthorn/fzy

use consts::*;
use matcher::eq;
use matrix::Matrix;

pub fn score(needle: &str, haystack: &str) -> f64 {
    let needle_length = needle.chars().count();

    // empty needle
    if needle_length == 0 {
        return SCORE_MIN;
    }

    let haystack_length = haystack.chars().count();

    // perfect match
    if needle_length == haystack_length {
        return SCORE_MAX;
    }

    // unreasonably large haystack
    if haystack_length > 1024 {
        return SCORE_MIN;
    }

    let bonus = compute_bonus(haystack);

    let mut m = Matrix::new(needle_length, haystack_length);
    let mut d = Matrix::new(needle_length, haystack_length);

    for (i, n) in needle.chars().enumerate() {
        let mut prev_score = SCORE_MIN;
        let gap_score = if i == needle_length - 1 { SCORE_GAP_TRAILING } else { SCORE_GAP_INNER };

        for (j, h) in haystack.chars().enumerate() {
            if eq(n, h) {
                let bonus_score = bonus[j];

                let score = match i {
                    0 => ((j as f64) * SCORE_GAP_LEADING) + bonus_score,
                    _ if j > 0 => {
                        let m = m.get(i - 1, j - 1);
                        let d = d.get(i - 1, j - 1);

                        let m = m + bonus_score;
                        let d = d + SCORE_MATCH_CONSECUTIVE;

                        (m).max(d)
                    },
                    _ => SCORE_MIN
                };

                prev_score = score.max(prev_score + gap_score);

                d.set(i, j, score);
                m.set(i, j, prev_score);
            } else {
                prev_score += gap_score;

                d.set(i, j, SCORE_MIN);
                m.set(i, j, prev_score);
            }
        }
    }

    *m.get(needle_length - 1, haystack_length - 1)
}

pub fn compute_bonus(haystack: &str) -> Vec<f64> {
    let mut last_char = '/';

    let (_, len) = haystack.chars().size_hint();
    let len = len.unwrap_or_else(|| haystack.chars().count());

    haystack.chars().fold(Vec::with_capacity(len), |mut vec, ch| {
        vec.push(bonus_for_char(last_char, ch));
        last_char = ch;
        vec
    })
}

fn bonus_for_char(prev: char, current: char) -> f64 {
    match current {
        'a' ... 'z' | '0' ... '9' => bonus_for_prev(prev),
        'A' ... 'Z' => {
            match prev {
                'a' ... 'z' => SCORE_MATCH_CAPITAL,
                _ => bonus_for_prev(prev)
            }
        }
        _ => 0.0
    }
}

fn bonus_for_prev(ch: char) -> f64 {
    match ch {
        '/' => SCORE_MATCH_SLASH,
        '-' | '_' | ' ' => SCORE_MATCH_WORD,
        '.' => SCORE_MATCH_DOT,
        _ => 0.0
    }
}

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

    #[test]
    fn test_score_basic() {
        assert_eq!(score("", "asdf"), SCORE_MIN);
        assert_eq!(score("asdf", "asdf"), SCORE_MAX);

        let huge_string = "X".repeat(1025);
        assert_eq!(score("asdf", &huge_string), SCORE_MIN);
    }

    #[test]
    fn relative_scores() {
        // App/Models/Order is better than App/MOdels/zRder
        assert!(score("amor", "app/models/order") > score("amor", "app/models/zrder"));

        // App/MOdels/foo is better than App/M/fOo
        assert!(score("amo", "app/m/foo") < score("amo", "app/models/foo"));

        // GEMFIle.Lock < GEMFILe
        assert!(score("gemfil", "Gemfile.lock") < score("gemfil", "Gemfile"));

        // GEMFIle.Lock < GEMFILe
        assert!(score("gemfil", "Gemfile.lock") < score("gemfil", "Gemfile"));

        // Prefer shorter scorees
        assert!(score("abce", "abcdef") > score("abce", "abc de"));

        // Prefer shorter candidates
        assert!(score("test", "tests") > score("test", "testing"));

        // Scores first letter highly
        assert!(score("test", "testing") > score("test", "/testing"));

        // Prefer shorter scorees
        assert!(score("abc", "    a b c ") > score("abc", " a  b  c "));
        assert!(score("abc", " a b c    ") > score("abc", " a  b  c "));
    }

    #[test]
    fn score_utf8() {
        assert_eq!(score("ß", "öäßéè"), -0.02);
    }

    #[test]
    fn test_compute_bonus() {
        assert_eq!(compute_bonus("a/b/c/d"), vec![0.9, 0.0, 0.9, 0.0, 0.9, 0.0, 0.9]);
        assert_eq!(compute_bonus("aTestString"), vec![0.9, 0.7, 0.0, 0.0, 0.0, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0]);
    }

    #[test]
    fn test_for_char() {
        assert_eq!(bonus_for_char('*', '*'), 0.0);
        assert_eq!(bonus_for_char('a', 'a'), 0.0);

        assert_eq!(bonus_for_char('/', 'a'), SCORE_MATCH_SLASH);
        assert_eq!(bonus_for_char('/', 'A'), SCORE_MATCH_SLASH);
        assert_eq!(bonus_for_char('/', '0'), SCORE_MATCH_SLASH);

        assert_eq!(bonus_for_char('-', 'a'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char('-', 'A'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char('-', '0'), SCORE_MATCH_WORD);

        assert_eq!(bonus_for_char('_', 'a'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char('_', 'A'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char('_', '0'), SCORE_MATCH_WORD);

        assert_eq!(bonus_for_char(' ', 'a'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char(' ', 'A'), SCORE_MATCH_WORD);
        assert_eq!(bonus_for_char(' ', '0'), SCORE_MATCH_WORD);

        assert_eq!(bonus_for_char('.', 'a'), SCORE_MATCH_DOT);
        assert_eq!(bonus_for_char('.', 'A'), SCORE_MATCH_DOT);
        assert_eq!(bonus_for_char('.', '0'), SCORE_MATCH_DOT);

        assert_eq!(bonus_for_char('a', 'A'), SCORE_MATCH_CAPITAL);
    }
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]
use rustc_serialize::json::{self, ToJson};

use rs_es::Client;

use iron::prelude::*;
use iron::{status, Handler, Headers};
use iron::mime::Mime;

use logger::Logger;

use router::Router;

use params::*;

use oath::*;

use config::*;
use search::SearchResult;
use resource::Resource;

use std::collections::HashMap;
use std::env;
use std::io::Read;
use std::marker::PhantomData;

macro_rules! try_or_422 {
  ($expr:expr) => (match $expr {
    Ok(val)  => val,
    Err(err) => {
      let mut error = HashMap::new();
      error.insert("error", format!("{}", err));

      let content_type = "application/json".parse::<Mime>().unwrap();
      return Ok(Response::with(
        (content_type, status::UnprocessableEntity, json::encode(&error).unwrap())
      ))
    }
  })
}

macro_rules! unauthorized {
  () => ({
    return Ok(Response::with(
      (status::Unauthorized)
    ))
  })
}

#[derive(Clone)]
pub struct Server<R: Resource> {
  config:   Config,
  endpoint: String,
  resource: PhantomData<R>
}

trait ReadableEndpoint {
  fn is_authorized(&self, auth_config: AuthConfig, headers: &Headers) -> bool {
    if auth_config.enabled == false {
      return true;
    }

    match headers.get_raw("Authorization") {
      Some(header) => match String::from_utf8(header[0].to_owned()) {
        Ok(header) => {
          match header.split("token ").collect::<Vec<&str>>().last() {
            Some(token) => {
              match token.parse::<u64>() {
                Ok(token) => totp_raw(auth_config.read.as_bytes(), 6, 0, 30) == token,
                Err(_)    => false,
              }
            },
            None => false
          }
        },
        Err(_) => false
      },
      None => false
    }
  }
}

trait WritableEndpoint {
  fn is_authorized(&self, auth_config: AuthConfig, headers: &Headers) -> bool {
    if auth_config.enabled == false {
      return true;
    }

    match headers.get_raw("Authorization") {
      Some(header) => match String::from_utf8(header[0].to_owned()) {
        Ok(header) => {
          match header.split("token ").collect::<Vec<&str>>().last() {
            Some(token) => {
              match token.parse::<u64>() {
                Ok(token) => totp_raw(auth_config.write.as_bytes(), 6, 0, 30) == token,
                Err(_)    => false,
              }
            },
            None => false
          }
        },
        Err(_) => false
      },
      None => false
    }
  }
}

pub struct SearchableHandler<R> {
  config:   Config,
  resource: PhantomData<R>
}

impl<R: Resource> SearchableHandler<R> {
  fn new(config: Config) -> Self {
    SearchableHandler::<R> { resource: PhantomData, config: config }
  }
}

impl<R: Resource> ReadableEndpoint for SearchableHandler<R> {}

impl<R: Resource> Handler for SearchableHandler<R> {
  fn handle(&self, req: &mut Request) -> IronResult<Response> {
    if !self.is_authorized(self.config.auth.to_owned(), &req.headers) {
      unauthorized!();
    }

    let mut client = Client::new(&*self.config.es.host, self.config.es.port);

    let params   = try_or_422!(req.get_ref::<Params>());
    let response = SearchResult {
      results: R::search(&mut client, &*self.config.es.index, params),
      params:  params.clone()
    };

    let content_type = "application/json".parse::<Mime>().unwrap();
    Ok(Response::with(
      (content_type, status::Ok, try_or_422!(json::encode(&response.to_json())))
    ))
  }
}

pub struct IndexableHandler<R> {
  config:   Config,
  resource: PhantomData<R>
}

impl<R: Resource> IndexableHandler<R> {
  fn new(config: Config) -> Self {
    IndexableHandler::<R> { resource: PhantomData, config: config }
  }
}

impl<R: Resource> WritableEndpoint for IndexableHandler<R> {}

impl<R: Resource> Handler for IndexableHandler<R> {
  fn handle(&self, req: &mut Request) -> IronResult<Response> {
    if !self.is_authorized(self.config.auth.to_owned(), &req.headers) {
      unauthorized!();
    }

    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let mut client = Client::new(&*self.config.es.host, self.config.es.port);

    let resource: R = try_or_422!(json::decode(&payload));
    try_or_422!(resource.index(&mut client, &*self.config.es.index));

    Ok(Response::with(status::Created))
  }
}

pub struct ResettableHandler<R> {
  config:   Config,
  resource: PhantomData<R>
}

impl<R: Resource> ResettableHandler<R> {
  fn new(config: Config) -> Self {
    ResettableHandler::<R> { resource: PhantomData, config: config }
  }
}

impl<R: Resource> WritableEndpoint for ResettableHandler<R> {}

impl<R: Resource> Handler for ResettableHandler<R> {
  fn handle(&self, req: &mut Request) -> IronResult<Response> {
    if !self.is_authorized(self.config.auth.to_owned(), &req.headers) {
      unauthorized!();
    }

    let mut client = Client::new(&*self.config.es.host, self.config.es.port);
    match R::reset_index(&mut client, &*self.config.es.index) {
      Ok(_)  => Ok(Response::with(status::NoContent)),
      Err(_) => Ok(Response::with(status::UnprocessableEntity))
    }
  }
}

impl<R: Resource> Server<R> {
  pub fn new(endpoint: String) -> Self {
    let config = match env::args().nth(1) {
      Some(file) => Config::from_file(file),
      None       => Config::from_env()
    };

    Server {
      config:   config,
      endpoint: endpoint,
      resource: PhantomData
    }
  }

  pub fn start(&self) {
    let host = format!("{}:{}", self.config.http.host, self.config.http.port);

    println!("Searchspot v{}\n{}\n{}\n", env!("CARGO_PKG_VERSION"),
                                         self.config.es,
                                         self.config.http);

    let mut router = Router::new();
    router.get(&self.endpoint,    SearchableHandler::<R>::new(self.config.clone()));
    router.post(&self.endpoint,   IndexableHandler::<R>::new(self.config.clone()));
    router.delete(&self.endpoint, ResettableHandler::<R>::new(self.config.clone()));

    match env::var("DYNO") { // for some reasons, chain::link makes heroku crash
      Ok(_)  => Iron::new(router).http(&*host),
      Err(_) => {
        let mut chain = Chain::new(router);
        chain.link(Logger::new(None));
        Iron::new(chain).http(&*host)
      }
    }.unwrap();
  }
}
<commit_msg>server: generate traits for authentication dynamically<commit_after>#![allow(non_upper_case_globals)]
use rustc_serialize::json::{self, ToJson};

use rs_es::Client;

use iron::prelude::*;
use iron::{status, Handler, Headers};
use iron::mime::Mime;

use logger::Logger;

use router::Router;

use params::*;

use oath::*;

use config::*;
use search::SearchResult;
use resource::Resource;

use std::collections::HashMap;
use std::env;
use std::io::Read;
use std::marker::PhantomData;

macro_rules! try_or_422 {
  ($expr:expr) => (match $expr {
    Ok(val)  => val,
    Err(err) => {
      let mut error = HashMap::new();
      error.insert("error", format!("{}", err));

      let content_type = "application/json".parse::<Mime>().unwrap();
      return Ok(Response::with(
        (content_type, status::UnprocessableEntity, json::encode(&error).unwrap())
      ))
    }
  })
}

macro_rules! unauthorized {
  () => ({
    return Ok(Response::with(
      (status::Unauthorized)
    ))
  })
}

macro_rules! authorization {
  ($trait_name:ident, $mode:ident) => {
    trait $trait_name {
      fn is_authorized(&self, auth_config: AuthConfig, headers: &Headers) -> bool {
        if auth_config.enabled == false {
          return true;
        }

        match headers.get_raw("Authorization") {
          Some(header) => match String::from_utf8(header[0].to_owned()) {
            Ok(header) => {
              match header.split("token ").collect::<Vec<&str>>().last() {
                Some(token) => {
                  match token.parse::<u64>() {
                    Ok(token) => totp_raw(auth_config.$mode.as_bytes(), 6, 0, 30) == token,
                    Err(_)    => false,
                  }
                },
                None => false
              }
            },
            Err(_) => false
          },
          None => false
        }
      }
    }
  }
}

#[derive(Clone)]
pub struct Server<R: Resource> {
  config:   Config,
  endpoint: String,
  resource: PhantomData<R>
}

authorization!(ReadableEndpoint, read);
authorization!(WritableEndpoint, write);

pub struct SearchableHandler<R> {
  config:   Config,
  resource: PhantomData<R>
}

impl<R: Resource> SearchableHandler<R> {
  fn new(config: Config) -> Self {
    SearchableHandler::<R> { resource: PhantomData, config: config }
  }
}

impl<R: Resource> ReadableEndpoint for SearchableHandler<R> {}

impl<R: Resource> Handler for SearchableHandler<R> {
  fn handle(&self, req: &mut Request) -> IronResult<Response> {
    if !self.is_authorized(self.config.auth.to_owned(), &req.headers) {
      unauthorized!();
    }

    let mut client = Client::new(&*self.config.es.host, self.config.es.port);

    let params   = try_or_422!(req.get_ref::<Params>());
    let response = SearchResult {
      results: R::search(&mut client, &*self.config.es.index, params),
      params:  params.clone()
    };

    let content_type = "application/json".parse::<Mime>().unwrap();
    Ok(Response::with(
      (content_type, status::Ok, try_or_422!(json::encode(&response.to_json())))
    ))
  }
}

pub struct IndexableHandler<R> {
  config:   Config,
  resource: PhantomData<R>
}

impl<R: Resource> IndexableHandler<R> {
  fn new(config: Config) -> Self {
    IndexableHandler::<R> { resource: PhantomData, config: config }
  }
}

impl<R: Resource> WritableEndpoint for IndexableHandler<R> {}

impl<R: Resource> Handler for IndexableHandler<R> {
  fn handle(&self, req: &mut Request) -> IronResult<Response> {
    if !self.is_authorized(self.config.auth.to_owned(), &req.headers) {
      unauthorized!();
    }

    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let mut client = Client::new(&*self.config.es.host, self.config.es.port);

    let resource: R = try_or_422!(json::decode(&payload));
    try_or_422!(resource.index(&mut client, &*self.config.es.index));

    Ok(Response::with(status::Created))
  }
}

pub struct ResettableHandler<R> {
  config:   Config,
  resource: PhantomData<R>
}

impl<R: Resource> ResettableHandler<R> {
  fn new(config: Config) -> Self {
    ResettableHandler::<R> { resource: PhantomData, config: config }
  }
}

impl<R: Resource> WritableEndpoint for ResettableHandler<R> {}

impl<R: Resource> Handler for ResettableHandler<R> {
  fn handle(&self, req: &mut Request) -> IronResult<Response> {
    if !self.is_authorized(self.config.auth.to_owned(), &req.headers) {
      unauthorized!();
    }

    let mut client = Client::new(&*self.config.es.host, self.config.es.port);
    match R::reset_index(&mut client, &*self.config.es.index) {
      Ok(_)  => Ok(Response::with(status::NoContent)),
      Err(_) => Ok(Response::with(status::UnprocessableEntity))
    }
  }
}

impl<R: Resource> Server<R> {
  pub fn new(endpoint: String) -> Self {
    let config = match env::args().nth(1) {
      Some(file) => Config::from_file(file),
      None       => Config::from_env()
    };

    Server {
      config:   config,
      endpoint: endpoint,
      resource: PhantomData
    }
  }

  pub fn start(&self) {
    let host = format!("{}:{}", self.config.http.host, self.config.http.port);

    println!("Searchspot v{}\n{}\n{}\n", env!("CARGO_PKG_VERSION"),
                                         self.config.es,
                                         self.config.http);

    let mut router = Router::new();
    router.get(&self.endpoint,    SearchableHandler::<R>::new(self.config.clone()));
    router.post(&self.endpoint,   IndexableHandler::<R>::new(self.config.clone()));
    router.delete(&self.endpoint, ResettableHandler::<R>::new(self.config.clone()));

    match env::var("DYNO") { // for some reasons, chain::link makes heroku crash
      Ok(_)  => Iron::new(router).http(&*host),
      Err(_) => {
        let mut chain = Chain::new(router);
        chain.link(Logger::new(None));
        Iron::new(chain).http(&*host)
      }
    }.unwrap();
  }
}
<|endoftext|>


<commit_before>use std::thread;
use std::collections::HashSet;
use std::net::SocketAddr;
use std::error::FromError;

// MIO
use mio::tcp::{listen, TcpListener, TcpStream};
use mio::util::Slab;
use mio::Socket;
use mio::buf::{RingBuf};
use mio::{Interest, PollOpt, NonBlock, Token, EventLoop, Handler, ReadHint};
use mio::buf::Buf;
use mio::{TryRead, TryWrite};

use rand::{self, Rng};

// Data structures.
use store::Store;
use replica::{Replica, Emit, Broadcast};
use state_machine::StateMachine;

// Cap'n Proto
use capnp::serialize_packed;
use capnp::{
    MessageBuilder,
    MessageReader,
    ReaderOptions,
    MallocMessageBuilder,
    OwnedSpaceMessageReader,
};
use messages_capnp::{
    rpc_request,
    rpc_response,
    client_request,
    client_response,
    request_vote_response,
    append_entries_response,
    append_entries_request,
};
use super::{Error, Result};

// MIO Tokens
const ELECTION_TIMEOUT: Token = Token(0);
const HEARTBEAT_TIMEOUT: Token = Token(1);
const LISTENER:  Token = Token(2);

const ELECTION_MIN: u64 = 150;
const ELECTION_MAX: u64 = 300;
const HEARTBEAT_DURATION: u64 = 50;

/// The Raft Distributed Consensus Algorithm requires two RPC calls to be available:
///
///   * `append_entries` which is used as both a heartbeat (with no payload) and the primary
///     interface for requests.
///   * `request_vote` which is used by candidates during campaigns to obtain a vote.
///
/// A `Server` acts as a replicated state machine. The server's role in the cluster depends on it's
/// own status. It will maintain both volatile state (which can be safely lost) and persistent
/// state (which must be carefully stored and kept safe).
///
/// Currently, the `Server` API is not well defined. **We are looking for feedback and suggestions.**
pub struct Server<S, M> where S: Store, M: StateMachine {
    replica: Replica<S, M>,
    // Channels and Sockets
    listener: NonBlock<TcpListener>,
    connections: Slab<Connection>,
}

/// The implementation of the Server. In most use cases, creating a `Server` should just be
/// done via `::new()`.
impl<S, M> Server<S, M> where S: Store, M: StateMachine {

    /// Creates a new Raft node with the cluster members specified.
    ///
    /// # Arguments
    ///
    /// * `addr` - The address of the new node.
    /// * `peers` - The address of all peers in the Raft cluster.
    /// * `store` - The persitent log store.
    /// * `state_machine` - The client state machine to which client commands will be applied.
    pub fn spawn(addr: SocketAddr,
                 peers: HashSet<SocketAddr>,
                 store: S,
                 state_machine: M) {
        // Create an event loop
        let mut event_loop = EventLoop::<Server<S, M>>::new().unwrap();
        // Setup the socket, make it not block.
        let listener = listen(&addr).unwrap();
        listener.set_reuseaddr(true).unwrap();
        event_loop.register(&listener, LISTENER).unwrap();
        let timeout = rand::thread_rng().gen_range::<u64>(ELECTION_MIN, ELECTION_MAX);
        event_loop.timeout_ms(ELECTION_TIMEOUT, timeout).unwrap();
        event_loop.timeout_ms(HEARTBEAT_TIMEOUT, HEARTBEAT_DURATION).unwrap();
        let replica = Replica::new(addr, peers, store, state_machine);
        // Fire up the thread.
        thread::Builder::new().name(format!("Server {}", addr)).spawn(move || {
            let mut raft_node = Server {
                listener: listener,
                replica: replica,
                connections: Slab::new_starting_at(Token(2), 128),
            };
            event_loop.run(&mut raft_node).unwrap();
        }).unwrap();
    }
}

impl<S, M> Handler for Server<S, M> where S: Store, M: StateMachine {

    type Message = ();
    type Timeout = Token;

    /// A registered IoHandle has available writing space.
    fn writable(&mut self, reactor: &mut EventLoop<Server<S, M>>, token: Token) {
        match token {
            ELECTION_TIMEOUT => unreachable!(),
            HEARTBEAT_TIMEOUT => unreachable!(),
            LISTENER => unreachable!(),
            tok => {
                self.connections[tok].writable(reactor, &mut self.replica).unwrap();
            }
        }
    }

    /// A registered IoHandle has available data to read
    fn readable(&mut self, reactor: &mut EventLoop<Server<S, M>>, token: Token, _hint: ReadHint) {
        match token {
            ELECTION_TIMEOUT => unreachable!(),
            HEARTBEAT_TIMEOUT => unreachable!(),
            LISTENER => {
                let stream = match self.listener.accept().unwrap() {
                    Some(s) => s,
                    None => return, // Socket isn't quite ready.
                }; // Result<Option<_>,_>
                let conn = Connection::new(stream);
                let tok = self.connections.insert(conn)
                    .ok().expect("Could not add connection to slab.");

                // Register the connection
                self.connections[tok].token = tok;
                reactor.register_opt(&self.connections[tok].stream, tok, Interest::readable(), PollOpt::edge() | PollOpt::oneshot())
                    .ok().expect("Could not register socket with event loop.");
            },
            tok => {
                self.connections[tok].readable(reactor, &mut self.replica).unwrap();
            }
        }
    }

    /// A registered timer has expired
    fn timeout(&mut self, reactor: &mut EventLoop<Server<S, M>>, token: Token) {
        let mut message = MallocMessageBuilder::new_default();
        let request = message.init_root::<rpc_request::Builder>();

        match token {
            ELECTION_TIMEOUT => {
                let timeout = rand::thread_rng().gen_range::<u64>(ELECTION_MIN, ELECTION_MAX);
                let _send_message = self.replica.election_timeout(request.init_request_vote());
                reactor.timeout_ms(ELECTION_TIMEOUT, timeout).unwrap();
                // TODO: send messages if necessary
                unimplemented!();
                // self.broadcast(&mut builder_message);
            },
            HEARTBEAT_TIMEOUT => {
                let _send_message = self.replica.heartbeat_timeout(request.init_append_entries());
                reactor.timeout_ms(HEARTBEAT_TIMEOUT, HEARTBEAT_DURATION).unwrap();
                // TODO: send messages if necessary
                unimplemented!();
                // self.broadcast(&mut builder_message);
            },
            _ => unreachable!(),
        }
    }
}

struct Connection {
    stream: NonBlock<TcpStream>,
    token: Token,
    interest: Interest,
    current_read: RingBuf,
    current_write: RingBuf,
    next_write: Option<MallocMessageBuilder>,
}

impl Connection {
    fn new(sock: NonBlock<TcpStream>) -> Connection {
        Connection {
            stream: sock,
            token: Token(-1),
            interest: Interest::hup(),
            current_read: RingBuf::new(4096),
            current_write: RingBuf::new(4096),
            next_write: None,
        }
    }

    fn writable<S, M>(&mut self, event_loop: &mut EventLoop<Server<S, M>>, replica: &mut Replica<S,M>)
                      -> Result<()>
    where S: Store, M: StateMachine {
        // Attempt to write data.
        // The `current_write` buffer will be advanced based on how much we wrote.
        match self.stream.write(&mut self.current_write) {
            Ok(None) => {
                // This is a buffer flush. WOULDBLOCK
                self.interest.insert(Interest::writable());
            },
            Ok(Some(r)) => {
                // We managed to write data!
                // TODO: We're *probably* not ready to shoot data again?
                match (self.current_write.has_remaining(), self.next_write.is_some()) {
                    // Need to write more of what we have.
                    (true, _) => (),
                    // Need to roll over.
                    (false, true) => try!(
                        serialize_packed::write_packed_message_unbuffered(&mut self.current_write,
                                                                          self.next_write.as_mut().unwrap())
                    ),
                    // We're done writing for now.
                    (false, false) => self.interest.remove(Interest::writable()),

                }
            },
            Err(e) => return Err(Error::from_error(e)),
        }

        match event_loop.reregister(&self.stream, self.token, self.interest, PollOpt::edge() | PollOpt::oneshot()) {
            Ok(()) => Ok(()),
            Err(e) => Err(Error::from_error(e)),
        }
    }

    fn readable<S, M>(&mut self, event_loop: &mut EventLoop<Server<S, M>>, replica: &mut Replica<S,M>)
                      -> Result<()>
    where S: Store, M: StateMachine {
        let mut read = 0;
        match self.stream.read(&mut self.current_read) {
            Ok(Some(r)) => {
                // Just read `r` bytes.
                read = r;
            },
            Ok(None) => panic!("We just got readable, but were unable to read from the socket?"),
            Err(e) => return Err(Error::from_error(e)),
        };
        if read > 0 {
            match serialize_packed::new_reader_unbuffered(&mut self.current_read, ReaderOptions::new()) {
                // We have something reasonably interesting in the buffer!
                Ok(reader) => {
                    self.handle_reader(reader, event_loop, replica);
                },
                // It's not read entirely yet.
                // Should roll back, pending changes to bytes upstream.
                Err(_) => unimplemented!(),
            }
        }
        match event_loop.reregister(&self.stream, self.token, self.interest, PollOpt::edge()) {
            Ok(()) => Ok(()),
            Err(e) => Err(Error::from_error(e)),
        }
    }

    fn handle_reader<S, M>(&mut self, reader: OwnedSpaceMessageReader,
                           event_loop: &mut EventLoop<Server<S, M>>, replica: &mut Replica<S,M>)
    where S: Store, M: StateMachine {
        let mut builder_message = MallocMessageBuilder::new_default();
        let from = self.stream.peer_addr().unwrap();
        if let Ok(request) = reader.get_root::<rpc_request::Reader>() {
            match request.which().unwrap() {
                // TODO: Move these into replica?
                rpc_request::Which::AppendEntries(Ok(call)) => {
                    let builder = builder_message.init_root::<append_entries_response::Builder>();
                    match replica.append_entries_request(from, call, builder) {
                        Some(Emit) => {
                            // Special Cirsumstance Detection
                            unimplemented!();
                        },
                        None => (),
                    }
                },
                rpc_request::Which::RequestVote(Ok(call)) => {
                    let respond = {
                        let builder = builder_message.init_root::<request_vote_response::Builder>();
                        replica.request_vote_request(from, call, builder)
                    };
                    match respond {
                        Some(Emit) => {
                            // TODO
                            self.emit(builder_message);
                        },
                        None            => (),
                    }
                },
                _ => unimplemented!(),
            };
        } else if let Ok(response) = reader.get_root::<rpc_response::Reader>() {
            // We won't be responding. This is already a response.
            match response.which().unwrap() {
                rpc_response::Which::AppendEntries(Ok(call)) => {
                    let respond = {
                        let builder = builder_message.init_root::<append_entries_request::Builder>();
                        replica.append_entries_response(from, call, builder)
                    };
                    match respond {
                        Some(Emit) => {
                            // TODO
                            self.emit(builder_message);
                        },
                        None => (),
                    }
                },
                rpc_response::Which::RequestVote(Ok(call)) => {
                    let respond = {
                        let builder = builder_message.init_root::<append_entries_request::Builder>();
                        replica.request_vote_response(from, call, builder)
                    };
                    match respond {
                        Some(Broadcast) => {
                            // Won an election!
                            self.broadcast(builder_message);
                        },
                        None => (),
                    }

                },
                _ => unimplemented!(),
            }
        } else if let Ok(client_req) = reader.get_root::<client_request::Reader>() {
            let mut should_die = false;
            // We will be responding.
            match client_req.which().unwrap() {
                client_request::Which::Append(Ok(call)) => {
                    let respond = {
                        let builder = builder_message.init_root::<client_response::Builder>();
                        replica.client_append(from, call, builder)
                    };
                    match respond {
                        Some(emit) => {
                            self.emit(builder_message);
                        },
                        None => (),
                    }

                },
                client_request::Which::Die(Ok(call)) => {
                    should_die = true;
                    let mut builder = builder_message.init_root::<client_response::Builder>();
                    builder.set_success(());
                    self.interest.insert(Interest::writable());
                    debug!("Got a Die request from Client({}). Reason: {}", from, call);
                },
                client_request::Which::LeaderRefresh(()) => {
                    let respond = {
                        let builder = builder_message.init_root::<client_response::Builder>();
                        replica.client_leader_refresh(from, builder)
                    };
                    match respond {
                        Some(Emit) => {
                            self.emit(builder_message);
                        },
                        None => (),
                    }
                },
                _ => unimplemented!(),
            };

            // Do this here so that we can send the response.
            if should_die {
                panic!("Got a Die request.");
            }

        } else {
            // It's something we don't understand.
            unimplemented!();
        }
    }

    fn broadcast(&mut self, builder: MallocMessageBuilder) {
        unimplemented!();
    }

    /// If applicable, push the new message into `self.next_write`.
    ///
    // Because messages are passed asyncronously between Server instances, a Server could
    // get into a situation where multiple events are ready to be dispatched to a single
    // remote Server. In this situation, the Server will replace the existing event with
    // the new event, except in one special circumstance: if the new and existing messages
    // are both AppendEntryRequests with the same term, then the new message will be
    // dropped.
    fn emit(&mut self, mut builder: MallocMessageBuilder) {
        let overwrite = match self.next_write {
            Some(ref mut next) => {
                let opt_incoming = builder.get_root::<append_entries_request::Builder>();
                let opt_next = next.get_root::<append_entries_request::Builder>();
                match (opt_incoming, opt_next) {
                    (Ok(incoming), Ok(next)) => {
                        if incoming.get_term() == next.get_term() {
                            false
                        } else {
                            true
                        }
                    },
                    _ => true,
                }
            },
            None => true,
        };
        if overwrite {
            self.next_write = Some(builder);
        }
    }
}
<commit_msg>Improve timeouts<commit_after>use std::thread;
use std::collections::HashSet;
use std::net::SocketAddr;
use std::error::FromError;

// MIO
use mio::tcp::{listen, TcpListener, TcpStream};
use mio::util::Slab;
use mio::Socket;
use mio::buf::{RingBuf};
use mio::{Interest, PollOpt, NonBlock, Token, EventLoop, Handler, ReadHint};
use mio::buf::Buf;
use mio::{TryRead, TryWrite};

use rand::{self, Rng};

// Data structures.
use store::Store;
use replica::{Replica, Emit, Broadcast};
use state_machine::StateMachine;

// Cap'n Proto
use capnp::serialize_packed;
use capnp::{
    MessageBuilder,
    MessageReader,
    ReaderOptions,
    MallocMessageBuilder,
    OwnedSpaceMessageReader,
};
use messages_capnp::{
    rpc_request,
    rpc_response,
    client_request,
    client_response,
    request_vote_response,
    append_entries_response,
    append_entries_request,
};
use super::{Error, Result};

// MIO Tokens
const ELECTION_TIMEOUT: Token = Token(0);
const HEARTBEAT_TIMEOUT: Token = Token(1);
const LISTENER:  Token = Token(2);

const ELECTION_MIN: u64 = 150;
const ELECTION_MAX: u64 = 300;
const HEARTBEAT_DURATION: u64 = 50;

/// The Raft Distributed Consensus Algorithm requires two RPC calls to be available:
///
///   * `append_entries` which is used as both a heartbeat (with no payload) and the primary
///     interface for requests.
///   * `request_vote` which is used by candidates during campaigns to obtain a vote.
///
/// A `Server` acts as a replicated state machine. The server's role in the cluster depends on it's
/// own status. It will maintain both volatile state (which can be safely lost) and persistent
/// state (which must be carefully stored and kept safe).
///
/// Currently, the `Server` API is not well defined. **We are looking for feedback and suggestions.**
pub struct Server<S, M> where S: Store, M: StateMachine {
    replica: Replica<S, M>,
    // Channels and Sockets
    listener: NonBlock<TcpListener>,
    connections: Slab<Connection>,
}

/// The implementation of the Server. In most use cases, creating a `Server` should just be
/// done via `::new()`.
impl<S, M> Server<S, M> where S: Store, M: StateMachine {

    /// Creates a new Raft node with the cluster members specified.
    ///
    /// # Arguments
    ///
    /// * `addr` - The address of the new node.
    /// * `peers` - The address of all peers in the Raft cluster.
    /// * `store` - The persitent log store.
    /// * `state_machine` - The client state machine to which client commands will be applied.
    pub fn spawn(addr: SocketAddr,
                 peers: HashSet<SocketAddr>,
                 store: S,
                 state_machine: M) {
        // Create an event loop
        let mut event_loop = EventLoop::<Server<S, M>>::new().unwrap();
        // Setup the socket, make it not block.
        let listener = listen(&addr).unwrap();
        listener.set_reuseaddr(true).unwrap();
        event_loop.register(&listener, LISTENER).unwrap();
        let timeout = rand::thread_rng().gen_range::<u64>(ELECTION_MIN, ELECTION_MAX);
        event_loop.timeout_ms(ELECTION_TIMEOUT, timeout).unwrap();
        event_loop.timeout_ms(HEARTBEAT_TIMEOUT, HEARTBEAT_DURATION).unwrap();
        let replica = Replica::new(addr, peers, store, state_machine);
        // Fire up the thread.
        thread::Builder::new().name(format!("Server {}", addr)).spawn(move || {
            let mut raft_node = Server {
                listener: listener,
                replica: replica,
                connections: Slab::new_starting_at(Token(2), 128),
            };
            event_loop.run(&mut raft_node).unwrap();
        }).unwrap();
    }

    fn broadcast(&mut self, builder: MallocMessageBuilder) {
        unimplemented!();
    }
}

impl<S, M> Handler for Server<S, M> where S: Store, M: StateMachine {

    type Message = ();
    type Timeout = Token;

    /// A registered IoHandle has available writing space.
    fn writable(&mut self, reactor: &mut EventLoop<Server<S, M>>, token: Token) {
        match token {
            ELECTION_TIMEOUT => unreachable!(),
            HEARTBEAT_TIMEOUT => unreachable!(),
            LISTENER => unreachable!(),
            tok => {
                self.connections[tok].writable(reactor, &mut self.replica).unwrap();
            }
        }
    }

    /// A registered IoHandle has available data to read
    fn readable(&mut self, reactor: &mut EventLoop<Server<S, M>>, token: Token, _hint: ReadHint) {
        match token {
            ELECTION_TIMEOUT => unreachable!(),
            HEARTBEAT_TIMEOUT => unreachable!(),
            LISTENER => {
                let stream = match self.listener.accept().unwrap() {
                    Some(s) => s,
                    None => return, // Socket isn't quite ready.
                }; // Result<Option<_>,_>
                let conn = Connection::new(stream);
                let tok = self.connections.insert(conn)
                    .ok().expect("Could not add connection to slab.");

                // Register the connection
                self.connections[tok].token = tok;
                reactor.register_opt(&self.connections[tok].stream, tok, Interest::readable(), PollOpt::edge() | PollOpt::oneshot())
                    .ok().expect("Could not register socket with event loop.");
            },
            tok => {
                self.connections[tok].readable(reactor, &mut self.replica).unwrap();
            }
        }
    }

    /// A registered timer has expired
    fn timeout(&mut self, reactor: &mut EventLoop<Server<S, M>>, token: Token) {
        let mut message = MallocMessageBuilder::new_default();
        let mut send_message = None;
        match token {
            ELECTION_TIMEOUT => {
                let request = message.init_root::<rpc_request::Builder>();
                send_message = self.replica.election_timeout(request.init_request_vote());
                // Set timeout.
                let timeout = rand::thread_rng().gen_range::<u64>(ELECTION_MIN, ELECTION_MAX);
                reactor.timeout_ms(ELECTION_TIMEOUT, timeout).unwrap();

            },
            HEARTBEAT_TIMEOUT => {
                let request = message.init_root::<rpc_request::Builder>();
                send_message = self.replica.heartbeat_timeout(request.init_append_entries());
                // Set Timeout
                reactor.timeout_ms(HEARTBEAT_TIMEOUT, HEARTBEAT_DURATION).unwrap();
            },
            _ => unreachable!(),
        }
        // Send if necessary.
        match send_message {
            Some(Broadcast) => self.broadcast(message),
            None => (),
        }
    }
}

struct Connection {
    stream: NonBlock<TcpStream>,
    token: Token,
    interest: Interest,
    current_read: RingBuf,
    current_write: RingBuf,
    next_write: Option<MallocMessageBuilder>,
}

impl Connection {
    fn new(sock: NonBlock<TcpStream>) -> Connection {
        Connection {
            stream: sock,
            token: Token(-1),
            interest: Interest::hup(),
            current_read: RingBuf::new(4096),
            current_write: RingBuf::new(4096),
            next_write: None,
        }
    }

    fn writable<S, M>(&mut self, event_loop: &mut EventLoop<Server<S, M>>, replica: &mut Replica<S,M>)
                      -> Result<()>
    where S: Store, M: StateMachine {
        // Attempt to write data.
        // The `current_write` buffer will be advanced based on how much we wrote.
        match self.stream.write(&mut self.current_write) {
            Ok(None) => {
                // This is a buffer flush. WOULDBLOCK
                self.interest.insert(Interest::writable());
            },
            Ok(Some(r)) => {
                // We managed to write data!
                // TODO: We're *probably* not ready to shoot data again?
                match (self.current_write.has_remaining(), self.next_write.is_some()) {
                    // Need to write more of what we have.
                    (true, _) => (),
                    // Need to roll over.
                    (false, true) => try!(
                        serialize_packed::write_packed_message_unbuffered(&mut self.current_write,
                                                                          self.next_write.as_mut().unwrap())
                    ),
                    // We're done writing for now.
                    (false, false) => self.interest.remove(Interest::writable()),

                }
            },
            Err(e) => return Err(Error::from_error(e)),
        }

        match event_loop.reregister(&self.stream, self.token, self.interest, PollOpt::edge() | PollOpt::oneshot()) {
            Ok(()) => Ok(()),
            Err(e) => Err(Error::from_error(e)),
        }
    }

    fn readable<S, M>(&mut self, event_loop: &mut EventLoop<Server<S, M>>, replica: &mut Replica<S,M>)
                      -> Result<()>
    where S: Store, M: StateMachine {
        let mut read = 0;
        match self.stream.read(&mut self.current_read) {
            Ok(Some(r)) => {
                // Just read `r` bytes.
                read = r;
            },
            Ok(None) => panic!("We just got readable, but were unable to read from the socket?"),
            Err(e) => return Err(Error::from_error(e)),
        };
        if read > 0 {
            match serialize_packed::new_reader_unbuffered(&mut self.current_read, ReaderOptions::new()) {
                // We have something reasonably interesting in the buffer!
                Ok(reader) => {
                    self.handle_reader(reader, event_loop, replica);
                },
                // It's not read entirely yet.
                // Should roll back, pending changes to bytes upstream.
                Err(_) => unimplemented!(),
            }
        }
        match event_loop.reregister(&self.stream, self.token, self.interest, PollOpt::edge()) {
            Ok(()) => Ok(()),
            Err(e) => Err(Error::from_error(e)),
        }
    }

    fn handle_reader<S, M>(&mut self, reader: OwnedSpaceMessageReader,
                           event_loop: &mut EventLoop<Server<S, M>>, replica: &mut Replica<S,M>)
    where S: Store, M: StateMachine {
        let mut builder_message = MallocMessageBuilder::new_default();
        let from = self.stream.peer_addr().unwrap();
        if let Ok(request) = reader.get_root::<rpc_request::Reader>() {
            match request.which().unwrap() {
                // TODO: Move these into replica?
                rpc_request::Which::AppendEntries(Ok(call)) => {
                    let builder = builder_message.init_root::<append_entries_response::Builder>();
                    match replica.append_entries_request(from, call, builder) {
                        Some(Emit) => {
                            // Special Cirsumstance Detection
                            unimplemented!();
                        },
                        None => (),
                    }
                },
                rpc_request::Which::RequestVote(Ok(call)) => {
                    let respond = {
                        let builder = builder_message.init_root::<request_vote_response::Builder>();
                        replica.request_vote_request(from, call, builder)
                    };
                    match respond {
                        Some(Emit) => {
                            // TODO
                            self.emit(builder_message);
                        },
                        None            => (),
                    }
                },
                _ => unimplemented!(),
            };
        } else if let Ok(response) = reader.get_root::<rpc_response::Reader>() {
            // We won't be responding. This is already a response.
            match response.which().unwrap() {
                rpc_response::Which::AppendEntries(Ok(call)) => {
                    let respond = {
                        let builder = builder_message.init_root::<append_entries_request::Builder>();
                        replica.append_entries_response(from, call, builder)
                    };
                    match respond {
                        Some(Emit) => {
                            // TODO
                            self.emit(builder_message);
                        },
                        None => (),
                    }
                },
                rpc_response::Which::RequestVote(Ok(call)) => {
                    let respond = {
                        let builder = builder_message.init_root::<append_entries_request::Builder>();
                        replica.request_vote_response(from, call, builder)
                    };
                    match respond {
                        Some(Broadcast) => {
                            // Won an election!
                            self.broadcast(builder_message);
                        },
                        None => (),
                    }

                },
                _ => unimplemented!(),
            }
        } else if let Ok(client_req) = reader.get_root::<client_request::Reader>() {
            let mut should_die = false;
            // We will be responding.
            match client_req.which().unwrap() {
                client_request::Which::Append(Ok(call)) => {
                    let respond = {
                        let builder = builder_message.init_root::<client_response::Builder>();
                        replica.client_append(from, call, builder)
                    };
                    match respond {
                        Some(emit) => {
                            self.emit(builder_message);
                        },
                        None => (),
                    }

                },
                client_request::Which::Die(Ok(call)) => {
                    should_die = true;
                    let mut builder = builder_message.init_root::<client_response::Builder>();
                    builder.set_success(());
                    self.interest.insert(Interest::writable());
                    debug!("Got a Die request from Client({}). Reason: {}", from, call);
                },
                client_request::Which::LeaderRefresh(()) => {
                    let respond = {
                        let builder = builder_message.init_root::<client_response::Builder>();
                        replica.client_leader_refresh(from, builder)
                    };
                    match respond {
                        Some(Emit) => {
                            self.emit(builder_message);
                        },
                        None => (),
                    }
                },
                _ => unimplemented!(),
            };

            // Do this here so that we can send the response.
            if should_die {
                panic!("Got a Die request.");
            }

        } else {
            // It's something we don't understand.
            unimplemented!();
        }
    }

    fn broadcast(&mut self, builder: MallocMessageBuilder) {
        unimplemented!();
    }

    /// If applicable, push the new message into `self.next_write`.
    ///
    // Because messages are passed asyncronously between Server instances, a Server could
    // get into a situation where multiple events are ready to be dispatched to a single
    // remote Server. In this situation, the Server will replace the existing event with
    // the new event, except in one special circumstance: if the new and existing messages
    // are both AppendEntryRequests with the same term, then the new message will be
    // dropped.
    fn emit(&mut self, mut builder: MallocMessageBuilder) {
        let overwrite = match self.next_write {
            Some(ref mut next) => {
                let opt_incoming = builder.get_root::<append_entries_request::Builder>();
                let opt_next = next.get_root::<append_entries_request::Builder>();
                match (opt_incoming, opt_next) {
                    (Ok(incoming), Ok(next)) => {
                        if incoming.get_term() == next.get_term() {
                            false
                        } else {
                            true
                        }
                    },
                    _ => true,
                }
            },
            None => true,
        };
        if overwrite {
            self.next_write = Some(builder);
        }
    }
}
<|endoftext|>