<commit_before>// Copyright 2013 Torste Aikio and gl-rs developers.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[feature(globs)];
#[feature(macro_rules)];

extern mod glfw;
extern mod gl;

use std::libc;

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

use std::io;
use std::path::PosixPath;

use gl::types::*;

//Do these need to be right here?
struct Position {
    x: f64,
    y: f64
}

struct HorizVelocity {
    x: f64
}

struct VertVelocity {
    y: f64
}

struct Sprite {
    x_size: f64,
    y_size: f64,
    color: [f64, ..4]
}

struct Components {
    position: Option<@mut Position>,
    horiz_velocity: Option<@mut HorizVelocity>,
    vert_velocity: Option<@mut VertVelocity>,
    sprite: Option<@mut Sprite>
}

trait System {
    fn process(&self, entity: @Components) -> ();
}

// We need to figure out how to integrate World with the main game loop
// in Artemis world has a `setDelta` method for timestep
struct World {
    entities: ~[@Components],
    systems: ~[@System]
}

impl World {
    fn new() -> World {
        return World {entities: ~[], systems: ~[]};
    }

    fn process(&self) {
        for system in self.systems.iter() {
            for entity in self.entities.iter() {
                system.process(*entity);
            }
        }
    }
}

struct MovementSystem;

impl System for MovementSystem {
    fn process(&self, entity: @Components) -> () {
        match entity.position {
            Some(pos) => {
                match entity.vert_velocity {
                    Some(v) => pos.y += v.y,
                    None => ()
                }
                match entity.horiz_velocity {
                    Some(v) => pos.x += v.x,
                    None => ()
                }
            },
            None => ()
        }
    }
}

struct EdgeCollisionSystem;

impl System for EdgeCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.vert_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.y >= 1.0 || pos.y <= 0.0 {
                    vel.y *= -1.0;
                }
            },
            (_, _) => ()
        }
    }
}

struct PaddleCollisionSystem {
    left_paddle: @Components,
    right_paddle: @Components,
    paddle_width: f64,
    paddle_height: f64
}

impl System for PaddleCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.horiz_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.x >= (4.0-self.paddle_width) {
                    if std::num::abs(pos.y - self.right_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
                if pos.x <= self.paddle_width {
                    if std::num::abs(pos.y - self.left_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
            },
            (_, _) => ()
        }
    }
}

struct RenderSystem {
    program: GLuint,
    position_uniform: GLint,
    scale_uniform: GLint,
    color_uniform: GLint
}

impl System for RenderSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.sprite) {
            (Some(pos), Some(sprite)) => {
                // Set uniforms
                gl::ProgramUniform2f(self.program, self.position_uniform, pos.x as f32, pos.y as f32);
                gl::ProgramUniform2f(self.program, self.scale_uniform, sprite.x_size as f32, sprite.y_size as f32);
                gl::ProgramUniform4f(self.program, self.color_uniform, sprite.color[0] as f32, sprite.color[1] as f32, sprite.color[2] as f32, sprite.color[3] as f32);
                // Draw a rect from the 4 vertices
                gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
            },
            (_, _) => ()
        }
    }
}

enum PaddleSide {
    RIGHT,
    LEFT
}

fn new_ball() -> @Components {
    let components = @Components { position: Some(@mut Position { x: 2.0, y: 1.5 }), horiz_velocity: Some(@mut HorizVelocity { x: 1.0/60.0 }), vert_velocity: Some(@mut VertVelocity { y: 0.0 }), sprite: Some(@mut Sprite {x_size: 0.05, y_size: 0.05, color: [0.3, 0.3, 0.8, 1.0]})};
    return components;
}

fn new_paddle(side: PaddleSide) -> @Components {
    let xpos = match side {
        RIGHT => 3.9,
        LEFT => 0.1
    };
    let components = @Components { position: Some(@mut Position { x: xpos, y: 1.5 }), horiz_velocity: None, vert_velocity: Some(@mut VertVelocity { y: 0.0 }), sprite: Some(@mut Sprite {x_size: 0.1, y_size: 0.4, color: [xpos/4.0, 1.0-(xpos/4.0), 0.3, 1.0]})};
    return components;
}

// Vertex data
static VERTEX_DATA: [GLfloat, ..8] = [
    -1.0,  1.0,
    -1.0, -1.0,
     1.0,  1.0,
     1.0, -1.0
];

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

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &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(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint, out_color: &str) -> GLuint {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    // gl::BindFragDataLocation needs to be called before linking to have effect
    unsafe {
        out_color.with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));
    }
    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(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    program
}

fn main() {
    do glfw::set_error_callback |_, description| {
        println!("GLFW Error: {}", description);
    }

    do glfw::start {
        // initialize game world
        let left_paddle: @Components = new_paddle(LEFT);
        let right_paddle: @Components = new_paddle(RIGHT);
        let ball: @Components = new_ball();
        let ms: @System = @MovementSystem as @System;
        let es: @System = @EdgeCollisionSystem as @System;
        let ps: @System = @PaddleCollisionSystem{ right_paddle: right_paddle, left_paddle: left_paddle, paddle_height: 0.4, paddle_width: 0.1} as @System;

        let mut world: World = World::new();
        world.entities.push(left_paddle);
        world.entities.push(right_paddle);
        world.entities.push(ball);
        world.systems.push(ms);
        world.systems.push(es);
        world.systems.push(ps);

        // Choose a GL profile that is compatible with OS X 10.7+
        glfw::window_hint::context_version(3, 2);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(1024, 600, "Pong", glfw::Windowed).unwrap();
        window.set_key_callback(key_callback);
        window.make_context_current();

        // Load the OpenGL function pointers
        gl::load_with(glfw::get_proc_address);

        // Create GLSL shaders
        let vs_src = io::read_whole_file_str(&PosixPath("main.vs.glsl")).unwrap();
        let vs = compile_shader(vs_src, gl::VERTEX_SHADER);
        let fs_src = io::read_whole_file_str(&PosixPath("main.fs.glsl")).unwrap();
        let fs = compile_shader(fs_src, gl::FRAGMENT_SHADER);
        let program = link_program(vs, fs, "out_color");

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

        let position_uniform: GLint;
        let scale_uniform: GLint;
        let color_uniform: GLint;

        unsafe {
            position_uniform = "position".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            scale_uniform = "scale".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            color_uniform = "color".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            // 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,
                           (VERTEX_DATA.len() * sys::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&VERTEX_DATA[0]),
                           gl::STATIC_DRAW);

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

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

        let rs: @System = @RenderSystem {program: program, position_uniform: position_uniform, scale_uniform: scale_uniform, color_uniform: color_uniform} as @System;
        world.systems.push(rs);

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

            // Clear the screen to black
            gl::ClearColor(0.8, 0.8, 0.8, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // process game world
            world.process();

            // Swap buffers
            window.swap_buffers();
        }

        // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        unsafe {
            gl::DeleteBuffers(1, &vbo);
            gl::DeleteVertexArrays(1, &vao);
        }
    }
}

fn key_callback(window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
    if action == glfw::Press && key == glfw::KeyEscape {
        window.set_should_close(true);
    }
}
<commit_msg>code formatting<commit_after>// Copyright 2013 Torste Aikio and gl-rs developers.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[feature(globs)];
#[feature(macro_rules)];

extern mod glfw;
extern mod gl;

use std::libc;

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

use std::io;
use std::path::PosixPath;

use gl::types::*;

// COMPONENT DEFINITIONS
struct Position {
    x: f64,
    y: f64
}

struct HorizVelocity {
    x: f64
}

struct VertVelocity {
    y: f64
}

struct Sprite {
    x_size: f64,
    y_size: f64,
    color: [f64, ..4]
}

struct Components {
    position: Option<@mut Position>,
    horiz_velocity: Option<@mut HorizVelocity>,
    vert_velocity: Option<@mut VertVelocity>,
    sprite: Option<@mut Sprite>
}

// SYSTEM DEFINITIONS
trait System {
    fn process(&self, entity: @Components) -> ();
}

struct MovementSystem;

impl System for MovementSystem {
    fn process(&self, entity: @Components) -> () {
        match entity.position {
            Some(pos) => {
                match entity.vert_velocity {
                    Some(v) => pos.y += v.y,
                    None => ()
                }
                match entity.horiz_velocity {
                    Some(v) => pos.x += v.x,
                    None => ()
                }
            },
            None => ()
        }
    }
}

