<commit_before>use std::mem::size_of;
use std::os::raw::c_void;
use std::path::Path;
use std::ptr;
use std::rc::Rc;

use gl;
use gltf;
use gltf::json::mesh::Mode;

// use camera::Camera;
use render::math::*;
use render::{Material, Root};
use shader::*;
use importdata::ImportData;

#[derive(Debug)]
pub struct Vertex {
    pub position: Vector3,
    pub normal: Vector3,
    pub tangent: Vector4,
    pub tex_coord_0: Vector2,
    pub tex_coord_1: Vector2,
    pub color_0: Vector4,
    pub joints_0: [u16; 4],
    pub weights_0: Vector4,
}

impl Default for Vertex {
    fn default() -> Self {
        Vertex {
            position: Vector3::zero(),
            normal: Vector3::zero(),
            tangent: Vector4::zero(),
            tex_coord_0: Vector2::zero(),
            tex_coord_1: Vector2::zero(),
            color_0: Vector4::zero(),
            joints_0: [0; 4],
            weights_0: Vector4::zero(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Texture {
    pub id: u32,
    pub type_: String,
    pub path: String,
}

pub struct Primitive {
    pub bounds: Aabb3,

    vao: u32,
    vbo: u32,
    num_vertices: u32,

    ebo: Option<u32>,
    num_indices: u32,

    material: Rc<Material>,

    pbr_shader: Rc<PbrShader>,

    // TODO!: mode, targets
}

impl Primitive {
    pub fn new(
        bounds: Aabb3,
        vertices: &[Vertex],
        indices: Option<Vec<u32>>,
        material: Rc<Material>,
        shader: Rc<PbrShader>,
    ) -> Primitive {
        let num_indices = indices.as_ref().map(|i| i.len()).unwrap_or(0);
        let mut prim = Primitive {
            bounds,
            num_vertices: vertices.len() as u32,
            num_indices: num_indices as u32,
            vao: 0, vbo: 0, ebo: None,
            material,
            pbr_shader: shader,
        };

        // now that we have all the required data, set the vertex buffers and its attribute pointers.
        unsafe { prim.setup_primitive(vertices, indices) }
        prim
    }

    pub fn from_gltf(
        g_primitive: &gltf::Primitive,
        primitive_index: usize,
        mesh_index: usize,
        root: &mut Root,
        imp: &ImportData,
        base_path: &Path) -> Primitive
    {
        let buffers = &imp.buffers;
        let reader = g_primitive.reader(|buffer| Some(&buffers[buffer.index()]));
        let positions = {
            let iter = reader
                .read_positions()
                .expect(&format!(
                    "primitives must have the POSITION attribute (mesh: {}, primitive: {})",
                    mesh_index, primitive_index));
            iter.collect::<Vec<_>>()
        };

        let bounds = g_primitive.bounding_box();
        let bounds = Aabb3 {
            min: bounds.min.into(),
            max: bounds.max.into()
        };

        let mut vertices: Vec<Vertex> = positions
            .into_iter()
            .map(|position| {
                Vertex {
                    position: Vector3::from(position),
                    ..Vertex::default()
                }
            }).collect();

        let mut shader_flags = ShaderFlags::empty();

        // normals
        if let Some(normals) = reader.read_normals() {
            for (i, normal) in normals.enumerate() {
                vertices[i].normal = Vector3::from(normal);
            }
            shader_flags |= ShaderFlags::HAS_NORMALS;
        }
        else {
            debug!("Found no NORMALs for primitive {} of mesh {} \
                   (flat normal calculation not implemented yet)", primitive_index, mesh_index);
        }

        // tangents
        if let Some(tangents) = reader.read_tangents() {
            for (i, tangent) in tangents.enumerate() {
                vertices[i].tangent = Vector4::from(tangent);
            }
            shader_flags |= ShaderFlags::HAS_TANGENTS;
        }
        else {
            debug!("Found no TANGENTS for primitive {} of mesh {} \
                   (tangent calculation not implemented yet)", primitive_index, mesh_index);
        }

        // texture coordinates
        let mut tex_coord_set = 0;
        while let Some(tex_coords) = reader.read_tex_coords(tex_coord_set) {
            if tex_coord_set > 1 {
                warn!("Ignoring texture coordinate set {}, \
                        only supporting 2 sets at the moment. (mesh: {}, primitive: {})",
                        tex_coord_set, mesh_index, primitive_index);
                tex_coord_set += 1;
                continue;
            }
            for (i, tex_coord) in tex_coords.into_f32().enumerate() {
                match tex_coord_set {
                    0 => vertices[i].tex_coord_0 = Vector2::from(tex_coord),
                    1 => vertices[i].tex_coord_1 = Vector2::from(tex_coord),
                    _ => unreachable!()
                }
            }
            shader_flags |= ShaderFlags::HAS_UV;
            tex_coord_set += 1;
        }

        // colors
        if let Some(colors) = reader.read_colors(0) {
            let colors = colors.into_rgba_f32();
            for (i, c) in colors.enumerate() {
                vertices[i].color_0 = c.into();
            }
            shader_flags |= ShaderFlags::HAS_COLORS;
        }
        if reader.read_colors(1).is_some() {
            warn!("Ignoring further color attributes, only supporting COLOR_0. (mesh: {}, primitive: {})",
                mesh_index, primitive_index);
        }

        if let Some(joints) = reader.read_joints(0) {
            for (i, joint) in joints.into_u16().enumerate() {
                vertices[i].joints_0 = joint;
            }
        }
        if reader.read_joints(1).is_some() {
            warn!("Ignoring further joint attributes, only supporting JOINTS_0. (mesh: {}, primitive: {})",
                mesh_index, primitive_index);
        }

        if let Some(weights) = reader.read_weights(0) {
            for (i, weights) in weights.into_f32().enumerate() {
                vertices[i].weights_0 = weights.into();
            }
        }
        if reader.read_weights(1).is_some() {
            warn!("Ignoring further weight attributes, only supporting WEIGHTS_0. (mesh: {}, primitive: {})",
                mesh_index, primitive_index);
        }

        let indices = reader
            .read_indices()
            .map(|read_indices| {
                read_indices.into_u32().collect::<Vec<_>>()
            });

        assert_eq!(g_primitive.mode(), Mode::Triangles, "not yet implemented: primitive mode must be Triangles.");

        let g_material = g_primitive.material();

        let mut material = None;
        if let Some(mat) = root.materials.iter().find(|m| (***m).index == g_material.index()) {
            material = Rc::clone(mat).into()
        }

        if material.is_none() { // no else due to borrow checker madness
            let mat = Rc::new(Material::from_gltf(&g_material, root, imp, base_path));
            root.materials.push(Rc::clone(&mat));
            material = Some(mat);
        };
        let material = material.unwrap();
        shader_flags |= material.shader_flags();

        let mut new_shader = false; // borrow checker workaround
        let shader =
            if let Some(shader) = root.shaders.get(&shader_flags) {
                Rc::clone(shader)
            }
            else {
                new_shader = true;
                PbrShader::new(shader_flags).into()

            };
        if new_shader {
            root.shaders.insert(shader_flags, Rc::clone(&shader));
        }

        Primitive::new(bounds, &vertices, indices, material, shader)
    }

    /// render the mesh
    pub unsafe fn draw(&self, model_matrix: &Matrix4, mvp_matrix: &Matrix4, camera_position: &Vector3) {
        // TODO!: determine if shader+material already active to reduce work...

        if self.material.double_sided {
            gl::Disable(gl::CULL_FACE);
        } else {
            gl::Enable(gl::CULL_FACE);
        }

        self.configure_shader(model_matrix, mvp_matrix, camera_position);

        // draw mesh
        gl::BindVertexArray(self.vao);
        if self.ebo.is_some() {
            gl::DrawElements(gl::TRIANGLES, self.num_indices as i32, gl::UNSIGNED_INT, ptr::null());
        }
        else {
            gl::DrawArrays(gl::TRIANGLES, 0, self.num_vertices as i32)
        }

        gl::BindVertexArray(0);
        gl::ActiveTexture(gl::TEXTURE0);
    }

    unsafe fn configure_shader(&self, model_matrix: &Matrix4,
        mvp_matrix: &Matrix4, camera_position: &Vector3)
    {
        // let pbr_shader = &Rc::get_mut(&mut self.pbr_shader).unwrap();
        let mat = &self.material;
        let shader = &self.pbr_shader.shader;
        let uniforms = &self.pbr_shader.uniforms;
        self.pbr_shader.shader.use_program();

        // camera params
        shader.set_mat4(uniforms.u_ModelMatrix, model_matrix);
        shader.set_mat4(uniforms.u_MVPMatrix, mvp_matrix);
        shader.set_vector3(uniforms.u_Camera, camera_position);

        // NOTE: for sampler numbers, see also PbrShader constructor
        shader.set_vector4(uniforms.u_BaseColorFactor, &mat.base_color_factor);
        if let Some(ref base_color_texture) = mat.base_color_texture {
            gl::ActiveTexture(gl::TEXTURE0);
            gl::BindTexture(gl::TEXTURE_2D, base_color_texture.id);
        }
        if let Some(ref normal_texture) = mat.normal_texture {
            gl::ActiveTexture(gl::TEXTURE1);
            gl::BindTexture(gl::TEXTURE_2D, normal_texture.id);
        }
        if let Some(ref emissive_texture) = mat.emissive_texture {
            gl::ActiveTexture(gl::TEXTURE2);
            gl::BindTexture(gl::TEXTURE_2D, emissive_texture.id);

            shader.set_vector3(uniforms.u_EmissiveFactor, &mat.emissive_factor);
        }

        if let Some(ref mr_texture) = mat.metallic_roughness_texture {
            gl::ActiveTexture(gl::TEXTURE3);
            gl::BindTexture(gl::TEXTURE_2D, mr_texture.id);
        }
        shader.set_vec2(uniforms.u_MetallicRoughnessValues,
            mat.metallic_factor, mat.roughness_factor);

        if let Some(ref occlusion_texture) = mat.occlusion_texture {
            gl::ActiveTexture(gl::TEXTURE4);
            gl::BindTexture(gl::TEXTURE_2D, occlusion_texture.id);

            shader.set_float(uniforms.u_OcclusionStrength, mat.occlusion_strength);
        }
    }

    unsafe fn setup_primitive(&mut self, vertices: &[Vertex], indices: Option<Vec<u32>>) {
        // create buffers/arrays
        gl::GenVertexArrays(1, &mut self.vao);
        gl::GenBuffers(1, &mut self.vbo);
        if indices.is_some() {
            let mut ebo = 0;
            gl::GenBuffers(1, &mut ebo);
            self.ebo = Some(ebo);
        }

        gl::BindVertexArray(self.vao);
        // load data into vertex buffers
        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        let size = (vertices.len() * size_of::<Vertex>()) as isize;
        let data = &vertices[0] as *const Vertex as *const c_void;
        gl::BufferData(gl::ARRAY_BUFFER, size, data, gl::STATIC_DRAW);

        if let Some(ebo) = self.ebo {
            let indices = indices.unwrap();
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
            let size = (indices.len() * size_of::<u32>()) as isize;
            let data = &indices[0] as *const u32 as *const c_void;
            gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, size, data, gl::STATIC_DRAW);
        }

        // set the vertex attribute pointers
        let size = size_of::<Vertex>() as i32;
        // POSITION
        gl::EnableVertexAttribArray(0);
        gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, position) as *const c_void);
        // NORMAL
        gl::EnableVertexAttribArray(1);
        gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, normal) as *const c_void);
        // TANGENT
        gl::EnableVertexAttribArray(2);
        gl::VertexAttribPointer(2, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tangent) as *const c_void);
        // TEXCOORD_0
        gl::EnableVertexAttribArray(3);
        gl::VertexAttribPointer(3, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tex_coord_0) as *const c_void);
        // TEXCOORD_1
        gl::EnableVertexAttribArray(4);
        gl::VertexAttribPointer(4, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tex_coord_1) as *const c_void);
        // COLOR_0
        gl::EnableVertexAttribArray(5);
        gl::VertexAttribPointer(5, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, color_0) as *const c_void);
        // JOINTS_0
        gl::EnableVertexAttribArray(6);
        // TODO: normalization?
        gl::VertexAttribPointer(6, 4, gl::UNSIGNED_SHORT, gl::FALSE, size, offset_of!(Vertex, joints_0) as *const c_void);
        // WEIGHTS_0
        gl::EnableVertexAttribArray(7);
        gl::VertexAttribPointer(7, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, weights_0) as *const c_void);

        gl::BindVertexArray(0);
    }
}
<commit_msg>pass normal_scale to shader<commit_after>use std::mem::size_of;
use std::os::raw::c_void;
use std::path::Path;
use std::ptr;
use std::rc::Rc;

