<commit_before>#[feature(globs)];

extern mod extra;
extern mod glfw;
extern mod gl;
extern mod cgmath;

use std::cast;
use std::ptr;
use std::str;
use std::vec;
use std::libc;

use gl::types::*;

use cgmath::matrix::Matrix;
use cgmath::matrix::Mat3;
use cgmath::matrix::Mat4;
use cgmath::matrix::ToMat4;
use cgmath::vector::Vec4;
use cgmath::angle::rad;

#[link(name="GLU")]
extern {}

static vertex_shader_src : &'static str = r"
#version 110

uniform mat4 transform;

attribute vec4 position;

varying vec3 color;

void main() {
    gl_Position = transform * position;
    color = (position.xyz + vec3(1.0, 1.0, 1.0)) * 0.5;
}
";

static fragment_shader_src : &'static str = r"
#version 110

varying vec3 color;

void main() {
    gl_FragColor = vec4(color, 1.0);
}
";

#[start]
fn start(argc: int, argv: **u8) -> int {
    std::rt::start_on_main_thread(argc, argv, main)
}

fn main() {
   glfw::set_error_callback(~ErrorContext);

   let window_width = 800;
   let window_height = 600;

    do glfw::start {
        let window = glfw::Window::create(window_width, window_height, "Hello, I am a window.", glfw::Windowed)
            .expect("Failed to create GLFW window.");

        //window.set_cursor_mode(glfw::CursorDisabled);
        window.make_context_current();

        gl::load_with(glfw::get_proc_address);

        let vs = compile_shader(vertex_shader_src.as_bytes(), gl::VERTEX_SHADER);
        let fs = compile_shader(fragment_shader_src.as_bytes(), gl::FRAGMENT_SHADER);
        let program = link_program(vs, fs);

        check_gl("after link");

        let mut uniform_transform;

        unsafe {
            uniform_transform = "transform".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
        }

        check_gl("after uniform transform location");

        let mut vao = 0;
        let mut vbo = 0;
        let mut ebo = 0;

        let vertices : ~[GLfloat] = ~[
            /* Front face */
            -1.0, -1.0,  1.0, 1.0, /* bottom left */
            1.0, -1.0,  1.0, 1.0,  /* bottom right */
            -1.0,  1.0,  1.0, 1.0, /* top left */
            1.0,  1.0,  1.0, 1.0,  /* top right */

            /* Back face */
            1.0, -1.0, -1.0, 1.0, /* bottom right */
            -1.0, -1.0, -1.0, 1.0,  /* bottom left */
            1.0, 1.0, -1.0, 1.0, /* top right */
            -1.0, 1.0, -1.0, 1.0,  /* top left */

            /* Right face */
            1.0, -1.0, 1.0, 1.0, /* bottom front */
            1.0, -1.0, -1.0, 1.0, /* bottom back */
            1.0, 1.0, 1.0, 1.0, /* top front */
            1.0, 1.0, -1.0, 1.0, /* top back */

            /* Left face */
            -1.0, -1.0, -1.0, 1.0, /* bottom back */
            -1.0, -1.0, 1.0, 1.0, /* bottom front */
            -1.0, 1.0, -1.0, 1.0, /* top back */
            -1.0, 1.0, 1.0, 1.0, /* top front */

            /* Top face */
            -1.0, 1.0, 1.0, 1.0, /* front left */
            1.0, 1.0, 1.0, 1.0, /* front right */
            -1.0, 1.0, -1.0, 1.0, /* back left */
            1.0, 1.0, -1.0, 1.0, /* back right */

            /* Bottom face */
            -1.0, -1.0, -1.0, 1.0, /* back left */
            1.0, -1.0, -1.0, 1.0, /* back right */
            -1.0, -1.0, 1.0, 1.0, /* front left */
            1.0, -1.0, 1.0, 1.0, /* front right */
        ];

        let elements : ~[GLshort] = ~[
            0, 1, 2, 3, 2, 1,
            4, 5, 6, 7, 6, 5,
            8, 9, 10, 11, 10, 9,
            12, 13, 14, 15, 14, 13,
            16, 17, 18, 19, 18, 17,
            20, 21, 22, 23, 22, 21,
        ];

        unsafe {
            gl::Enable(gl::DEPTH_TEST);
            gl::Enable(gl::CULL_FACE);

            glfw::set_swap_interval(1);

            // Create Vertex Array Object
            gl::GenVertexArrays(1, &mut vao);
            gl::BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl::GenBuffers(1, &mut vbo);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
            gl::BufferData(gl::ARRAY_BUFFER,
                           (vertices.len() * std::mem::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&vertices[0]),
                           gl::STATIC_DRAW);

            check_gl("after vertex buffer");

            // Create a Vertex Buffer Object and copy the element data to it
            gl::GenBuffers(1, &mut ebo);
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
            gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
                           (elements.len() * std::mem::size_of::<GLshort>()) as GLsizeiptr,
                           cast::transmute(&elements[0]),
                           gl::STATIC_DRAW);

            check_gl("after element buffer");

            // Use shader program
            gl::UseProgram(program);

            // Specify the layout of the vertex data
            let vert_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
            gl::EnableVertexAttribArray(vert_attr as GLuint);
            gl::VertexAttribPointer(vert_attr as GLuint, 4, gl::FLOAT,
                                    gl::FALSE as GLboolean, 0, ptr::null());

            check_gl("after vertex attrib pointer");
        }

        check_gl("after buffers");

        window.set_cursor_pos_callback(~CursorPosContext);
        window.set_key_callback(~KeyContext);

        let start_time = extra::time::precise_time_ns();
        let mut last_frame_time = start_time;
        let mut num_frames = 0;

        while !window.should_close() {
            glfw::poll_events();

            gl::Viewport(0,0, window_width as GLint, window_height as GLint);

            gl::ClearColor(0.0, 0.0, 0.0, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

            let elapsed : u64 = extra::time::precise_time_ns() - start_time;
            let angle : f32 = elapsed as f32 / (1000*1000*1000) as f32;

            let translation = Mat4::<f32>::from_cols(
                Vec4::<f32>::unit_x(),
                Vec4::<f32>::unit_y(),
                Vec4::<f32>::unit_z(),
                Vec4::<f32>::new(0.0f32, 0.0f32, -5.0f32, 1.0f32));
            let rotation_x = Mat3::<f32>::from_angle_x(rad(0.5f32)).to_mat4();
            let rotation_y = Mat3::<f32>::from_angle_y(rad(angle)).to_mat4();
            let projection = cgmath::projection::perspective(rad(1.57 as f32), (4.0/3.0) as f32, 0.1 as f32, 10.0 as f32);
            let transform = projection.mul_m(&translation).mul_m(&rotation_x).mul_m(&rotation_y);

            unsafe {
                gl::UniformMatrix4fv(uniform_transform, 1, gl::FALSE, cast::transmute(&transform));

                check_gl("after uniform transform");

                gl::DrawElements(gl::TRIANGLES, elements.len() as i32, gl::UNSIGNED_SHORT, ptr::null());
            }

            window.swap_buffers();

            check_gl("main loop");

            let cur_time = extra::time::precise_time_ns();
            num_frames += 1;
            if (cur_time - last_frame_time) > (1000*1000*1000) {
                println!("{} frames per second", num_frames);
                num_frames = 0;
                last_frame_time = cur_time;
            }
        }
    }
}

extern "C" {
    fn gluErrorString(error: GLenum) -> *GLubyte;
}

fn check_gl(message : &str) {
    let err = gl::GetError();
    if (err != gl::NO_ERROR) {
        unsafe {
            let err = std::str::raw::from_c_str(gluErrorString(err) as *i8);
            fail!("GL error {} at {}", err, message);
        }
    }
}

fn compile_shader(src: &[u8], ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        //transmute is used here because `as` causes ICE
        //wait a sec, is `src` null-terminated properly?
        gl::ShaderSource(shader, 1, std::cast::transmute(std::ptr::to_unsafe_ptr(&src.as_ptr())), ptr::null());
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!(str::raw::from_utf8(buf).to_owned());
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint) -> GLuint {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    gl::LinkProgram(program);
    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!(str::raw::from_utf8(buf).to_owned());
        }
    }
    program
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
    fn call(&self, _: glfw::Error, description: ~str) {
        fail!("GLFW Error: {:s}", description);
    }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
    fn call(&self, _: &glfw::Window, _: f64, _: f64) {
    }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
    fn call(&self, window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
        match (action, key) {
            (glfw::Press, glfw::KeyEscape) => window.set_should_close(true),
            _ => println!("unexpected key callback for action {:?} key {:?}", action, key)
        }
    }
}
<commit_msg>simple lighting<commit_after>#[feature(globs)];

extern mod extra;
extern mod glfw;
extern mod gl;
extern mod cgmath;

use std::cast;
use std::ptr;
use std::str;
use std::vec;
use std::libc;

use gl::types::*;

use cgmath::matrix::Matrix;
use cgmath::matrix::Mat3;
use cgmath::matrix::Mat4;
use cgmath::matrix::ToMat4;
use cgmath::vector::Vec4;
use cgmath::angle::rad;

#[link(name="GLU")]
extern {}

static vertex_shader_src : &'static str = r"
#version 110

uniform mat4 transform;

attribute vec4 position;
attribute vec3 normal;

varying vec4 frag_position;
varying vec3 frag_normal;

void main() {
    frag_position = position;
    frag_normal = normal;
    gl_Position = transform * frag_position;
}
";

static fragment_shader_src : &'static str = r"
#version 110

varying vec4 frag_position;
varying vec3 frag_normal;

const vec4 obj_diffuse = vec4(0.2, 0.6, 0.2, 1.0);

const vec3 light_direction = vec3(0.408248, -0.816497, 0.408248);
const vec4 light_diffuse = vec4(0.8, 0.8, 0.8, 0.0);
const vec4 light_ambient = vec4(0.2, 0.2, 0.2, 1.0);

void main() {
    vec3 mv_light_direction = light_direction;

    vec4 diffuse_factor
        = max(-dot(frag_normal, mv_light_direction), 0.0) * light_diffuse;
    vec4 ambient_diffuse_factor = diffuse_factor + light_ambient;

    gl_FragColor = ambient_diffuse_factor * obj_diffuse;
}
";

#[start]
fn start(argc: int, argv: **u8) -> int {
    std::rt::start_on_main_thread(argc, argv, main)
}