struct EdgeCollisionSystem;

impl System for EdgeCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.vert_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.y >= 1.0 || pos.y <= 0.0 {
                    vel.y *= -1.0;
                }
            },
            (_, _) => ()
        }
    }
}

struct PaddleCollisionSystem {
    left_paddle: @Components,
    right_paddle: @Components,
    paddle_width: f64,
    paddle_height: f64
}

impl System for PaddleCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.horiz_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.x >= (4.0-self.paddle_width) {
                    if std::num::abs(pos.y - self.right_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
                if pos.x <= self.paddle_width {
                    if std::num::abs(pos.y - self.left_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
            },
            (_, _) => ()
        }
    }
}

struct RenderSystem {
    program: GLuint,
    position_uniform: GLint,
    scale_uniform: GLint,
    color_uniform: GLint
}

impl System for RenderSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.sprite) {
            (Some(pos), Some(sprite)) => {
                // Set uniforms
                gl::ProgramUniform2f(self.program, self.position_uniform, pos.x as f32, pos.y as f32);
                gl::ProgramUniform2f(self.program, self.scale_uniform, sprite.x_size as f32, sprite.y_size as f32);
                gl::ProgramUniform4f(self.program, self.color_uniform, sprite.color[0] as f32, sprite.color[1] as f32, sprite.color[2] as f32, sprite.color[3] as f32);
                // Draw a rect from the 4 vertices
                gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
            },
            (_, _) => ()
        }
    }
}

// WORLD DEFINITION
// We need to figure out how to integrate World with the main game loop
// in Artemis world has a `setDelta` method for timestep
struct World {
    entities: ~[@Components],
    systems: ~[@System]
}

impl World {
    fn new() -> World {
        return World {entities: ~[], systems: ~[]};
    }

    fn process(&self) {
        for system in self.systems.iter() {
            for entity in self.entities.iter() {
                system.process(*entity);
            }
        }
    }
}

//ENTITY CONSTRUCTORS
enum PaddleSide {
    RIGHT,
    LEFT
}

fn new_ball() -> @Components {
    let components = @Components {
        position: Some(@mut Position { x: 2.0, y: 1.5 }),
        horiz_velocity: Some(@mut HorizVelocity { x: 1.0/60.0 }),
        vert_velocity: Some(@mut VertVelocity { y: 0.0 }),
        sprite: Some(@mut Sprite {
            x_size: 0.05,
            y_size: 0.05,
            color: [0.3, 0.3, 0.8, 1.0]
        })
    };
    return components;
}

fn new_paddle(side: PaddleSide) -> @Components {
    let xpos = match side {
        RIGHT => 3.9,
        LEFT => 0.1
    };
    let components = @Components {
        position: Some(@mut Position { x: xpos, y: 1.5 }),
        horiz_velocity: None,
        vert_velocity: Some(@mut VertVelocity { y: 0.0 }),
        sprite: Some(@mut Sprite {
            x_size: 0.1,
            y_size: 0.4,
            color: [xpos/4.0, 1.0-(xpos/4.0), 0.3, 1.0]
        })
    };
    return components;
}

// OPENGL ETC STUFF

// Vertex data
static VERTEX_DATA: [GLfloat, ..8] = [
    -1.0,  1.0,
    -1.0, -1.0,
     1.0,  1.0,
     1.0, -1.0
];

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

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &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(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint, out_color: &str) -> GLuint {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    // gl::BindFragDataLocation needs to be called before linking to have effect
    unsafe {
        out_color.with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));
    }
    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(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    program
}

fn main() {
    do glfw::set_error_callback |_, description| {
        println!("GLFW Error: {}", description);
    }

    do glfw::start {
        // initialize game world
        let left_paddle: @Components = new_paddle(LEFT);
        let right_paddle: @Components = new_paddle(RIGHT);
        let ball: @Components = new_ball();
        let ms: @System = @MovementSystem as @System;
        let es: @System = @EdgeCollisionSystem as @System;
        let ps: @System = @PaddleCollisionSystem{ right_paddle: right_paddle, left_paddle: left_paddle, paddle_height: 0.4, paddle_width: 0.1} as @System;

        let mut world: World = World::new();
        world.entities.push(left_paddle);
        world.entities.push(right_paddle);
        world.entities.push(ball);
        world.systems.push(ms);
        world.systems.push(es);
        world.systems.push(ps);

        // Choose a GL profile that is compatible with OS X 10.7+
        glfw::window_hint::context_version(3, 2);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(1024, 600, "Pong", glfw::Windowed).unwrap();
        window.set_key_callback(key_callback);
        window.make_context_current();

        // Load the OpenGL function pointers
        gl::load_with(glfw::get_proc_address);

        // Create GLSL shaders
        let vs_src = io::read_whole_file_str(&PosixPath("main.vs.glsl")).unwrap();
        let vs = compile_shader(vs_src, gl::VERTEX_SHADER);
        let fs_src = io::read_whole_file_str(&PosixPath("main.fs.glsl")).unwrap();
        let fs = compile_shader(fs_src, gl::FRAGMENT_SHADER);
        let program = link_program(vs, fs, "out_color");

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

        let position_uniform: GLint;
        let scale_uniform: GLint;
        let color_uniform: GLint;

        unsafe {
            position_uniform = "position".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            scale_uniform = "scale".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            color_uniform = "color".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            // 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,
                           (VERTEX_DATA.len() * sys::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&VERTEX_DATA[0]),
                           gl::STATIC_DRAW);

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

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

        let rs: @System = @RenderSystem {program: program, position_uniform: position_uniform, scale_uniform: scale_uniform, color_uniform: color_uniform} as @System;
        world.systems.push(rs);

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

            // Clear the screen to black
            gl::ClearColor(0.8, 0.8, 0.8, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // process game world
            world.process();

            // Swap buffers
            window.swap_buffers();
        }

        // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        unsafe {
            gl::DeleteBuffers(1, &vbo);
            gl::DeleteVertexArrays(1, &vao);
        }
    }
}

fn key_callback(window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
    if action == glfw::Press && key == glfw::KeyEscape {
        window.set_should_close(true);
    }
}
<|endoftext|>


<commit_before>#![crate_type="lib"]

use std::ops::{Deref, DerefMut};
use std::{ptr, slice, str};

pub use std::io::{Result, SeekFrom};
pub use std::syscall::*;

/// Helper function for handling C strings, please do not copy it or make it pub or change it
fn c_string_to_str<'a>(ptr: *const u8) -> &'a str {
    if ptr > 0 as *const u8 {
        let mut len = 0;
        while unsafe { ptr::read(ptr.offset(len as isize)) } > 0 {
            len += 1;
        }

        unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
    } else {
        ""
    }
}

#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct Packet {
    pub id: usize,
    pub a: usize,
    pub b: usize,
    pub c: usize,
    pub d: usize
}

impl Deref for Packet {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        unsafe {
            std::slice::from_raw_parts(self as *const Packet as *const u8, std::mem::size_of::<Packet>()) as &[u8]
        }
    }
}

impl DerefMut for Packet {
    fn deref_mut(&mut self) -> &mut [u8] {
        unsafe {
            std::slice::from_raw_parts_mut(self as *mut Packet as *mut u8, std::mem::size_of::<Packet>()) as &mut [u8]
        }
    }
}

pub trait Resource {
    #[allow(unused_variables)]
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        Err(SysError::new(EBADF))
    }

    #[allow(unused_variables)]
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        Err(SysError::new(EBADF))
    }

    #[allow(unused_variables)]
    fn seek(&mut self, pos: SeekFrom) -> Result<usize> {
        Err(SysError::new(EBADF))
    }
}

pub trait Scheme {
    fn handle(&mut self, packet: &mut Packet) {
        packet.a = SysError::mux(match packet.a {
            SYS_OPEN => self.open(c_string_to_str(packet.b as *const u8), packet.c, packet.d),
            SYS_UNLINK => self.unlink(c_string_to_str(packet.b as *const u8)),
            _ => Err(SysError::new(ENOSYS))
        });
    }