use gl;
use gltf;
use gltf::json::mesh::Mode;

// use camera::Camera;
use render::math::*;
use render::{Material, Root};
use shader::*;
use importdata::ImportData;

#[derive(Debug)]
pub struct Vertex {
    pub position: Vector3,
    pub normal: Vector3,
    pub tangent: Vector4,
    pub tex_coord_0: Vector2,
    pub tex_coord_1: Vector2,
    pub color_0: Vector4,
    pub joints_0: [u16; 4],
    pub weights_0: Vector4,
}

impl Default for Vertex {
    fn default() -> Self {
        Vertex {
            position: Vector3::zero(),
            normal: Vector3::zero(),
            tangent: Vector4::zero(),
            tex_coord_0: Vector2::zero(),
            tex_coord_1: Vector2::zero(),
            color_0: Vector4::zero(),
            joints_0: [0; 4],
            weights_0: Vector4::zero(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Texture {
    pub id: u32,
    pub type_: String,
    pub path: String,
}

pub struct Primitive {
    pub bounds: Aabb3,

    vao: u32,
    vbo: u32,
    num_vertices: u32,

    ebo: Option<u32>,
    num_indices: u32,

    material: Rc<Material>,

    pbr_shader: Rc<PbrShader>,

    // TODO!: mode, targets
}

impl Primitive {
    pub fn new(
        bounds: Aabb3,
        vertices: &[Vertex],
        indices: Option<Vec<u32>>,
        material: Rc<Material>,
        shader: Rc<PbrShader>,
    ) -> Primitive {
        let num_indices = indices.as_ref().map(|i| i.len()).unwrap_or(0);
        let mut prim = Primitive {
            bounds,
            num_vertices: vertices.len() as u32,
            num_indices: num_indices as u32,
            vao: 0, vbo: 0, ebo: None,
            material,
            pbr_shader: shader,
        };

        // now that we have all the required data, set the vertex buffers and its attribute pointers.
        unsafe { prim.setup_primitive(vertices, indices) }
        prim
    }

    pub fn from_gltf(
        g_primitive: &gltf::Primitive,
        primitive_index: usize,
        mesh_index: usize,
        root: &mut Root,
        imp: &ImportData,
        base_path: &Path) -> Primitive
    {
        let buffers = &imp.buffers;
        let reader = g_primitive.reader(|buffer| Some(&buffers[buffer.index()]));
        let positions = {
            let iter = reader
                .read_positions()
                .expect(&format!(
                    "primitives must have the POSITION attribute (mesh: {}, primitive: {})",
                    mesh_index, primitive_index));
            iter.collect::<Vec<_>>()
        };

        let bounds = g_primitive.bounding_box();
        let bounds = Aabb3 {
            min: bounds.min.into(),
            max: bounds.max.into()
        };

        let mut vertices: Vec<Vertex> = positions
            .into_iter()
            .map(|position| {
                Vertex {
                    position: Vector3::from(position),
                    ..Vertex::default()
                }
            }).collect();

        let mut shader_flags = ShaderFlags::empty();

        // normals
        if let Some(normals) = reader.read_normals() {
            for (i, normal) in normals.enumerate() {
                vertices[i].normal = Vector3::from(normal);
            }
            shader_flags |= ShaderFlags::HAS_NORMALS;
        }
        else {
            debug!("Found no NORMALs for primitive {} of mesh {} \
                   (flat normal calculation not implemented yet)", primitive_index, mesh_index);
        }

        // tangents
        if let Some(tangents) = reader.read_tangents() {
            for (i, tangent) in tangents.enumerate() {
                vertices[i].tangent = Vector4::from(tangent);
            }
            shader_flags |= ShaderFlags::HAS_TANGENTS;
        }
        else {
            debug!("Found no TANGENTS for primitive {} of mesh {} \
                   (tangent calculation not implemented yet)", primitive_index, mesh_index);
        }

        // texture coordinates
        let mut tex_coord_set = 0;
        while let Some(tex_coords) = reader.read_tex_coords(tex_coord_set) {
            if tex_coord_set > 1 {
                warn!("Ignoring texture coordinate set {}, \
                        only supporting 2 sets at the moment. (mesh: {}, primitive: {})",
                        tex_coord_set, mesh_index, primitive_index);
                tex_coord_set += 1;
                continue;
            }
            for (i, tex_coord) in tex_coords.into_f32().enumerate() {
                match tex_coord_set {
                    0 => vertices[i].tex_coord_0 = Vector2::from(tex_coord),
                    1 => vertices[i].tex_coord_1 = Vector2::from(tex_coord),
                    _ => unreachable!()
                }
            }
            shader_flags |= ShaderFlags::HAS_UV;
            tex_coord_set += 1;
        }

        // colors
        if let Some(colors) = reader.read_colors(0) {
            let colors = colors.into_rgba_f32();
            for (i, c) in colors.enumerate() {
                vertices[i].color_0 = c.into();
            }
            shader_flags |= ShaderFlags::HAS_COLORS;
        }
        if reader.read_colors(1).is_some() {
            warn!("Ignoring further color attributes, only supporting COLOR_0. (mesh: {}, primitive: {})",
                mesh_index, primitive_index);
        }

        if let Some(joints) = reader.read_joints(0) {
            for (i, joint) in joints.into_u16().enumerate() {
                vertices[i].joints_0 = joint;
            }
        }
        if reader.read_joints(1).is_some() {
            warn!("Ignoring further joint attributes, only supporting JOINTS_0. (mesh: {}, primitive: {})",
                mesh_index, primitive_index);
        }

        if let Some(weights) = reader.read_weights(0) {
            for (i, weights) in weights.into_f32().enumerate() {
                vertices[i].weights_0 = weights.into();
            }
        }
        if reader.read_weights(1).is_some() {
            warn!("Ignoring further weight attributes, only supporting WEIGHTS_0. (mesh: {}, primitive: {})",
                mesh_index, primitive_index);
        }

        let indices = reader
            .read_indices()
            .map(|read_indices| {
                read_indices.into_u32().collect::<Vec<_>>()
            });

        assert_eq!(g_primitive.mode(), Mode::Triangles, "not yet implemented: primitive mode must be Triangles.");

        let g_material = g_primitive.material();

        let mut material = None;
        if let Some(mat) = root.materials.iter().find(|m| (***m).index == g_material.index()) {
            material = Rc::clone(mat).into()
        }

        if material.is_none() { // no else due to borrow checker madness
            let mat = Rc::new(Material::from_gltf(&g_material, root, imp, base_path));
            root.materials.push(Rc::clone(&mat));
            material = Some(mat);
        };
        let material = material.unwrap();
        shader_flags |= material.shader_flags();

        let mut new_shader = false; // borrow checker workaround
        let shader =
            if let Some(shader) = root.shaders.get(&shader_flags) {
                Rc::clone(shader)
            }
            else {
                new_shader = true;
                PbrShader::new(shader_flags).into()

            };
        if new_shader {
            root.shaders.insert(shader_flags, Rc::clone(&shader));
        }

        Primitive::new(bounds, &vertices, indices, material, shader)
    }

    /// render the mesh
    pub unsafe fn draw(&self, model_matrix: &Matrix4, mvp_matrix: &Matrix4, camera_position: &Vector3) {
        // TODO!: determine if shader+material already active to reduce work...

        if self.material.double_sided {
            gl::Disable(gl::CULL_FACE);
        } else {
            gl::Enable(gl::CULL_FACE);
        }

        self.configure_shader(model_matrix, mvp_matrix, camera_position);

        // draw mesh
        gl::BindVertexArray(self.vao);
        if self.ebo.is_some() {
            gl::DrawElements(gl::TRIANGLES, self.num_indices as i32, gl::UNSIGNED_INT, ptr::null());
        }
        else {
            gl::DrawArrays(gl::TRIANGLES, 0, self.num_vertices as i32)
        }

        gl::BindVertexArray(0);
        gl::ActiveTexture(gl::TEXTURE0);
    }

    unsafe fn configure_shader(&self, model_matrix: &Matrix4,
        mvp_matrix: &Matrix4, camera_position: &Vector3)
    {
        // let pbr_shader = &Rc::get_mut(&mut self.pbr_shader).unwrap();
        let mat = &self.material;
        let shader = &self.pbr_shader.shader;
        let uniforms = &self.pbr_shader.uniforms;
        self.pbr_shader.shader.use_program();

        // camera params
        shader.set_mat4(uniforms.u_ModelMatrix, model_matrix);
        shader.set_mat4(uniforms.u_MVPMatrix, mvp_matrix);
        shader.set_vector3(uniforms.u_Camera, camera_position);

        // NOTE: for sampler numbers, see also PbrShader constructor
        shader.set_vector4(uniforms.u_BaseColorFactor, &mat.base_color_factor);
        if let Some(ref base_color_texture) = mat.base_color_texture {
            gl::ActiveTexture(gl::TEXTURE0);
            gl::BindTexture(gl::TEXTURE_2D, base_color_texture.id);
        }
        if let Some(ref normal_texture) = mat.normal_texture {
            gl::ActiveTexture(gl::TEXTURE1);
            gl::BindTexture(gl::TEXTURE_2D, normal_texture.id);
            shader.set_float(uniforms.u_NormalScale, mat.normal_scale.unwrap_or(1.0));
        }
        if let Some(ref emissive_texture) = mat.emissive_texture {
            gl::ActiveTexture(gl::TEXTURE2);
            gl::BindTexture(gl::TEXTURE_2D, emissive_texture.id);

            shader.set_vector3(uniforms.u_EmissiveFactor, &mat.emissive_factor);
        }

        if let Some(ref mr_texture) = mat.metallic_roughness_texture {
            gl::ActiveTexture(gl::TEXTURE3);
            gl::BindTexture(gl::TEXTURE_2D, mr_texture.id);
        }
        shader.set_vec2(uniforms.u_MetallicRoughnessValues,
            mat.metallic_factor, mat.roughness_factor);

        if let Some(ref occlusion_texture) = mat.occlusion_texture {
            gl::ActiveTexture(gl::TEXTURE4);
            gl::BindTexture(gl::TEXTURE_2D, occlusion_texture.id);

            shader.set_float(uniforms.u_OcclusionStrength, mat.occlusion_strength);
        }
    }

    unsafe fn setup_primitive(&mut self, vertices: &[Vertex], indices: Option<Vec<u32>>) {
        // create buffers/arrays
        gl::GenVertexArrays(1, &mut self.vao);
        gl::GenBuffers(1, &mut self.vbo);
        if indices.is_some() {
            let mut ebo = 0;
            gl::GenBuffers(1, &mut ebo);
            self.ebo = Some(ebo);
        }

        gl::BindVertexArray(self.vao);
        // load data into vertex buffers
        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        let size = (vertices.len() * size_of::<Vertex>()) as isize;
        let data = &vertices[0] as *const Vertex as *const c_void;
        gl::BufferData(gl::ARRAY_BUFFER, size, data, gl::STATIC_DRAW);

        if let Some(ebo) = self.ebo {
            let indices = indices.unwrap();
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
            let size = (indices.len() * size_of::<u32>()) as isize;
            let data = &indices[0] as *const u32 as *const c_void;
            gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, size, data, gl::STATIC_DRAW);
        }

        // set the vertex attribute pointers
        let size = size_of::<Vertex>() as i32;
        // POSITION
        gl::EnableVertexAttribArray(0);
        gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, position) as *const c_void);
        // NORMAL
        gl::EnableVertexAttribArray(1);
        gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, normal) as *const c_void);
        // TANGENT
        gl::EnableVertexAttribArray(2);
        gl::VertexAttribPointer(2, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tangent) as *const c_void);
        // TEXCOORD_0
        gl::EnableVertexAttribArray(3);
        gl::VertexAttribPointer(3, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tex_coord_0) as *const c_void);
        // TEXCOORD_1
        gl::EnableVertexAttribArray(4);
        gl::VertexAttribPointer(4, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tex_coord_1) as *const c_void);
        // COLOR_0
        gl::EnableVertexAttribArray(5);
        gl::VertexAttribPointer(5, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, color_0) as *const c_void);
        // JOINTS_0
        gl::EnableVertexAttribArray(6);
        // TODO: normalization?
        gl::VertexAttribPointer(6, 4, gl::UNSIGNED_SHORT, gl::FALSE, size, offset_of!(Vertex, joints_0) as *const c_void);
        // WEIGHTS_0
        gl::EnableVertexAttribArray(7);
        gl::VertexAttribPointer(7, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, weights_0) as *const c_void);

        gl::BindVertexArray(0);
    }
}
<|endoftext|>