fn main() {
   glfw::set_error_callback(~ErrorContext);

   let window_width = 800;
   let window_height = 600;

    do glfw::start {
        let window = glfw::Window::create(window_width, window_height, "Hello, I am a window.", glfw::Windowed)
            .expect("Failed to create GLFW window.");

        //window.set_cursor_mode(glfw::CursorDisabled);
        window.make_context_current();

        gl::load_with(glfw::get_proc_address);

        let vs = compile_shader(vertex_shader_src.as_bytes(), gl::VERTEX_SHADER);
        let fs = compile_shader(fragment_shader_src.as_bytes(), gl::FRAGMENT_SHADER);
        let program = link_program(vs, fs);

        check_gl("after link");

        let mut uniform_transform;

        unsafe {
            uniform_transform = "transform".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
        }

        check_gl("after uniform transform location");

        let mut vao = 0;
        let mut vbo = 0;
        let mut nbo = 0;
        let mut ebo = 0;

        let vertices : ~[GLfloat] = ~[
            /* Front face */
            -1.0, -1.0,  1.0, 1.0, /* bottom left */
            1.0, -1.0,  1.0, 1.0,  /* bottom right */
            -1.0,  1.0,  1.0, 1.0, /* top left */
            1.0,  1.0,  1.0, 1.0,  /* top right */

            /* Back face */
            1.0, -1.0, -1.0, 1.0, /* bottom right */
            -1.0, -1.0, -1.0, 1.0,  /* bottom left */
            1.0, 1.0, -1.0, 1.0, /* top right */
            -1.0, 1.0, -1.0, 1.0,  /* top left */

            /* Right face */
            1.0, -1.0, 1.0, 1.0, /* bottom front */
            1.0, -1.0, -1.0, 1.0, /* bottom back */
            1.0, 1.0, 1.0, 1.0, /* top front */
            1.0, 1.0, -1.0, 1.0, /* top back */

            /* Left face */
            -1.0, -1.0, -1.0, 1.0, /* bottom back */
            -1.0, -1.0, 1.0, 1.0, /* bottom front */
            -1.0, 1.0, -1.0, 1.0, /* top back */
            -1.0, 1.0, 1.0, 1.0, /* top front */

            /* Top face */
            -1.0, 1.0, 1.0, 1.0, /* front left */
            1.0, 1.0, 1.0, 1.0, /* front right */
            -1.0, 1.0, -1.0, 1.0, /* back left */
            1.0, 1.0, -1.0, 1.0, /* back right */

            /* Bottom face */
            -1.0, -1.0, -1.0, 1.0, /* back left */
            1.0, -1.0, -1.0, 1.0, /* back right */
            -1.0, -1.0, 1.0, 1.0, /* front left */
            1.0, -1.0, 1.0, 1.0, /* front right */
        ];

        let normals : ~[GLfloat] = ~[
            /* Front face */
            0.0, 0.0, 1.0,
            0.0, 0.0, 1.0,
            0.0, 0.0, 1.0,
            0.0, 0.0, 1.0,

            /* Back face */
            0.0, 0.0, -1.0,
            0.0, 0.0, -1.0,
            0.0, 0.0, -1.0,
            0.0, 0.0, -1.0,

            /* Right face */
            1.0, 0.0, 0.0,
            1.0, 0.0, 0.0,
            1.0, 0.0, 0.0,
            1.0, 0.0, 0.0,

            /* Left face */
            -1.0, 0.0, 0.0,
            -1.0, 0.0, 0.0,
            -1.0, 0.0, 0.0,
            -1.0, 0.0, 0.0,

            /* Top face */
            0.0, 1.0, 0.0,
            0.0, 1.0, 0.0,
            0.0, 1.0, 0.0,
            0.0, 1.0, 0.0,

            /* Bottom face */
            0.0, -1.0, 0.0,
            0.0, -1.0, 0.0,
            0.0, -1.0, 0.0,
            0.0, -1.0, 0.0,
        ];

        let elements : ~[GLshort] = ~[
            0, 1, 2, 3, 2, 1,
            4, 5, 6, 7, 6, 5,
            8, 9, 10, 11, 10, 9,
            12, 13, 14, 15, 14, 13,
            16, 17, 18, 19, 18, 17,
            20, 21, 22, 23, 22, 21,
        ];

        unsafe {
            gl::Enable(gl::DEPTH_TEST);
            gl::Enable(gl::CULL_FACE);

            glfw::set_swap_interval(1);

            // Create Vertex Array Object
            gl::GenVertexArrays(1, &mut vao);
            gl::BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl::GenBuffers(1, &mut vbo);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
            gl::BufferData(gl::ARRAY_BUFFER,
                           (vertices.len() * std::mem::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&vertices[0]),
                           gl::STATIC_DRAW);

            check_gl("after vertex buffer");

            // Create a Vertex Buffer Object and copy the normal data to it
            gl::GenBuffers(1, &mut nbo);
            gl::BindBuffer(gl::ARRAY_BUFFER, nbo);
            gl::BufferData(gl::ARRAY_BUFFER,
                           (normals.len() * std::mem::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&normals[0]),
                           gl::STATIC_DRAW);

            check_gl("after normal buffer");

            // Create a Vertex Buffer Object and copy the element data to it
            gl::GenBuffers(1, &mut ebo);
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
            gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
                           (elements.len() * std::mem::size_of::<GLshort>()) as GLsizeiptr,
                           cast::transmute(&elements[0]),
                           gl::STATIC_DRAW);

            check_gl("after element buffer");

            // Use shader program
            gl::UseProgram(program);

            // Specify the layout of the vertex data
            let vert_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
            assert!(vert_attr as u32 != gl::INVALID_VALUE);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
            gl::EnableVertexAttribArray(vert_attr as GLuint);
            gl::VertexAttribPointer(vert_attr as GLuint, 4, gl::FLOAT,
                                    gl::FALSE as GLboolean, 0, ptr::null());

            check_gl("after vertex attrib pointer");

            let normal_attr = "normal".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
            assert!(normal_attr as u32 != gl::INVALID_VALUE);
            gl::BindBuffer(gl::ARRAY_BUFFER, nbo);
            gl::EnableVertexAttribArray(normal_attr as GLuint);
            gl::VertexAttribPointer(normal_attr as GLuint, 3, gl::FLOAT,
                                    gl::FALSE as GLboolean, 0, ptr::null());

            check_gl("after normal attrib pointer");
        }

        check_gl("after buffers");

        window.set_cursor_pos_callback(~CursorPosContext);
        window.set_key_callback(~KeyContext);

        let start_time = extra::time::precise_time_ns();
        let mut last_frame_time = start_time;
        let mut num_frames = 0;

        while !window.should_close() {
            glfw::poll_events();

            gl::Viewport(0,0, window_width as GLint, window_height as GLint);

            gl::ClearColor(0.0, 0.0, 0.0, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

            let elapsed : u64 = extra::time::precise_time_ns() - start_time;
            let angle : f32 = elapsed as f32 / (1000*1000*1000) as f32;

            let translation = Mat4::<f32>::from_cols(
                Vec4::<f32>::unit_x(),
                Vec4::<f32>::unit_y(),
                Vec4::<f32>::unit_z(),
                Vec4::<f32>::new(0.0f32, 0.0f32, -5.0f32, 1.0f32));
            let rotation_x = Mat3::<f32>::from_angle_x(rad(0.5f32)).to_mat4();
            let rotation_y = Mat3::<f32>::from_angle_y(rad(angle)).to_mat4();
            let projection = cgmath::projection::perspective(rad(1.57 as f32), (4.0/3.0) as f32, 0.1 as f32, 10.0 as f32);
            let transform = projection.mul_m(&translation).mul_m(&rotation_x).mul_m(&rotation_y);

            unsafe {
                gl::UniformMatrix4fv(uniform_transform, 1, gl::FALSE, cast::transmute(&transform));

                check_gl("after uniform transform");

                gl::DrawElements(gl::TRIANGLES, elements.len() as i32, gl::UNSIGNED_SHORT, ptr::null());
            }

            window.swap_buffers();

            check_gl("main loop");

            let cur_time = extra::time::precise_time_ns();
            num_frames += 1;
            if (cur_time - last_frame_time) > (1000*1000*1000) {
                println!("{} frames per second", num_frames);
                num_frames = 0;
                last_frame_time = cur_time;
            }
        }
    }
}

extern "C" {
    fn gluErrorString(error: GLenum) -> *GLubyte;
}

fn check_gl(message : &str) {
    let err = gl::GetError();
    if (err != gl::NO_ERROR) {
        unsafe {
            let err = std::str::raw::from_c_str(gluErrorString(err) as *i8);
            fail!("GL error {} at {}", err, message);
        }
    }
}

fn compile_shader(src: &[u8], ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        //transmute is used here because `as` causes ICE
        //wait a sec, is `src` null-terminated properly?
        gl::ShaderSource(shader, 1, std::cast::transmute(std::ptr::to_unsafe_ptr(&src.as_ptr())), ptr::null());
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!(str::raw::from_utf8(buf).to_owned());
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint) -> GLuint {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    gl::LinkProgram(program);
    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!(str::raw::from_utf8(buf).to_owned());
        }
    }
    program
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
    fn call(&self, _: glfw::Error, description: ~str) {
        fail!("GLFW Error: {:s}", description);
    }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
    fn call(&self, _: &glfw::Window, _: f64, _: f64) {
    }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
    fn call(&self, window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
        match (action, key) {
            (glfw::Press, glfw::KeyEscape) => window.set_should_close(true),
            _ => println!("unexpected key callback for action {:?} key {:?}", action, key)
        }
    }
}
<|endoftext|>


<commit_before>use data::{IndexedDataShard, OwnedSample};

use varraydb::{VarrayDb};

use std::path::{Path};

pub struct VarrayDbShard {
  db:           VarrayDb,
  start_idx:    usize,
  end_idx:      usize,
}

impl VarrayDbShard {
  pub fn open(prefix: &Path) -> VarrayDbShard {
    let db = VarrayDb::open(prefix).unwrap();
    let len = db.len();
    VarrayDbShard{
      db:           db,
      start_idx:    0,
      end_idx:      len,
    }
  }

  pub fn open_partition(prefix: &Path, part: usize, num_parts: usize) -> VarrayDbShard {
    let mut shard = Self::open(prefix);
    unimplemented!();
  }

  pub fn open_range(prefix: &Path, start_idx: usize, end_idx: usize) -> VarrayDbShard {
    let mut shard = Self::open(prefix);
    shard.start_idx = start_idx;
    shard.end_idx = end_idx;
    shard
  }
}

impl IndexedDataShard<OwnedSample<u8>> for VarrayDbShard {
  fn len(&self) -> usize {
    self.end_idx - self.start_idx
  }

  fn get(&mut self, offset: usize) -> OwnedSample<u8> {
    let idx = self.start_idx + offset;
    assert!(idx >= self.start_idx);
    assert!(idx < self.end_idx);
    let value = self.db.get(idx);
    OwnedSample{
      input:    value.to_owned(),
    }
  }
}
<commit_msg>Store the path of the varraydb.<commit_after>use data::{IndexedDataShard, OwnedSample};

use varraydb::{VarrayDb};

use std::path::{Path, PathBuf};

pub struct VarrayDbShard {
  prefix:       PathBuf,
  db:           VarrayDb,
  start_idx:    usize,
  end_idx:      usize,
}

impl VarrayDbShard {
  pub fn open(prefix: PathBuf) -> VarrayDbShard {
    let db = VarrayDb::open(&prefix).unwrap();
    let len = db.len();
    VarrayDbShard{
      prefix:       prefix,
      db:           db,
      start_idx:    0,
      end_idx:      len,
    }
  }

  pub fn open_partition(prefix: PathBuf, part: usize, num_parts: usize) -> VarrayDbShard {
    let mut shard = Self::open(prefix);
    unimplemented!();
  }

  pub fn open_range(prefix: PathBuf, start_idx: usize, end_idx: usize) -> VarrayDbShard {
    let mut shard = Self::open(prefix);
    shard.start_idx = start_idx;
    shard.end_idx = end_idx;
    shard
  }
}

impl IndexedDataShard<OwnedSample<u8>> for VarrayDbShard {
  fn len(&self) -> usize {
    self.end_idx - self.start_idx
  }

  fn get(&mut self, offset: usize) -> OwnedSample<u8> {
    let idx = self.start_idx + offset;
    assert!(idx >= self.start_idx);
    assert!(idx < self.end_idx);
    let value = self.db.get(idx);
    OwnedSample{
      input:    value.to_owned(),
    }
  }
}
<|endoftext|>


<commit_before>use noise::Noise;
use std::clone::Clone;

static PRIME_POSITION: i32 = 999961;
static PRIME_SEED: i32 = 748361;

/// Generator for pseudo-random integer noise.
///
/// This is implemented using `libnoise`'s integer noise function. See the [libnoise documentation](
/// http://libnoise.sourceforge.net/noisegen/#coherentnoise) for details.
#[derive(Clone)]
pub struct DefaultI32Noise {
    seed: i32,
}