    #[allow(unused_variables)]
    fn open(&mut self, path: &str, flags: usize, mode: usize) -> Result<usize> {
        Err(SysError::new(ENOENT))
    }

    #[allow(unused_variables)]
    fn unlink(&mut self, path: &str) -> Result<usize> {
        Err(SysError::new(ENOENT))
    }

    #[allow(unused_variables)]
    fn mkdir(&mut self, path: &str, mode: usize) -> Result<usize> {
        Err(SysError::new(ENOENT))
    }
}
<commit_msg>Add file operations to scheme trait<commit_after>#![crate_type="lib"]

use std::ops::{Deref, DerefMut};
use std::{ptr, slice, str};

pub use std::io::{Result, SeekFrom};
pub use std::syscall::*;

/// Helper function for handling C strings, please do not copy it or make it pub or change it
fn c_string_to_str<'a>(ptr: *const u8) -> &'a str {
    if ptr > 0 as *const u8 {
        let mut len = 0;
        while unsafe { ptr::read(ptr.offset(len as isize)) } > 0 {
            len += 1;
        }

        unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
    } else {
        ""
    }
}

#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct Packet {
    pub id: usize,
    pub a: usize,
    pub b: usize,
    pub c: usize,
    pub d: usize
}

impl Deref for Packet {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        unsafe {
            std::slice::from_raw_parts(self as *const Packet as *const u8, std::mem::size_of::<Packet>()) as &[u8]
        }
    }
}

impl DerefMut for Packet {
    fn deref_mut(&mut self) -> &mut [u8] {
        unsafe {
            std::slice::from_raw_parts_mut(self as *mut Packet as *mut u8, std::mem::size_of::<Packet>()) as &mut [u8]
        }
    }
}

pub trait Scheme {
    fn handle(&mut self, packet: &mut Packet) {
        packet.a = SysError::mux(match packet.a {
            SYS_OPEN => self.open(c_string_to_str(packet.b as *const u8), packet.c, packet.d),
            SYS_UNLINK => self.unlink(c_string_to_str(packet.b as *const u8)).and(Ok(0)),
            SYS_MKDIR => self.mkdir(c_string_to_str(packet.b as *const u8), packet.c).and(Ok(0)),

            SYS_READ => self.read(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }),
            SYS_WRITE => self.write(packet.b, unsafe { slice::from_raw_parts(packet.c as *const u8, packet.d) }),
            SYS_LSEEK => match packet.d {
                SEEK_SET => self.seek(packet.b, SeekFrom::Start(packet.c as u64)),
                SEEK_CUR => self.seek(packet.b, SeekFrom::Current(packet.c as i64)),
                SEEK_END => self.seek(packet.b, SeekFrom::End(packet.c as i64)),
                _ => Err(SysError::new(EINVAL))
            },
            SYS_FSYNC => self.sync(packet.b).and(Ok(0)),
            SYS_FTRUNCATE => self.truncate(packet.b, packet.c).and(Ok(0)),

            _ => Err(SysError::new(ENOSYS))
        });
    }

    /* Scheme operations */

    #[allow(unused_variables)]
    fn open(&mut self, path: &str, flags: usize, mode: usize) -> Result<usize> {
        Err(SysError::new(ENOENT))
    }

    #[allow(unused_variables)]
    fn unlink(&mut self, path: &str) -> Result<()> {
        Err(SysError::new(ENOENT))
    }

    #[allow(unused_variables)]
    fn mkdir(&mut self, path: &str, mode: usize) -> Result<()> {
        Err(SysError::new(ENOENT))
    }

    /* Resource operations */

    #[allow(unused_variables)]
    fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
        Err(SysError::new(EBADF))
    }

    #[allow(unused_variables)]
    fn write(&mut self, id: usize, buf: &[u8]) -> Result<usize> {
        Err(SysError::new(EBADF))
    }

    #[allow(unused_variables)]
    fn seek(&mut self, id: usize, pos: SeekFrom) -> Result<usize> {
        Err(SysError::new(EBADF))
    }

    #[allow(unused_variables)]
    fn sync(&mut self, id: usize) -> Result<()> {
        Err(SysError::new(EBADF))
    }

    #[allow(unused_variables)]
    fn truncate(&mut self, id: usize, len: usize) -> Result<()> {
        Err(SysError::new(EBADF))
    }
}
<|endoftext|>


<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        self._beta_once_normal(0)
    }

    fn _beta_once(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_normal(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full normal-order β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single normal-order β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn normal_order() {
        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta_full(should_reduce), Var(1))
    }
}
<commit_msg>fix names<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        self._beta_once(0);
    }

    fn _beta_once(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full normal-order β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single normal-order β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn normal_order() {
        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta_full(should_reduce), Var(1))
    }
}
<|endoftext|>


<commit_before>use ser::Error;
use ser::part::{PartSerializer, Sink};
use serde::ser::Serialize;
use std::str;
use url::form_urlencoded::Serializer as UrlEncodedSerializer;
use url::form_urlencoded::Target as UrlEncodedTarget;

pub struct ValueSink<'key, 'target, Target>
    where Target: 'target + UrlEncodedTarget,
{
    urlencoder: &'target mut UrlEncodedSerializer<Target>,
    key: &'key str,
}