<commit_before>//! The state of the game itself.

use message;

use std::collections::HashMap;
use std::sync::mpsc;

use math::distance_between;
use rand::{thread_rng, Rng};

use self::super::Client;
use self::super::WebSocketEvent;

static BULLET_RADIUS: f32 = 5.0;
static PLAYER_RADIUS: f32 = 10.0;
static BULLET_SPEED: f32 = 3.0;
static PLAYER_SPEED: f32 = 2.0;
static MAP_HEIGHT: f32 = 500.0;
static MAP_WIDTH: f32 = 500.0;
static TICKS_BETWEEN_FULL_UPDATES: u32 = 600; // 10s @ 60FPS

/// The `GameState` contains the whole state of the game.
///
/// It consists of both players, and all the clients which are currently connected.
#[derive(Debug)]
pub struct GameState {
    players: HashMap<u32, message::Player>,
    bullets: HashMap<u32, message::OwnedBullet>,
    clients: HashMap<u32, Client>,
    next_bullet_id: u32,
    ticks_since_last_full_update: u32,
}

impl GameState {
    /// Create a new game state.
    pub fn new() -> GameState {
        GameState {
            players: HashMap::new(),
            bullets: HashMap::new(),
            clients: HashMap::new(),
            next_bullet_id: 0,
            ticks_since_last_full_update: 0,
        }
    }

    /// Tries to process every available websocket event without blocking.
    pub fn process_websocket_events(&mut self, game_messages: &mpsc::Receiver<WebSocketEvent>) {
        loop {
            match game_messages.try_recv() {
                Ok(message) => self.process_websocket_event(message),
                Err(mpsc::TryRecvError::Empty) => return,
                Err(mpsc::TryRecvError::Disconnected) => return, // Server thread died
            }
        }
    }

    /// Updates the game state in one tick.
    pub fn process_game_update(&mut self) {
        // Do a normal position update
        let player_ids: Vec<_> = self.players.keys().map(|i| *i).collect();
        let mut force_stopped_player_ids = Vec::new();
        for cur_player_id in &player_ids {
            let collides_with_player = {
                let cur_player = self.players.get(cur_player_id).unwrap();
                match (cur_player.move_x, cur_player.move_y) {
                    (None, None) => continue,
                    (Some(move_x), Some(move_y)) => {
                        let mut collides = false;
                        for cmp_player_id in &player_ids {
                            if cmp_player_id != cur_player_id {
                                let cmp_player = self.players.get(cmp_player_id).unwrap();
                                if distance_between(cur_player.x + move_x,
                                                    cur_player.y + move_y,
                                                    cmp_player.x,
                                                    cmp_player.y) <
                                   2.0 * PLAYER_RADIUS {
                                    collides = true;
                                    break;
                                }
                            }
                        }
                        collides
                    }
                    _ => panic!("Invariant not met: player moves only in one direction"),
                }
            };

            let mut player = self.players.get_mut(cur_player_id).unwrap();
            let mut collides_with_map = false;

            if !collides_with_player {
                player.x = (player.x + player.move_x.unwrap_or(0.0) * PLAYER_SPEED)
                               .max(PLAYER_RADIUS)
                               .min(MAP_WIDTH - PLAYER_RADIUS);
                player.y = (player.y + player.move_y.unwrap_or(0.0) * PLAYER_SPEED)
                               .max(PLAYER_RADIUS)
                               .min(MAP_HEIGHT - PLAYER_RADIUS);

                collides_with_map = (player.x == PLAYER_RADIUS ||
                                     player.x == MAP_WIDTH - PLAYER_RADIUS) ||
                                    (player.y == PLAYER_RADIUS ||
                                     player.y == MAP_WIDTH - PLAYER_RADIUS);
            }

            if collides_with_player || collides_with_map {
                force_stopped_player_ids.push(cur_player_id);
            }
        }

        for force_stopped_player_id in force_stopped_player_ids {
            let (x, y) = {
                let mut player = self.players.get_mut(force_stopped_player_id).unwrap();
                player.move_x = None;
                player.move_y = None;
                (player.x, player.y)
            };
            self.send_to_everybody(message::Message::PlayerStopped {
                id: *force_stopped_player_id,
                x: x,
                y: y,
            });
        }

        let mut destroyed_bullets = Vec::new();
        let mut destroyed_players = Vec::new();

        for (_, bullet) in &mut self.bullets {
            bullet.bullet.x += bullet.bullet.move_x.unwrap_or(0.0) * BULLET_SPEED;
            bullet.bullet.y += bullet.bullet.move_y.unwrap_or(0.0) * BULLET_SPEED;

            if bullet.bullet.x < 0.0 || bullet.bullet.x > MAP_WIDTH || bullet.bullet.y < 0.0 ||
               bullet.bullet.y > MAP_HEIGHT {
                destroyed_bullets.push(bullet.bullet.id);
            }
        }

        // Check for collisions
        for (_, bullet) in &self.bullets {
            for (_, player) in &self.players {
                if bullet.owner_id != player.id &&
                   distance_between(bullet.bullet.x, bullet.bullet.y, player.x, player.y) <
                   BULLET_RADIUS + PLAYER_RADIUS {
                    destroyed_bullets.push(bullet.bullet.id);
                    destroyed_players.push((player.id, bullet.bullet.id));
                }
            }
        }

        // Process destroy requests
        let mut rng = thread_rng();
        for (player_id, bullet_id) in destroyed_players {
            let bullet = self.bullets.get(&bullet_id).unwrap();
            self.send_to_everybody(message::Message::PlayerDestroyed {
                id: player_id,
                killer_id: Some(bullet.owner_id),
                bullet_id: Some(bullet_id),
            });

            let (new_x, new_y) = self.random_free_spot(&mut rng);

            {
                let dead_player = self.players.get_mut(&player_id).unwrap();
                dead_player.x = new_x;
                dead_player.y = new_y;
            }

            self.send_to_everybody(message::Message::PlayerSpawned {
                id: player_id,
                x: new_x,
                y: new_y,
            });
        }

        for bullet_id in destroyed_bullets {
            let _ = self.bullets.remove(&bullet_id);
        }
    }