impl DefaultI32Noise {
    pub fn new(seed: i32) -> DefaultI32Noise {
        DefaultI32Noise{
            seed: (seed * PRIME_SEED) ^ (seed << 13),
        }
    }
}

impl Noise<i32, f64> for DefaultI32Noise {
    fn value(&self, position: i32) -> f64 {
        let a = position * PRIME_POSITION + self.seed;
        let b = (a << 13) ^ a;
        let c = (b * (b * b * 60493 + 19990303) + 1376312589) & 0x7fffffff;
        1.0 - (c as f64) / 1073741824.0
    }
}

/// Generator that always returns the same value.
#[derive(Clone)]
pub struct ConstantNoise<Out> {
    value: Out,
}

impl<Out: Clone> ConstantNoise<Out> {
    pub fn new(value: Out) -> ConstantNoise<Out> {
        ConstantNoise{
            value: value
        }
    }
}

impl<In, Out: Clone> Noise<In, Out> for ConstantNoise<Out> {
    fn value(&self, _: In) -> Out {
        self.value.clone()
    }
}

/// Generator that always returns the position parameter unchanged.
#[derive(Clone)]
pub struct NoOpNoise;

impl<In> Noise<In, In> for NoOpNoise {
    fn value(&self, position: In) -> In {
        position
    }
}

/// Wrapper that can be used to use Box<Noise<...>> as source to other
/// building blocks.
pub struct UnboxNoise<'a, In, Out> {
    source: Box<Noise<In, Out> + 'a>
}

impl<'a, In, Out> UnboxNoise<'a, In, Out> {
    pub fn new(source: Box<Noise<In, Out> + 'a>) -> UnboxNoise<'a, In, Out> {
        UnboxNoise{
            source: source
        }
    }
}

impl<'a, In, Out> Noise<In, Out> for UnboxNoise<'a, In, Out> {
    fn value(&self, position: In) -> Out {
        self.source.value(position)
    }
}

#[cfg(test)]
mod test {
    use super::{DefaultI32Noise, ConstantNoise, NoOpNoise};
    use std::iter::range_step_inclusive;
    use std::num::Int;
    use noise::Noise;

    #[test]
    fn integer_noise_test() {
        let noise = DefaultI32Noise::new(0);
        for n in range_step_inclusive(Int::min_value(), Int::max_value(), 1001) {
            let value = noise.value(n);
            assert!(-1.0 <= value && value <= 1.0);
        }
    }

    #[test]
    fn constant_noise_test() {
        let noise = ConstantNoise::new('x');
        for n in range_step_inclusive(Int::min_value(), Int::max_value(), 1001) {
            let value = noise.value(n);
            assert!(value == 'x');
        }
    }

    #[test]
    fn no_op_noise_test() {
        let noise = NoOpNoise;
        for n in range_step_inclusive(Int::min_value(), Int::max_value(), 1001) {
            let value = noise.value(n);
            assert!(value == n);
        }
    }
}
<commit_msg>Fixes typo<commit_after>use noise::Noise;
use std::clone::Clone;

static PRIME_POSITION: i32 = 999961;
static PRIME_SEED: i32 = 748361;

/// Generator for pseudo-random integer noise.
///
/// This is implemented using `libnoise`'s integer noise function. See the [libnoise documentation](
/// http://libnoise.sourceforge.net/noisegen/#coherentnoise) for details.
#[derive(Clone)]
pub struct DefaultI32Noise {
    seed: i32,
}

impl DefaultI32Noise {
    pub fn new(seed: i32) -> DefaultI32Noise {
        DefaultI32Noise{
            seed: (seed * PRIME_SEED) ^ (seed << 13),
        }
    }
}

impl Noise<i32, f64> for DefaultI32Noise {
    fn value(&self, position: i32) -> f64 {
        let a = position * PRIME_POSITION + self.seed;
        let b = (a << 13) ^ a;
        let c = (b * (b * b * 60493 + 19990303) + 1376312589) & 0x7fffffff;
        1.0 - (c as f64) / 1073741824.0
    }
}

/// Generator that always returns the same value.
#[derive(Clone)]
pub struct ConstantNoise<Out> {
    value: Out,
}

impl<Out: Clone> ConstantNoise<Out> {
    pub fn new(value: Out) -> ConstantNoise<Out> {
        ConstantNoise{
            value: value
        }
    }
}

impl<In, Out: Clone> Noise<In, Out> for ConstantNoise<Out> {
    fn value(&self, _: In) -> Out {
        self.value.clone()
    }
}

/// Generator that always returns the position parameter unchanged.
#[derive(Clone)]
pub struct NoOpNoise;

impl<In> Noise<In, In> for NoOpNoise {
    fn value(&self, position: In) -> In {
        position
    }
}

/// Wrapper that can be used to use `Box<Noise<...>>` as source to other
/// building blocks.
pub struct UnboxNoise<'a, In, Out> {
    source: Box<Noise<In, Out> + 'a>
}

impl<'a, In, Out> UnboxNoise<'a, In, Out> {
    pub fn new(source: Box<Noise<In, Out> + 'a>) -> UnboxNoise<'a, In, Out> {
        UnboxNoise{
            source: source
        }
    }
}

impl<'a, In, Out> Noise<In, Out> for UnboxNoise<'a, In, Out> {
    fn value(&self, position: In) -> Out {
        self.source.value(position)
    }
}

#[cfg(test)]
mod test {
    use super::{DefaultI32Noise, ConstantNoise, NoOpNoise};
    use std::iter::range_step_inclusive;
    use std::num::Int;
    use noise::Noise;

    #[test]
    fn integer_noise_test() {
        let noise = DefaultI32Noise::new(0);
        for n in range_step_inclusive(Int::min_value(), Int::max_value(), 1001) {
            let value = noise.value(n);
            assert!(-1.0 <= value && value <= 1.0);
        }
    }

    #[test]
    fn constant_noise_test() {
        let noise = ConstantNoise::new('x');
        for n in range_step_inclusive(Int::min_value(), Int::max_value(), 1001) {
            let value = noise.value(n);
            assert!(value == 'x');
        }
    }