impl<'key, 'target, Target> ValueSink<'key, 'target, Target>
    where Target: 'target + UrlEncodedTarget,
{
    pub fn new(urlencoder: &'target mut UrlEncodedSerializer<Target>,
               key: &'key str)
               -> Self {
        ValueSink {
            urlencoder: urlencoder,
            key: key,
        }
    }
}

impl<'key, 'target, Target> Sink for ValueSink<'key, 'target, Target>
    where Target: 'target + UrlEncodedTarget,
{
    type Ok = ();

    fn serialize_bool(self, value: bool) -> Result<Self::Ok, Error> {
        self.serialize_string(value.to_string())
    }

    fn serialize_str(self, value: &str) -> Result<(), Error> {
        self.urlencoder.append_pair(self.key, value);
        Ok(())
    }

    fn serialize_static_str(self, value: &'static str) -> Result<(), Error> {
        self.serialize_str(value)
    }

    fn serialize_string(self, value: String) -> Result<(), Error> {
        self.serialize_str(&value)
    }

    fn serialize_none(self) -> Result<Self::Ok, Error> {
        Ok(())
    }

    fn serialize_some<T: ?Sized + Serialize>(self,
                                             value: &T)
                                             -> Result<Self::Ok, Error> {
        value.serialize(PartSerializer::new(self))
    }

    fn unsupported(self) -> Error {
        Error::Custom("unsupported value".into())
    }
}
<commit_msg>Do not allocate a String to serialize a bool<commit_after>use ser::Error;
use ser::part::{PartSerializer, Sink};
use serde::ser::Serialize;
use std::str;
use url::form_urlencoded::Serializer as UrlEncodedSerializer;
use url::form_urlencoded::Target as UrlEncodedTarget;

pub struct ValueSink<'key, 'target, Target>
    where Target: 'target + UrlEncodedTarget,
{
    urlencoder: &'target mut UrlEncodedSerializer<Target>,
    key: &'key str,
}

impl<'key, 'target, Target> ValueSink<'key, 'target, Target>
    where Target: 'target + UrlEncodedTarget,
{
    pub fn new(urlencoder: &'target mut UrlEncodedSerializer<Target>,
               key: &'key str)
               -> Self {
        ValueSink {
            urlencoder: urlencoder,
            key: key,
        }
    }
}

impl<'key, 'target, Target> Sink for ValueSink<'key, 'target, Target>
    where Target: 'target + UrlEncodedTarget,
{
    type Ok = ();

    fn serialize_bool(self, value: bool) -> Result<Self::Ok, Error> {
        self.serialize_static_str(if value { "true" } else { "false" })
    }

    fn serialize_str(self, value: &str) -> Result<(), Error> {
        self.urlencoder.append_pair(self.key, value);
        Ok(())
    }

    fn serialize_static_str(self, value: &'static str) -> Result<(), Error> {
        self.serialize_str(value)
    }

    fn serialize_string(self, value: String) -> Result<(), Error> {
        self.serialize_str(&value)
    }

    fn serialize_none(self) -> Result<Self::Ok, Error> {
        Ok(())
    }

    fn serialize_some<T: ?Sized + Serialize>(self,
                                             value: &T)
                                             -> Result<Self::Ok, Error> {
        value.serialize(PartSerializer::new(self))
    }

    fn unsupported(self) -> Error {
        Error::Custom("unsupported value".into())
    }
}
<|endoftext|>


<commit_before>const VALID_TAGS: [&'static str; 9] = ["*", "_", "**", "__", "***", "~~", "`", "``", "```"];

// FIXME: process line breaks as <br/>

pub fn parse(escaped: &str) -> String {
  let mut symbols = 0;
  let mut skip = 0;
  let mut in_tag = false;

  let mut result = String::new();
  let mut buffer = String::new();

  let mut iter = escaped.chars().peekable();
  while let Some(c) = iter.next() {
    if skip > 0 {
      skip -= 1;
      continue;
    }
    match c {
      '*' | '_' | '`' => {
        if symbols == 0 {
          result.push_str(&buffer);
          buffer.clear();
        }
        symbols += 1;
      },
      '<' => in_tag = true,
      '>' => in_tag = false,
      _ => {}
    }
    buffer.push(c);
    if in_tag {
      continue;
    }
    if let Some(&next) = iter.peek() {
      if next != c && symbols > 0 && VALID_TAGS.contains(&buffer.as_ref()) {
        let s = iter.clone().collect::<String>();
        if let Some(m) = s.find(&buffer) {
          let mut content = &s[..m];
          skip = content.chars().count() + buffer.len();
          let (tag, styles) = match buffer.as_ref() {
            "_" | "*" => ("span", Some("emphasis")),
            "__" => ("span", Some("underline")),
            "**" => ("span", Some("strong")),
            "***" => ("span", Some("strong emphasis")),
            "~~" => ("span", Some("strikethrough")),
            "`" | "``" => ("span", Some("code")),
            "```" => {
              content = content.trim_matches('\n');
              ("pre", None)
            },
            _ => unreachable!()
          };
          let is_code = if let Some("code") = styles { false } else { true };
          let class = styles.map(|x| format!(" class=\"{}\"", x)).unwrap_or_else(Default::default);
          if is_code {
            result.push_str(&format!("<{tag}{class}>{content}</{tag}>",
            tag = tag,
            class = class,
            content = content));
          } else {
            result.push_str(&format!("<{tag}{class}>{content}</{tag}>",
            tag = tag,
            class = class,
            content = parse(content)));
          }
          buffer.clear();
          symbols = 0;
        }
      }
    }
  }
  result += &buffer;
  result
}

#[cfg(test)]
mod test {
  extern crate test;

  const TEST_STRING: &'static str = "blah __underline__ blah **bold** and ***bold italic*** with __***underline bold italics***__";
  const TEST_EXPECTED: &'static str = "blah underline blah bold and bold italic with underline bold italics";

  #[test]
  fn parse() {
    assert_eq!(TEST_EXPECTED, super::parse(TEST_STRING));
  }

  #[test]
  fn parse_weird() {
    assert_eq!("kek gon give it **to ya**",
      super::parse("__kek gon give it **to ya__**"));
  }

  #[test]
  fn parse_text() {
    let text = "this is some normal text";
    assert_eq!(text, super::parse(text));
  }

  #[test]
  fn parse_two_asterisks() {
    assert_eq!("**", super::parse("**"));
  }

  #[test]
  fn parse_three_asterisks() {
    assert_eq!("***", super::parse("***"));
  }

  #[test]
  #[ignore]
  // honestly, I can't be bothered to fix this. sure, it doesn't match discord's markdown parser
  // exactly, but it's late, this is hard for my brain, and who types this kind of shit anyway
  fn parse_four_asterisks() {
    assert_eq!("**four!**",
      super::parse("****four!****"));
  }

  #[test]
  fn parse_inner() {
    let source = "*italicized __underlines__ yo*";
    let output = "italicized underlines yo";
    assert_eq!(output, super::parse(source));
  }

  #[test]
  fn parse_tag() {
    let source = "some text yo";
    assert_eq!(source, super::parse(source));
  }

  #[bench]
  fn parse_benchmark(b: &mut self::test::Bencher) {
    b.iter(|| super::parse(TEST_STRING));
  }
}
<commit_msg>refactor(archive_viewer): use push_str syntax<commit_after>const VALID_TAGS: [&'static str; 9] = ["*", "_", "**", "__", "***", "~~", "`", "``", "```"];

// FIXME: process line breaks as <br/>

pub fn parse(escaped: &str) -> String {
  let mut symbols = 0;
  let mut skip = 0;
  let mut in_tag = false;

  let mut result = String::new();
  let mut buffer = String::new();

  let mut iter = escaped.chars().peekable();
  while let Some(c) = iter.next() {
    if skip > 0 {
      skip -= 1;
      continue;
    }
    match c {
      '*' | '_' | '`' => {
        if symbols == 0 {
          result.push_str(&buffer);
          buffer.clear();
        }
        symbols += 1;
      },
      '<' => in_tag = true,
      '>' => in_tag = false,
      _ => {}
    }
    buffer.push(c);
    if in_tag {
      continue;
    }
    if let Some(&next) = iter.peek() {
      if next != c && symbols > 0 && VALID_TAGS.contains(&buffer.as_ref()) {
        let s = iter.clone().collect::<String>();
        if let Some(m) = s.find(&buffer) {
          let mut content = &s[..m];
          skip = content.chars().count() + buffer.len();
          let (tag, styles) = match buffer.as_ref() {
            "_" | "*" => ("span", Some("emphasis")),
            "__" => ("span", Some("underline")),
            "**" => ("span", Some("strong")),
            "***" => ("span", Some("strong emphasis")),
            "~~" => ("span", Some("strikethrough")),
            "`" | "``" => ("span", Some("code")),
            "```" => {
              content = content.trim_matches('\n');
              ("pre", None)
            },
            _ => unreachable!()
          };
          let is_code = if let Some("code") = styles { false } else { true };
          let class = styles.map(|x| format!(" class=\"{}\"", x)).unwrap_or_else(Default::default);
          if is_code {
            result.push_str(&format!("<{tag}{class}>{content}</{tag}>",
            tag = tag,
            class = class,
            content = content));
          } else {
            result.push_str(&format!("<{tag}{class}>{content}</{tag}>",
            tag = tag,
            class = class,
            content = parse(content)));
          }
          buffer.clear();
          symbols = 0;
        }
      }
    }
  }
  result.push_str(&buffer);
  result
}

#[cfg(test)]
mod test {
  extern crate test;

  const TEST_STRING: &'static str = "blah __underline__ blah **bold** and ***bold italic*** with __***underline bold italics***__";
  const TEST_EXPECTED: &'static str = "blah underline blah bold and bold italic with underline bold italics";

  #[test]
  fn parse() {
    assert_eq!(TEST_EXPECTED, super::parse(TEST_STRING));
  }

  #[test]
  fn parse_weird() {
    assert_eq!("kek gon give it **to ya**",
      super::parse("__kek gon give it **to ya__**"));
  }

  #[test]
  fn parse_text() {
    let text = "this is some normal text";
    assert_eq!(text, super::parse(text));
  }

  #[test]
  fn parse_two_asterisks() {
    assert_eq!("**", super::parse("**"));
  }

  #[test]
  fn parse_three_asterisks() {
    assert_eq!("***", super::parse("***"));
  }

  #[test]
  #[ignore]
  // honestly, I can't be bothered to fix this. sure, it doesn't match discord's markdown parser
  // exactly, but it's late, this is hard for my brain, and who types this kind of shit anyway
  fn parse_four_asterisks() {
    assert_eq!("**four!**",
      super::parse("****four!****"));
  }

  #[test]
  fn parse_inner() {
    let source = "*italicized __underlines__ yo*";
    let output = "italicized underlines yo";
    assert_eq!(output, super::parse(source));
  }

  #[test]
  fn parse_tag() {
    let source = "some text yo";
    assert_eq!(source, super::parse(source));
  }

  #[bench]
  fn parse_benchmark(b: &mut self::test::Bencher) {
    b.iter(|| super::parse(TEST_STRING));
  }
}
<|endoftext|>


<commit_before>use std::fs;
use std::io;

use tempfile::tempfile;

use ::Unpacker;

// magic
use std::io::Seek;
use std::io::Write;

pub trait Tee: io::BufRead {
    fn reset(&mut self) -> io::Result<()>;
    fn len_and_reset(&mut self) -> io::Result<u64>;
    fn as_seekable(&mut self) -> io::Result<&mut Seeker>;
}

pub struct TempFileTee {
    inner: io::BufReader<fs::File>,
}

pub fn read_all<R: io::Read>(mut reader: &mut R, mut buf: &mut [u8]) -> io::Result<usize> {
    let mut pos = 0;
    loop {
        match reader.read(&mut buf[pos..]) {
            Ok(0) => return Ok(pos),
            Ok(n) => {
                pos += n;
                if pos == buf.len() {
                    return Ok(pos);
                }
            }
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
}

impl TempFileTee {
    pub fn if_necessary<U: io::Read>(mut from: U, log: &Unpacker) -> io::Result<Box<Tee>> {
        const MEM_LIMIT: usize = 32 * 1024;
        let mut buf = [0u8; MEM_LIMIT];
        let read = read_all(&mut from, &mut buf)?;
        if read < MEM_LIMIT {
            return Ok(Box::new(BufReaderTee::new(io::Cursor::new(buf[..read].to_vec()))));
        }

        let mut tmp = tempfile()?;

        {
            let mut writer = io::BufWriter::new(&tmp);
            writer.write_all(&buf)?;
            let written = io::copy(&mut from, &mut writer)?;
            log.log(3, || format!("file spills to temp file: {}kB", (MEM_LIMIT as u64 + written) / 1024))?;
        }

        tmp.seek(BEGINNING)?;

        Ok(Box::new(TempFileTee {
            inner: io::BufReader::new(tmp),
        }))
    }
}

const BEGINNING: io::SeekFrom = io::SeekFrom::Start(0);
const END: io::SeekFrom = io::SeekFrom::End(0);

impl Tee for TempFileTee {
    fn reset(&mut self) -> io::Result<()> {
        self.inner.seek(BEGINNING).map(|_| ())
    }

    fn len_and_reset(&mut self) -> io::Result<u64> {
        let len = self.inner.seek(END)?;
        self.reset()?;
        Ok(len)
    }

    fn as_seekable(&mut self) -> io::Result<&mut Seeker> {
        Ok(&mut self.inner)
    }
}

// Look, I didn't want to implement these. I wanted to return the implementation.
// But I couldn't make it compile, and I might care enough eventually.
impl io::Read for TempFileTee {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

impl io::BufRead for TempFileTee {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt)
    }
}

pub struct BufReaderTee<R: io::Read> {
    inner: Box<io::BufReader<R>>,
}

impl<R: io::Read> BufReaderTee<R> {
    pub fn new(from: R) -> Self {
        BufReaderTee {
            inner: Box::new(io::BufReader::new(from))
        }
    }
}

impl<R: io::Read> Tee for BufReaderTee<R>
    where R: io::Seek + 'static
{
    fn reset(&mut self) -> io::Result<()> {
        self.inner.seek(io::SeekFrom::Start(0)).map(|_| ())
    }

    fn len_and_reset(&mut self) -> io::Result<u64> {
        let len = self.inner.seek(END)?;
        self.reset()?;
        Ok(len)
    }

    fn as_seekable(&mut self) -> io::Result<&mut Seeker> {
        Ok(&mut *self.inner)
    }
}

impl<R: io::Read> io::Read for BufReaderTee<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

impl<R: io::Read> io::BufRead for BufReaderTee<R> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt)
    }
}

pub struct FailingTee<T> {
    inner: Box<T>,
    temp: Option<io::BufReader<fs::File>>,
}

impl<U: io::Read> FailingTee<io::BufReader<U>> {
    pub fn new(from: U) -> Self {
        FailingTee {
            inner: Box::new(io::BufReader::new(from)),
            temp: None,
        }
    }
}

impl<T> Tee for FailingTee<T>
    where T: io::BufRead
{
    // TODO: I don't think these are unreachable; e.g. a bad gzip stream inside a gzip stream.
    fn reset(&mut self) -> io::Result<()> {
        unreachable!();
    }

    fn len_and_reset(&mut self) -> io::Result<u64> {
        unreachable!();
    }

    fn as_seekable(&mut self) -> io::Result<&mut Seeker> {
        let mut temp = tempfile()?;
        {
            let mut fd = io::BufWriter::new(&mut temp);
            io::copy(self, &mut fd)?;
        }

        let reader = io::BufReader::new(temp);
        self.temp = Some(reader);
        Ok(self.temp.as_mut().unwrap())
    }
}

impl<T> io::Read for FailingTee<T>
    where T: io::Read {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

impl<T> io::BufRead for FailingTee<T>
    where T: io::BufRead {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt)
    }
}

pub trait Seeker: io::Seek + io::Read {
}

impl<R: io::Read> Seeker for io::BufReader<R>
    where R: io::Seek {
}

pub struct BoxReader<'a, R: io::Read + 'a> {
    pub inner: &'a mut R,
}

impl<'a, R: io::Read> io::Read for BoxReader<'a, R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

impl<'a, R> io::BufRead for BoxReader<'a, R>
where R: io::BufRead {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt)
    }
}