    /// Send the current state to each client.
    pub fn send_state_updates(&mut self) {
        if self.ticks_since_last_full_update == TICKS_BETWEEN_FULL_UPDATES {
            self.ticks_since_last_full_update = 0;
            self.send_to_everybody(self.serialize());
        } else {
            self.ticks_since_last_full_update += 1;
        }
    }

    /// Process a web socket event.
    fn process_websocket_event(&mut self, message: WebSocketEvent) {
        match message {
            WebSocketEvent::ClientCreated { client } => {
                let welcome_message = message::Message::Welcome {
                    id: client.id,
                    speed: PLAYER_SPEED,
                    size: PLAYER_RADIUS,
                    bullet_speed: BULLET_SPEED,
                    bullet_size: BULLET_RADIUS,
                };

                let _ = client.send(welcome_message.to_string());
                self.send_to_everybody(message::Message::PlayerJoined { id: client.id });

                let (x, y) = self.random_free_spot(&mut thread_rng());
                let _ = self.players
                            .insert(client.id, message::Player::not_moving(client.id, x, y));
                self.send_to_everybody(message::Message::PlayerSpawned {
                    id: client.id,
                    x: x,
                    y: y,
                });

                let _ = client.send(self.serialize().to_string());

                let _ = self.clients.insert(client.id, client);
            }
            WebSocketEvent::ClientClosed { client_id } => {
                let _ = self.players.remove(&client_id);
                let _ = self.clients.remove(&client_id);

                self.send_to_everybody(message::Message::PlayerLeft { id: client_id });

                let mut destroyed_bullets = Vec::new();
                for (_, bullet) in &self.bullets {
                    if bullet.owner_id == client_id {
                        destroyed_bullets.push(bullet.bullet.id);
                    }
                }
                for bullet_id in destroyed_bullets {
                    let _ = self.bullets.remove(&bullet_id);
                }
            }
            WebSocketEvent::ClientMessage { client_id, message } => {
                self.process_client_message(client_id, message);
            }
        }
    }

    /// Serialize the entire game state into one json string.
    fn serialize(&self) -> message::Message {
        let players: Vec<_> = self.players
                                  .values()
                                  .cloned()
                                  .collect();
        let bullets: Vec<_> = self.bullets
                                  .values()
                                  .map(|b| &b.bullet)
                                  .cloned()
                                  .collect();
        message::Message::WorldState {
            player_count: self.clients.len() as u32,
            alive_players: players,
            alive_bullets: bullets,
        }
    }

    /// Process a simple string message from the client.
    fn process_client_message(&mut self, client_id: u32, message: message::Message) {
        match message {
            message::Message::StartMoving { move_x, move_y } => {
                let resp = {
                    let player = self.players.get_mut(&client_id).unwrap();
                    player.move_x = Some(move_x);
                    player.move_y = Some(move_y);

                    message::Message::PlayerMoving {
                        id: player.id,
                        x: player.x,
                        y: player.y,
                        move_x: move_x,
                        move_y: move_y,
                    }
                };
                self.send_to_everybody(resp);
            }
            message::Message::StopMoving => {
                let resp = {
                    let player = self.players.get_mut(&client_id).unwrap();
                    player.move_x = None;
                    player.move_y = None;

                    message::Message::PlayerStopped {
                        id: player.id,
                        x: player.x,
                        y: player.y,
                    }
                };
                self.send_to_everybody(resp);
            }
            message::Message::Fire { move_x, move_y } => {
                let player = self.players.get(&client_id).unwrap();

                // Have to move the bullet out of the way of the player to avoid an instant collision.
                let start_x = player.x + move_x * (BULLET_RADIUS + PLAYER_RADIUS + 1.0);
                let start_y = player.y + move_y * (BULLET_RADIUS + PLAYER_RADIUS + 1.0);

                let new_bullet = message::Bullet::moving(self.next_bullet_id,
                                                         start_x,
                                                         start_y,
                                                         move_x,
                                                         move_y);
                let _ = self.bullets
                            .insert(self.next_bullet_id,
                                    message::OwnedBullet::new(new_bullet, player.id));

                let resp = message::Message::ShotsFired {
                    id: player.id,
                    bullet_id: self.next_bullet_id,
                    x: start_x,
                    y: start_y,
                    aim_x: move_x,
                    aim_y: move_y,
                };
                self.send_to_everybody(resp);

                self.next_bullet_id += 1;
            }
            _ => panic!("Unprocessed message! {}", message.to_string()),
        }
    }

    fn random_free_spot<R: Rng>(&self, rng: &mut R) -> (f32, f32) {
        static MAX_ITERATIONS: u32 = 100;

        let min_vial_x = PLAYER_RADIUS;
        let min_vial_y = PLAYER_RADIUS;
        let max_vial_x = MAP_WIDTH - PLAYER_RADIUS;
        let max_vial_y = MAP_HEIGHT - PLAYER_RADIUS;

        for _ in 1..MAX_ITERATIONS {
            let x: f32 = rng.gen_range(min_vial_x, max_vial_x);
            let y: f32 = rng.gen_range(min_vial_y, max_vial_y);

            let mut collides = false;

            for (_, player) in &self.players {
                if distance_between(x, y, player.x, player.y) < 2.0 * PLAYER_RADIUS {
                    collides = true;
                    break;
                }
            }

            for (_, bullet) in &self.bullets {
                if distance_between(x, y, bullet.bullet.x, bullet.bullet.y) <
                   PLAYER_RADIUS + BULLET_RADIUS {
                    collides = true;
                    break;
                }
            }

            if !collides {
                return (x, y);
            }
        }
        println!("Failed to find a random empty spot for player after {} iterations",
                 MAX_ITERATIONS);

        (rng.gen_range(0.0, MAP_WIDTH), rng.gen_range(0.0, MAP_HEIGHT))
    }

    fn send_to_everybody(&self, what: message::Message) {
        let value = what.to_string();
        for (_, client) in &self.clients {
            // Always ignore if the send fails.
            // We will eventually get a disconnect WebSocketMessage where we will cleanly do the disconnect.
            let _ = client.send(value.clone());
        }
    }
}

impl Drop for GameState {
    fn drop(&mut self) {
        self.send_to_everybody(message::Message::GoAway {
            reason: "Server termination".to_string(),
        });
    }
}
<commit_msg>Let players slide on walls<commit_after>//! The state of the game itself.

use message;

use std::collections::HashMap;
use std::sync::mpsc;

use math::distance_between;
use rand::{thread_rng, Rng};

use self::super::Client;
use self::super::WebSocketEvent;

static BULLET_RADIUS: f32 = 5.0;
static PLAYER_RADIUS: f32 = 10.0;
static BULLET_SPEED: f32 = 3.0;
static PLAYER_SPEED: f32 = 2.0;
static MAP_HEIGHT: f32 = 500.0;
static MAP_WIDTH: f32 = 500.0;
static TICKS_BETWEEN_FULL_UPDATES: u32 = 600; // 10s @ 60FPS

/// The `GameState` contains the whole state of the game.
///
/// It consists of both players, and all the clients which are currently connected.
#[derive(Debug)]
pub struct GameState {
    players: HashMap<u32, message::Player>,
    bullets: HashMap<u32, message::OwnedBullet>,
    clients: HashMap<u32, Client>,
    next_bullet_id: u32,
    ticks_since_last_full_update: u32,
}

impl GameState {
    /// Create a new game state.
    pub fn new() -> GameState {
        GameState {
            players: HashMap::new(),
            bullets: HashMap::new(),
            clients: HashMap::new(),
            next_bullet_id: 0,
            ticks_since_last_full_update: 0,
        }
    }

    /// Tries to process every available websocket event without blocking.
    pub fn process_websocket_events(&mut self, game_messages: &mpsc::Receiver<WebSocketEvent>) {
        loop {
            match game_messages.try_recv() {
                Ok(message) => self.process_websocket_event(message),
                Err(mpsc::TryRecvError::Empty) => return,
                Err(mpsc::TryRecvError::Disconnected) => return, // Server thread died
            }
        }
    }