    #[test]
    fn no_op_noise_test() {
        let noise = NoOpNoise;
        for n in range_step_inclusive(Int::min_value(), Int::max_value(), 1001) {
            let value = noise.value(n);
            assert!(value == n);
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

fn run_command_or_fail<P, S>(dir: &str, cmd: P, args: &[S])
where
    P: AsRef<Path>,
    S: Borrow<str> + AsRef<OsStr>,
{
    let cmd = cmd.as_ref();
    let cmd = if cmd.components().count() > 1 && cmd.is_relative() {
        // If `cmd` is a relative path (and not a bare command that should be
        // looked up in PATH), absolutize it relative to `dir`, as otherwise the
        // behavior of std::process::Command is undefined.
        // https://github.com/rust-lang/rust/issues/37868
        PathBuf::from(dir)
            .join(cmd)
            .canonicalize()
            .expect("canonicalization failed")
    } else {
        PathBuf::from(cmd)
    };
    eprintln!(
        "Running command: \"{} {}\" in dir: {}",
        cmd.display(),
        args.join(" "),
        dir
    );
    let ret = Command::new(cmd).current_dir(dir).args(args).status();
    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => return,
        Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
        Ok((false, None)) => panic!("Command got killed"),
        Err(e) => panic!("Command failed with error: {}", e),
    }
}

fn main() {
    let librdkafka_version = env!("CARGO_PKG_VERSION")
        .split('-')
        .next()
        .expect("Crate version is not valid");

    if env::var("DEP_OPENSSL_VENDORED").is_ok() {
        let openssl_root = env::var("DEP_OPENSSL_ROOT").expect("DEP_OPENSSL_ROOT is not set");
        env::set_var("CFLAGS", format!("-I{}/include", openssl_root));
        env::set_var("LDFLAGS", format!("-L{}/lib", openssl_root));
    }

    if env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok() {
        eprintln!("librdkafka will be linked dynamically");
        let pkg_probe = pkg_config::Config::new()
            .cargo_metadata(true)
            .atleast_version(librdkafka_version)
            .probe("rdkafka");

        match pkg_probe {
            Ok(library) => {
                eprintln!("librdkafka found on the system:");
                eprintln!("  Name: {:?}", library.libs);
                eprintln!("  Path: {:?}", library.link_paths);
                eprintln!("  Version: {}", library.version);
            }
            Err(_) => {
                eprintln!(
                    "librdkafka {} cannot be found on the system",
                    librdkafka_version
                );
                eprintln!("Dynamic linking failed. Exiting.");
                process::exit(1);
            }
        }
    } else {
        if !Path::new("librdkafka/LICENSE").exists() {
            eprintln!("Setting up submodules");
            run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
        }
        eprintln!("Building and linking librdkafka statically");
        build_librdkafka();
    }
}

#[cfg(not(feature = "cmake-build"))]
fn build_librdkafka() {
    let mut configure_flags: Vec<String> = Vec::new();

    let mut cflags = Vec::new();
    if let Ok(var) = env::var("CFLAGS") {
        cflags.push(var);
    }

    let mut ldflags = Vec::new();
    if let Ok(var) = env::var("LDFLAGS") {
        ldflags.push(var);
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        configure_flags.push("--enable-ssl".into());
        if let Ok(openssl_root) = env::var("DEP_OPENSSL_ROOT") {
            cflags.push(format!("-I{}/include", openssl_root));
            ldflags.push(format!("-L{}/lib", openssl_root));
        }
    } else {
        configure_flags.push("--disable-ssl".into());
    }

    if env::var("CARGO_FEATURE_GSSAPI").is_ok() {
        configure_flags.push("--enable-gssapi".into());
        println!("cargo:rustc-link-lib=sasl2");
    } else {
        configure_flags.push("--disable-gssapi".into());
    }

    if env::var("CARGO_FEATURE_LIBZ").is_ok() {
        // There is no --enable-zlib option, but it is enabled by default.
        if let Ok(z_root) = env::var("DEP_Z_ROOT") {
            cflags.push(format!("-I{}/include", z_root));
            ldflags.push(format!("-L{}/build", z_root));
        }
    } else {
        configure_flags.push("--disable-zlib".into());
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        configure_flags.push("--enable-zstd".into());
        if let Ok(zstd_root) = env::var("DEP_ZSTD_ROOT") {
            cflags.push(format!("-I{}/include", zstd_root));
            ldflags.push(format!("-L{}", zstd_root));
        }
    } else {
        configure_flags.push("--disable-zstd".into());
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        configure_flags.push("--enable-lz4-ext".into());
        if let Ok(lz4_root) = env::var("DEP_LZ4_ROOT") {
            cflags.push(format!("-I{}/include", lz4_root));
            ldflags.push(format!("-L{}", lz4_root));
        }
    } else {
        configure_flags.push("--disable-lz4-ext".into());
    }

    env::set_var("CFLAGS", cflags.join(" "));
    env::set_var("LDFLAGS", ldflags.join(" "));

    let out_dir = env::var("OUT_DIR").expect("OUT_DIR missing");

    if !Path::new(&out_dir).join("LICENSE").exists() {
        // We're not allowed to build in-tree directly, as ~/.cargo/registry is
        // globally shared. mklove doesn't support out-of-tree builds [0], so we
        // work around the issue by creating a clone of librdkafka inside of
        // OUT_DIR, and build inside of *that* tree.
        //
        // https://github.com/edenhill/mklove/issues/17
        println!("Cloning librdkafka");
        run_command_or_fail(".", "cp", &["-a", "librdkafka/.", &out_dir]);
    }

    println!("Configuring librdkafka");
    run_command_or_fail(&out_dir, "./configure", configure_flags.as_slice());

    println!("Compiling librdkafka");
    env::set_var("MAKEFLAGS", env::var_os("CARGO_MAKEFLAGS").expect("CARGO_MAKEFLAGS env var missing"));
    run_command_or_fail(
        &out_dir,
        if cfg!(target_os = "freebsd") { "gmake" } else { "make" },
        &["libs"],
    );

    println!(
        "cargo:rustc-link-search=native={}/src",
        out_dir,
    );
    println!("cargo:rustc-link-lib=static=rdkafka");
    println!("cargo:root={}", out_dir);
}

#[cfg(feature = "cmake-build")]
fn build_librdkafka() {
    let mut config = cmake::Config::new("librdkafka");

    config
        .define("RDKAFKA_BUILD_STATIC", "1")
        .define("RDKAFKA_BUILD_TESTS", "0")
        .define("RDKAFKA_BUILD_EXAMPLES", "0")
        // CMAKE_INSTALL_LIBDIR is inferred as "lib64" on some platforms, but we
        // want a stable location that we can add to the linker search path.
        // Since we're not actually installing to /usr or /usr/local, there's no
        // harm to always using "lib" here.
        .define("CMAKE_INSTALL_LIBDIR", "lib");

    if env::var("CARGO_FEATURE_LIBZ").is_ok() {
        config.define("WITH_ZLIB", "1");
        config.register_dep("z");
        if let Ok(z_root) = env::var("DEP_Z_ROOT") {
            env::set_var("CMAKE_LIBRARY_PATH", format!("{}/build", z_root));
        }
    } else {
        config.define("WITH_ZLIB", "0");
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        config.define("WITH_SSL", "1");
        config.register_dep("openssl");
    } else {
        config.define("WITH_SSL", "0");
    }

    if env::var("CARGO_FEATURE_GSSAPI").is_ok() {
        config.define("WITH_SASL", "1");
        println!("cargo:rustc-link-lib=sasl2");
    } else {
        config.define("WITH_SASL", "0");
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        config.define("WITH_ZSTD", "1");
        config.register_dep("zstd");
    } else {
        config.define("WITH_ZSTD", "0");
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        config.define("ENABLE_LZ4_EXT", "1");
        config.register_dep("lz4");
    } else {
        config.define("ENABLE_LZ4_EXT", "0");
    }

    if let Ok(system_name) = env::var("CMAKE_SYSTEM_NAME") {
        config.define("CMAKE_SYSTEM_NAME", system_name);
    }

    // The CMake build will incorrectly use config.h from the non-CMake build,
    // if it exists, so remove it if it does.
    match std::fs::remove_file("librdkafka/config.h") {
        Ok(()) => (),
        Err(err) => match err.kind() {
            std::io::ErrorKind::NotFound => (),
            _ => panic!("Unable to remove config.h from non-CMake build: {}", err),
        }
    }

    println!("Configuring and compiling librdkafka");
    let dst = config.build();

    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=rdkafka");
}
<commit_msg>rdkafka-sys: don't bother removing in-tree config.h<commit_after>use std::borrow::Borrow;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

fn run_command_or_fail<P, S>(dir: &str, cmd: P, args: &[S])
where
    P: AsRef<Path>,
    S: Borrow<str> + AsRef<OsStr>,
{
    let cmd = cmd.as_ref();
    let cmd = if cmd.components().count() > 1 && cmd.is_relative() {
        // If `cmd` is a relative path (and not a bare command that should be
        // looked up in PATH), absolutize it relative to `dir`, as otherwise the
        // behavior of std::process::Command is undefined.
        // https://github.com/rust-lang/rust/issues/37868
        PathBuf::from(dir)
            .join(cmd)
            .canonicalize()
            .expect("canonicalization failed")
    } else {
        PathBuf::from(cmd)
    };
    eprintln!(
        "Running command: \"{} {}\" in dir: {}",
        cmd.display(),
        args.join(" "),
        dir
    );
    let ret = Command::new(cmd).current_dir(dir).args(args).status();
    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => return,
        Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
        Ok((false, None)) => panic!("Command got killed"),
        Err(e) => panic!("Command failed with error: {}", e),
    }
}

fn main() {
    let librdkafka_version = env!("CARGO_PKG_VERSION")
        .split('-')
        .next()
        .expect("Crate version is not valid");

    if env::var("DEP_OPENSSL_VENDORED").is_ok() {
        let openssl_root = env::var("DEP_OPENSSL_ROOT").expect("DEP_OPENSSL_ROOT is not set");
        env::set_var("CFLAGS", format!("-I{}/include", openssl_root));
        env::set_var("LDFLAGS", format!("-L{}/lib", openssl_root));
    }

    if env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok() {
        eprintln!("librdkafka will be linked dynamically");
        let pkg_probe = pkg_config::Config::new()
            .cargo_metadata(true)
            .atleast_version(librdkafka_version)
            .probe("rdkafka");

        match pkg_probe {
            Ok(library) => {
                eprintln!("librdkafka found on the system:");
                eprintln!("  Name: {:?}", library.libs);
                eprintln!("  Path: {:?}", library.link_paths);
                eprintln!("  Version: {}", library.version);
            }
            Err(_) => {
                eprintln!(
                    "librdkafka {} cannot be found on the system",
                    librdkafka_version
                );
                eprintln!("Dynamic linking failed. Exiting.");
                process::exit(1);
            }
        }
    } else {
        if !Path::new("librdkafka/LICENSE").exists() {
            eprintln!("Setting up submodules");
            run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
        }
        eprintln!("Building and linking librdkafka statically");
        build_librdkafka();
    }
}

#[cfg(not(feature = "cmake-build"))]
fn build_librdkafka() {
    let mut configure_flags: Vec<String> = Vec::new();

    let mut cflags = Vec::new();
    if let Ok(var) = env::var("CFLAGS") {
        cflags.push(var);
    }

    let mut ldflags = Vec::new();
    if let Ok(var) = env::var("LDFLAGS") {
        ldflags.push(var);
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        configure_flags.push("--enable-ssl".into());
        if let Ok(openssl_root) = env::var("DEP_OPENSSL_ROOT") {
            cflags.push(format!("-I{}/include", openssl_root));
            ldflags.push(format!("-L{}/lib", openssl_root));
        }
    } else {
        configure_flags.push("--disable-ssl".into());
    }

    if env::var("CARGO_FEATURE_GSSAPI").is_ok() {
        configure_flags.push("--enable-gssapi".into());
        println!("cargo:rustc-link-lib=sasl2");
    } else {
        configure_flags.push("--disable-gssapi".into());
    }

    if env::var("CARGO_FEATURE_LIBZ").is_ok() {
        // There is no --enable-zlib option, but it is enabled by default.
        if let Ok(z_root) = env::var("DEP_Z_ROOT") {
            cflags.push(format!("-I{}/include", z_root));
            ldflags.push(format!("-L{}/build", z_root));
        }
    } else {
        configure_flags.push("--disable-zlib".into());
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        configure_flags.push("--enable-zstd".into());
        if let Ok(zstd_root) = env::var("DEP_ZSTD_ROOT") {
            cflags.push(format!("-I{}/include", zstd_root));
            ldflags.push(format!("-L{}", zstd_root));
        }
    } else {
        configure_flags.push("--disable-zstd".into());
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        configure_flags.push("--enable-lz4-ext".into());
        if let Ok(lz4_root) = env::var("DEP_LZ4_ROOT") {
            cflags.push(format!("-I{}/include", lz4_root));
            ldflags.push(format!("-L{}", lz4_root));
        }
    } else {
        configure_flags.push("--disable-lz4-ext".into());
    }

    env::set_var("CFLAGS", cflags.join(" "));
    env::set_var("LDFLAGS", ldflags.join(" "));

    let out_dir = env::var("OUT_DIR").expect("OUT_DIR missing");

    if !Path::new(&out_dir).join("LICENSE").exists() {
        // We're not allowed to build in-tree directly, as ~/.cargo/registry is
        // globally shared. mklove doesn't support out-of-tree builds [0], so we
        // work around the issue by creating a clone of librdkafka inside of
        // OUT_DIR, and build inside of *that* tree.
        //
        // https://github.com/edenhill/mklove/issues/17
        println!("Cloning librdkafka");
        run_command_or_fail(".", "cp", &["-a", "librdkafka/.", &out_dir]);
    }

    println!("Configuring librdkafka");
    run_command_or_fail(&out_dir, "./configure", configure_flags.as_slice());

    println!("Compiling librdkafka");
    env::set_var("MAKEFLAGS", env::var_os("CARGO_MAKEFLAGS").expect("CARGO_MAKEFLAGS env var missing"));
    run_command_or_fail(
        &out_dir,
        if cfg!(target_os = "freebsd") { "gmake" } else { "make" },
        &["libs"],
    );

    println!(
        "cargo:rustc-link-search=native={}/src",
        out_dir,
    );
    println!("cargo:rustc-link-lib=static=rdkafka");
    println!("cargo:root={}", out_dir);
}

#[cfg(feature = "cmake-build")]
fn build_librdkafka() {
    let mut config = cmake::Config::new("librdkafka");

    config
        .define("RDKAFKA_BUILD_STATIC", "1")
        .define("RDKAFKA_BUILD_TESTS", "0")
        .define("RDKAFKA_BUILD_EXAMPLES", "0")
        // CMAKE_INSTALL_LIBDIR is inferred as "lib64" on some platforms, but we
        // want a stable location that we can add to the linker search path.
        // Since we're not actually installing to /usr or /usr/local, there's no
        // harm to always using "lib" here.
        .define("CMAKE_INSTALL_LIBDIR", "lib");

    if env::var("CARGO_FEATURE_LIBZ").is_ok() {
        config.define("WITH_ZLIB", "1");
        config.register_dep("z");
        if let Ok(z_root) = env::var("DEP_Z_ROOT") {
            env::set_var("CMAKE_LIBRARY_PATH", format!("{}/build", z_root));
        }
    } else {
        config.define("WITH_ZLIB", "0");
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        config.define("WITH_SSL", "1");
        config.register_dep("openssl");
    } else {
        config.define("WITH_SSL", "0");
    }

    if env::var("CARGO_FEATURE_GSSAPI").is_ok() {
        config.define("WITH_SASL", "1");
        println!("cargo:rustc-link-lib=sasl2");
    } else {
        config.define("WITH_SASL", "0");
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        config.define("WITH_ZSTD", "1");
        config.register_dep("zstd");
    } else {
        config.define("WITH_ZSTD", "0");
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        config.define("ENABLE_LZ4_EXT", "1");
        config.register_dep("lz4");
    } else {
        config.define("ENABLE_LZ4_EXT", "0");
    }

    if let Ok(system_name) = env::var("CMAKE_SYSTEM_NAME") {
        config.define("CMAKE_SYSTEM_NAME", system_name);
    }

    println!("Configuring and compiling librdkafka");
    let dst = config.build();

    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=rdkafka");
}
<|endoftext|>


<commit_before>use std::io;

#[derive(Clone, PartialEq, Debug)]
pub enum ErrorKind {
    Io(::std::io::ErrorKind),
    SerdeJsonSer(::serde_json::error::Category),
    SerdeJsonDe(String),
    InterfaceNotFound(String),
    InvalidParameter(String),
    MethodNotFound(String),
    MethodNotImplemented(String),
    VarlinkErrorReply(crate::Reply),
    CallContinuesMismatch,
    MethodCalledAlready,
    ConnectionBusy,
    IteratorOldReply,
    Server,
    Timeout,
    ConnectionClosed,
    InvalidAddress,
    Generic,
}

impl ::std::fmt::Display for ErrorKind {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match self {
            ErrorKind::Io(_) => write!(f, "IO error"),
            ErrorKind::SerdeJsonSer(_) => write!(f, "JSON Serialization Error"),
            ErrorKind::SerdeJsonDe(v) => write!(f, "JSON Deserialization Error of '{}'", v),
            ErrorKind::InterfaceNotFound(v) => write!(f, "Interface not found: '{}'", v),
            ErrorKind::InvalidParameter(v) => write!(f, "Invalid parameter: '{}'", v),
            ErrorKind::MethodNotFound(v) => write!(f, "Method not found: '{}'", v),
            ErrorKind::MethodNotImplemented(v) => write!(f, "Method not implemented: '{}'", v),
            ErrorKind::VarlinkErrorReply(v) => write!(f, "Varlink error reply: '{:#?}'", v),
            ErrorKind::CallContinuesMismatch => write!(
                f,
                "Call::reply() called with continues, but without more in the request"
            ),
            ErrorKind::MethodCalledAlready => write!(f, "Varlink: method called already"),
            ErrorKind::ConnectionBusy => write!(f, "Varlink: connection busy with other method"),
            ErrorKind::IteratorOldReply => write!(f, "Varlink: Iterator called on old reply"),
            ErrorKind::Server => write!(f, "Server Error"),
            ErrorKind::Timeout => write!(f, "Timeout Error"),
            ErrorKind::ConnectionClosed => write!(f, "Connection Closed"),
            ErrorKind::InvalidAddress => write!(f, "Invalid varlink address URI"),
            ErrorKind::Generic => Ok(()),
        }
    }
}

impl From<&std::io::Error> for ErrorKind {
    fn from(e: &io::Error) -> Self {
        match e.kind() {
            io::ErrorKind::BrokenPipe
            | io::ErrorKind::ConnectionAborted
            | io::ErrorKind::ConnectionReset => ErrorKind::ConnectionClosed,
            kind => ErrorKind::Io(kind),
        }
    }
}

impl From<&serde_json::error::Error> for ErrorKind {
    fn from(e: &serde_json::error::Error) -> Self {
        ErrorKind::SerdeJsonSer(e.classify())
    }
}

pub struct Error(
    pub ErrorKind,
    pub Option<Box<dyn std::error::Error + 'static>>,
    pub Option<&'static str>,
);

impl Error {
    pub fn kind(&self) -> &ErrorKind {
        &self.0
    }
}

impl From<ErrorKind> for Error {
    fn from(e: ErrorKind) -> Self {
        Error(e, None, None)
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.1.as_ref().map(|e| e.as_ref())
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Display::fmt(&self.0, f)
    }
}

impl std::fmt::Debug for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use std::error::Error as StdError;