#[cfg(test)]
mod tests {
    use tee;

    use std::cmp::min;
    use std::io;

    struct Readie {
        limit: usize,
        len: usize,
    }

    impl io::Read for Readie {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            let to_take = min(buf.len(), min(self.limit, self.len));

            for i in 0..to_take {
                buf[i] = (i + 1) as u8;
            }

            self.len -= to_take;
            Ok(to_take)
        }
    }

    #[test]
    fn repeated_read_short() {
        let mut r = Readie {
            limit: 1,
            len: 5,
        };
        let mut a = [0u8; 8];
        assert_eq!(5, tee::read_all(&mut r, &mut a).expect("read"));
        assert_eq!([1, 1, 1, 1, 1, 0, 0, 0], a);
    }

    #[test]
    fn repeated_read_over() {
        let mut r = Readie {
            limit: 2,
            len: 12,
        };
        let mut a = [0u8; 8];
        assert_eq!(8, tee::read_all(&mut r, &mut a).expect("read"));
        assert_eq!([1, 2, 1, 2, 1, 2, 1, 2], a);
    }

    #[test]
    fn repeated_read_whole() {
        let mut r = Readie {
            limit: 12,
            len: 8,
        };
        let mut a = [0u8; 8];
        assert_eq!(8, tee::read_all(&mut r, &mut a).expect("read"));
        assert_eq!([1, 2, 3, 4, 5, 6, 7, 8], a);
    }

}<commit_msg>pretty sure that seek is necessary<commit_after>use std::fs;
use std::io;

use tempfile::tempfile;

use ::Unpacker;

// magic
use std::io::Seek;
use std::io::Write;

pub trait Tee: io::BufRead {
    fn reset(&mut self) -> io::Result<()>;
    fn len_and_reset(&mut self) -> io::Result<u64>;
    fn as_seekable(&mut self) -> io::Result<&mut Seeker>;
}

pub struct TempFileTee {
    inner: io::BufReader<fs::File>,
}