    /// Updates the game state in one tick.
    pub fn process_game_update(&mut self) {
        // Do a normal position update
        let player_ids: Vec<_> = self.players.keys().map(|i| *i).collect();
        let mut force_stopped_player_ids = Vec::new();
        for cur_player_id in &player_ids {
            let collides_with_player = {
                let cur_player = self.players.get(cur_player_id).unwrap();
                match (cur_player.move_x, cur_player.move_y) {
                    (None, None) => continue,
                    (Some(move_x), Some(move_y)) => {
                        let mut collides = false;
                        for cmp_player_id in &player_ids {
                            if cmp_player_id != cur_player_id {
                                let cmp_player = self.players.get(cmp_player_id).unwrap();
                                if distance_between(cur_player.x + move_x,
                                                    cur_player.y + move_y,
                                                    cmp_player.x,
                                                    cmp_player.y) <
                                   2.0 * PLAYER_RADIUS {
                                    collides = true;
                                    break;
                                }
                            }
                        }
                        collides
                    }
                    _ => panic!("Invariant not met: player moves only in one direction"),
                }
            };

            let mut player = self.players.get_mut(cur_player_id).unwrap();
            let mut collides_with_map = false;

            if !collides_with_player {
                collides_with_map = Self::move_player(&mut player.x, player.move_x) ||
                                    Self::move_player(&mut player.y, player.move_y);
            }

            if collides_with_player || collides_with_map {
                force_stopped_player_ids.push(cur_player_id);
            }
        }

        for force_stopped_player_id in force_stopped_player_ids {
            let (x, y) = {
                let mut player = self.players.get_mut(force_stopped_player_id).unwrap();
                player.move_x = None;
                player.move_y = None;
                (player.x, player.y)
            };
            self.send_to_everybody(message::Message::PlayerStopped {
                id: *force_stopped_player_id,
                x: x,
                y: y,
            });
        }

        let mut destroyed_bullets = Vec::new();
        let mut destroyed_players = Vec::new();

        for (_, bullet) in &mut self.bullets {
            bullet.bullet.x += bullet.bullet.move_x.unwrap_or(0.0) * BULLET_SPEED;
            bullet.bullet.y += bullet.bullet.move_y.unwrap_or(0.0) * BULLET_SPEED;

            if bullet.bullet.x < 0.0 || bullet.bullet.x > MAP_WIDTH || bullet.bullet.y < 0.0 ||
               bullet.bullet.y > MAP_HEIGHT {
                destroyed_bullets.push(bullet.bullet.id);
            }
        }

        // Check for collisions
        for (_, bullet) in &self.bullets {
            for (_, player) in &self.players {
                if bullet.owner_id != player.id &&
                   distance_between(bullet.bullet.x, bullet.bullet.y, player.x, player.y) <
                   BULLET_RADIUS + PLAYER_RADIUS {
                    destroyed_bullets.push(bullet.bullet.id);
                    destroyed_players.push((player.id, bullet.bullet.id));
                }
            }
        }

        // Process destroy requests
        let mut rng = thread_rng();
        for (player_id, bullet_id) in destroyed_players {
            let bullet = self.bullets.get(&bullet_id).unwrap();
            self.send_to_everybody(message::Message::PlayerDestroyed {
                id: player_id,
                killer_id: Some(bullet.owner_id),
                bullet_id: Some(bullet_id),
            });

            let (new_x, new_y) = self.random_free_spot(&mut rng);

            {
                let dead_player = self.players.get_mut(&player_id).unwrap();
                dead_player.x = new_x;
                dead_player.y = new_y;
            }

            self.send_to_everybody(message::Message::PlayerSpawned {
                id: player_id,
                x: new_x,
                y: new_y,
            });
        }

        for bullet_id in destroyed_bullets {
            let _ = self.bullets.remove(&bullet_id);
        }
    }

    /// Send the current state to each client.
    pub fn send_state_updates(&mut self) {
        if self.ticks_since_last_full_update == TICKS_BETWEEN_FULL_UPDATES {
            self.ticks_since_last_full_update = 0;
            self.send_to_everybody(self.serialize());
        } else {
            self.ticks_since_last_full_update += 1;
        }
    }

    /// Process a web socket event.
    fn process_websocket_event(&mut self, message: WebSocketEvent) {
        match message {
            WebSocketEvent::ClientCreated { client } => {
                let welcome_message = message::Message::Welcome {
                    id: client.id,
                    speed: PLAYER_SPEED,
                    size: PLAYER_RADIUS,
                    bullet_speed: BULLET_SPEED,
                    bullet_size: BULLET_RADIUS,
                };

                let _ = client.send(welcome_message.to_string());
                self.send_to_everybody(message::Message::PlayerJoined { id: client.id });

                let (x, y) = self.random_free_spot(&mut thread_rng());
                let _ = self.players
                            .insert(client.id, message::Player::not_moving(client.id, x, y));
                self.send_to_everybody(message::Message::PlayerSpawned {
                    id: client.id,
                    x: x,
                    y: y,
                });

                let _ = client.send(self.serialize().to_string());

                let _ = self.clients.insert(client.id, client);
            }
            WebSocketEvent::ClientClosed { client_id } => {
                let _ = self.players.remove(&client_id);
                let _ = self.clients.remove(&client_id);

                self.send_to_everybody(message::Message::PlayerLeft { id: client_id });

                let mut destroyed_bullets = Vec::new();
                for (_, bullet) in &self.bullets {
                    if bullet.owner_id == client_id {
                        destroyed_bullets.push(bullet.bullet.id);
                    }
                }
                for bullet_id in destroyed_bullets {
                    let _ = self.bullets.remove(&bullet_id);
                }
            }
            WebSocketEvent::ClientMessage { client_id, message } => {
                self.process_client_message(client_id, message);
            }
        }
    }

    /// Serialize the entire game state into one json string.
    fn serialize(&self) -> message::Message {
        let players: Vec<_> = self.players
                                  .values()
                                  .cloned()
                                  .collect();
        let bullets: Vec<_> = self.bullets
                                  .values()
                                  .map(|b| &b.bullet)
                                  .cloned()
                                  .collect();
        message::Message::WorldState {
            player_count: self.clients.len() as u32,
            alive_players: players,
            alive_bullets: bullets,
        }
    }

    /// Process a simple string message from the client.
    fn process_client_message(&mut self, client_id: u32, message: message::Message) {
        match message {
            message::Message::StartMoving { move_x, move_y } => {
                let resp = {
                    let player = self.players.get_mut(&client_id).unwrap();
                    player.move_x = Some(move_x);
                    player.move_y = Some(move_y);

                    message::Message::PlayerMoving {
                        id: player.id,
                        x: player.x,
                        y: player.y,
                        move_x: move_x,
                        move_y: move_y,
                    }
                };
                self.send_to_everybody(resp);
            }
            message::Message::StopMoving => {
                let resp = {
                    let player = self.players.get_mut(&client_id).unwrap();
                    player.move_x = None;
                    player.move_y = None;

                    message::Message::PlayerStopped {
                        id: player.id,
                        x: player.x,
                        y: player.y,
                    }
                };
                self.send_to_everybody(resp);
            }
            message::Message::Fire { move_x, move_y } => {
                let player = self.players.get(&client_id).unwrap();

                // Have to move the bullet out of the way of the player to avoid an instant collision.
                let start_x = player.x + move_x * (BULLET_RADIUS + PLAYER_RADIUS + 1.0);
                let start_y = player.y + move_y * (BULLET_RADIUS + PLAYER_RADIUS + 1.0);

                let new_bullet = message::Bullet::moving(self.next_bullet_id,
                                                         start_x,
                                                         start_y,
                                                         move_x,
                                                         move_y);
                let _ = self.bullets
                            .insert(self.next_bullet_id,
                                    message::OwnedBullet::new(new_bullet, player.id));

                let resp = message::Message::ShotsFired {
                    id: player.id,
                    bullet_id: self.next_bullet_id,
                    x: start_x,
                    y: start_y,
                    aim_x: move_x,
                    aim_y: move_y,
                };
                self.send_to_everybody(resp);

                self.next_bullet_id += 1;
            }
            _ => panic!("Unprocessed message! {}", message.to_string()),
        }
    }

    /// Find a valid unoccupied spot for a player to spawn.
    fn random_free_spot<R: Rng>(&self, rng: &mut R) -> (f32, f32) {
        static MAX_ITERATIONS: u32 = 100;

        let min_vial_x = PLAYER_RADIUS;
        let min_vial_y = PLAYER_RADIUS;
        let max_vial_x = MAP_WIDTH - PLAYER_RADIUS;
        let max_vial_y = MAP_HEIGHT - PLAYER_RADIUS;

        for _ in 1..MAX_ITERATIONS {
            let x: f32 = rng.gen_range(min_vial_x, max_vial_x);
            let y: f32 = rng.gen_range(min_vial_y, max_vial_y);

            let mut collides = false;

            for (_, player) in &self.players {
                if distance_between(x, y, player.x, player.y) < 2.0 * PLAYER_RADIUS {
                    collides = true;
                    break;
                }
            }

            for (_, bullet) in &self.bullets {
                if distance_between(x, y, bullet.bullet.x, bullet.bullet.y) <
                   PLAYER_RADIUS + BULLET_RADIUS {
                    collides = true;
                    break;
                }
            }

            if !collides {
                return (x, y);
            }
        }
        println!("Failed to find a random empty spot for player after {} iterations",
                 MAX_ITERATIONS);

        (rng.gen_range(0.0, MAP_WIDTH), rng.gen_range(0.0, MAP_HEIGHT))
    }

    /// Send a specified message to all connected clients.
    fn send_to_everybody(&self, what: message::Message) {
        let value = what.to_string();
        for (_, client) in &self.clients {
            // Always ignore if the send fails.
            // We will eventually get a disconnect WebSocketMessage where we will cleanly do the disconnect.
            let _ = client.send(value.clone());
        }
    }

    /// Moves a player along one axis based on its current position and movement vector.
    ///
    /// Returns whether the player crashed into a wall during movement.
    fn move_player(pos: &mut f32, mov: Option<f32>) -> bool {
        let new_pos = *pos + mov.unwrap_or(0.0) * PLAYER_SPEED;
        *pos = new_pos.max(PLAYER_RADIUS)
                      .min(MAP_WIDTH - PLAYER_RADIUS);

        new_pos < PLAYER_RADIUS || new_pos > MAP_WIDTH - PLAYER_RADIUS
    }
}