        if let Some(ref o) = self.2 {
            std::fmt::Display::fmt(o, f)?;
        }

        std::fmt::Debug::fmt(&self.0, f)?;
        if let Some(e) = self.source() {
            std::fmt::Display::fmt("\nCaused by:\n", f)?;
            std::fmt::Debug::fmt(&e, f)?;
        }
        Ok(())
    }
}

#[macro_export]
macro_rules! map_context {
    () => {
        |e| $crate::context!(e, $crate::ErrorKind::from(&e))
    };
}

#[macro_export]
macro_rules! context {
    ( $k:expr ) => ({
        $crate::error::Error($k, None, Some(concat!(file!(), ":", line!(), ": ")))
    });
    ( None, $k:expr ) => ({
        $crate::error::Error($k, None, Some(concat!(file!(), ":", line!(), ": ")))
    });
    ( $e:path, $k:expr ) => ({
        $crate::error::Error($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
    });
}

pub type Result<T> = std::result::Result<T, Error>;
<commit_msg>varlink/src/error.rs: make Error Send + Sync<commit_after>use std::io;

#[derive(Clone, PartialEq, Debug)]
pub enum ErrorKind {
    Io(::std::io::ErrorKind),
    SerdeJsonSer(::serde_json::error::Category),
    SerdeJsonDe(String),
    InterfaceNotFound(String),
    InvalidParameter(String),
    MethodNotFound(String),
    MethodNotImplemented(String),
    VarlinkErrorReply(crate::Reply),
    CallContinuesMismatch,
    MethodCalledAlready,
    ConnectionBusy,
    IteratorOldReply,
    Server,
    Timeout,
    ConnectionClosed,
    InvalidAddress,
    Generic,
}

impl ::std::fmt::Display for ErrorKind {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match self {
            ErrorKind::Io(_) => write!(f, "IO error"),
            ErrorKind::SerdeJsonSer(_) => write!(f, "JSON Serialization Error"),
            ErrorKind::SerdeJsonDe(v) => write!(f, "JSON Deserialization Error of '{}'", v),
            ErrorKind::InterfaceNotFound(v) => write!(f, "Interface not found: '{}'", v),
            ErrorKind::InvalidParameter(v) => write!(f, "Invalid parameter: '{}'", v),
            ErrorKind::MethodNotFound(v) => write!(f, "Method not found: '{}'", v),
            ErrorKind::MethodNotImplemented(v) => write!(f, "Method not implemented: '{}'", v),
            ErrorKind::VarlinkErrorReply(v) => write!(f, "Varlink error reply: '{:#?}'", v),
            ErrorKind::CallContinuesMismatch => write!(
                f,
                "Call::reply() called with continues, but without more in the request"
            ),
            ErrorKind::MethodCalledAlready => write!(f, "Varlink: method called already"),
            ErrorKind::ConnectionBusy => write!(f, "Varlink: connection busy with other method"),
            ErrorKind::IteratorOldReply => write!(f, "Varlink: Iterator called on old reply"),
            ErrorKind::Server => write!(f, "Server Error"),
            ErrorKind::Timeout => write!(f, "Timeout Error"),
            ErrorKind::ConnectionClosed => write!(f, "Connection Closed"),
            ErrorKind::InvalidAddress => write!(f, "Invalid varlink address URI"),
            ErrorKind::Generic => Ok(()),
        }
    }
}

impl From<&std::io::Error> for ErrorKind {
    fn from(e: &io::Error) -> Self {
        match e.kind() {
            io::ErrorKind::BrokenPipe
            | io::ErrorKind::ConnectionAborted
            | io::ErrorKind::ConnectionReset => ErrorKind::ConnectionClosed,
            kind => ErrorKind::Io(kind),
        }
    }
}

impl From<&serde_json::error::Error> for ErrorKind {
    fn from(e: &serde_json::error::Error) -> Self {
        ErrorKind::SerdeJsonSer(e.classify())
    }
}

pub struct Error(
    pub ErrorKind,
    pub Option<Box<dyn std::error::Error + 'static + Send + Sync>>,
    pub Option<&'static str>,
);

impl Error {
    pub fn kind(&self) -> &ErrorKind {
        &self.0
    }
}

impl From<ErrorKind> for Error {
    fn from(e: ErrorKind) -> Self {
        Error(e, None, None)
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.1.as_ref().map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Display::fmt(&self.0, f)
    }
}

impl std::fmt::Debug for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use std::error::Error as StdError;

        if let Some(ref o) = self.2 {
            std::fmt::Display::fmt(o, f)?;
        }

        std::fmt::Debug::fmt(&self.0, f)?;
        if let Some(e) = self.source() {
            std::fmt::Display::fmt("\nCaused by:\n", f)?;
            std::fmt::Debug::fmt(&e, f)?;
        }
        Ok(())
    }
}

#[macro_export]
macro_rules! map_context {
    () => {
        |e| $crate::context!(e, $crate::ErrorKind::from(&e))
    };
}

#[macro_export]
macro_rules! context {
    ( $k:expr ) => ({
        $crate::error::Error($k, None, Some(concat!(file!(), ":", line!(), ": ")))
    });
    ( None, $k:expr ) => ({
        $crate::error::Error($k, None, Some(concat!(file!(), ":", line!(), ": ")))
    });
    ( $e:path, $k:expr ) => ({
        $crate::error::Error($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
    });
}

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


<commit_before>// Copyright 2017 TiKV Project Authors. Licensed under Apache-2.0.

use prometheus::IntGauge;
use std::error::Error;
use std::fmt::{self, Debug, Display, Formatter};
use std::io;
use std::sync::{Arc, Mutex};
use std::thread::{self, Builder, JoinHandle};

use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::executor::block_on;
use futures::stream::StreamExt;
use tokio::task::LocalSet;

use super::metrics::*;

pub struct Stopped<T>(pub T);

impl<T> Display for Stopped<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "channel has been closed")
    }
}

impl<T> Debug for Stopped<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "channel has been closed")
    }
}

impl<T> From<Stopped<T>> for Box<dyn Error + Sync + Send + 'static> {
    fn from(_: Stopped<T>) -> Box<dyn Error + Sync + Send + 'static> {
        box_err!("channel has been closed")
    }
}

pub trait Runnable<T: Display> {
    fn run(&mut self, t: T);
    fn shutdown(&mut self) {}
}

/// Scheduler provides interface to schedule task to underlying workers.
pub struct Scheduler<T> {
    name: Arc<String>,
    sender: UnboundedSender<Option<T>>,
    metrics_pending_task_count: IntGauge,
}

pub fn dummy_scheduler<T: Display>() -> Scheduler<T> {
    let (tx, _) = unbounded();
    Scheduler::new("dummy future scheduler".to_owned(), tx)
}

impl<T: Display> Scheduler<T> {
    fn new<S: Into<String>>(name: S, sender: UnboundedSender<Option<T>>) -> Scheduler<T> {
        let name = name.into();
        Scheduler {
            metrics_pending_task_count: WORKER_PENDING_TASK_VEC.with_label_values(&[&name]),
            name: Arc::new(name),
            sender,
        }
    }

    /// Schedules a task to run.
    ///
    /// If the worker is stopped, an error will return.
    pub fn schedule(&self, task: T) -> Result<(), Stopped<T>> {
        debug!("scheduling task {}", task);
        if let Err(err) = self.sender.unbounded_send(Some(task)) {
            return Err(Stopped(err.into_inner().unwrap()));
        }
        self.metrics_pending_task_count.inc();
        Ok(())
    }
}

impl<T: Display> Clone for Scheduler<T> {
    fn clone(&self) -> Scheduler<T> {
        Scheduler {
            name: Arc::clone(&self.name),
            sender: self.sender.clone(),
            metrics_pending_task_count: self.metrics_pending_task_count.clone(),
        }
    }
}

/// A worker that can schedule time consuming tasks.
pub struct Worker<T: Display> {
    scheduler: Scheduler<T>,
    receiver: Mutex<Option<UnboundedReceiver<Option<T>>>>,
    handle: Option<JoinHandle<()>>,
}

// TODO: add metrics.
fn poll<R, T>(mut runner: R, mut rx: UnboundedReceiver<Option<T>>)
where
    R: Runnable<T> + Send + 'static,
    T: Display + Send + 'static,
{
    tikv_alloc::add_thread_memory_accessor();
    let current_thread = thread::current();
    let name = current_thread.name().unwrap();
    let metrics_pending_task_count = WORKER_PENDING_TASK_VEC.with_label_values(&[name]);
    let metrics_handled_task_count = WORKER_HANDLED_TASK_VEC.with_label_values(&[name]);

    let handle = LocalSet::new();
    {
        let task = async {
            while let Some(msg) = rx.next().await {
                if let Some(t) = msg {
                    runner.run(t);
                    metrics_pending_task_count.dec();
                    metrics_handled_task_count.inc();
                } else {
                    break;
                }
            }
        };
        // `UnboundedReceiver` never returns an error.
        block_on(handle.run_until(task));
    }
    runner.shutdown();
    tikv_alloc::remove_thread_memory_accessor();
}