pub fn read_all<R: io::Read>(mut reader: &mut R, mut buf: &mut [u8]) -> io::Result<usize> {
    let mut pos = 0;
    loop {
        match reader.read(&mut buf[pos..]) {
            Ok(0) => return Ok(pos),
            Ok(n) => {
                pos += n;
                if pos == buf.len() {
                    return Ok(pos);
                }
            }
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
}

impl TempFileTee {
    pub fn if_necessary<U: io::Read>(mut from: U, log: &Unpacker) -> io::Result<Box<Tee>> {
        const MEM_LIMIT: usize = 32 * 1024;
        let mut buf = [0u8; MEM_LIMIT];
        let read = read_all(&mut from, &mut buf)?;
        if read < MEM_LIMIT {
            return Ok(Box::new(BufReaderTee::new(io::Cursor::new(buf[..read].to_vec()))));
        }

        let mut tmp = tempfile()?;

        {
            let mut writer = io::BufWriter::new(&tmp);
            writer.write_all(&buf)?;
            let written = io::copy(&mut from, &mut writer)?;
            log.log(3, || format!("file spills to temp file: {}kB", (MEM_LIMIT as u64 + written) / 1024))?;
        }

        tmp.seek(BEGINNING)?;

        Ok(Box::new(TempFileTee {
            inner: io::BufReader::new(tmp),
        }))
    }
}

const BEGINNING: io::SeekFrom = io::SeekFrom::Start(0);
const END: io::SeekFrom = io::SeekFrom::End(0);

impl Tee for TempFileTee {
    fn reset(&mut self) -> io::Result<()> {
        self.inner.seek(BEGINNING).map(|_| ())
    }

    fn len_and_reset(&mut self) -> io::Result<u64> {
        let len = self.inner.seek(END)?;
        self.reset()?;
        Ok(len)
    }

    fn as_seekable(&mut self) -> io::Result<&mut Seeker> {
        Ok(&mut self.inner)
    }
}

// Look, I didn't want to implement these. I wanted to return the implementation.
// But I couldn't make it compile, and I might care enough eventually.
impl io::Read for TempFileTee {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

impl io::BufRead for TempFileTee {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt)
    }
}

pub struct BufReaderTee<R: io::Read> {
    inner: Box<io::BufReader<R>>,
}

impl<R: io::Read> BufReaderTee<R> {
    pub fn new(from: R) -> Self {
        BufReaderTee {
            inner: Box::new(io::BufReader::new(from))
        }
    }
}

impl<R: io::Read> Tee for BufReaderTee<R>
    where R: io::Seek + 'static
{
    fn reset(&mut self) -> io::Result<()> {
        self.inner.seek(io::SeekFrom::Start(0)).map(|_| ())
    }

    fn len_and_reset(&mut self) -> io::Result<u64> {
        let len = self.inner.seek(END)?;
        self.reset()?;
        Ok(len)
    }

    fn as_seekable(&mut self) -> io::Result<&mut Seeker> {
        Ok(&mut *self.inner)
    }
}

impl<R: io::Read> io::Read for BufReaderTee<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

impl<R: io::Read> io::BufRead for BufReaderTee<R> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt)
    }
}

pub struct FailingTee<T> {
    inner: Box<T>,
    temp: Option<io::BufReader<fs::File>>,
}

impl<U: io::Read> FailingTee<io::BufReader<U>> {
    pub fn new(from: U) -> Self {
        FailingTee {
            inner: Box::new(io::BufReader::new(from)),
            temp: None,
        }
    }
}

impl<T> Tee for FailingTee<T>
    where T: io::BufRead
{
    // TODO: I don't think these are unreachable; e.g. a bad gzip stream inside a gzip stream.
    fn reset(&mut self) -> io::Result<()> {
        unreachable!();
    }

    fn len_and_reset(&mut self) -> io::Result<u64> {
        unreachable!();
    }

    fn as_seekable(&mut self) -> io::Result<&mut Seeker> {
        let mut temp = tempfile()?;
        {
            let mut fd = io::BufWriter::new(&mut temp);
            io::copy(self, &mut fd)?;
        }

        temp.seek(io::SeekFrom::Start(0))?;

        let reader = io::BufReader::new(temp);
        self.temp = Some(reader);
        Ok(self.temp.as_mut().unwrap())
    }
}

impl<T> io::Read for FailingTee<T>
    where T: io::Read {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

impl<T> io::BufRead for FailingTee<T>
    where T: io::BufRead {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt)
    }
}

pub trait Seeker: io::Seek + io::Read {
}

impl<R: io::Read> Seeker for io::BufReader<R>
    where R: io::Seek {
}

pub struct BoxReader<'a, R: io::Read + 'a> {
    pub inner: &'a mut R,
}

impl<'a, R: io::Read> io::Read for BoxReader<'a, R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

impl<'a, R> io::BufRead for BoxReader<'a, R>
where R: io::BufRead {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt)
    }
}

#[cfg(test)]
mod tests {
    use tee;

    use std::cmp::min;
    use std::io;

    struct Readie {
        limit: usize,
        len: usize,
    }

    impl io::Read for Readie {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            let to_take = min(buf.len(), min(self.limit, self.len));

            for i in 0..to_take {
                buf[i] = (i + 1) as u8;
            }

            self.len -= to_take;
            Ok(to_take)
        }
    }

    #[test]
    fn repeated_read_short() {
        let mut r = Readie {
            limit: 1,
            len: 5,
        };
        let mut a = [0u8; 8];
        assert_eq!(5, tee::read_all(&mut r, &mut a).expect("read"));
        assert_eq!([1, 1, 1, 1, 1, 0, 0, 0], a);
    }

    #[test]
    fn repeated_read_over() {
        let mut r = Readie {
            limit: 2,
            len: 12,
        };
        let mut a = [0u8; 8];
        assert_eq!(8, tee::read_all(&mut r, &mut a).expect("read"));
        assert_eq!([1, 2, 1, 2, 1, 2, 1, 2], a);
    }

    #[test]
    fn repeated_read_whole() {
        let mut r = Readie {
            limit: 12,
            len: 8,
        };
        let mut a = [0u8; 8];
        assert_eq!(8, tee::read_all(&mut r, &mut a).expect("read"));
        assert_eq!([1, 2, 3, 4, 5, 6, 7, 8], a);
    }

}<|endoftext|>


<commit_before>use libloading::{Library, Symbol};
use regex::{Regex, RegexBuilder};
use serde_derive::Deserialize;
use std::collections::HashMap;
use std::fs;
use std::io;
use std::mem;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::SystemTime;
use tree_sitter::{Language, PropertySheet};

#[cfg(unix)]
const DYLIB_EXTENSION: &'static str = "so";

#[cfg(windows)]
const DYLIB_EXTENSION: &'static str = "dll";

const BUILD_TARGET: &'static str = env!("BUILD_TARGET");

struct LanguageRepo {
    name: String,
    path: PathBuf,
    language: Option<Language>,
    configurations: Vec<LanguageConfiguration>,
}

pub struct LanguageConfiguration {
    _name: String,
    _content_regex: Option<Regex>,
    _first_line_regex: Option<Regex>,
    file_types: Vec<String>,
    _highlight_property_sheet: Option<Result<PropertySheet, PathBuf>>,
}

pub struct Loader {
    parser_lib_path: PathBuf,
    language_repos: Vec<LanguageRepo>,
    language_configuration_ids_by_file_type: HashMap<String, Vec<(usize, usize)>>,
}

unsafe impl Send for Loader {}
unsafe impl Sync for Loader {}

impl Loader {
    pub fn new(parser_lib_path: PathBuf) -> Self {
        Loader {
            parser_lib_path,
            language_repos: Vec::new(),
            language_configuration_ids_by_file_type: HashMap::new(),
        }
    }

    pub fn find_all_languages(&mut self, parser_src_paths: &Vec<PathBuf>) -> io::Result<()> {
        for parser_container_dir in parser_src_paths.iter() {
            if let Ok(entries) = fs::read_dir(parser_container_dir) {
                for entry in entries {
                    let entry = entry?;
                    if let Some(parser_dir_name) = entry.file_name().to_str() {
                        if parser_dir_name.starts_with("tree-sitter-") {
                            self.find_language_at_path(&parser_container_dir.join(parser_dir_name))
                                .ok();
                        }
                    }
                }
            }
        }
        Ok(())
    }

    pub fn language_at_path(&mut self, path: &Path) -> io::Result<Option<Language>> {
        if let Ok(id) = self.find_language_at_path(path) {
            Ok(Some(self.language_configuration_for_id(id)?.0))
        } else {
            Ok(None)
        }
    }

    pub fn language_configuration_for_file_name(
        &mut self,
        path: &Path,
    ) -> io::Result<Option<(Language, &LanguageConfiguration)>> {
        let ids = path
            .file_name()
            .and_then(|n| n.to_str())
            .and_then(|file_name| self.language_configuration_ids_by_file_type.get(file_name))
            .or_else(|| {
                path.extension()
                    .and_then(|extension| extension.to_str())
                    .and_then(|extension| {
                        self.language_configuration_ids_by_file_type.get(extension)
                    })
            });
        if let Some(ids) = ids {
            // TODO use `content-regex` to pick one
            for (repo_id, configuration_id) in ids.iter().cloned() {
                let (language, configurations) = self.language_configuration_for_id(repo_id)?;
                return Ok(Some((language, &configurations[configuration_id])));
            }
        }
        Ok(None)
    }