impl Drop for GameState {
    fn drop(&mut self) {
        self.send_to_everybody(message::Message::GoAway {
            reason: "Server termination".to_string(),
        });
    }
}
<|endoftext|>


<commit_before>//! Roundtrip (write-read) tests for supported LAS versions and attributes.

#[macro_use]
extern crate approx;
extern crate chrono;
extern crate las;
extern crate uuid;

use las::{Header, Point, Reader, Writer};
use std::io::Cursor;

pub fn roundtrip(header: &Header, point: &Point, should_succeed: bool) {
    let mut cursor = Cursor::new(Vec::new());
    {
        match Writer::new(&mut cursor, header.clone()).and_then(
            |mut writer| {
                writer.write(point.clone())
            },
        ) {
            Ok(()) => if !should_succeed {
                panic!("Expected write to fail, but it succeeded");
            },
            Err(err) => {
                if should_succeed {
                    panic!("Write error: {}", err)
                } else {
                    return;
                }
            }
        }
    }
    cursor.set_position(0);
    let mut reader = Reader::new(cursor).unwrap();
    let other = reader
        .read()
        .expect("Error when reading the ont point")
        .unwrap();
    assert_eq!(point, &other);
    assert_eq!(
        None,
        reader.read().expect("Error when reading past last point")
    );

    let other = reader.header();
    assert_eq!(header.file_source_id, other.file_source_id);
    assert_eq!(header.gps_time_type, other.gps_time_type);
    assert_eq!(
        header.has_synthetic_return_numbers,
        other.has_synthetic_return_numbers
    );
    assert_eq!(header.guid, other.guid);
    assert_eq!(header.version, other.version);
    assert_eq!(header.system_identifier, other.system_identifier);
    assert_eq!(header.generating_software, other.generating_software);
    assert_eq!(header.date, other.date);
    assert_eq!(header.padding, other.padding);
    assert_eq!(header.point_format, other.point_format);
    assert_eq!(header.transforms, other.transforms);
    assert_relative_eq!(point.x, other.bounds.min.x);
    assert_relative_eq!(point.x, other.bounds.max.x);
    assert_relative_eq!(point.y, other.bounds.min.y);
    assert_relative_eq!(point.y, other.bounds.max.y);
    assert_relative_eq!(point.z, other.bounds.min.z);
    assert_relative_eq!(point.z, other.bounds.max.z);
    assert_eq!(1, other.number_of_points);
    if point.return_number > 0 {
        assert_eq!(1, other.number_of_points_by_return[&point.return_number]);
    }
    assert_eq!(
        header.vlrs().collect::<Vec<_>>(),
        other.vlrs().collect::<Vec<_>>()
    );
    assert_eq!(
        header.evlrs().collect::<Vec<_>>(),
        other.evlrs().collect::<Vec<_>>()
    );
}

macro_rules! roundtrip_point {
    ($name:ident, $modify_point:expr) => {
        roundtrip_point!($name, $modify_point, 0);
    };
    ($name:ident, $modify_point:expr, $min_version_minor:expr) => {
        roundtrip_point!($name, $modify_point, $min_version_minor, |_f| {});
    };
    ($name:ident, $modify_point:expr, $min_version_minor:expr, $modify_point_format:expr) => {
        #[test]
        fn $name() {
            use las::{Header, Point, Version};
            use las::point::Format;

            let version = super::version();
            let should_succeed = version >= Version::new(1, $min_version_minor);
            let mut point_format = Format::default();
            $modify_point_format(&mut point_format);
            let mut point = Point::default();
            $modify_point(&mut point);
            let mut header = Header::default();
            header.version = version;
            header.point_format = point_format;
            ::roundtrip(&header, &point, should_succeed);
        }
    };
}

macro_rules! roundtrip_header {
    ($name:ident, $modify_header:expr) => {
        roundtrip_header!($name, $modify_header, 0);
    };
    ($name:ident, $modify_header:expr, $min_version_minor:expr) => {
        #[test]
        fn $name() {
            use las::{Version, Header, Point};

            let version = super::version();
            let should_succeed = version >= Version::new(1, $min_version_minor);
            let mut header = Header::default();
            header.version = version;
            $modify_header(&mut header);
            ::roundtrip(&header, &Point::default(), should_succeed);
        }
    };
}

macro_rules! version { ($name:ident, $major:expr, $minor:expr) => {
mod $name {
    use las::Version;

    fn version() -> Version {
        Version::new($major, $minor)
    }

    mod point {
        use las::Color;
        use las::point::{Classification, ScanDirection};

        roundtrip_point!(xyz, |p: &mut Point| { p.x = 1.; p.y = 2.; p.z = 3.; });
        roundtrip_point!(intensity, |p: &mut Point| p.intensity = 42);
        roundtrip_point!(return_number, |p: &mut Point| p.return_number = 2);
        roundtrip_point!(number_of_returns, |p: &mut Point| p.number_of_returns = 2);
        roundtrip_point!(scan_direction, |p: &mut Point| p.scan_direction = ScanDirection::LeftToRight);
        roundtrip_point!(is_edge_of_flight_line, |p: &mut Point| p.is_edge_of_flight_line = true);
        roundtrip_point!(classification, |p: &mut Point| p.classification = Classification::Ground);
        roundtrip_point!(is_synthetic, |p: &mut Point| p.is_synthetic = true);
        roundtrip_point!(is_key_point, |p: &mut Point| p.is_key_point = true);
        roundtrip_point!(is_withheld, |p: &mut Point| p.is_withheld = true);
        roundtrip_point!(is_overlap, |p: &mut Point| {
            p.classification = Classification::Unclassified;
            p.is_overlap = true;
        });
        roundtrip_point!(scanner_channel, |p: &mut Point| {
            p.scanner_channel = 1;
            p.gps_time = Some(42.);
        }, 4, |f: &mut Format| f.extend());
        roundtrip_point!(scan_angle_rank, |p: &mut Point| p.scan_angle = 3.);
        roundtrip_point!(user_data, |p: &mut Point| p.user_data = 42);
        roundtrip_point!(point_source_id, |p: &mut Point| p.point_source_id = 42);
        roundtrip_point!(gps_time, |p: &mut Point| p.gps_time = Some(42.), 0, |f: &mut Format| f.has_gps_time = true);
        roundtrip_point!(color, |p: &mut Point| p.color = Some(Color { red: 1, green: 2, blue: 3 }), 2, |f: &mut Format| f.has_color = true);
        // TODO waveform
        roundtrip_point!(nir, |p: &mut Point| {
            p.color = Some(Color { red: 1, green: 2, blue: 3});
            p.nir = Some(42);
            p.gps_time = Some(42.);
        }, 4, |f: &mut Format| {
            f.extend();
            f.has_color = true;
            f.has_nir = true;
        });
        roundtrip_point!(extra_bytes, |p: &mut Point| p.extra_bytes = vec![42], 0, |f: &mut Format| f.extra_bytes = 1);
    }

    mod header {
        use chrono::{Utc, TimeZone};
        use las::GpsTimeType;
        use uuid::Uuid;

        roundtrip_header!(file_source_id, |h: &mut Header| h.file_source_id = 42, 1);
        roundtrip_header!(gps_time_type, |h: &mut Header| h.gps_time_type = GpsTimeType::Standard, 2);
        roundtrip_header!(has_synthetic_return_numbers, |h: &mut Header| h.has_synthetic_return_numbers = true, 3);
        roundtrip_header!(guid, |h: &mut Header| h.guid = Uuid::from_bytes(&[42; 16]).unwrap());
        roundtrip_header!(system_identifier, |h: &mut Header| h.system_identifier = "roundtrip test".to_string());
        roundtrip_header!(generating_software, |h: &mut Header| h.generating_software = "roundtrip test".to_string());
        roundtrip_header!(date, |h: &mut Header| h.date = Some(Utc.ymd(2017, 10, 30)));
        roundtrip_header!(padding, |h: &mut Header| h.padding = b"You probably shouldn't do this".to_vec());
        roundtrip_header!(vlr_padding, |h: &mut Header| h.vlr_padding = b"You probably shouldn't do this either".to_vec());
        roundtrip_header!(transforms, |h: &mut Header| {
            use las::{Transform, Vector};

            let transform = Transform { scale: 0.1, offset: -1. };
            h.transforms = Vector {
                x: transform,
                y: transform,
                z: transform,
            };
        });
        roundtrip_header!(vlrs, |h: &mut Header| h.push_vlr(Default::default()));
        roundtrip_header!(evlrs, |h: &mut Header| {
            use std::u16;
            use las::Vlr;

            let mut vlr = Vlr::default();
            vlr.is_extended = true;
            vlr.data = vec![42; u16::MAX as usize + 1];
            h.push_vlr(vlr);
        }, 4);
        roundtrip_header!(end_of_points_padding, |h: &mut Header| h.end_of_points_padding = vec![42]);
    }
}
}}

version!(las_1_0, 1, 0);
version!(las_1_1, 1, 1);
version!(las_1_2, 1, 2);
version!(las_1_3, 1, 3);
version!(las_1_4, 1, 4);
<commit_msg>Allow cyclomatic_complexity in roundtrip<commit_after>//! Roundtrip (write-read) tests for supported LAS versions and attributes.

#[macro_use]
extern crate approx;
extern crate chrono;
extern crate las;
extern crate uuid;

use las::{Header, Point, Reader, Writer};
use std::io::Cursor;