impl<T: Display + Send + 'static> Worker<T> {
    /// Creates a worker.
    pub fn new<S: Into<String>>(name: S) -> Worker<T> {
        let (tx, rx) = unbounded();
        Worker {
            scheduler: Scheduler::new(name, tx),
            receiver: Mutex::new(Some(rx)),
            handle: None,
        }
    }

    /// Starts the worker.
    pub fn start<R>(&mut self, runner: R) -> Result<(), io::Error>
    where
        R: Runnable<T> + Send + 'static,
    {
        let mut receiver = self.receiver.lock().unwrap();
        info!("starting working thread"; "worker" => &self.scheduler.name);
        if receiver.is_none() {
            warn!("worker has been started"; "worker" => &self.scheduler.name);
            return Ok(());
        }

        let rx = receiver.take().unwrap();
        let h = Builder::new()
            .name(thd_name!(self.scheduler.name.as_ref()))
            .spawn(move || poll(runner, rx))?;

        self.handle = Some(h);
        Ok(())
    }

    /// Gets a scheduler to schedule the task.
    pub fn scheduler(&self) -> Scheduler<T> {
        self.scheduler.clone()
    }

    /// Schedules a task to run.
    ///
    /// If the worker is stopped, an error will return.
    pub fn schedule(&self, task: T) -> Result<(), Stopped<T>> {
        self.scheduler.schedule(task)
    }

    /// Checks if underlying worker can't handle task immediately.
    pub fn is_busy(&self) -> bool {
        self.handle.is_none()
    }

    pub fn name(&self) -> &str {
        self.scheduler.name.as_str()
    }

    /// Stops the worker thread.
    pub fn stop(&mut self) -> Option<thread::JoinHandle<()>> {
        // close sender explicitly so the background thread will exit.
        info!("stoping worker"; "worker" => &self.scheduler.name);
        let handle = self.handle.take()?;
        if let Err(e) = self.scheduler.sender.unbounded_send(None) {
            warn!("failed to stop worker thread"; "err" => ?e);
        }

        Some(handle)
    }
}

#[cfg(test)]
mod tests {
    use std::sync::mpsc::{self, Sender};
    use std::time::Duration;
    use std::time::Instant;

    use crate::timer::GLOBAL_TIMER_HANDLE;
    use futures::compat::Future01CompatExt;
    use tokio::task::spawn_local;
    use tokio_timer::timer;

    use super::*;

    struct StepRunner {
        timer: timer::Handle,
        ch: Sender<u64>,
    }

    impl Runnable<u64> for StepRunner {
        fn run(&mut self, step: u64) {
            self.ch.send(step).unwrap();
            let f = self
                .timer
                .delay(Instant::now() + Duration::from_millis(step))
                .compat();
            spawn_local(f);
        }

        fn shutdown(&mut self) {
            self.ch.send(0).unwrap();
        }
    }

    #[test]
    fn test_future_worker() {
        let mut worker = Worker::new("test-async-worker");
        let (tx, rx) = mpsc::channel();
        worker
            .start(StepRunner {
                timer: GLOBAL_TIMER_HANDLE.clone(),
                ch: tx,
            })
            .unwrap();
        assert!(!worker.is_busy());
        // The default tick size of tokio_timer is 100ms.
        let start = Instant::now();
        worker.schedule(500).unwrap();
        worker.schedule(1000).unwrap();
        worker.schedule(1500).unwrap();
        assert_eq!(rx.recv_timeout(Duration::from_secs(3)).unwrap(), 500);
        assert_eq!(rx.recv_timeout(Duration::from_secs(3)).unwrap(), 1000);
        assert_eq!(rx.recv_timeout(Duration::from_secs(3)).unwrap(), 1500);
        // above three tasks are executed concurrently, should be less then 2s.
        assert!(start.elapsed() < Duration::from_secs(2));
        worker.stop().unwrap().join().unwrap();
        // now worker can't handle any task
        assert!(worker.is_busy());
        // when shutdown, StepRunner should send back a 0.
        assert_eq!(0, rx.recv().unwrap());
    }
}
<commit_msg>tikv_util: use tokio runtime as FutureWorker runner (#8687)<commit_after>// Copyright 2017 TiKV Project Authors. Licensed under Apache-2.0.

use prometheus::IntGauge;
use std::error::Error;
use std::fmt::{self, Debug, Display, Formatter};
use std::io;
use std::sync::{Arc, Mutex};
use std::thread::{self, Builder, JoinHandle};

use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::stream::StreamExt;
use tokio::task::LocalSet;

use super::metrics::*;

pub struct Stopped<T>(pub T);

impl<T> Display for Stopped<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "channel has been closed")
    }
}

impl<T> Debug for Stopped<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "channel has been closed")
    }
}

impl<T> From<Stopped<T>> for Box<dyn Error + Sync + Send + 'static> {
    fn from(_: Stopped<T>) -> Box<dyn Error + Sync + Send + 'static> {
        box_err!("channel has been closed")
    }
}

pub trait Runnable<T: Display> {
    fn run(&mut self, t: T);
    fn shutdown(&mut self) {}
}

/// Scheduler provides interface to schedule task to underlying workers.
pub struct Scheduler<T> {
    name: Arc<String>,
    sender: UnboundedSender<Option<T>>,
    metrics_pending_task_count: IntGauge,
}

pub fn dummy_scheduler<T: Display>() -> Scheduler<T> {
    let (tx, _) = unbounded();
    Scheduler::new("dummy future scheduler".to_owned(), tx)
}

impl<T: Display> Scheduler<T> {
    fn new<S: Into<String>>(name: S, sender: UnboundedSender<Option<T>>) -> Scheduler<T> {
        let name = name.into();
        Scheduler {
            metrics_pending_task_count: WORKER_PENDING_TASK_VEC.with_label_values(&[&name]),
            name: Arc::new(name),
            sender,
        }
    }

    /// Schedules a task to run.
    ///
    /// If the worker is stopped, an error will return.
    pub fn schedule(&self, task: T) -> Result<(), Stopped<T>> {
        debug!("scheduling task {}", task);
        if let Err(err) = self.sender.unbounded_send(Some(task)) {
            return Err(Stopped(err.into_inner().unwrap()));
        }
        self.metrics_pending_task_count.inc();
        Ok(())
    }
}

impl<T: Display> Clone for Scheduler<T> {
    fn clone(&self) -> Scheduler<T> {
        Scheduler {
            name: Arc::clone(&self.name),
            sender: self.sender.clone(),
            metrics_pending_task_count: self.metrics_pending_task_count.clone(),
        }
    }
}

/// A worker that can schedule time consuming tasks.
pub struct Worker<T: Display> {
    scheduler: Scheduler<T>,
    receiver: Mutex<Option<UnboundedReceiver<Option<T>>>>,
    handle: Option<JoinHandle<()>>,
}

// TODO: add metrics.
fn poll<R, T>(mut runner: R, mut rx: UnboundedReceiver<Option<T>>)
where
    R: Runnable<T> + Send + 'static,
    T: Display + Send + 'static,
{
    tikv_alloc::add_thread_memory_accessor();
    let current_thread = thread::current();
    let name = current_thread.name().unwrap();
    let metrics_pending_task_count = WORKER_PENDING_TASK_VEC.with_label_values(&[name]);
    let metrics_handled_task_count = WORKER_HANDLED_TASK_VEC.with_label_values(&[name]);

    let handle = LocalSet::new();
    {
        let task = async {
            while let Some(msg) = rx.next().await {
                if let Some(t) = msg {
                    runner.run(t);
                    metrics_pending_task_count.dec();
                    metrics_handled_task_count.inc();
                } else {
                    break;
                }
            }
        };
        // `UnboundedReceiver` never returns an error.
        tokio::runtime::Builder::new()
            .basic_scheduler()
            .build()
            .unwrap()
            .block_on(handle.run_until(task));
    }
    runner.shutdown();
    tikv_alloc::remove_thread_memory_accessor();
}

impl<T: Display + Send + 'static> Worker<T> {
    /// Creates a worker.
    pub fn new<S: Into<String>>(name: S) -> Worker<T> {
        let (tx, rx) = unbounded();
        Worker {
            scheduler: Scheduler::new(name, tx),
            receiver: Mutex::new(Some(rx)),
            handle: None,
        }
    }

    /// Starts the worker.
    pub fn start<R>(&mut self, runner: R) -> Result<(), io::Error>
    where
        R: Runnable<T> + Send + 'static,
    {
        let mut receiver = self.receiver.lock().unwrap();
        info!("starting working thread"; "worker" => &self.scheduler.name);
        if receiver.is_none() {
            warn!("worker has been started"; "worker" => &self.scheduler.name);
            return Ok(());
        }

        let rx = receiver.take().unwrap();
        let h = Builder::new()
            .name(thd_name!(self.scheduler.name.as_ref()))
            .spawn(move || poll(runner, rx))?;

        self.handle = Some(h);
        Ok(())
    }

    /// Gets a scheduler to schedule the task.
    pub fn scheduler(&self) -> Scheduler<T> {
        self.scheduler.clone()
    }

    /// Schedules a task to run.
    ///
    /// If the worker is stopped, an error will return.
    pub fn schedule(&self, task: T) -> Result<(), Stopped<T>> {
        self.scheduler.schedule(task)
    }

    /// Checks if underlying worker can't handle task immediately.
    pub fn is_busy(&self) -> bool {
        self.handle.is_none()
    }

    pub fn name(&self) -> &str {
        self.scheduler.name.as_str()
    }

    /// Stops the worker thread.
    pub fn stop(&mut self) -> Option<thread::JoinHandle<()>> {
        // close sender explicitly so the background thread will exit.
        info!("stoping worker"; "worker" => &self.scheduler.name);
        let handle = self.handle.take()?;
        if let Err(e) = self.scheduler.sender.unbounded_send(None) {
            warn!("failed to stop worker thread"; "err" => ?e);
        }

        Some(handle)
    }
}

#[cfg(test)]
mod tests {
    use std::sync::mpsc::{self, Sender};
    use std::time::Duration;
    use std::time::Instant;

    use crate::timer::GLOBAL_TIMER_HANDLE;
    use futures::compat::Future01CompatExt;
    use tokio::task::spawn_local;
    use tokio_timer::timer;

    use super::*;

    struct StepRunner {
        timer: timer::Handle,
        ch: Sender<u64>,
    }

    impl Runnable<u64> for StepRunner {
        fn run(&mut self, step: u64) {
            self.ch.send(step).unwrap();
            let f = self
                .timer
                .delay(Instant::now() + Duration::from_millis(step))
                .compat();
            spawn_local(f);
        }

        fn shutdown(&mut self) {
            self.ch.send(0).unwrap();
        }
    }

    #[test]
    fn test_future_worker() {
        let mut worker = Worker::new("test-async-worker");
        let (tx, rx) = mpsc::channel();
        worker
            .start(StepRunner {
                timer: GLOBAL_TIMER_HANDLE.clone(),
                ch: tx,
            })
            .unwrap();
        assert!(!worker.is_busy());
        // The default tick size of tokio_timer is 100ms.
        let start = Instant::now();
        worker.schedule(500).unwrap();
        worker.schedule(1000).unwrap();
        worker.schedule(1500).unwrap();
        assert_eq!(rx.recv_timeout(Duration::from_secs(3)).unwrap(), 500);
        assert_eq!(rx.recv_timeout(Duration::from_secs(3)).unwrap(), 1000);
        assert_eq!(rx.recv_timeout(Duration::from_secs(3)).unwrap(), 1500);
        // above three tasks are executed concurrently, should be less then 2s.
        assert!(start.elapsed() < Duration::from_secs(2));
        worker.stop().unwrap().join().unwrap();
        // now worker can't handle any task
        assert!(worker.is_busy());
        // when shutdown, StepRunner should send back a 0.
        assert_eq!(0, rx.recv().unwrap());
    }