    fn language_configuration_for_id(
        &mut self,
        id: usize,
    ) -> io::Result<(Language, &Vec<LanguageConfiguration>)> {
        let repo = &self.language_repos[id];
        let language = if let Some(language) = repo.language {
            language
        } else {
            let src_path = repo.path.join("src");
            let language = self.load_language_at_path(&repo.name, &src_path, &src_path)?;
            self.language_repos[id].language = Some(language);
            language
        };
        Ok((language, &self.language_repos[id].configurations))
    }

    pub fn load_language_at_path(
        &self,
        name: &str,
        src_path: &Path,
        header_path: &Path,
    ) -> io::Result<Language> {
        let parser_path = src_path.join("parser.c");

        let scanner_path;
        let scanner_c_path = src_path.join("scanner.c");
        if scanner_c_path.exists() {
            scanner_path = Some(scanner_c_path);
        } else {
            let scanner_cc_path = src_path.join("scanner.cc");
            if scanner_cc_path.exists() {
                scanner_path = Some(scanner_cc_path);
            } else {
                scanner_path = None;
            }
        }

        self.load_language_from_sources(name, &header_path, &parser_path, &scanner_path)
    }

    pub fn load_language_from_sources(
        &self,
        name: &str,
        header_path: &Path,
        parser_path: &Path,
        scanner_path: &Option<PathBuf>,
    ) -> io::Result<Language> {
        let mut library_path = self.parser_lib_path.join(name);
        library_path.set_extension(DYLIB_EXTENSION);

        if needs_recompile(&library_path, &parser_path, &scanner_path)? {
            let mut config = cc::Build::new();
            config
                .cpp(true)
                .opt_level(2)
                .cargo_metadata(false)
                .target(BUILD_TARGET)
                .host(BUILD_TARGET);
            let compiler = config.get_compiler();
            let mut command = Command::new(compiler.path());
            for (key, value) in compiler.env() {
                command.env(key, value);
            }

            if cfg!(windows) {
                command
                    .args(&["/nologo", "/LD", "/I"])
                    .arg(header_path)
                    .arg("/Od")
                    .arg(parser_path);
                if let Some(scanner_path) = scanner_path.as_ref() {
                    command.arg(scanner_path);
                }
                command
                    .arg("/link")
                    .arg(format!("/out:{}", library_path.to_str().unwrap()));
            } else {
                command
                    .arg("-shared")
                    .arg("-fPIC")
                    .arg("-I")
                    .arg(header_path)
                    .arg("-o")
                    .arg(&library_path);
                if let Some(scanner_path) = scanner_path.as_ref() {
                    if scanner_path.extension() == Some("c".as_ref()) {
                        command.arg("-xc").arg("-std=c99").arg(scanner_path);
                    } else {
                        command.arg(scanner_path);
                    }
                }
                command.arg("-xc").arg(parser_path);
            }

            let output = command.output()?;
            if !output.status.success() {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Parser compilation failed.\nStdout: {}\nStderr: {}",
                        String::from_utf8_lossy(&output.stdout),
                        String::from_utf8_lossy(&output.stderr)
                    )
                    .as_str(),
                ));
            }
        }

        let library = Library::new(library_path)?;
        let language_fn_name = format!("tree_sitter_{}", replace_dashes_with_underscores(name));
        let language = unsafe {
            let language_fn: Symbol<unsafe extern "C" fn() -> Language> =
                library.get(language_fn_name.as_bytes())?;
            language_fn()
        };
        mem::forget(library);
        Ok(language)
    }

    fn find_language_at_path<'a>(&'a mut self, parser_path: &Path) -> io::Result<usize> {
        #[derive(Deserialize)]
        struct LanguageConfigurationJSON {
            name: String,
            #[serde(rename = "file-types")]
            file_types: Option<Vec<String>>,
            #[serde(rename = "content-regex")]
            content_regex: Option<String>,
            #[serde(rename = "first-line-regex")]
            first_line_regex: Option<String>,
            highlights: Option<String>,
        }

        #[derive(Deserialize)]
        struct PackageJSON {
            name: String,
            #[serde(rename = "tree-sitter")]
            tree_sitter: Option<Vec<LanguageConfigurationJSON>>,
        }

        let package_json_contents = fs::read_to_string(&parser_path.join("package.json"))?;
        let package_json: PackageJSON = serde_json::from_str(&package_json_contents)?;
        let configurations = package_json
            .tree_sitter
            .map_or(Vec::new(), |configurations| {
                configurations
                    .into_iter()
                    .map(|conf| LanguageConfiguration {
                        _name: conf.name,
                        file_types: conf.file_types.unwrap_or(Vec::new()),
                        _content_regex: conf
                            .content_regex
                            .and_then(|r| RegexBuilder::new(&r).multi_line(true).build().ok()),
                        _first_line_regex: conf
                            .first_line_regex
                            .and_then(|r| RegexBuilder::new(&r).multi_line(true).build().ok()),
                        _highlight_property_sheet: conf.highlights.map(|d| Err(d.into())),
                    })
                    .collect()
            });

        for (i, configuration) in configurations.iter().enumerate() {
            for file_type in &configuration.file_types {
                self.language_configuration_ids_by_file_type
                    .entry(file_type.to_string())
                    .or_insert(Vec::new())
                    .push((self.language_repos.len(), i));
            }
        }

        self.language_repos.push(LanguageRepo {
            name: package_json
                .name
                .split_at("tree-sitter-".len())
                .1
                .to_string(),
            path: parser_path.to_owned(),
            language: None,
            configurations,
        });

        Ok(self.language_repos.len() - 1)
    }
}

fn needs_recompile(
    lib_path: &Path,
    parser_c_path: &Path,
    scanner_path: &Option<PathBuf>,
) -> io::Result<bool> {
    if !lib_path.exists() {
        return Ok(true);
    }
    let lib_mtime = mtime(lib_path)?;
    if mtime(parser_c_path)? > lib_mtime {
        return Ok(true);
    }
    if let Some(scanner_path) = scanner_path {
        if mtime(scanner_path)? > lib_mtime {
            return Ok(true);
        }
    }
    Ok(false)
}

fn mtime(path: &Path) -> io::Result<SystemTime> {
    Ok(fs::metadata(path)?.modified()?)
}

fn replace_dashes_with_underscores(name: &str) -> String {
    let mut result = String::with_capacity(name.len());
    for c in name.chars() {
        if c == '-' {
            result.push('_');
        } else {
            result.push(c);
        }
    }
    result
}
<commit_msg>Add context to error message when opening dynamic libraries<commit_after>use super::error::{Error, Result};
use libloading::{Library, Symbol};
use regex::{Regex, RegexBuilder};
use serde_derive::Deserialize;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::SystemTime;
use std::{fs, mem};
use tree_sitter::{Language, PropertySheet};

#[cfg(unix)]
const DYLIB_EXTENSION: &'static str = "so";

#[cfg(windows)]
const DYLIB_EXTENSION: &'static str = "dll";

const BUILD_TARGET: &'static str = env!("BUILD_TARGET");

struct LanguageRepo {
    name: String,
    path: PathBuf,
    language: Option<Language>,
    configurations: Vec<LanguageConfiguration>,
}

pub struct LanguageConfiguration {
    _name: String,
    _content_regex: Option<Regex>,
    _first_line_regex: Option<Regex>,
    file_types: Vec<String>,
    _highlight_property_sheet: Option<std::result::Result<PropertySheet, PathBuf>>,
}

pub struct Loader {
    parser_lib_path: PathBuf,
    language_repos: Vec<LanguageRepo>,
    language_configuration_ids_by_file_type: HashMap<String, Vec<(usize, usize)>>,
}

unsafe impl Send for Loader {}
unsafe impl Sync for Loader {}

impl Loader {
    pub fn new(parser_lib_path: PathBuf) -> Self {
        Loader {
            parser_lib_path,
            language_repos: Vec::new(),
            language_configuration_ids_by_file_type: HashMap::new(),
        }
    }