#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
pub fn roundtrip(header: &Header, point: &Point, should_succeed: bool) {
    let mut cursor = Cursor::new(Vec::new());
    {
        match Writer::new(&mut cursor, header.clone()).and_then(
            |mut writer| {
                writer.write(point.clone())
            },
        ) {
            Ok(()) => if !should_succeed {
                panic!("Expected write to fail, but it succeeded");
            },
            Err(err) => {
                if should_succeed {
                    panic!("Write error: {}", err)
                } else {
                    return;
                }
            }
        }
    }
    cursor.set_position(0);
    let mut reader = Reader::new(cursor).unwrap();
    let other = reader
        .read()
        .expect("Error when reading the ont point")
        .unwrap();
    assert_eq!(point, &other);
    assert_eq!(
        None,
        reader.read().expect("Error when reading past last point")
    );

    let other = reader.header();
    assert_eq!(header.file_source_id, other.file_source_id);
    assert_eq!(header.gps_time_type, other.gps_time_type);
    assert_eq!(
        header.has_synthetic_return_numbers,
        other.has_synthetic_return_numbers
    );
    assert_eq!(header.guid, other.guid);
    assert_eq!(header.version, other.version);
    assert_eq!(header.system_identifier, other.system_identifier);
    assert_eq!(header.generating_software, other.generating_software);
    assert_eq!(header.date, other.date);
    assert_eq!(header.padding, other.padding);
    assert_eq!(header.point_format, other.point_format);
    assert_eq!(header.transforms, other.transforms);
    assert_relative_eq!(point.x, other.bounds.min.x);
    assert_relative_eq!(point.x, other.bounds.max.x);
    assert_relative_eq!(point.y, other.bounds.min.y);
    assert_relative_eq!(point.y, other.bounds.max.y);
    assert_relative_eq!(point.z, other.bounds.min.z);
    assert_relative_eq!(point.z, other.bounds.max.z);
    assert_eq!(1, other.number_of_points);
    if point.return_number > 0 {
        assert_eq!(1, other.number_of_points_by_return[&point.return_number]);
    }
    assert_eq!(
        header.vlrs().collect::<Vec<_>>(),
        other.vlrs().collect::<Vec<_>>()
    );
    assert_eq!(
        header.evlrs().collect::<Vec<_>>(),
        other.evlrs().collect::<Vec<_>>()
    );
}

macro_rules! roundtrip_point {
    ($name:ident, $modify_point:expr) => {
        roundtrip_point!($name, $modify_point, 0);
    };
    ($name:ident, $modify_point:expr, $min_version_minor:expr) => {
        roundtrip_point!($name, $modify_point, $min_version_minor, |_f| {});
    };
    ($name:ident, $modify_point:expr, $min_version_minor:expr, $modify_point_format:expr) => {
        #[test]
        fn $name() {
            use las::{Header, Point, Version};
            use las::point::Format;

            let version = super::version();
            let should_succeed = version >= Version::new(1, $min_version_minor);
            let mut point_format = Format::default();
            $modify_point_format(&mut point_format);
            let mut point = Point::default();
            $modify_point(&mut point);
            let mut header = Header::default();
            header.version = version;
            header.point_format = point_format;
            ::roundtrip(&header, &point, should_succeed);
        }
    };
}

macro_rules! roundtrip_header {
    ($name:ident, $modify_header:expr) => {
        roundtrip_header!($name, $modify_header, 0);
    };
    ($name:ident, $modify_header:expr, $min_version_minor:expr) => {
        #[test]
        fn $name() {
            use las::{Version, Header, Point};

            let version = super::version();
            let should_succeed = version >= Version::new(1, $min_version_minor);
            let mut header = Header::default();
            header.version = version;
            $modify_header(&mut header);
            ::roundtrip(&header, &Point::default(), should_succeed);
        }
    };
}

macro_rules! version { ($name:ident, $major:expr, $minor:expr) => {
mod $name {
    use las::Version;

    fn version() -> Version {
        Version::new($major, $minor)
    }

    mod point {
        use las::Color;
        use las::point::{Classification, ScanDirection};

        roundtrip_point!(xyz, |p: &mut Point| { p.x = 1.; p.y = 2.; p.z = 3.; });
        roundtrip_point!(intensity, |p: &mut Point| p.intensity = 42);
        roundtrip_point!(return_number, |p: &mut Point| p.return_number = 2);
        roundtrip_point!(number_of_returns, |p: &mut Point| p.number_of_returns = 2);
        roundtrip_point!(scan_direction, |p: &mut Point| p.scan_direction = ScanDirection::LeftToRight);
        roundtrip_point!(is_edge_of_flight_line, |p: &mut Point| p.is_edge_of_flight_line = true);
        roundtrip_point!(classification, |p: &mut Point| p.classification = Classification::Ground);
        roundtrip_point!(is_synthetic, |p: &mut Point| p.is_synthetic = true);
        roundtrip_point!(is_key_point, |p: &mut Point| p.is_key_point = true);
        roundtrip_point!(is_withheld, |p: &mut Point| p.is_withheld = true);
        roundtrip_point!(is_overlap, |p: &mut Point| {
            p.classification = Classification::Unclassified;
            p.is_overlap = true;
        });
        roundtrip_point!(scanner_channel, |p: &mut Point| {
            p.scanner_channel = 1;
            p.gps_time = Some(42.);
        }, 4, |f: &mut Format| f.extend());
        roundtrip_point!(scan_angle_rank, |p: &mut Point| p.scan_angle = 3.);
        roundtrip_point!(user_data, |p: &mut Point| p.user_data = 42);
        roundtrip_point!(point_source_id, |p: &mut Point| p.point_source_id = 42);
        roundtrip_point!(gps_time, |p: &mut Point| p.gps_time = Some(42.), 0, |f: &mut Format| f.has_gps_time = true);
        roundtrip_point!(color, |p: &mut Point| p.color = Some(Color { red: 1, green: 2, blue: 3 }), 2, |f: &mut Format| f.has_color = true);
        // TODO waveform
        roundtrip_point!(nir, |p: &mut Point| {
            p.color = Some(Color { red: 1, green: 2, blue: 3});
            p.nir = Some(42);
            p.gps_time = Some(42.);
        }, 4, |f: &mut Format| {
            f.extend();
            f.has_color = true;
            f.has_nir = true;
        });
        roundtrip_point!(extra_bytes, |p: &mut Point| p.extra_bytes = vec![42], 0, |f: &mut Format| f.extra_bytes = 1);
    }

    mod header {
        use chrono::{Utc, TimeZone};
        use las::GpsTimeType;
        use uuid::Uuid;

        roundtrip_header!(file_source_id, |h: &mut Header| h.file_source_id = 42, 1);
        roundtrip_header!(gps_time_type, |h: &mut Header| h.gps_time_type = GpsTimeType::Standard, 2);
        roundtrip_header!(has_synthetic_return_numbers, |h: &mut Header| h.has_synthetic_return_numbers = true, 3);
        roundtrip_header!(guid, |h: &mut Header| h.guid = Uuid::from_bytes(&[42; 16]).unwrap());
        roundtrip_header!(system_identifier, |h: &mut Header| h.system_identifier = "roundtrip test".to_string());
        roundtrip_header!(generating_software, |h: &mut Header| h.generating_software = "roundtrip test".to_string());
        roundtrip_header!(date, |h: &mut Header| h.date = Some(Utc.ymd(2017, 10, 30)));
        roundtrip_header!(padding, |h: &mut Header| h.padding = b"You probably shouldn't do this".to_vec());
        roundtrip_header!(vlr_padding, |h: &mut Header| h.vlr_padding = b"You probably shouldn't do this either".to_vec());
        roundtrip_header!(transforms, |h: &mut Header| {
            use las::{Transform, Vector};

            let transform = Transform { scale: 0.1, offset: -1. };
            h.transforms = Vector {
                x: transform,
                y: transform,
                z: transform,
            };
        });
        roundtrip_header!(vlrs, |h: &mut Header| h.push_vlr(Default::default()));
        roundtrip_header!(evlrs, |h: &mut Header| {
            use std::u16;
            use las::Vlr;

            let mut vlr = Vlr::default();
            vlr.is_extended = true;
            vlr.data = vec![42; u16::MAX as usize + 1];
            h.push_vlr(vlr);
        }, 4);
        roundtrip_header!(end_of_points_padding, |h: &mut Header| h.end_of_points_padding = vec![42]);
    }
}
}}

version!(las_1_0, 1, 0);
version!(las_1_1, 1, 1);
version!(las_1_2, 1, 2);
version!(las_1_3, 1, 3);
version!(las_1_4, 1, 4);
<|endoftext|>


<commit_before>#![feature(slice_internals)]
use std::fs::File;
use std::io::{BufReader, Read};

use core::slice::memchr;

fn main() {
    let filename = "chry_multiplied.fa";

    let file = File::open(filename).unwrap();
    let mut reader = BufReader::new(file);

    let mut at = 0;
    let mut gc = 0;

    let mut buffer = [0; 32768];
    let mut inheader = true;

    'reader: loop {
        if reader.read(&mut buffer).expect("Read error") == 0 {
            break;
        }

        let mut stop = 0;
        loop {
            let offset = if inheader {
                match memchr::memchr(b'\n', &buffer[stop..buffer.len()]) {
                    Some(pos) => {
                        inheader = false;
                        stop + pos + 1
                    }
                    None => continue 'reader,
                }
            } else {
                0
            };

            stop = match memchr::memchr(b'>', &buffer[offset..buffer.len()]) {
                Some(pos) => offset + pos,
                None => buffer.len(),
            };
            // Count records
            for c in buffer[offset..stop].iter() {
                gc += (*c & 10 == 2) as u32;
                at += (*c & 10 == 0) as u32;
            }

            if stop == buffer.len() {
                break;
            }
            inheader = true;
        }
    }

    let gc_ratio: f64 = gc as f64 / (gc as f64 + at as f64);
    println!("GC ratio (gc/(gc+at)): {}", gc_ratio);
}
<commit_msg>vectorized io, no faster<commit_after>#![feature(slice_internals)]
use std::io::{BufReader, Read};
use std::{fs::File, io::IoSliceMut};

use core::slice::memchr;