    #[test]
    fn test_block_on_inside_worker() {
        struct BlockingRunner;

        impl Runnable<bool> for BlockingRunner {
            fn run(&mut self, _: bool) {
                futures::executor::block_on(async {});
            }
        }

        let mut worker = Worker::new("test-block-on-worker");
        worker.start(BlockingRunner).unwrap();
        worker.schedule(true).unwrap();
        worker.schedule(false).unwrap();
        worker.stop().unwrap().join().unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Sam Doshi (sam@metal-fish.co.uk)
// Copyright 2013-2014 Philippe Delrieu (philippe.delrieu@free.fr)
//
// Licensed under either of
//          Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//          MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).
// This file may not be copied, modified, or distributed except according to those terms.

use std::os::raw::{c_char, c_void, c_int};
use ffi::types::*;

#[link(name = "portmidi")]
extern "C" {
    pub fn Pm_Initialize() -> PmError;
    pub fn Pm_Terminate() -> PmError;
    fn Pm_HasHostError(stream: *const PortMidiStream) -> c_int;
    pub fn Pm_GetErrorText(errorCode: PmError) -> *const c_char;
    fn Pm_GetHostErrorText(msg: *const c_char, len: c_int);
    pub fn Pm_CountDevices() -> c_int;
    pub fn Pm_GetDefaultInputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDefaultOutputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDeviceInfo(id: PmDeviceId) -> *const PmDeviceInfo;
    pub fn Pm_OpenInput(stream: *const *const PortMidiStream,
                        inputDevice: PmDeviceId,
                        inputDriverInfo: *const c_void,
                        bufferSize: i32,
                        time_proc: *const c_void,
                        time_info: *const c_void)
                        -> PmError;
    pub fn Pm_OpenOutput(stream: *const *const PortMidiStream,
                         outputDevice: PmDeviceId,
                         inputDriverInfo: *const c_void,
                         bufferSize: i32,
                         time_proc: *const c_void,
                         time_info: *const c_void,
                         latency: i32)
                         -> PmError;
    pub fn Pm_Read(stream: *const PortMidiStream, buffer: *mut PmEvent, length: c_int) -> c_int;
    fn Pm_Abort(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Close(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Poll(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Write(stream: *const PortMidiStream,
                    buffer: *const PmEvent,
                    length: c_int)
                    -> PmError;
    pub fn Pm_WriteShort(stream: *const PortMidiStream,
                         timestamp: PmTimestamp,
                         message: PmMessage)
                         -> PmError;
}
<commit_msg>Disable warning for unused function<commit_after>// Copyright 2014-2015 Sam Doshi (sam@metal-fish.co.uk)
// Copyright 2013-2014 Philippe Delrieu (philippe.delrieu@free.fr)
//
// Licensed under either of
//          Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//          MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).
// This file may not be copied, modified, or distributed except according to those terms.

use std::os::raw::{c_char, c_void, c_int};
use ffi::types::*;

#[allow(dead_code)]
#[link(name = "portmidi")]
extern "C" {
    pub fn Pm_Initialize() -> PmError;
    pub fn Pm_Terminate() -> PmError;
    fn Pm_HasHostError(stream: *const PortMidiStream) -> c_int;
    pub fn Pm_GetErrorText(errorCode: PmError) -> *const c_char;
    fn Pm_GetHostErrorText(msg: *const c_char, len: c_int);
    pub fn Pm_CountDevices() -> c_int;
    pub fn Pm_GetDefaultInputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDefaultOutputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDeviceInfo(id: PmDeviceId) -> *const PmDeviceInfo;
    pub fn Pm_OpenInput(stream: *const *const PortMidiStream,
                        inputDevice: PmDeviceId,
                        inputDriverInfo: *const c_void,
                        bufferSize: i32,
                        time_proc: *const c_void,
                        time_info: *const c_void)
                        -> PmError;
    pub fn Pm_OpenOutput(stream: *const *const PortMidiStream,
                         outputDevice: PmDeviceId,
                         inputDriverInfo: *const c_void,
                         bufferSize: i32,
                         time_proc: *const c_void,
                         time_info: *const c_void,
                         latency: i32)
                         -> PmError;
    pub fn Pm_Read(stream: *const PortMidiStream, buffer: *mut PmEvent, length: c_int) -> c_int;
    fn Pm_Abort(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Close(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Poll(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Write(stream: *const PortMidiStream,
                    buffer: *const PmEvent,
                    length: c_int)
                    -> PmError;
    pub fn Pm_WriteShort(stream: *const PortMidiStream,
                         timestamp: PmTimestamp,
                         message: PmMessage)
                         -> PmError;
}
<|endoftext|>


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

#[cfg(test)]
mod tests {
    struct A {
	value: u32
    }

    impl Default for A {
	fn default() -> Self {
	    A { value: 68 }
	}
    }

    #[derive(Debug, PartialEq)]
    enum Kind {
	A,
	B,
	C
    }

    impl Default for Kind {
	fn default() -> Kind {
	    Kind::A
	}
    }

    #[test]
    fn default_test1() {
	let a: A = A::default();
	assert_eq!(a.value, 68);
    }

    #[test]
    fn default_test2() {
	let a: A = Default::default();
	assert_eq!(a.value, 68);
    }

    #[test]
    fn default_test3() {
	let a: A = Default::default();
	assert_eq!(a.value, 68);
    }

    #[test]
    fn default_test4() {
	let (x, y): (Option<String>, f64) = Default::default();
	assert_eq!(x, None::<String>);
	assert_eq!(y, 0.0);
    }

    #[test]
    fn default_test5() {
	let (a, b, (c, d)): (i32, u32, (bool, bool)) = Default::default();
	assert_eq!(a, 0);
	assert_eq!(b, 0);
	assert_eq!(c, false);
	assert_eq!(d, false);
    }

    #[test]
    fn default_test6() {
	assert_eq!(<()>::default(), ());
	assert_eq!(bool::default(), false);
	assert_eq!(char::default(), '\x00');
	assert_eq!(usize::default(), 0);
	assert_eq!(u8::default(), 0);
	assert_eq!(u16::default(), 0);
	assert_eq!(u32::default(), 0);
	assert_eq!(u64::default(), 0);
	assert_eq!(isize::default(), 0);
	assert_eq!(i8::default(), 0);
	assert_eq!(i16::default(), 0);
	assert_eq!(i32::default(), 0);
	assert_eq!(i64::default(), 0);
	assert_eq!(f32::default(), 0.0f32);
	assert_eq!(f64::default(), 0.0f64);
    }
    #[test]
    fn default_test7() {
	let x: Kind = Default::default();
	let y: Kind = Kind::default();

	assert_eq!(x, Kind::A);
	assert_eq!(y, Kind::A);
    }
}
<commit_msg>improve readability<commit_after>#![feature(core)]
extern crate core;

#[cfg(test)]
mod tests {
    use core::default::Default;

    struct A {
	value: u32
    }

    impl Default for A {
	fn default() -> Self {
	    A { value: 68 }
	}
    }

    #[derive(Debug, PartialEq)]
    enum Kind {
	A,
	B,
	C
    }

    impl Default for Kind {
	fn default() -> Kind {
	    Kind::A
	}
    }

    #[test]
    fn default_test1() {
	let a: A = A::default();
	assert_eq!(a.value, 68);
    }

    #[test]
    fn default_test2() {
	let a: A = Default::default();
	assert_eq!(a.value, 68);
    }

    #[test]
    fn default_test3() {
	let a: A = Default::default();
	assert_eq!(a.value, 68);
    }

    #[test]
    fn default_test4() {
	let (x, y): (Option<String>, f64) = Default::default();
	assert_eq!(x, None::<String>);
	assert_eq!(y, 0.0);
    }

    #[test]
    fn default_test5() {
	let (a, b, (c, d)): (i32, u32, (bool, bool)) = Default::default();
	assert_eq!(a, 0);
	assert_eq!(b, 0);
	assert_eq!(c, false);
	assert_eq!(d, false);
    }

    #[test]
    fn default_test6() {
	assert_eq!(<()>::default(), ());
	assert_eq!(bool::default(), false);
	assert_eq!(char::default(), '\x00');
	assert_eq!(usize::default(), 0);
	assert_eq!(u8::default(), 0);
	assert_eq!(u16::default(), 0);
	assert_eq!(u32::default(), 0);
	assert_eq!(u64::default(), 0);
	assert_eq!(isize::default(), 0);
	assert_eq!(i8::default(), 0);
	assert_eq!(i16::default(), 0);
	assert_eq!(i32::default(), 0);
	assert_eq!(i64::default(), 0);
	assert_eq!(f32::default(), 0.0f32);
	assert_eq!(f64::default(), 0.0f64);
    }
    #[test]
    fn default_test7() {
	let x: Kind = Default::default();
	let y: Kind = Kind::default();

	assert_eq!(x, Kind::A);
	assert_eq!(y, Kind::A);
    }
}
<|endoftext|>


<commit_before>use intern::intern;
use generate;
use grammar::repr::*;
use test_util::{compare, expect_debug, normalized_grammar};
use lr1::core::*;
use lr1::interpret::interpret;
use lr1::lookahead::Token;
use lr1::lookahead::Token::EOF;
use lr1::lookahead::TokenSet;
use lr1::tls::Lr1Tls;
use tls::Tls;

use super::{LR, build_lr0_states, build_lr1_states};

fn nt(t: &str) -> NonterminalString {
    NonterminalString(intern(t))
}

const ITERATIONS: usize = 22;

fn random_test<'g>(grammar: &Grammar,
                   states: &'g [LR1State<'g>],
                   start_symbol: NonterminalString) {
    for i in 0..ITERATIONS {
        let input_tree = generate::random_parse_tree(grammar, start_symbol);
        let output_tree = interpret(&states, input_tree.terminals()).unwrap();

        println!("test {}", i);
        println!("input_tree = {}", input_tree);
        println!("output_tree = {}", output_tree);

        compare(output_tree, input_tree);
    }
}

macro_rules! tokens {
    ($($x:expr),*) => {
        vec![$(TerminalString::quoted(intern($x))),*].into_iter()
    }
}

fn items<'g>(grammar: &'g Grammar, nonterminal: &str, index: usize, la: Token)
             -> LR1Items<'g>
{
    let set = TokenSet::from(la);
    let lr1: LR<TokenSet> = LR::new(&grammar, nt(nonterminal), set.clone());
    let items =
        lr1.transitive_closure(
            lr1.items(nt(nonterminal), index, &set));
    items
}

#[test]
fn start_state() {
    let grammar = normalized_grammar(r#"
grammar;
    extern { enum Tok { "C" => .., "D" => .. } }
    A = B "C";
    B: Option<u32> = {
        "D" => Some(1),
        () => None
    };
"#);
    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
    let items = items(&grammar, "A", 0, EOF);
    expect_debug(items.vec, r#"[
    A = (*) B "C" [EOF],
    B = (*) ["C"],
    B = (*) "D" ["C"]
]"#);
}

#[test]
fn start_state_1() {
    let grammar = normalized_grammar(r#"
grammar;
extern { enum Tok { "B1" => .., "C1" => .. } }
A = B C;
B: Option<u32> = {
    "B1" => Some(1),
    () => None
};
C: Option<u32> = {
    "C1" => Some(1),
    () => None
};
"#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    expect_debug(items(&grammar, "A", 0, EOF).vec, r#"[
    A = (*) B C [EOF],
    B = (*) ["C1", EOF],
    B = (*) "B1" ["C1", EOF]
]"#);

    expect_debug(items(&grammar, "A", 1, EOF).vec, r#"[
    A = B (*) C [EOF],
    C = (*) [EOF],
    C = (*) "C1" [EOF]
]"#);
}

#[test]
fn expr_grammar1() {
    let _tls = Tls::test();

    let grammar = normalized_grammar(r#"
grammar;
    extern { enum Tok { "-" => .., "N" => .., "(" => .., ")" => .. } }

    S: () =
        E => ();

    E: () = {
        E "-" T => (),
        T => ()
    };

    T: () = {
        "N" => (),
        "(" E ")" => ()
    };
"#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    // for now, just test that process does not result in an error
    // and yields expected number of states.
    let states = build_lr1_states(&grammar, nt("S")).unwrap();
    assert_eq!(states.len(), 16);

    // execute it on some sample inputs.
    let tree = interpret(&states, tokens!["N", "-", "(", "N", "-", "N", ")"]).unwrap();
    assert_eq!(
        &format!("{:?}", tree)[..],
        r#"[S: [E: [E: [T: "N"]], "-", [T: "(", [E: [E: [T: "N"]], "-", [T: "N"]], ")"]]]"#);

    // incomplete:
    assert!(interpret(&states, tokens!["N", "-", "(", "N", "-", "N"]).is_err());

    // incomplete:
    assert!(interpret(&states, tokens!["N", "-"]).is_err());

    // unexpected character:
    assert!(interpret(&states, tokens!["N", "-", ")", "N", "-", "N", "("]).is_err());

    // parens first:
    let tree = interpret(&states, tokens!["(", "N", "-", "N", ")", "-", "N"]).unwrap();
    println!("{}", tree);
    assert_eq!(
        &format!("{}", tree)[..],
        r#"[S: [E: [E: [T: "(", [E: [E: [T: "N"]], "-", [T: "N"]], ")"]], "-", [T: "N"]]]"#);

    // run some random tests
    random_test(&grammar, &states, nt("S"));
}

#[test]
fn shift_reduce_conflict1() {
    let _tls = Tls::test();

    // This grammar gets a shift-reduce conflict because if the input
    // is "&" (*) "L", then we see two possibilities, and we must decide
    // between them:
    //
    // "&" (*) "L" E
    //  |       |  |
    //  +-------+--|
    //          |
    //          E
    //
    // or
    //
    // "&"      (*) "L"
    //  |            |
    //  |  OPT_L     E
    //  |   |        |
    //  +---+---+----+
    //          |
    //          E
    //
    // to some extent this may be a false conflict, in that inlined
    // rules would address it, but it's an interesting one for
    // producing a useful error message.

    let grammar = normalized_grammar(r#"
        grammar;
        extern { enum Tok { "L" => .., "&" => .., } }
        E: () = {
            "L",
            "&" OPT_L E
        };
        OPT_L: () = {
            (),
            "L"
        };
    "#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    assert!(build_lr1_states(&grammar, nt("E")).is_err());
}

/// One of the few grammars that IS LR(0).
#[test]
fn lr0_expr_grammar_with_explicit_eof() {
    let _tls = Tls::test();

    let grammar = normalized_grammar(r#"
grammar;

S: () = E "$";

E: () = {
    E "-" T,
    T,
};

T: () = {
    "N",
    "(" E ")",
};
"#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    // for now, just test that process does not result in an error
    // and yields expected number of states.
    let states = build_lr0_states(&grammar, nt("S")).unwrap();
    assert_eq!(states.len(), 10);
}

/// Without the artifical '$', grammar is not LR(0).
#[test]
fn lr0_expr_grammar_with_implicit_eof() {
    let _tls = Tls::test();

    let grammar = normalized_grammar(r#"
grammar;

S: () = E;

E: () = {
    E "-" T,
    T,
};

T: () = {
    "N",
    "(" E ")",
};
"#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    build_lr0_states(&grammar, nt("S")).unwrap_err();
}

<commit_msg>make a test for #144 (which fails)<commit_after>use intern::intern;
use generate;
use grammar::repr::*;
use test_util::{compare, expect_debug, normalized_grammar};
use lr1::core::*;
use lr1::interpret::interpret;
use lr1::lookahead::Token;
use lr1::lookahead::Token::EOF;
use lr1::lookahead::TokenSet;
use lr1::tls::Lr1Tls;
use tls::Tls;

use super::{LR, build_lr0_states, build_lr1_states};

fn nt(t: &str) -> NonterminalString {
    NonterminalString(intern(t))
}

const ITERATIONS: usize = 22;

fn random_test<'g>(grammar: &Grammar,
                   states: &'g [LR1State<'g>],
                   start_symbol: NonterminalString) {
    for i in 0..ITERATIONS {
        let input_tree = generate::random_parse_tree(grammar, start_symbol);
        let output_tree = interpret(&states, input_tree.terminals()).unwrap();

        println!("test {}", i);
        println!("input_tree = {}", input_tree);
        println!("output_tree = {}", output_tree);

        compare(output_tree, input_tree);
    }
}

macro_rules! tokens {
    ($($x:expr),*) => {
        vec![$(TerminalString::quoted(intern($x))),*].into_iter()
    }
}

fn items<'g>(grammar: &'g Grammar, nonterminal: &str, index: usize, la: Token)
             -> LR1Items<'g>
{
    let set = TokenSet::from(la);
    let lr1: LR<TokenSet> = LR::new(&grammar, nt(nonterminal), set.clone());
    let items =
        lr1.transitive_closure(
            lr1.items(nt(nonterminal), index, &set));
    items
}

#[test]
fn start_state() {
    let grammar = normalized_grammar(r#"
grammar;
    extern { enum Tok { "C" => .., "D" => .. } }
    A = B "C";
    B: Option<u32> = {
        "D" => Some(1),
        () => None
    };
"#);
    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
    let items = items(&grammar, "A", 0, EOF);
    expect_debug(items.vec, r#"[
    A = (*) B "C" [EOF],
    B = (*) ["C"],
    B = (*) "D" ["C"]
]"#);
}

#[test]
fn start_state_1() {
    let grammar = normalized_grammar(r#"
grammar;
extern { enum Tok { "B1" => .., "C1" => .. } }
A = B C;
B: Option<u32> = {
    "B1" => Some(1),
    () => None
};
C: Option<u32> = {
    "C1" => Some(1),
    () => None
};
"#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    expect_debug(items(&grammar, "A", 0, EOF).vec, r#"[
    A = (*) B C [EOF],
    B = (*) ["C1", EOF],
    B = (*) "B1" ["C1", EOF]
]"#);

    expect_debug(items(&grammar, "A", 1, EOF).vec, r#"[
    A = B (*) C [EOF],
    C = (*) [EOF],
    C = (*) "C1" [EOF]
]"#);
}

#[test]
fn expr_grammar1() {
    let _tls = Tls::test();

    let grammar = normalized_grammar(r#"
grammar;
    extern { enum Tok { "-" => .., "N" => .., "(" => .., ")" => .. } }

    S: () =
        E => ();

    E: () = {
        E "-" T => (),
        T => ()
    };

    T: () = {
        "N" => (),
        "(" E ")" => ()
    };
"#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    // for now, just test that process does not result in an error
    // and yields expected number of states.
    let states = build_lr1_states(&grammar, nt("S")).unwrap();
    assert_eq!(states.len(), 16);

    // execute it on some sample inputs.
    let tree = interpret(&states, tokens!["N", "-", "(", "N", "-", "N", ")"]).unwrap();
    assert_eq!(
        &format!("{:?}", tree)[..],
        r#"[S: [E: [E: [T: "N"]], "-", [T: "(", [E: [E: [T: "N"]], "-", [T: "N"]], ")"]]]"#);

    // incomplete:
    assert!(interpret(&states, tokens!["N", "-", "(", "N", "-", "N"]).is_err());

    // incomplete:
    assert!(interpret(&states, tokens!["N", "-"]).is_err());

    // unexpected character:
    assert!(interpret(&states, tokens!["N", "-", ")", "N", "-", "N", "("]).is_err());

    // parens first:
    let tree = interpret(&states, tokens!["(", "N", "-", "N", ")", "-", "N"]).unwrap();
    println!("{}", tree);
    assert_eq!(
        &format!("{}", tree)[..],
        r#"[S: [E: [E: [T: "(", [E: [E: [T: "N"]], "-", [T: "N"]], ")"]], "-", [T: "N"]]]"#);

    // run some random tests
    random_test(&grammar, &states, nt("S"));
}

#[test]
fn shift_reduce_conflict1() {
    let _tls = Tls::test();

    // This grammar gets a shift-reduce conflict because if the input
    // is "&" (*) "L", then we see two possibilities, and we must decide
    // between them:
    //
    // "&" (*) "L" E
    //  |       |  |
    //  +-------+--|
    //          |
    //          E
    //
    // or
    //
    // "&"      (*) "L"
    //  |            |
    //  |  OPT_L     E
    //  |   |        |
    //  +---+---+----+
    //          |
    //          E
    //
    // to some extent this may be a false conflict, in that inlined
    // rules would address it, but it's an interesting one for
    // producing a useful error message.

    let grammar = normalized_grammar(r#"
        grammar;
        extern { enum Tok { "L" => .., "&" => .., } }
        E: () = {
            "L",
            "&" OPT_L E
        };
        OPT_L: () = {
            (),
            "L"
        };
    "#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    assert!(build_lr1_states(&grammar, nt("E")).is_err());
}

/// One of the few grammars that IS LR(0).
#[test]
fn lr0_expr_grammar_with_explicit_eof() {
    let _tls = Tls::test();

    let grammar = normalized_grammar(r#"
grammar;

S: () = E "$";

E: () = {
    E "-" T,
    T,
};

T: () = {
    "N",
    "(" E ")",
};
"#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    // for now, just test that process does not result in an error
    // and yields expected number of states.
    let states = build_lr0_states(&grammar, nt("S")).unwrap();
    assert_eq!(states.len(), 10);
}

/// Without the artifical '$', grammar is not LR(0).
#[test]
fn lr0_expr_grammar_with_implicit_eof() {
    let _tls = Tls::test();

    let grammar = normalized_grammar(r#"
grammar;

S: () = E;

E: () = {
    E "-" T,
    T,
};

T: () = {
    "N",
    "(" E ")",
};
"#);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());

    build_lr0_states(&grammar, nt("S")).unwrap_err();
}

/// When we moved to storing items as (lr0 -> TokenSet) pairs, a bug
/// in the transitive closure routine could cause us to have `(Foo,
/// S0)` and `(Foo, S1)` as distinct items instead of `(Foo, S0|S1)`.
#[test]
fn issue_144() {
    let _tls = Tls::test();

    let grammar = normalized_grammar(r##"
grammar;

pub ForeignItem: () = {
  AttrsAndVis "item_foreign_fn",
  AttrsAndVis "unsafe" "item_foreign_fn",
};

AttrsAndVis: () = {
    MaybeOuterAttrs visibility,
};

MaybeOuterAttrs: () = {
    OuterAttrs,
    (),
};

visibility: () = {
  "pub",
  (),
};

OuterAttrs: () = {
    OuterAttr,
    OuterAttrs OuterAttr,
};

OuterAttr: () = {
    "#" "[" "]",
};

Ident: () = {
    "IDENT",
};

ty: () = {
    "ty"
};
"##);

    let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
    build_lr1_states(&grammar, nt("ForeignItem")).unwrap();
}
<|endoftext|>