    pub fn find_all_languages(&mut self, parser_src_paths: &Vec<PathBuf>) -> Result<()> {
        for parser_container_dir in parser_src_paths.iter() {
            if let Ok(entries) = fs::read_dir(parser_container_dir) {
                for entry in entries {
                    let entry = entry?;
                    if let Some(parser_dir_name) = entry.file_name().to_str() {
                        if parser_dir_name.starts_with("tree-sitter-") {
                            self.find_language_at_path(&parser_container_dir.join(parser_dir_name))
                                .ok();
                        }
                    }
                }
            }
        }
        Ok(())
    }

    pub fn language_at_path(&mut self, path: &Path) -> Result<Option<Language>> {
        if let Ok(id) = self.find_language_at_path(path) {
            Ok(Some(self.language_configuration_for_id(id)?.0))
        } else {
            Ok(None)
        }
    }

    pub fn language_configuration_for_file_name(
        &mut self,
        path: &Path,
    ) -> Result<Option<(Language, &LanguageConfiguration)>> {
        let ids = path
            .file_name()
            .and_then(|n| n.to_str())
            .and_then(|file_name| self.language_configuration_ids_by_file_type.get(file_name))
            .or_else(|| {
                path.extension()
                    .and_then(|extension| extension.to_str())
                    .and_then(|extension| {
                        self.language_configuration_ids_by_file_type.get(extension)
                    })
            });
        if let Some(ids) = ids {
            // TODO use `content-regex` to pick one
            for (repo_id, configuration_id) in ids.iter().cloned() {
                let (language, configurations) = self.language_configuration_for_id(repo_id)?;
                return Ok(Some((language, &configurations[configuration_id])));
            }
        }
        Ok(None)
    }

    fn language_configuration_for_id(
        &mut self,
        id: usize,
    ) -> Result<(Language, &Vec<LanguageConfiguration>)> {
        let repo = &self.language_repos[id];
        let language = if let Some(language) = repo.language {
            language
        } else {
            let src_path = repo.path.join("src");
            let language = self.load_language_at_path(&repo.name, &src_path, &src_path)?;
            self.language_repos[id].language = Some(language);
            language
        };
        Ok((language, &self.language_repos[id].configurations))
    }

    pub fn load_language_at_path(
        &self,
        name: &str,
        src_path: &Path,
        header_path: &Path,
    ) -> Result<Language> {
        let parser_path = src_path.join("parser.c");

        let scanner_path;
        let scanner_c_path = src_path.join("scanner.c");
        if scanner_c_path.exists() {
            scanner_path = Some(scanner_c_path);
        } else {
            let scanner_cc_path = src_path.join("scanner.cc");
            if scanner_cc_path.exists() {
                scanner_path = Some(scanner_cc_path);
            } else {
                scanner_path = None;
            }
        }

        self.load_language_from_sources(name, &header_path, &parser_path, &scanner_path)
    }

    pub fn load_language_from_sources(
        &self,
        name: &str,
        header_path: &Path,
        parser_path: &Path,
        scanner_path: &Option<PathBuf>,
    ) -> Result<Language> {
        let mut library_path = self.parser_lib_path.join(name);
        library_path.set_extension(DYLIB_EXTENSION);

        if needs_recompile(&library_path, &parser_path, &scanner_path)? {
            let mut config = cc::Build::new();
            config
                .cpp(true)
                .opt_level(2)
                .cargo_metadata(false)
                .target(BUILD_TARGET)
                .host(BUILD_TARGET);
            let compiler = config.get_compiler();
            let mut command = Command::new(compiler.path());
            for (key, value) in compiler.env() {
                command.env(key, value);
            }

            if cfg!(windows) {
                command
                    .args(&["/nologo", "/LD", "/I"])
                    .arg(header_path)
                    .arg("/Od")
                    .arg(parser_path);
                if let Some(scanner_path) = scanner_path.as_ref() {
                    command.arg(scanner_path);
                }
                command
                    .arg("/link")
                    .arg(format!("/out:{}", library_path.to_str().unwrap()));
            } else {
                command
                    .arg("-shared")
                    .arg("-fPIC")
                    .arg("-I")
                    .arg(header_path)
                    .arg("-o")
                    .arg(&library_path);
                if let Some(scanner_path) = scanner_path.as_ref() {
                    if scanner_path.extension() == Some("c".as_ref()) {
                        command.arg("-xc").arg("-std=c99").arg(scanner_path);
                    } else {
                        command.arg(scanner_path);
                    }
                }
                command.arg("-xc").arg(parser_path);
            }

            let output = command.output()?;
            if !output.status.success() {
                return Err(Error(format!(
                    "Parser compilation failed.\nStdout: {}\nStderr: {}",
                    String::from_utf8_lossy(&output.stdout),
                    String::from_utf8_lossy(&output.stderr)
                )));
            }
        }

        let library = Library::new(&library_path).map_err(|e| {
            Error(format!(
                "Error opening dynamic library {:?}: {}",
                &library_path, e
            ))
        })?;
        let language_fn_name = format!("tree_sitter_{}", replace_dashes_with_underscores(name));
        let language = unsafe {
            let language_fn: Symbol<unsafe extern "C" fn() -> Language> =
                library.get(language_fn_name.as_bytes())?;
            language_fn()
        };
        mem::forget(library);
        Ok(language)
    }

    fn find_language_at_path<'a>(&'a mut self, parser_path: &Path) -> Result<usize> {
        #[derive(Deserialize)]
        struct LanguageConfigurationJSON {
            name: String,
            #[serde(rename = "file-types")]
            file_types: Option<Vec<String>>,
            #[serde(rename = "content-regex")]
            content_regex: Option<String>,
            #[serde(rename = "first-line-regex")]
            first_line_regex: Option<String>,
            highlights: Option<String>,
        }

        #[derive(Deserialize)]
        struct PackageJSON {
            name: String,
            #[serde(rename = "tree-sitter")]
            tree_sitter: Option<Vec<LanguageConfigurationJSON>>,
        }

        let package_json_contents = fs::read_to_string(&parser_path.join("package.json"))?;
        let package_json: PackageJSON = serde_json::from_str(&package_json_contents)?;
        let configurations = package_json
            .tree_sitter
            .map_or(Vec::new(), |configurations| {
                configurations
                    .into_iter()
                    .map(|conf| LanguageConfiguration {
                        _name: conf.name,
                        file_types: conf.file_types.unwrap_or(Vec::new()),
                        _content_regex: conf
                            .content_regex
                            .and_then(|r| RegexBuilder::new(&r).multi_line(true).build().ok()),
                        _first_line_regex: conf
                            .first_line_regex
                            .and_then(|r| RegexBuilder::new(&r).multi_line(true).build().ok()),
                        _highlight_property_sheet: conf.highlights.map(|d| Err(d.into())),
                    })
                    .collect()
            });

        for (i, configuration) in configurations.iter().enumerate() {
            for file_type in &configuration.file_types {
                self.language_configuration_ids_by_file_type
                    .entry(file_type.to_string())
                    .or_insert(Vec::new())
                    .push((self.language_repos.len(), i));
            }
        }

        self.language_repos.push(LanguageRepo {
            name: package_json
                .name
                .split_at("tree-sitter-".len())
                .1
                .to_string(),
            path: parser_path.to_owned(),
            language: None,
            configurations,
        });

        Ok(self.language_repos.len() - 1)
    }
}

fn needs_recompile(
    lib_path: &Path,
    parser_c_path: &Path,
    scanner_path: &Option<PathBuf>,
) -> Result<bool> {
    if !lib_path.exists() {
        return Ok(true);
    }
    let lib_mtime = mtime(lib_path)?;
    if mtime(parser_c_path)? > lib_mtime {
        return Ok(true);
    }
    if let Some(scanner_path) = scanner_path {
        if mtime(scanner_path)? > lib_mtime {
            return Ok(true);
        }
    }
    Ok(false)
}

fn mtime(path: &Path) -> Result<SystemTime> {
    Ok(fs::metadata(path)?.modified()?)
}

fn replace_dashes_with_underscores(name: &str) -> String {
    let mut result = String::with_capacity(name.len());
    for c in name.chars() {
        if c == '-' {
            result.push('_');
        } else {
            result.push(c);
        }
    }
    result
}
<|endoftext|>