const BUFSIZE: usize = 32768;
fn main() {
    let filename = "chry_multiplied.fa";

    let file = File::open(filename).unwrap();
    let mut reader = BufReader::new(file);

    let mut at = 0;
    let mut gc = 0;

    let mut buf1 = [0u8; BUFSIZE];
    let mut buf2 = [0u8; BUFSIZE];
    let mut buf3 = [0u8; BUFSIZE];
    let mut buf4 = [0u8; BUFSIZE];
    let mut buf5 = [0u8; BUFSIZE];
    let mut buf6 = [0u8; BUFSIZE];
    let mut buf7 = [0u8; BUFSIZE];
    let mut buf8 = [0u8; BUFSIZE];
    let mut bufs = [
        IoSliceMut::new(&mut buf1),
        IoSliceMut::new(&mut buf2),
        IoSliceMut::new(&mut buf3),
        IoSliceMut::new(&mut buf4),
        IoSliceMut::new(&mut buf5),
        IoSliceMut::new(&mut buf6),
        IoSliceMut::new(&mut buf7),
        IoSliceMut::new(&mut buf8),
    ];
    let mut inheader = true;

    'reader: loop {
        if reader.read_vectored(&mut bufs).expect("Read error") == 0 {
            break;
        }

        for buffer in bufs.iter() {
            let mut stop = 0;
            loop {
                let offset = if inheader {
                    match memchr::memchr(b'\n', &buffer[stop..buffer.len()]) {
                        Some(pos) => {
                            inheader = false;
                            stop + pos + 1
                        }
                        None => continue 'reader,
                    }
                } else {
                    0
                };

                stop = match memchr::memchr(b'>', &buffer[offset..buffer.len()]) {
                    Some(pos) => offset + pos,
                    None => buffer.len(),
                };
                // Count records
                for c in buffer[offset..stop].iter() {
                    gc += (*c & 10 == 2) as u32;
                    at += (*c & 10 == 0) as u32;
                }

                if stop == buffer.len() {
                    break;
                }
                inheader = true;
            }
        }
    }

    let gc_ratio: f64 = gc as f64 / (gc as f64 + at as f64);
    println!("GC ratio (gc/(gc+at)): {}", gc_ratio);
}
<|endoftext|>


<commit_before>#![crate_type="dylib"]
#![cfg_attr(not(feature = "with-syntex"), feature(plugin, plugin_registrar, quote, rustc_private))]

extern crate syn;
use syn::aster;
#[macro_use]
extern crate quote;

#[cfg(not(feature = "with-syntex"))]
extern crate syntax;
//extern crate rustc;
#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;

#[cfg(feature = "with-syntex")]
extern crate syntex;
#[cfg(feature = "with-syntex")]
extern crate syntex_syntax as syntax;
#[cfg(feature = "with-syntex")]
use std::path::Path;

//mod resource;
mod util;
mod tuple;
mod map;

#[cfg(feature = "with-syntex")]
pub fn expand<S, D>(src: S, dst: D) -> Result<(), syntex::Error>
    where S: AsRef<Path>, D: AsRef<Path>
{
    let mut reg = syntex::Registry::new();

    reg.add_decorator("derive_NifMap", map_transcoder_decorator_shim);
    reg.add_decorator("derive_NifTuple", tuple_transcoder_decorator_shim);

    //reg.add_decorator("NifResource", resource::resource_struct_def_decorator);
    //use syntax::ext::base::{TTMacroExpander, ExtCtxt, MacResult};
    //use syntax::tokenstream::TokenTree;;
    //use syntax::codemap::Span;

    //struct ResourceStructInitMacroHack {}
    //impl TTMacroExpander for ResourceStructInitMacroHack {
    //    fn expand<'cx>(&self,
    //                   ecx: &'cx mut ExtCtxt,
    //                   span: Span,
    //                   token_tree: &[TokenTree])
    //        -> Box<MacResult+'cx> {
    //            resource::resource_struct_init_macro(ecx, span, token_tree)
    //        }
    //}
    //reg.add_macro("resource_struct_init", ResourceStructInitMacroHack {});

    reg.expand("", src.as_ref(), dst.as_ref())
}

macro_rules! shim_syn_decorator {
    ($decorator_name:ident, $shim_name:ident, $wrapped:path) => {
        fn $shim_name(
            cx: &mut syntax::ext::base::ExtCtxt,
            span: syntax::codemap::Span,
            meta_item: &syntax::ast::MetaItem,
            annotatable: &syntax::ext::base::Annotatable,
            push: &mut FnMut(syntax::ext::base::Annotatable),
        ) {
            let item = match *annotatable {
                syntax::ext::base::Annotatable::Item(ref item) => item,
                _ => {
                    cx.span_err(
                        meta_item.span,
                        "decorated item must be either struct or enum"
                        );
                    return;
                },
            };

            let s = syntax::print::pprust::item_to_string(item);
            let syn_item = syn::parse_macro_input(&s).unwrap();

            let expanded = match $wrapped(&syn_item) {
                Ok(expanded) => expanded.to_string(),
                Err(msg) => {
                    cx.span_err(span, &msg);
                    return;
                }
            };

            use syntax::parse;
            let name = stringify!($decorator_name).to_string();
            let cfg = Vec::new();
            let sess = parse::ParseSess::new();
            let impl_item = parse::parse_item_from_source_str(name, expanded, cfg, &sess);
            push(syntax::ext::base::Annotatable::Item(impl_item.unwrap().unwrap()));
        }
    };
}

shim_syn_decorator!(NifTuple, tuple_transcoder_decorator_shim, tuple::transcoder_decorator);
shim_syn_decorator!(NifMap, map_transcoder_decorator_shim, map::transcoder_decorator);

#[cfg(not(feature = "with-syntex"))]
#[plugin_registrar]
pub fn register(reg: &mut rustc_plugin::Registry) {
    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_NifMap"),
        syntax::ext::base::MultiDecorator(Box::new(map_transcoder_decorator_shim)));
    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_NifTuple"),
        syntax::ext::base::MultiDecorator(Box::new(tuple_transcoder_decorator_shim)));

    //reg.register_syntax_extension(
    //    builder.name("NifResource"),
    //    syntax::ext::base::MultiDecorator(Box::new(resource::resource_struct_def_decorator)));
    //reg.register_macro("resource_struct_init", resource::resource_struct_init_macro);
}
<commit_msg>fix compilation on latest syntex<commit_after>#![crate_type="dylib"]
#![cfg_attr(not(feature = "with-syntex"), feature(plugin, plugin_registrar, quote, rustc_private))]

extern crate syn;
use syn::aster;
#[macro_use]
extern crate quote;

#[cfg(not(feature = "with-syntex"))]
extern crate syntax;
//extern crate rustc;
#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;

#[cfg(feature = "with-syntex")]
extern crate syntex;
#[cfg(feature = "with-syntex")]
extern crate syntex_syntax as syntax;
#[cfg(feature = "with-syntex")]
use std::path::Path;

//mod resource;
mod util;
mod tuple;
mod map;

#[cfg(feature = "with-syntex")]
pub fn expand<S, D>(src: S, dst: D) -> Result<(), syntex::Error>
    where S: AsRef<Path>, D: AsRef<Path>
{
    let mut reg = syntex::Registry::new();

    reg.add_decorator("derive_NifMap", map_transcoder_decorator_shim);
    reg.add_decorator("derive_NifTuple", tuple_transcoder_decorator_shim);

    //reg.add_decorator("NifResource", resource::resource_struct_def_decorator);
    //use syntax::ext::base::{TTMacroExpander, ExtCtxt, MacResult};
    //use syntax::tokenstream::TokenTree;;
    //use syntax::codemap::Span;

    //struct ResourceStructInitMacroHack {}
    //impl TTMacroExpander for ResourceStructInitMacroHack {
    //    fn expand<'cx>(&self,
    //                   ecx: &'cx mut ExtCtxt,
    //                   span: Span,
    //                   token_tree: &[TokenTree])
    //        -> Box<MacResult+'cx> {
    //            resource::resource_struct_init_macro(ecx, span, token_tree)
    //        }
    //}
    //reg.add_macro("resource_struct_init", ResourceStructInitMacroHack {});

    reg.expand("", src.as_ref(), dst.as_ref())
}

macro_rules! shim_syn_decorator {
    ($decorator_name:ident, $shim_name:ident, $wrapped:path) => {
        fn $shim_name(
            cx: &mut syntax::ext::base::ExtCtxt,
            span: syntax::codemap::Span,
            meta_item: &syntax::ast::MetaItem,
            annotatable: &syntax::ext::base::Annotatable,
            push: &mut FnMut(syntax::ext::base::Annotatable),
        ) {
            let item = match *annotatable {
                syntax::ext::base::Annotatable::Item(ref item) => item,
                _ => {
                    cx.span_err(
                        meta_item.span,
                        "decorated item must be either struct or enum"
                        );
                    return;
                },
            };

            let s = syntax::print::pprust::item_to_string(item);
            let syn_item = syn::parse_macro_input(&s).unwrap();

            let expanded = match $wrapped(&syn_item) {
                Ok(expanded) => expanded.to_string(),
                Err(msg) => {
                    cx.span_err(span, &msg);
                    return;
                }
            };

            use syntax::parse;
            let name = stringify!($decorator_name).to_string();
            let sess = parse::ParseSess::new();
            let impl_item = parse::parse_item_from_source_str(name, expanded, &sess);
            push(syntax::ext::base::Annotatable::Item(impl_item.unwrap().unwrap()));
        }
    };
}

shim_syn_decorator!(NifTuple, tuple_transcoder_decorator_shim, tuple::transcoder_decorator);
shim_syn_decorator!(NifMap, map_transcoder_decorator_shim, map::transcoder_decorator);

#[cfg(not(feature = "with-syntex"))]
#[plugin_registrar]
pub fn register(reg: &mut rustc_plugin::Registry) {
    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_NifMap"),
        syntax::ext::base::MultiDecorator(Box::new(map_transcoder_decorator_shim)));
    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_NifTuple"),
        syntax::ext::base::MultiDecorator(Box::new(tuple_transcoder_decorator_shim)));

    //reg.register_syntax_extension(
    //    builder.name("NifResource"),
    //    syntax::ext::base::MultiDecorator(Box::new(resource::resource_struct_def_decorator)));
    //reg.register_macro("resource_struct_init", resource::resource_struct_init_macro);
}
<|endoftext|>