<commit_before>//! The `pixelshader` module allows user-defined fragment shaders to be used
//! with ggez for cool and spooky effects. See the `shader` example for a
//! taste...

use gfx::*;
use gfx::handle::*;
use gfx::pso::*;
use gfx::pso::buffer::*;
use gfx::shade::*;
use gfx::state::*;
use gfx::traits::{FactoryExt, Pod};
use std::cell::RefCell;
use std::fmt;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::path::Path;
use std::rc::Rc;

use Context;
use error::*;
use graphics;

/// A type for empty shader data for shaders that do not require any aditional
/// data to be sent to the GPU
#[derive(Clone, Copy, Debug)]
pub struct EmptyConst;

impl<F> Structure<F> for EmptyConst {
    fn query(_name: &str) -> Option<Element<F>> {
        None
    }
}

unsafe impl Pod for EmptyConst {}

/// An ID used by the `GraphicsContext` to uniquely identify a pixel shader
pub type PixelShaderId = usize;

/// A `PixelShader` reprensents a handle user-defined shader that can be used
/// with a ggez graphics context that is generic over `gfx::Resources`
///
/// As an end-user you shouldn't ever have to touch this and should use
/// `PixelShader` instead.
#[derive(Clone)]
pub struct PixelShaderGeneric<Spec: graphics::BackendSpec, C: Structure<ConstFormat>> {
    id: PixelShaderId,
    buffer: Rc<Buffer<Spec::Resources, C>>,
}

/// A `PixelShader` reprensents a handle user-defined shader that can be used
/// with a ggez graphics context
pub type PixelShader<C> = PixelShaderGeneric<graphics::GlBackendSpec, C>;

pub(crate) fn create_shader<C, S, Spec>
    (source: &[u8],
     consts: C,
     name: S,
     encoder: &mut Encoder<Spec::Resources, Spec::CommandBuffer>,
     factory: &mut Spec::Factory,
     multisample_samples: u8)
     -> GameResult<(PixelShaderGeneric<Spec, C>, Box<PixelShaderDraw<Spec>>)>
    where C: 'static + Pod + Structure<ConstFormat> + Clone + Copy,
          S: Into<String>,
          Spec: graphics::BackendSpec + 'static
{
    let buffer = factory.create_constant_buffer(1);
    let buffer = Rc::new(buffer);

    encoder.update_buffer(&buffer, &[consts], 0)?;

    let pso = {
        let init = ConstInit::<C>(graphics::pipe::new(), name.into(), PhantomData);
        let set = factory
            .create_shader_set(include_bytes!("shader/basic_150.glslv"), &source)?;
        let sample = if multisample_samples > 1 {
            Some(MultiSample)
        } else {
            None
        };
        let rasterizer = Rasterizer {
            front_face: FrontFace::CounterClockwise,
            cull_face: CullFace::Nothing,
            method: RasterMethod::Fill,
            offset: None,
            samples: sample,
        };

        factory
            .create_pipeline_state(&set, Primitive::TriangleList, rasterizer, init)?
    };

    let program = PixelShaderProgram {
        buffer: buffer.clone(),
        pso,
    };
    let draw: Box<PixelShaderDraw<Spec>> = Box::new(program);

    let id = 0;
    let shader = PixelShaderGeneric { id, buffer };

    Ok((shader, draw))
}

impl<C> PixelShader<C>
    where C: 'static + Pod + Structure<ConstFormat> + Clone + Copy
{
    /// Create a new `PixelShader` given a gfx pipeline object
    pub fn new<P: AsRef<Path>, S: Into<String>>(ctx: &mut Context,
                                                path: P,
                                                consts: C,
                                                name: S)
                                                -> GameResult<PixelShader<C>> {
        let source = {
            let mut buf = Vec::new();
            let mut reader = ctx.filesystem.open(path)?;
            reader.read_to_end(&mut buf)?;
            buf
        };

        let (mut shader, draw) = create_shader(&source,
                                               consts,
                                               name,
                                               &mut ctx.gfx_context.encoder,
                                               &mut *ctx.gfx_context.factory,
                                               ctx.gfx_context.multisample_samples)?;
        shader.id = ctx.gfx_context.shaders.len();
        ctx.gfx_context.shaders.push(draw);

        Ok(shader)
    }

    /// Send data to the GPU for use with the `PixelShader`
    pub fn send(&self, ctx: &mut Context, consts: C) -> GameResult<()> {
        ctx.gfx_context
            .encoder
            .update_buffer(&self.buffer, &[consts], 0)?;
        Ok(())
    }

    /// Gets the shader ID for the `PixelShader` which is used by the
    /// `GraphicsContext` for identifying shaders in its cache
    pub fn shader_id(&self) -> PixelShaderId {
        self.id
    }
}

impl<Spec, C> fmt::Debug for PixelShaderGeneric<Spec, C>
    where Spec: graphics::BackendSpec,
          C: Structure<ConstFormat>
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "<PixelShader[{}]: {:p}>", self.id, self)
    }
}

struct PixelShaderProgram<Spec: graphics::BackendSpec, C: Structure<ConstFormat>> {
    buffer: Rc<Buffer<Spec::Resources, C>>,
    pso: PipelineState<Spec::Resources, ConstMeta<C>>,
}

impl<Spec, C> fmt::Debug for PixelShaderProgram<Spec, C>
    where Spec: graphics::BackendSpec,
          C: Structure<ConstFormat>
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "<PixelShaderProgram: {:p}>", self)
    }
}

/// A trait that is used to create trait objects to abstract away the
/// Structure<ConstFormat> type of the constant data for drawing
pub trait PixelShaderDraw<Spec: graphics::BackendSpec>: fmt::Debug {
    /// Draw with the current PixelShader
    fn draw(&self,
            &mut Encoder<Spec::Resources, Spec::CommandBuffer>,
            &Slice<Spec::Resources>,
            &graphics::pipe::Data<Spec::Resources>);
}

impl<Spec, C> PixelShaderDraw<Spec> for PixelShaderProgram<Spec, C>
    where Spec: graphics::BackendSpec,
          C: Structure<ConstFormat>
{
    fn draw(&self,
            encoder: &mut Encoder<Spec::Resources, Spec::CommandBuffer>,
            slice: &Slice<Spec::Resources>,
            data: &graphics::pipe::Data<Spec::Resources>) {
        encoder.draw(slice, &self.pso, &ConstData(data, &self.buffer));
    }
}

/// A lock for RAII shader regions. The shader automatically gets cleared once
/// the lock goes out of scope
#[derive(Debug)]
pub struct PixelShaderLock {
    cell: Rc<RefCell<Option<PixelShaderId>>>,
    previous_shader: Option<PixelShaderId>,
}

impl Drop for PixelShaderLock {
    fn drop(&mut self) {
        *self.cell.borrow_mut() = self.previous_shader;
    }
}

/// Use a shader until the returned lock goes out of scope
pub fn use_shader<C>(ctx: &mut Context, ps: &PixelShader<C>) -> PixelShaderLock
    where C: Structure<ConstFormat>
{
    let cell = ctx.gfx_context.current_shader.clone();
    let previous_shader = (*cell.borrow()).clone();
    set_shader(ctx, ps);
    PixelShaderLock {
        cell,
        previous_shader,
    }
}

/// Set the current pixel shader for the Context to render with
pub fn set_shader<C>(ctx: &mut Context, ps: &PixelShader<C>)
    where C: Structure<ConstFormat>
{
    *ctx.gfx_context.current_shader.borrow_mut() = Some(ps.id);
}

/// Clears the the current pixel shader for the Context making use the default
pub fn clear_shader(ctx: &mut Context) {
    *ctx.gfx_context.current_shader.borrow_mut() = None;
}

#[derive(Debug)]
struct ConstMeta<C: Structure<ConstFormat>>(graphics::pipe::Meta, ConstantBuffer<C>);

#[derive(Debug)]
struct ConstData<'a, R: Resources, C: 'a>(&'a graphics::pipe::Data<R>, &'a Buffer<R, C>);

impl<'a, R, C> PipelineData<R> for ConstData<'a, R, C>
    where R: Resources,
          C: Structure<ConstFormat>
{
    type Meta = ConstMeta<C>;

    fn bake_to(&self,
               out: &mut RawDataSet<R>,
               meta: &Self::Meta,
               man: &mut Manager<R>,
               access: &mut AccessInfo<R>) {
        self.0.bake_to(out, &meta.0, man, access);
        meta.1.bind_to(out, &self.1, man, access);
    }
}

#[derive(Debug)]
struct ConstInit<'a, C>(graphics::pipe::Init<'a>, String, PhantomData<C>);

impl<'a, C> PipelineInit for ConstInit<'a, C>
    where C: Structure<ConstFormat>
{
    type Meta = ConstMeta<C>;

    fn link_to<'s>(&self,
                   desc: &mut Descriptor,
                   info: &'s ProgramInfo)
                   -> Result<Self::Meta, InitError<&'s str>> {
        let mut meta1 = ConstantBuffer::<C>::new();

        let mut index = None;
        for (i, cb) in info.constant_buffers.iter().enumerate() {
            match meta1.link_constant_buffer(cb, &self.1.as_str()) {
                Some(Ok(d)) => {
                    assert!(meta1.is_active());
                    desc.constant_buffers[cb.slot as usize] = Some(d);
                    index = Some(i);
                    break;
                }
                Some(Err(e)) => return Err(InitError::ConstantBuffer(&cb.name, Some(e))),
                None => (),
            }
        }

        if let Some(index) = index {
            // create a local clone of the program info so that we can remove
            // the var we found from the `constant_buffer`
            let mut program_info = info.clone();
            program_info.constant_buffers.remove(index);

            let meta0 = match self.0.link_to(desc, &program_info) {
                Ok(m) => m,
                Err(e) => {
                    // unfortunately... the error lifetime is bound to the
                    // lifetime of our cloned program info which is bad since it
                    // will go out of scope at the end of the function, so lets
                    // convert the error to one that is bound to the lifetime of
                    // the program info that was passed in!
                    macro_rules! fixlifetimes {
                        ($e:ident {
                            $( $ty:path => $a:ident, )*
                        }) => {{
                            match $e {
                                $( $ty(name, _) => {
                                    let var = info.$a.iter().find(|v| v.name == name).unwrap();
                                    // We can do better with the error data...
                                    return Err($ty(&var.name, None));
                                } )*
                            }
                        }}
                    }
                    fixlifetimes!(e {
                        InitError::VertexImport => vertex_attributes,
                        InitError::ConstantBuffer => constant_buffers,
                        InitError::GlobalConstant => globals,
                        InitError::ResourceView => textures,
                        InitError::UnorderedView => unordereds,
                        InitError::Sampler => samplers,
                        InitError::PixelExport => outputs,
                    });
                }
            };

            Ok(ConstMeta(meta0, meta1))
        } else {
            Ok(ConstMeta(self.0.link_to(desc, &info)?, meta1))
        }
    }
}
<commit_msg>added PixelShader::from_u8<commit_after>//! The `pixelshader` module allows user-defined fragment shaders to be used
//! with ggez for cool and spooky effects. See the `shader` example for a
//! taste...

use gfx::*;
use gfx::handle::*;
use gfx::pso::*;
use gfx::pso::buffer::*;
use gfx::shade::*;
use gfx::state::*;
use gfx::traits::{FactoryExt, Pod};
use std::cell::RefCell;
use std::fmt;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::path::Path;
use std::rc::Rc;

use Context;
use error::*;
use graphics;

/// A type for empty shader data for shaders that do not require any aditional
/// data to be sent to the GPU
#[derive(Clone, Copy, Debug)]
pub struct EmptyConst;

impl<F> Structure<F> for EmptyConst {
    fn query(_name: &str) -> Option<Element<F>> {
        None
    }
}

unsafe impl Pod for EmptyConst {}

/// An ID used by the `GraphicsContext` to uniquely identify a pixel shader
pub type PixelShaderId = usize;

/// A `PixelShader` reprensents a handle user-defined shader that can be used
/// with a ggez graphics context that is generic over `gfx::Resources`
///
/// As an end-user you shouldn't ever have to touch this and should use
/// `PixelShader` instead.
#[derive(Clone)]
pub struct PixelShaderGeneric<Spec: graphics::BackendSpec, C: Structure<ConstFormat>> {
    id: PixelShaderId,
    buffer: Rc<Buffer<Spec::Resources, C>>,
}

/// A `PixelShader` reprensents a handle user-defined shader that can be used
/// with a ggez graphics context
pub type PixelShader<C> = PixelShaderGeneric<graphics::GlBackendSpec, C>;

pub(crate) fn create_shader<C, S, Spec>
    (source: &[u8],
     consts: C,
     name: S,
     encoder: &mut Encoder<Spec::Resources, Spec::CommandBuffer>,
     factory: &mut Spec::Factory,
     multisample_samples: u8)
     -> GameResult<(PixelShaderGeneric<Spec, C>, Box<PixelShaderDraw<Spec>>)>
    where C: 'static + Pod + Structure<ConstFormat> + Clone + Copy,
          S: Into<String>,
          Spec: graphics::BackendSpec + 'static
{
    let buffer = factory.create_constant_buffer(1);
    let buffer = Rc::new(buffer);

    encoder.update_buffer(&buffer, &[consts], 0)?;

    let pso = {
        let init = ConstInit::<C>(graphics::pipe::new(), name.into(), PhantomData);
        let set = factory
            .create_shader_set(include_bytes!("shader/basic_150.glslv"), &source)?;
        let sample = if multisample_samples > 1 {
            Some(MultiSample)
        } else {
            None
        };
        let rasterizer = Rasterizer {
            front_face: FrontFace::CounterClockwise,
            cull_face: CullFace::Nothing,
            method: RasterMethod::Fill,
            offset: None,
            samples: sample,
        };

        factory
            .create_pipeline_state(&set, Primitive::TriangleList, rasterizer, init)?
    };

    let program = PixelShaderProgram {
        buffer: buffer.clone(),
        pso,
    };
    let draw: Box<PixelShaderDraw<Spec>> = Box::new(program);

    let id = 0;
    let shader = PixelShaderGeneric { id, buffer };

    Ok((shader, draw))
}

impl<C> PixelShader<C>
    where C: 'static + Pod + Structure<ConstFormat> + Clone + Copy
{
    /// Create a new `PixelShader` given a gfx pipeline object
    pub fn new<P: AsRef<Path>, S: Into<String>>(ctx: &mut Context,
                                                path: P,
                                                consts: C,
                                                name: S)
                                                -> GameResult<PixelShader<C>> {
        let source = {
            let mut buf = Vec::new();
            let mut reader = ctx.filesystem.open(path)?;
            reader.read_to_end(&mut buf)?;
            buf
        };
        PixelShader::from_u8(ctx, &source, consts, name)
    }

    /// Create a new `PixelShader` directly from source given a gfx pipeline
    /// object
    pub fn from_u8<S: Into<String>>(ctx: &mut Context,
                                    source: &[u8],
                                    consts: C,
                                    name: S)
                                    -> GameResult<PixelShader<C>> {
        let (mut shader, draw) = create_shader(&source,
                                               consts,
                                               name,
                                               &mut ctx.gfx_context.encoder,
                                               &mut *ctx.gfx_context.factory,
                                               ctx.gfx_context.multisample_samples)?;
        shader.id = ctx.gfx_context.shaders.len();
        ctx.gfx_context.shaders.push(draw);

        Ok(shader)
    }

    /// Send data to the GPU for use with the `PixelShader`
    pub fn send(&self, ctx: &mut Context, consts: C) -> GameResult<()> {
        ctx.gfx_context
            .encoder
            .update_buffer(&self.buffer, &[consts], 0)?;
        Ok(())
    }

    /// Gets the shader ID for the `PixelShader` which is used by the
    /// `GraphicsContext` for identifying shaders in its cache
    pub fn shader_id(&self) -> PixelShaderId {
        self.id
    }
}

impl<Spec, C> fmt::Debug for PixelShaderGeneric<Spec, C>
    where Spec: graphics::BackendSpec,
          C: Structure<ConstFormat>
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "<PixelShader[{}]: {:p}>", self.id, self)
    }
}

struct PixelShaderProgram<Spec: graphics::BackendSpec, C: Structure<ConstFormat>> {
    buffer: Rc<Buffer<Spec::Resources, C>>,
    pso: PipelineState<Spec::Resources, ConstMeta<C>>,
}

impl<Spec, C> fmt::Debug for PixelShaderProgram<Spec, C>
    where Spec: graphics::BackendSpec,
          C: Structure<ConstFormat>
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "<PixelShaderProgram: {:p}>", self)
    }
}

/// A trait that is used to create trait objects to abstract away the
/// Structure<ConstFormat> type of the constant data for drawing
pub trait PixelShaderDraw<Spec: graphics::BackendSpec>: fmt::Debug {
    /// Draw with the current PixelShader
    fn draw(&self,
            &mut Encoder<Spec::Resources, Spec::CommandBuffer>,
            &Slice<Spec::Resources>,
            &graphics::pipe::Data<Spec::Resources>);
}

impl<Spec, C> PixelShaderDraw<Spec> for PixelShaderProgram<Spec, C>
    where Spec: graphics::BackendSpec,
          C: Structure<ConstFormat>
{
    fn draw(&self,
            encoder: &mut Encoder<Spec::Resources, Spec::CommandBuffer>,
            slice: &Slice<Spec::Resources>,
            data: &graphics::pipe::Data<Spec::Resources>) {
        encoder.draw(slice, &self.pso, &ConstData(data, &self.buffer));
    }
}

/// A lock for RAII shader regions. The shader automatically gets cleared once
/// the lock goes out of scope
#[derive(Debug)]
pub struct PixelShaderLock {
    cell: Rc<RefCell<Option<PixelShaderId>>>,
    previous_shader: Option<PixelShaderId>,
}

impl Drop for PixelShaderLock {
    fn drop(&mut self) {
        *self.cell.borrow_mut() = self.previous_shader;
    }
}

/// Use a shader until the returned lock goes out of scope
pub fn use_shader<C>(ctx: &mut Context, ps: &PixelShader<C>) -> PixelShaderLock
    where C: Structure<ConstFormat>
{
    let cell = ctx.gfx_context.current_shader.clone();
    let previous_shader = (*cell.borrow()).clone();
    set_shader(ctx, ps);
    PixelShaderLock {
        cell,
        previous_shader,
    }
}

/// Set the current pixel shader for the Context to render with
pub fn set_shader<C>(ctx: &mut Context, ps: &PixelShader<C>)
    where C: Structure<ConstFormat>
{
    *ctx.gfx_context.current_shader.borrow_mut() = Some(ps.id);
}

/// Clears the the current pixel shader for the Context making use the default
pub fn clear_shader(ctx: &mut Context) {
    *ctx.gfx_context.current_shader.borrow_mut() = None;
}

#[derive(Debug)]
struct ConstMeta<C: Structure<ConstFormat>>(graphics::pipe::Meta, ConstantBuffer<C>);

#[derive(Debug)]
struct ConstData<'a, R: Resources, C: 'a>(&'a graphics::pipe::Data<R>, &'a Buffer<R, C>);

impl<'a, R, C> PipelineData<R> for ConstData<'a, R, C>
    where R: Resources,
          C: Structure<ConstFormat>
{
    type Meta = ConstMeta<C>;

    fn bake_to(&self,
               out: &mut RawDataSet<R>,
               meta: &Self::Meta,
               man: &mut Manager<R>,
               access: &mut AccessInfo<R>) {
        self.0.bake_to(out, &meta.0, man, access);
        meta.1.bind_to(out, &self.1, man, access);
    }
}

#[derive(Debug)]
struct ConstInit<'a, C>(graphics::pipe::Init<'a>, String, PhantomData<C>);

impl<'a, C> PipelineInit for ConstInit<'a, C>
    where C: Structure<ConstFormat>
{
    type Meta = ConstMeta<C>;

    fn link_to<'s>(&self,
                   desc: &mut Descriptor,
                   info: &'s ProgramInfo)
                   -> Result<Self::Meta, InitError<&'s str>> {
        let mut meta1 = ConstantBuffer::<C>::new();

        let mut index = None;
        for (i, cb) in info.constant_buffers.iter().enumerate() {
            match meta1.link_constant_buffer(cb, &self.1.as_str()) {
                Some(Ok(d)) => {
                    assert!(meta1.is_active());
                    desc.constant_buffers[cb.slot as usize] = Some(d);
                    index = Some(i);
                    break;
                }
                Some(Err(e)) => return Err(InitError::ConstantBuffer(&cb.name, Some(e))),
                None => (),
            }
        }

        if let Some(index) = index {
            // create a local clone of the program info so that we can remove
            // the var we found from the `constant_buffer`
            let mut program_info = info.clone();
            program_info.constant_buffers.remove(index);

            let meta0 = match self.0.link_to(desc, &program_info) {
                Ok(m) => m,
                Err(e) => {
                    // unfortunately... the error lifetime is bound to the
                    // lifetime of our cloned program info which is bad since it
                    // will go out of scope at the end of the function, so lets
                    // convert the error to one that is bound to the lifetime of
                    // the program info that was passed in!
                    macro_rules! fixlifetimes {
                        ($e:ident {
                            $( $ty:path => $a:ident, )*
                        }) => {{
                            match $e {
                                $( $ty(name, _) => {
                                    let var = info.$a.iter().find(|v| v.name == name).unwrap();
                                    // We can do better with the error data...
                                    return Err($ty(&var.name, None));
                                } )*
                            }
                        }}
                    }
                    fixlifetimes!(e {
                        InitError::VertexImport => vertex_attributes,
                        InitError::ConstantBuffer => constant_buffers,
                        InitError::GlobalConstant => globals,
                        InitError::ResourceView => textures,
                        InitError::UnorderedView => unordereds,
                        InitError::Sampler => samplers,
                        InitError::PixelExport => outputs,
                    });
                }
            };

            Ok(ConstMeta(meta0, meta1))
        } else {
            Ok(ConstMeta(self.0.link_to(desc, &info)?, meta1))
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{Read};
use byteorder::{LittleEndian as E, ReadBytesExt};
use gfx;
use gfx::format::I8Norm;
use render::ObjectVertex;

const SCALE: f32 = 1.0 / 256.0;

pub struct Mesh<R: gfx::Resources> {
    pub slice: gfx::Slice<R>,
    pub buffer: gfx::handle::Buffer<R, ObjectVertex>,
}

pub struct Wheel<R: gfx::Resources> {
    pub mesh: Option<Mesh<R>>,
    pub steering: u32,
    pub width: f32,
    pub radius: f32,
}

pub struct Debrie<R: gfx::Resources> {
    pub mesh: Mesh<R>,
}

pub struct Model<R: gfx::Resources> {
    pub body: Mesh<R>,
    pub color: [u32; 2],
    pub wheels: Vec<Wheel<R>>,
    pub debris: Vec<Debrie<R>>,
}

pub fn load_c3d<I, R, F>(source: &mut I, factory: &mut F) -> Mesh<R> where
    I: ReadBytesExt,
    R: gfx::Resources,
    F: gfx::traits::FactoryExt<R>,
{
    let version = source.read_u32::<E>().unwrap();
    assert_eq!(version, 8);
    let num_positions = source.read_u32::<E>().unwrap();
    let num_normals   = source.read_u32::<E>().unwrap();
    let num_polygons  = source.read_u32::<E>().unwrap();
    let _total_verts  = source.read_u32::<E>().unwrap();

    let coord_max  = [
        source.read_i32::<E>().unwrap() as f32 * SCALE,
        source.read_i32::<E>().unwrap() as f32 * SCALE,
        source.read_i32::<E>().unwrap() as f32 * SCALE,
    ];
    let coord_min  = [
        source.read_i32::<E>().unwrap() as f32 * SCALE,
        source.read_i32::<E>().unwrap() as f32 * SCALE,
        source.read_i32::<E>().unwrap() as f32 * SCALE,
    ];
    let _parent_off = [
        source.read_i32::<E>().unwrap(),
        source.read_i32::<E>().unwrap(),
        source.read_i32::<E>().unwrap(),
    ];
    let _max_radius = source.read_u32::<E>().unwrap();
    let _parent_rot = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
    source.by_ref().bytes().nth(8 * (1 + 3 + 9)-1).unwrap().unwrap();

    info!("\tReading {} positions...", num_positions);
    let mut positions = Vec::with_capacity(num_positions as usize);
    for _ in 0 .. num_positions {
        for _ in 0..3 {
            source.read_u32::<E>().unwrap(); //unknown
        }
        let mut pos = [0u8; 3];
        source.read(&mut pos).unwrap();
        let _sort_info = source.read_u32::<E>().unwrap();
        positions.push(pos);
    }

    info!("\tReading {} normals...", num_normals);
    let mut normals = Vec::with_capacity(num_normals as usize);
    for _ in 0 .. num_normals {
        let mut norm = [0u8; 4];
        source.read(&mut norm).unwrap();
        let _sort_info = source.read_u32::<E>().unwrap();
        normals.push(norm);
    }

    info!("\tReading {} polygons...", num_polygons);
    let mut vertices = Vec::with_capacity(num_polygons as usize * 3);
    for i in 0 .. num_polygons {
        let num_corners = source.read_u32::<E>().unwrap();
        assert_eq!(num_corners, 3);
        let _sort_info = source.read_u32::<E>().unwrap();
        let color = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
        let mut dummy = [0; 4];
        source.read(&mut dummy[..4]).unwrap(); //skip flat normal
        source.read(&mut dummy[..3]).unwrap(); //skip middle point
        //if i == 631 { source.read_u8().unwrap(); } //wtf?
        for _ in 0..3 {
            let pid = source.read_u32::<E>().unwrap();
            let nid = source.read_u32::<E>().unwrap();
            let v = (positions[pid as usize], normals[nid as usize], color);
            vertices.push(v);
        }
    }

    info!("\tCompacting...");
    vertices.sort();
    vertices.dedup();

    let gpu_verts: Vec<_> = vertices.into_iter().map(|(p, n, c)| ObjectVertex {
        pos: [
            coord_min[0] + p[0] as f32 * (coord_max[0] - coord_min[0]) / 255.0,
            coord_min[1] + p[1] as f32 * (coord_max[1] - coord_min[1]) / 255.0,
            coord_min[2] + p[2] as f32 * (coord_max[2] - coord_min[2]) / 255.0,
            1.0],
        color: c,
        normal: [I8Norm(n[0] as i8), I8Norm(n[1] as i8), I8Norm(n[2] as i8), I8Norm(n[3] as i8)],
    }).collect();
    info!("\tGot {} GPU vertices...", gpu_verts.len());
    let (vbuf, slice) = factory.create_vertex_buffer_with_slice(&gpu_verts, ());

    Mesh {
        slice: slice,
        buffer: vbuf,
    }
}

pub fn load_m3d<I, R, F>(source: &mut I, factory: &mut F) -> Model<R> where
    I: ReadBytesExt,
    R: gfx::Resources,
    F: gfx::traits::FactoryExt<R>,
{
    info!("\tReading the body...");
    let mut model = Model {
        body: load_c3d(source, factory),
        color: [0, 0],
        wheels: Vec::new(),
        debris: Vec::new(),
    };
    let _bounds = [
        source.read_u32::<E>().unwrap(),
        source.read_u32::<E>().unwrap(),
        source.read_u32::<E>().unwrap(),
    ];
    let _max_radius = source.read_u32::<E>().unwrap();
    let num_wheels = source.read_u32::<E>().unwrap();
    let num_debris = source.read_u32::<E>().unwrap();
    model.color = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
    model.wheels.reserve_exact(num_wheels as usize);
    model.debris.reserve_exact(num_debris as usize);

    info!("\tReading wheels...");
    for _ in 0 .. num_wheels {
        let steering = source.read_u32::<E>().unwrap();
        source.by_ref().bytes().nth(3*8).unwrap().unwrap();
        let width = source.read_u32::<E>().unwrap() as f32 * SCALE;
        let radius = source.read_u32::<E>().unwrap() as f32 * SCALE;
        let _bound_index = source.read_u32::<E>().unwrap();
        model.wheels.push(Wheel {
            mesh: if steering != 0 {
                Some(load_c3d(source, factory))
            } else {None},
            steering: steering,
            width: width,
            radius: radius,
        })
    }

    info!("\tReading debris...");
    for _ in 0 .. num_debris {
        let mesh = load_c3d(source, factory);
        let _phys_bound = load_c3d(source, factory);
        model.debris.push(Debrie {
            mesh: mesh,
        })
    }

    //let _phys_bound = load_c3d(source, factory);
    model
}
<commit_msg>Fixed M3D loading<commit_after>use std::io::{Read};
use byteorder::{LittleEndian as E, ReadBytesExt};
use gfx;
use gfx::format::I8Norm;
use render::ObjectVertex;

const SCALE: f32 = 1.0 / 256.0;

pub struct Mesh<R: gfx::Resources> {
    pub slice: gfx::Slice<R>,
    pub buffer: gfx::handle::Buffer<R, ObjectVertex>,
}

pub struct Wheel<R: gfx::Resources> {
    pub mesh: Option<Mesh<R>>,
    pub steer: u32,
    pub width: f32,
    pub radius: f32,
}

pub struct Debrie<R: gfx::Resources> {
    pub mesh: Mesh<R>,
}

pub struct Model<R: gfx::Resources> {
    pub body: Mesh<R>,
    pub color: [u32; 2],
    pub wheels: Vec<Wheel<R>>,
    pub debris: Vec<Debrie<R>>,
}

pub fn load_c3d<I, R, F>(source: &mut I, factory: &mut F) -> Mesh<R> where
    I: ReadBytesExt,
    R: gfx::Resources,
    F: gfx::traits::FactoryExt<R>,
{
    let version = source.read_u32::<E>().unwrap();
    assert_eq!(version, 8);
    let num_positions = source.read_u32::<E>().unwrap();
    let num_normals   = source.read_u32::<E>().unwrap();
    let num_polygons  = source.read_u32::<E>().unwrap();
    let _total_verts  = source.read_u32::<E>().unwrap();

    let coord_max  = [
        source.read_i32::<E>().unwrap() as f32 * SCALE,
        source.read_i32::<E>().unwrap() as f32 * SCALE,
        source.read_i32::<E>().unwrap() as f32 * SCALE,
    ];
    let coord_min  = [
        source.read_i32::<E>().unwrap() as f32 * SCALE,
        source.read_i32::<E>().unwrap() as f32 * SCALE,
        source.read_i32::<E>().unwrap() as f32 * SCALE,
    ];
    let _parent_off = [
        source.read_i32::<E>().unwrap(),
        source.read_i32::<E>().unwrap(),
        source.read_i32::<E>().unwrap(),
    ];
    let _max_radius = source.read_u32::<E>().unwrap();
    let _parent_rot = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
	for _ in 0 .. (1+3+9) {
		source.read_f64::<E>().unwrap();
	}

    info!("\tReading {} positions...", num_positions);
    let mut positions = Vec::with_capacity(num_positions as usize);
    for _ in 0 .. num_positions {
        for _ in 0..3 {
            source.read_u32::<E>().unwrap(); //unknown
        }
        let mut pos = [0u8; 3];
        source.read_exact(&mut pos).unwrap();
        let _sort_info = source.read_u32::<E>().unwrap();
        positions.push(pos);
    }

    info!("\tReading {} normals...", num_normals);
    let mut normals = Vec::with_capacity(num_normals as usize);
    for _ in 0 .. num_normals {
        let mut norm = [0u8; 4];
        source.read_exact(&mut norm).unwrap();
        let _sort_info = source.read_u32::<E>().unwrap();
        normals.push(norm);
    }

    info!("\tReading {} polygons...", num_polygons);
    let mut vertices = Vec::with_capacity(num_polygons as usize * 3);
    for _ in 0 .. num_polygons {
        let num_corners = source.read_u32::<E>().unwrap();
        //assert_eq!(num_corners, 3);
		assert!(3 <= num_corners && num_corners <= 4);
        let _sort_info = source.read_u32::<E>().unwrap();
        let color = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
        let mut dummy = [0; 4];
        source.read_exact(&mut dummy[..4]).unwrap(); //skip flat normal
        source.read_exact(&mut dummy[..3]).unwrap(); //skip middle point
        for _ in 0..num_corners {
            let pid = source.read_u32::<E>().unwrap();
            let nid = source.read_u32::<E>().unwrap();
            let v = (positions[pid as usize], normals[nid as usize], color);
            vertices.push(v);
        }
    }

	// sorted variable polygons
	for _ in 0 .. 3 {
		for _ in 0 .. num_polygons {
			let _poly_ind = source.read_u32::<E>().unwrap();
		}
	}

    info!("\tCompacting...");
    vertices.sort();
    vertices.dedup();

    let gpu_verts: Vec<_> = vertices.into_iter().map(|(p, n, c)| ObjectVertex {
        pos: [
            coord_min[0] + p[0] as f32 * (coord_max[0] - coord_min[0]) / 255.0,
            coord_min[1] + p[1] as f32 * (coord_max[1] - coord_min[1]) / 255.0,
            coord_min[2] + p[2] as f32 * (coord_max[2] - coord_min[2]) / 255.0,
            1.0],
        color: c,
        normal: [I8Norm(n[0] as i8), I8Norm(n[1] as i8), I8Norm(n[2] as i8), I8Norm(n[3] as i8)],
    }).collect();
    info!("\tGot {} GPU vertices...", gpu_verts.len());
    let (vbuf, slice) = factory.create_vertex_buffer_with_slice(&gpu_verts, ());

    Mesh {
        slice: slice,
        buffer: vbuf,
    }
}

pub fn load_m3d<I, R, F>(source: &mut I, factory: &mut F) -> Model<R> where
    I: ReadBytesExt,
    R: gfx::Resources,
    F: gfx::traits::FactoryExt<R>,
{
    info!("\tReading the body...");
    let mut model = Model {
        body: load_c3d(source, factory),
        color: [0, 0],
        wheels: Vec::new(),
        debris: Vec::new(),
    };
    let _bounds = [
        source.read_u32::<E>().unwrap(),
        source.read_u32::<E>().unwrap(),
        source.read_u32::<E>().unwrap(),
    ];
    let _max_radius = source.read_u32::<E>().unwrap();
    let num_wheels = source.read_u32::<E>().unwrap();
    let num_debris = source.read_u32::<E>().unwrap();
    model.color = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
    model.wheels.reserve_exact(num_wheels as usize);
    model.debris.reserve_exact(num_debris as usize);

    info!("\tReading {} wheels...", num_wheels);
    for _ in 0 .. num_wheels {
        let steer = source.read_u32::<E>().unwrap();
		for _ in 0..3 {
			source.read_f64::<E>().unwrap();
		}
        let width = source.read_u32::<E>().unwrap() as f32 * SCALE;
        let radius = source.read_u32::<E>().unwrap() as f32 * SCALE;
        let _bound_index = source.read_u32::<E>().unwrap();
		info!("\tSteer {}, width {}, radius {}", steer, width, radius);
        model.wheels.push(Wheel {
            mesh: if steer != 0 {
                Some(load_c3d(source, factory))
            } else {None},
            steer: steer,
            width: width,
            radius: radius,
        })
    }

    info!("\tReading {} debris...", num_debris);
    for _ in 0 .. num_debris {
        let mesh = load_c3d(source, factory);
        let _phys_bound = load_c3d(source, factory);
        model.debris.push(Debrie {
            mesh: mesh,
        })
    }

    //let _phys_bound = load_c3d(source, factory);
    model
}
<|endoftext|>


<commit_before>use {Model, User};
use util::{RequestUtils, CargoResult, ChainError, human};
use db::Connection;
use pg::rows::Row;
use util::errors::NotFound;
use http;
use app::App;

#[repr(u32)]
pub enum OwnerKind {
    User = 0,
    Team = 1,
}

/// Unifies the notion of a User or a Team.
pub enum Owner {
    User(User),
    Team(Team),
}

/// For now, just a Github Team. Can be upgraded to other teams
/// later if desirable.
pub struct Team {
    /// We're assuming these are stable
    pub github_id: i32,
    /// Unique table id
    pub id: i32,
    /// "github:org:team"
    /// An opaque unique ID, that was at one point parsed out to query Github.
    /// We only query membership with github using the github_id, though.
    /// This is the only name we should ever talk to Cargo about.
    pub login: String,
    /// Sugary goodness
    pub name: Option<String>,
    pub avatar: Option<String>,

}

#[derive(RustcEncodable)]
pub struct EncodableOwner {
    pub id: i32,
    pub login: String,
    pub kind: String,
    pub email: Option<String>,
    pub url: Option<String>,
    pub name: Option<String>,
    pub avatar: Option<String>,
}

/// Access rights to the crate (publishing and ownership management)
/// NOTE: The order of these variants matters!
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Rights {
    None,
    Publish,
    Full,
}

impl Team {
    /// Just gets the Team from the database by name.
    pub fn find_by_login(conn: &Connection, login: &str) -> CargoResult<Self> {
        let stmt = try!(conn.prepare("SELECT * FROM teams
                                      WHERE login = $1"));
        let rows = try!(stmt.query(&[&login]));
        let row = try!(rows.iter().next().chain_error(|| {
            NotFound
        }));
        Ok(Model::from_row(&row))
    }

    /// Tries to create the Team in the DB (assumes a `:` has already been found).
    pub fn create(app: &App, conn: &Connection, login: &str, req_user: &User)
                                                    -> CargoResult<Self> {
        // must look like system:xxxxxxx
        let mut chunks = login.split(":");
        match chunks.next().unwrap() {
            // github:rust-lang:owners
            "github" => {
                // Ok to unwrap since we know one ":" is contained
                let org = chunks.next().unwrap();
                let team = try!(chunks.next().ok_or_else(||
                    human("missing github team argument; \
                            format is github:org:team")
                ));
                Team::create_github_team(app, conn, login, org, team, req_user)
            }
            _ => {
                Err(human("unknown organization handler, \
                            only 'github:org:team' is supported"))
            }
        }
    }

    /// Tries to create a Github Team from scratch. Assumes `org` and `team` are
    /// correctly parsed out of the full `name`. `name` is passed as a
    /// convenience to avoid rebuilding it.
    pub fn create_github_team(app: &App, conn: &Connection, login: &str,
                              org_name: &str, team_name: &str, req_user: &User)
                              -> CargoResult<Self> {
        // GET orgs/:org/teams
        // check that `team` is the `slug` in results, and grab its data

        // "sanitization"
        fn whitelist(c: &char) -> bool {
            match *c {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => false,
                _ => true
            }
        }

        if let Some(c) = org_name.chars().find(whitelist) {
            return Err(human(format!("organization cannot contain special \
                                        characters like {}", c)));
        }

        #[derive(RustcDecodable)]
        struct GithubTeam {
            slug: String,   // the name we want to find
            id: i32,        // unique GH id (needed for membership queries)
            name: Option<String>,   // Pretty name
        }

        // FIXME: we just set per_page=100 and don't bother chasing pagination
        // links. A hundred teams should be enough for any org, right?
        let url = format!("/orgs/{}/teams?per_page=100", org_name);
        let token = http::token(req_user.gh_access_token.clone());
        let resp = try!(http::github(app, &url, &token));
        let teams: Vec<GithubTeam> = try!(http::parse_github_response(resp));

        let team = try!(teams.into_iter().find(|team| team.slug == team_name)
            .ok_or_else(||{
                human(format!("could not find the github team {}/{}",
                            org_name, team_name))
            })
        );

        if !try!(team_with_gh_id_contains_user(app, team.id, req_user)) {
            return Err(human("only members of a team can add it as an owner"));
        }

        #[derive(RustcDecodable)]
        struct Org {
            avatar_url: Option<String>,
        }

        let url = format!("/orgs/{}", org_name);
        let resp = try!(http::github(app, &url, &token));
        let org: Org = try!(http::parse_github_response(resp));

        Team::insert(conn, login, team.id, team.name, org.avatar_url)
    }

    pub fn insert(conn: &Connection,
                  login: &str,
                  github_id: i32,
                  name: Option<String>,
                  avatar: Option<String>)
                  -> CargoResult<Self> {

        let stmt = try!(conn.prepare("INSERT INTO teams
                                   (login, github_id, name, avatar)
                                   VALUES ($1, $2, $3, $4)
                                   RETURNING *"));

        let rows = try!(stmt.query(&[&login, &github_id, &name, &avatar]));
        let row = rows.iter().next().unwrap();
        Ok(Model::from_row(&row))
    }

    /// Phones home to Github to ask if this User is a member of the given team.
    /// Note that we're assuming that the given user is the one interested in
    /// the answer. If this is not the case, then we could accidentally leak
    /// private membership information here.
    pub fn contains_user(&self, app: &App, user: &User) -> CargoResult<bool> {
        team_with_gh_id_contains_user(app, self.github_id, user)
    }
}

fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User)
                                                -> CargoResult<bool> {
    // GET teams/:team_id/memberships/:user_name
    // check that "state": "active"

    #[derive(RustcDecodable)]
    struct Membership {
        state: String,
    }

    let url = format!("/teams/{}/memberships/{}",
                        &github_id, &user.gh_login);
    let token = http::token(user.gh_access_token.clone());
    let resp = try!(http::github(app, &url, &token));

    // Officially how `false` is returned
    if resp.get_code() == 404 { return Ok(false) }

    let membership: Membership = try!(http::parse_github_response(resp));

    // There is also `state: pending` for which we could possibly give
    // some feedback, but it's not obvious how that should work.
    Ok(membership.state == "active")
}

impl Model for Team {
    fn from_row(row: &Row) -> Self {
        Team {
            id: row.get("id"),
            name: row.get("name"),
            github_id: row.get("github_id"),
            login: row.get("login"),
            avatar: row.get("avatar"),
        }
    }

    fn table_name(_: Option<Self>) -> &'static str { "teams" }
}

impl Owner {
    /// Finds the owner by name, failing out if it doesn't exist.
    /// May be a user's GH login, or a full team name. This is case
    /// sensitive.
    pub fn find_by_login(conn: &Connection, name: &str) -> CargoResult<Owner> {
        let owner = if name.contains(":") {
            Owner::Team(try!(Team::find_by_login(conn, name).map_err(|_|
                human(format!("could not find team with name {}", name))
            )))
        } else {
            Owner::User(try!(User::find_by_login(conn, name).map_err(|_|
                human(format!("could not find user with login `{}`", name))
            )))
        };
        Ok(owner)
    }

    pub fn kind(&self) -> i32 {
        match *self {
            Owner::User(_) => OwnerKind::User as i32,
            Owner::Team(_) => OwnerKind::Team as i32,
        }
    }

    pub fn login(&self) -> &str {
        match *self {
            Owner::User(ref user) => &user.gh_login,
            Owner::Team(ref team) => &team.login,
        }
    }

    pub fn id(&self) -> i32 {
        match *self {
            Owner::User(ref user) => user.id,
            Owner::Team(ref team) => team.id,
        }
    }

    pub fn encodable(self) -> EncodableOwner {
        match self {
            Owner::User(User { id, email, name, gh_login, avatar, .. }) => {
                let url = format!("https://github.com/{}", gh_login);
                EncodableOwner {
                    id: id,
                    login: gh_login,
                    email: email,
                    avatar: avatar,
                    url: Some(url),
                    name: name,
                    kind: String::from("user"),
                }
            }
            Owner::Team(Team { id, name, login, avatar, .. }) => {
                let url = {
                    let mut parts = login.split(":");
                    parts.next(); // discard github
                    format!("https://github.com/{}/teams/{}",
                            parts.next().unwrap(), parts.next().unwrap())
                };
                EncodableOwner {
                    id: id,
                    login: login,
                    email: None,
                    url: Some(url),
                    avatar: avatar,
                    name: name,
                    kind: String::from("team"),
                }
            }
        }
    }
}

/// Given this set of owners, determines the strongest rights the
/// given user has.
///
/// Shortcircuits on `Full` because you can't beat it. In practice we'll always
/// see `[user, user, user, ..., team, team, team]`, so we could shortcircuit on
/// `Publish` as well, but this is a non-obvious invariant so we don't bother.
/// Sweet free optimization if teams are proving burdensome to check.
/// More than one team isn't really expected, though.
pub fn rights(app: &App, owners: &[Owner], user: &User) -> CargoResult<Rights> {
    let mut best = Rights::None;
    for owner in owners {
        match *owner {
            Owner::User(ref other_user) => if other_user.id == user.id {
                return Ok(Rights::Full);
            },
            Owner::Team(ref team) => if try!(team.contains_user(app, user)) {
                best = Rights::Publish;
            },
        }
    }
    Ok(best)
}

<commit_msg>Fix orgs teams urls<commit_after>use {Model, User};
use util::{RequestUtils, CargoResult, ChainError, human};
use db::Connection;
use pg::rows::Row;
use util::errors::NotFound;
use http;
use app::App;

#[repr(u32)]
pub enum OwnerKind {
    User = 0,
    Team = 1,
}

/// Unifies the notion of a User or a Team.
pub enum Owner {
    User(User),
    Team(Team),
}

/// For now, just a Github Team. Can be upgraded to other teams
/// later if desirable.
pub struct Team {
    /// We're assuming these are stable
    pub github_id: i32,
    /// Unique table id
    pub id: i32,
    /// "github:org:team"
    /// An opaque unique ID, that was at one point parsed out to query Github.
    /// We only query membership with github using the github_id, though.
    /// This is the only name we should ever talk to Cargo about.
    pub login: String,
    /// Sugary goodness
    pub name: Option<String>,
    pub avatar: Option<String>,

}

#[derive(RustcEncodable)]
pub struct EncodableOwner {
    pub id: i32,
    pub login: String,
    pub kind: String,
    pub email: Option<String>,
    pub url: Option<String>,
    pub name: Option<String>,
    pub avatar: Option<String>,
}

/// Access rights to the crate (publishing and ownership management)
/// NOTE: The order of these variants matters!
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Rights {
    None,
    Publish,
    Full,
}

impl Team {
    /// Just gets the Team from the database by name.
    pub fn find_by_login(conn: &Connection, login: &str) -> CargoResult<Self> {
        let stmt = try!(conn.prepare("SELECT * FROM teams
                                      WHERE login = $1"));
        let rows = try!(stmt.query(&[&login]));
        let row = try!(rows.iter().next().chain_error(|| {
            NotFound
        }));
        Ok(Model::from_row(&row))
    }

    /// Tries to create the Team in the DB (assumes a `:` has already been found).
    pub fn create(app: &App, conn: &Connection, login: &str, req_user: &User)
                                                    -> CargoResult<Self> {
        // must look like system:xxxxxxx
        let mut chunks = login.split(":");
        match chunks.next().unwrap() {
            // github:rust-lang:owners
            "github" => {
                // Ok to unwrap since we know one ":" is contained
                let org = chunks.next().unwrap();
                let team = try!(chunks.next().ok_or_else(||
                    human("missing github team argument; \
                            format is github:org:team")
                ));
                Team::create_github_team(app, conn, login, org, team, req_user)
            }
            _ => {
                Err(human("unknown organization handler, \
                            only 'github:org:team' is supported"))
            }
        }
    }

    /// Tries to create a Github Team from scratch. Assumes `org` and `team` are
    /// correctly parsed out of the full `name`. `name` is passed as a
    /// convenience to avoid rebuilding it.
    pub fn create_github_team(app: &App, conn: &Connection, login: &str,
                              org_name: &str, team_name: &str, req_user: &User)
                              -> CargoResult<Self> {
        // GET orgs/:org/teams
        // check that `team` is the `slug` in results, and grab its data

        // "sanitization"
        fn whitelist(c: &char) -> bool {
            match *c {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => false,
                _ => true
            }
        }

        if let Some(c) = org_name.chars().find(whitelist) {
            return Err(human(format!("organization cannot contain special \
                                        characters like {}", c)));
        }

        #[derive(RustcDecodable)]
        struct GithubTeam {
            slug: String,   // the name we want to find
            id: i32,        // unique GH id (needed for membership queries)
            name: Option<String>,   // Pretty name
        }

        // FIXME: we just set per_page=100 and don't bother chasing pagination
        // links. A hundred teams should be enough for any org, right?
        let url = format!("/orgs/{}/teams?per_page=100", org_name);
        let token = http::token(req_user.gh_access_token.clone());
        let resp = try!(http::github(app, &url, &token));
        let teams: Vec<GithubTeam> = try!(http::parse_github_response(resp));

        let team = try!(teams.into_iter().find(|team| team.slug == team_name)
            .ok_or_else(||{
                human(format!("could not find the github team {}/{}",
                            org_name, team_name))
            })
        );

        if !try!(team_with_gh_id_contains_user(app, team.id, req_user)) {
            return Err(human("only members of a team can add it as an owner"));
        }

        #[derive(RustcDecodable)]
        struct Org {
            avatar_url: Option<String>,
        }

        let url = format!("/orgs/{}", org_name);
        let resp = try!(http::github(app, &url, &token));
        let org: Org = try!(http::parse_github_response(resp));

        Team::insert(conn, login, team.id, team.name, org.avatar_url)
    }

    pub fn insert(conn: &Connection,
                  login: &str,
                  github_id: i32,
                  name: Option<String>,
                  avatar: Option<String>)
                  -> CargoResult<Self> {

        let stmt = try!(conn.prepare("INSERT INTO teams
                                   (login, github_id, name, avatar)
                                   VALUES ($1, $2, $3, $4)
                                   RETURNING *"));

        let rows = try!(stmt.query(&[&login, &github_id, &name, &avatar]));
        let row = rows.iter().next().unwrap();
        Ok(Model::from_row(&row))
    }

    /// Phones home to Github to ask if this User is a member of the given team.
    /// Note that we're assuming that the given user is the one interested in
    /// the answer. If this is not the case, then we could accidentally leak
    /// private membership information here.
    pub fn contains_user(&self, app: &App, user: &User) -> CargoResult<bool> {
        team_with_gh_id_contains_user(app, self.github_id, user)
    }
}

fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User)
                                                -> CargoResult<bool> {
    // GET teams/:team_id/memberships/:user_name
    // check that "state": "active"

    #[derive(RustcDecodable)]
    struct Membership {
        state: String,
    }

    let url = format!("/teams/{}/memberships/{}",
                        &github_id, &user.gh_login);
    let token = http::token(user.gh_access_token.clone());
    let resp = try!(http::github(app, &url, &token));

    // Officially how `false` is returned
    if resp.get_code() == 404 { return Ok(false) }

    let membership: Membership = try!(http::parse_github_response(resp));

    // There is also `state: pending` for which we could possibly give
    // some feedback, but it's not obvious how that should work.
    Ok(membership.state == "active")
}

impl Model for Team {
    fn from_row(row: &Row) -> Self {
        Team {
            id: row.get("id"),
            name: row.get("name"),
            github_id: row.get("github_id"),
            login: row.get("login"),
            avatar: row.get("avatar"),
        }
    }

    fn table_name(_: Option<Self>) -> &'static str { "teams" }
}

impl Owner {
    /// Finds the owner by name, failing out if it doesn't exist.
    /// May be a user's GH login, or a full team name. This is case
    /// sensitive.
    pub fn find_by_login(conn: &Connection, name: &str) -> CargoResult<Owner> {
        let owner = if name.contains(":") {
            Owner::Team(try!(Team::find_by_login(conn, name).map_err(|_|
                human(format!("could not find team with name {}", name))
            )))
        } else {
            Owner::User(try!(User::find_by_login(conn, name).map_err(|_|
                human(format!("could not find user with login `{}`", name))
            )))
        };
        Ok(owner)
    }

    pub fn kind(&self) -> i32 {
        match *self {
            Owner::User(_) => OwnerKind::User as i32,
            Owner::Team(_) => OwnerKind::Team as i32,
        }
    }

    pub fn login(&self) -> &str {
        match *self {
            Owner::User(ref user) => &user.gh_login,
            Owner::Team(ref team) => &team.login,
        }
    }

    pub fn id(&self) -> i32 {
        match *self {
            Owner::User(ref user) => user.id,
            Owner::Team(ref team) => team.id,
        }
    }

    pub fn encodable(self) -> EncodableOwner {
        match self {
            Owner::User(User { id, email, name, gh_login, avatar, .. }) => {
                let url = format!("https://github.com/{}", gh_login);
                EncodableOwner {
                    id: id,
                    login: gh_login,
                    email: email,
                    avatar: avatar,
                    url: Some(url),
                    name: name,
                    kind: String::from("user"),
                }
            }
            Owner::Team(Team { id, name, login, avatar, .. }) => {
                let url = {
                    let mut parts = login.split(":");
                    parts.next(); // discard github
                    format!("https://github.com/orgs/{}/teams/{}",
                            parts.next().unwrap(), parts.next().unwrap())
                };
                EncodableOwner {
                    id: id,
                    login: login,
                    email: None,
                    url: Some(url),
                    avatar: avatar,
                    name: name,
                    kind: String::from("team"),
                }
            }
        }
    }
}

/// Given this set of owners, determines the strongest rights the
/// given user has.
///
/// Shortcircuits on `Full` because you can't beat it. In practice we'll always
/// see `[user, user, user, ..., team, team, team]`, so we could shortcircuit on
/// `Publish` as well, but this is a non-obvious invariant so we don't bother.
/// Sweet free optimization if teams are proving burdensome to check.
/// More than one team isn't really expected, though.
pub fn rights(app: &App, owners: &[Owner], user: &User) -> CargoResult<Rights> {
    let mut best = Rights::None;
    for owner in owners {
        match *owner {
            Owner::User(ref other_user) => if other_user.id == user.id {
                return Ok(Rights::Full);
            },
            Owner::Team(ref team) => if try!(team.contains_user(app, user)) {
                best = Rights::Publish;
            },
        }
    }
    Ok(best)
}

<|endoftext|>


<commit_before>use crate::{ProgressBar, ProgressBarIter};
use rayon::iter::{
    plumbing::Consumer, plumbing::Folder, plumbing::UnindexedConsumer, IndexedParallelIterator,
    ParallelIterator,
};
use std::convert::TryFrom;

/// Wraps a Rayon parallel iterator.
///
/// See [`ProgressIterator`](trait.ProgressIterator.html) for method
/// documentation.
pub trait ParallelProgressIterator
    where
        Self: Sized + ParallelIterator,
{
    /// Wrap an iterator with a custom progress bar.
    fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self>;

    /// Wrap an iterator with an explicit element count.
    fn progress_count(self, len: u64) -> ProgressBarIter<Self> {
        self.progress_with(ProgressBar::new(len))
    }

    fn progress(self) -> ProgressBarIter<Self>
        where
            Self: IndexedParallelIterator,
    {
        let len = u64::try_from(self.len()).unwrap();
        self.progress_count(len)
    }
}

impl<S: Send, T: ParallelIterator<Item = S>> ParallelProgressIterator for T {
    fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self> {
        ProgressBarIter { it: self, progress }
    }
}

struct ProgressConsumer<C> {
    base: C,
    progress: ProgressBar,
}

impl<C> ProgressConsumer<C> {
    fn new(base: C, progress: ProgressBar) -> Self {
        ProgressConsumer { base, progress }
    }
}

impl<T, C: Consumer<T>> Consumer<T> for ProgressConsumer<C> {
    type Folder = ProgressFolder<C::Folder>;
    type Reducer = C::Reducer;
    type Result = C::Result;

    fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) {
        let (left, right, reducer) = self.base.split_at(index);
        (
            ProgressConsumer::new(left, self.progress.clone()),
            ProgressConsumer::new(right, self.progress),
            reducer,
        )
    }

    fn into_folder(self) -> Self::Folder {
        ProgressFolder {
            base: self.base.into_folder(),
            progress: self.progress,
        }
    }

    fn full(&self) -> bool {
        self.base.full()
    }
}

impl<T, C: UnindexedConsumer<T>> UnindexedConsumer<T> for ProgressConsumer<C> {
    fn split_off_left(&self) -> Self {
        ProgressConsumer::new(self.base.split_off_left(), self.progress.clone())
    }

    fn to_reducer(&self) -> Self::Reducer {
        self.base.to_reducer()
    }
}

struct ProgressFolder<C> {
    base: C,
    progress: ProgressBar,
}

impl<T, C: Folder<T>> Folder<T> for ProgressFolder<C> {
    type Result = C::Result;

    fn consume(self, item: T) -> Self {
        self.progress.inc(1);
        ProgressFolder {
            base: self.base.consume(item),
            progress: self.progress,
        }
    }

    fn complete(self) -> C::Result {
        self.base.complete()
    }

    fn full(&self) -> bool {
        self.base.full()
    }
}

impl<S: Send, T: ParallelIterator<Item = S>> ParallelIterator for ProgressBarIter<T> {
    type Item = S;

    fn drive_unindexed<C: UnindexedConsumer<Self::Item>>(self, consumer: C) -> C::Result {
        let consumer1 = ProgressConsumer::new(consumer, self.progress.clone());
        self.it.drive_unindexed(consumer1)
    }
}

#[cfg(test)]
mod test {
    use crate::{ProgressBar, ProgressBarIter, ParallelProgressIterator};
    use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

    #[test]
    fn it_can_wrap_a_parallel_iterator() {
        let v = vec![1, 2, 3];
        fn wrap<'a, T: ParallelIterator<Item = &'a i32>>(it: ProgressBarIter<T>) {
            assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]);
        }

        wrap(v.par_iter().progress_count(3));
        wrap({
            let pb = ProgressBar::new(v.len() as u64);
            v.par_iter().progress_with(pb)
        });
    }
}
<commit_msg>Implement IndexedParallelIterator for ProgressBarIter<commit_after>use crate::{ProgressBar, ProgressBarIter};
use rayon::iter::{
    plumbing::{Consumer, Folder, Producer, ProducerCallback, UnindexedConsumer},
    IndexedParallelIterator, ParallelIterator,
};
use std::convert::TryFrom;

/// Wraps a Rayon parallel iterator.
///
/// See [`ProgressIterator`](trait.ProgressIterator.html) for method
/// documentation.
pub trait ParallelProgressIterator
where
    Self: Sized + ParallelIterator,
{
    /// Wrap an iterator with a custom progress bar.
    fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self>;

    /// Wrap an iterator with an explicit element count.
    fn progress_count(self, len: u64) -> ProgressBarIter<Self> {
        self.progress_with(ProgressBar::new(len))
    }

    fn progress(self) -> ProgressBarIter<Self>
    where
        Self: IndexedParallelIterator,
    {
        let len = u64::try_from(self.len()).unwrap();
        self.progress_count(len)
    }
}

impl<S: Send, T: ParallelIterator<Item = S>> ParallelProgressIterator for T {
    fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self> {
        ProgressBarIter { it: self, progress }
    }
}

impl<S: Send, T: IndexedParallelIterator<Item = S>> IndexedParallelIterator for ProgressBarIter<T> {
    fn len(&self) -> usize {
        self.it.len()
    }

    fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> <C as Consumer<Self::Item>>::Result {
        let consumer = ProgressConsumer::new(consumer, self.progress);
        self.it.drive(consumer)
    }

    fn with_producer<CB: ProducerCallback<Self::Item>>(
        self,
        callback: CB,
    ) -> <CB as ProducerCallback<Self::Item>>::Output {
        return self.it.with_producer(Callback {
            callback,
            progress: self.progress,
        });

        struct Callback<CB> {
            callback: CB,
            progress: ProgressBar,
        }

        impl<T, CB: ProducerCallback<T>> ProducerCallback<T> for Callback<CB> {
            type Output = CB::Output;

            fn callback<P>(self, base: P) -> CB::Output
            where
                P: Producer<Item = T>,
            {
                let producer = ProgressProducer {
                    base,
                    progress: self.progress,
                };
                self.callback.callback(producer)
            }
        }
    }
}

struct ProgressProducer<T> {
    base: T,
    progress: ProgressBar,
}

impl<T, P: Producer<Item = T>> Producer for ProgressProducer<P> {
    type Item = T;
    type IntoIter = ProgressBarIter<P::IntoIter>;

    fn into_iter(self) -> Self::IntoIter {
        ProgressBarIter {
            it: self.base.into_iter(),
            progress: self.progress,
        }
    }

    fn min_len(&self) -> usize {
        self.base.min_len()
    }

    fn max_len(&self) -> usize {
        self.base.max_len()
    }

    fn split_at(self, index: usize) -> (Self, Self) {
        let (left, right) = self.base.split_at(index);
        (
            ProgressProducer {
                base: left,
                progress: self.progress.clone(),
            },
            ProgressProducer {
                base: right,
                progress: self.progress,
            },
        )
    }
}

struct ProgressConsumer<C> {
    base: C,
    progress: ProgressBar,
}

impl<C> ProgressConsumer<C> {
    fn new(base: C, progress: ProgressBar) -> Self {
        ProgressConsumer { base, progress }
    }
}

impl<T, C: Consumer<T>> Consumer<T> for ProgressConsumer<C> {
    type Folder = ProgressFolder<C::Folder>;
    type Reducer = C::Reducer;
    type Result = C::Result;

    fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) {
        let (left, right, reducer) = self.base.split_at(index);
        (
            ProgressConsumer::new(left, self.progress.clone()),
            ProgressConsumer::new(right, self.progress),
            reducer,
        )
    }

    fn into_folder(self) -> Self::Folder {
        ProgressFolder {
            base: self.base.into_folder(),
            progress: self.progress,
        }
    }

    fn full(&self) -> bool {
        self.base.full()
    }
}

impl<T, C: UnindexedConsumer<T>> UnindexedConsumer<T> for ProgressConsumer<C> {
    fn split_off_left(&self) -> Self {
        ProgressConsumer::new(self.base.split_off_left(), self.progress.clone())
    }

    fn to_reducer(&self) -> Self::Reducer {
        self.base.to_reducer()
    }
}

struct ProgressFolder<C> {
    base: C,
    progress: ProgressBar,
}

impl<T, C: Folder<T>> Folder<T> for ProgressFolder<C> {
    type Result = C::Result;

    fn consume(self, item: T) -> Self {
        self.progress.inc(1);
        ProgressFolder {
            base: self.base.consume(item),
            progress: self.progress,
        }
    }

    fn complete(self) -> C::Result {
        self.base.complete()
    }

    fn full(&self) -> bool {
        self.base.full()
    }
}

impl<S: Send, T: ParallelIterator<Item = S>> ParallelIterator for ProgressBarIter<T> {
    type Item = S;

    fn drive_unindexed<C: UnindexedConsumer<Self::Item>>(self, consumer: C) -> C::Result {
        let consumer1 = ProgressConsumer::new(consumer, self.progress.clone());
        self.it.drive_unindexed(consumer1)
    }
}

#[cfg(test)]
mod test {
    use crate::{ParallelProgressIterator, ProgressBar, ProgressBarIter};
    use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

    #[test]
    fn it_can_wrap_a_parallel_iterator() {
        let v = vec![1, 2, 3];
        fn wrap<'a, T: ParallelIterator<Item = &'a i32>>(it: ProgressBarIter<T>) {
            assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]);
        }

        wrap(v.par_iter().progress_count(3));
        wrap({
            let pb = ProgressBar::new(v.len() as u64);
            v.par_iter().progress_with(pb)
        });
    }
}
<|endoftext|>


<commit_before>use slack_hook::{Attachment, AttachmentBuilder, Field, PayloadBuilder, Slack, SlackText, SlackLink};
use slack_hook::SlackTextContent::{Text, Link};

use taste::{BenchmarkResult, TastingResult};

pub struct SlackNotifier {
    conn: Slack,
    channel: String,
    github_repo: String,
}

impl SlackNotifier {
    pub fn new(hook_url: &str, channel: &str, repo_url: &str) -> SlackNotifier {
        SlackNotifier {
            conn: Slack::new(hook_url).unwrap(),
            channel: String::from(channel),
            github_repo: String::from(repo_url),
        }
    }

    pub fn notify(&self, res: &TastingResult) -> Result<(), String> {
        let payload = PayloadBuilder::new()
            .text(vec![Text("I've tasted ".into()),
                       Text(format!("\"{}\" -- ", res.commit_msg.lines().next().unwrap()).into()),
                       Link(SlackLink::new(&format!("{}/commit/{}",
                                                    self.github_repo,
                                                    res.commit_id),
                                           &res.commit_id[0..6]))]
                .as_slice())
            .attachments(vec![result_to_attachment(&res)])
            .channel(self.channel.clone())
            .username("taster")
            .icon_emoji(":tea:")
            .build()
            .unwrap();

        match self.conn.send(&payload) {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("{:?}", e)),
        }
    }
}

fn result_to_attachment(res: &TastingResult) -> Attachment {
    let color = if !res.build || !res.bench {
        "danger"
    } else {
        "good"
    };

    let title = if !res.build {
        "Build failure!"
    } else if !res.bench {
        "Benchmark failure!"
    } else {
        "Performance results:"
    };

    let taste = if !res.build || !res.bench {
        "was inedible"
    } else {
        "tasted nice"
    };

    let mut allfields = Vec::new();
    for res in &res.results {
        let mut nv = res.iter()
            .map(|(k, v)| {
                let val = match v {
                    &BenchmarkResult::Neutral(ref s, _) => s,
                    _ => unimplemented!(),
                };
                Field {
                    title: k.clone(),
                    value: SlackText::new(val.clone()),
                    short: Some(true),
                }
            })
            .collect::<Vec<_>>();
        nv.sort_by(|a, b| b.title.cmp(&a.title));
        allfields.extend(nv);
    }

    AttachmentBuilder::new(format!("It {}.", taste))
        .color(color)
        .title(title)
        .fields(allfields)
        .build()
        .unwrap()
}
<commit_msg>Better Slack message<commit_after>use slack_hook::{Attachment, AttachmentBuilder, Field, PayloadBuilder, Slack, SlackText, SlackLink};
use slack_hook::SlackTextContent::{Text, Link};

use taste::{BenchmarkResult, TastingResult};

pub struct SlackNotifier {
    conn: Slack,
    channel: String,
    github_repo: String,
}

impl SlackNotifier {
    pub fn new(hook_url: &str, channel: &str, repo_url: &str) -> SlackNotifier {
        SlackNotifier {
            conn: Slack::new(hook_url).unwrap(),
            channel: String::from(channel),
            github_repo: String::from(repo_url),
        }
    }

    pub fn notify(&self, res: &TastingResult) -> Result<(), String> {
        let payload = PayloadBuilder::new()
            .text(vec![Text("I've tasted commit _".into()),
                       Text(format!("\"{}\"_ -- ", res.commit_msg.lines().next().unwrap())
                           .into()),
                       Link(SlackLink::new(&format!("{}/commit/{}",
                                                    self.github_repo,
                                                    res.commit_id),
                                           &res.commit_id[0..6]))]
                .as_slice())
            .attachments(vec![result_to_attachment(&res)])
            .channel(self.channel.clone())
            .username("taster")
            .icon_emoji(":tea:")
            .build()
            .unwrap();

        match self.conn.send(&payload) {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("{:?}", e)),
        }
    }
}

fn result_to_attachment(res: &TastingResult) -> Attachment {
    let color = if !res.build || !res.bench {
        "danger"
    } else {
        "good"
    };

    let title = if !res.build {
        "Build failure!"
    } else if !res.bench {
        "Benchmark failure!"
    } else {
        "Performance results:"
    };

    let taste = if !res.build || !res.bench {
        "was inedible"
    } else {
        "tasted nice"
    };

    let mut allfields = Vec::new();
    for res in &res.results {
        let mut nv = res.iter()
            .map(|(k, v)| {
                let val = match v {
                    &BenchmarkResult::Neutral(ref s, _) => s,
                    _ => unimplemented!(),
                };
                Field {
                    title: k.clone(),
                    value: SlackText::new(val.clone()),
                    short: Some(true),
                }
            })
            .collect::<Vec<_>>();
        nv.sort_by(|a, b| b.title.cmp(&a.title));
        allfields.extend(nv);
    }

    AttachmentBuilder::new(format!("It {}.", taste))
        .color(color)
        .title(title)
        .fields(allfields)
        .build()
        .unwrap()
}
<|endoftext|>


<commit_before>use euclid::TypedVector2D;

/// Unit tag for typed euclid structs.
///
/// `CellSpace` is the internal default space for game maps that spaces with other projections can
/// transform to and from usind `std::convert`.
///
/// When used for hex maps, `CellSpace` is still a regular euclidean space and does not use axis
/// staggering. Squares in `CellSpace` coordinates are distorted into lozenges when displayed as a
/// hex map. For a flat-top hex map, the convention is that the positive x-axis points to the
/// 4-o'clock (southeast) neighbor hex and the positive y-axis points to the 8-o'clock (southwest)
/// neighbor hex. For a pointy-top hex map, x-axis points to the 3-o'clock (east) neighbor hex and
/// y-axis points to the 7-o'clock (southwest) neighbor hex.
pub struct CellSpace;
pub type CellVector = TypedVector2D<i32, CellSpace>;

/// Define a transformation from a space to `CellSpace`.
///
/// Define this trait for your custom space to get `Space` trait automatically derived for you.
///
/// # Defining projections
///
/// First write the X and Y axis vectors of your projected space as column vecttors of a 2x2
/// matrix. For example the `CellSpace` X-axis unit vector becomes (2, 0) and the Y-axis unit
/// vector becomes (-1, 1) in the prefab map `TextSpace`. So we get the matrix
///
/// ```notrust
/// | 2  -1 |
/// | 0   1 |
/// ```
///
/// This is the your unprojection. For the projection matrix, compute the inverse, you'll get
///
/// ```notrust
/// | 2  -1 | ^-1     | 1/2  1/2 |
/// | 0   1 |      =  |   0    1 |
/// ```
///
/// The projection formula for vector v and projection matrix M is Mv, ie (with row-major matrix
/// representation):
///
/// ```notrust
/// [v[0] * M[0] + v[1] * M[1], v[0] * M[2] + v[1] * M[3]]
/// ```
pub trait Transformation {
    type Element: Copy;
    // To make this even more automagical, could just make the user specify a 2x2 transformation
    // matrix here, then figure out how to invert the matrix at compile time to keep unprojection
    // efficient and set up conversions between the probably integer element types and
    // the transformation matrix that needs to be floating point. LLVM should be able to optimize
    // away a 2x2 matrix inversion function called with const inputs.

    /// Transform `CellSpace` coordinates to this space.
    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2];

    /// Transform coordinates of this space to `CellSpace`.
    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2];
}

impl Transformation for CellSpace {
    type Element = i32;

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] { v.into() }
    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] { v.into() }
}

/// Projection from a different space into `CellSpace`.
pub trait Space {
    /// Project from local space to `CellSpace`.
    fn to_cell_space(self) -> CellVector;

    /// Project from `CellSpace` to local space.
    fn from_cell_space(cell: CellVector) -> Self;
}

impl<T, U> Space for TypedVector2D<T, U>
where
    T: Copy,
    U: Transformation<Element = T>,
{
    fn to_cell_space(self) -> CellVector { U::project(self).into() }
    fn from_cell_space(cell: CellVector) -> Self { U::unproject(cell).into() }
}
<commit_msg>Derive space for vec3<commit_after>use euclid::{TypedVector2D, TypedVector3D, vec3};

/// Unit tag for typed euclid structs.
///
/// `CellSpace` is the internal default space for game maps that spaces with other projections can
/// transform to and from usind `std::convert`.
///
/// When used for hex maps, `CellSpace` is still a regular euclidean space and does not use axis
/// staggering. Squares in `CellSpace` coordinates are distorted into lozenges when displayed as a
/// hex map. For a flat-top hex map, the convention is that the positive x-axis points to the
/// 4-o'clock (southeast) neighbor hex and the positive y-axis points to the 8-o'clock (southwest)
/// neighbor hex. For a pointy-top hex map, x-axis points to the 3-o'clock (east) neighbor hex and
/// y-axis points to the 7-o'clock (southwest) neighbor hex.
pub struct CellSpace;
pub type CellVector = TypedVector2D<i32, CellSpace>;

/// Define a transformation from a space to `CellSpace`.
///
/// Define this trait for your custom space to get `Space` trait automatically derived for you.
///
/// # Defining projections
///
/// First write the X and Y axis vectors of your projected space as column vecttors of a 2x2
/// matrix. For example the `CellSpace` X-axis unit vector becomes (2, 0) and the Y-axis unit
/// vector becomes (-1, 1) in the prefab map `TextSpace`. So we get the matrix
///
/// ```notrust
/// | 2  -1 |
/// | 0   1 |
/// ```
///
/// This is the your unprojection. For the projection matrix, compute the inverse, you'll get
///
/// ```notrust
/// | 2  -1 | ^-1     | 1/2  1/2 |
/// | 0   1 |      =  |   0    1 |
/// ```
///
/// The projection formula for vector v and projection matrix M is Mv, ie (with row-major matrix
/// representation):
///
/// ```notrust
/// [v[0] * M[0] + v[1] * M[1], v[0] * M[2] + v[1] * M[3]]
/// ```
pub trait Transformation {
    type Element: Copy;
    // To make this even more automagical, could just make the user specify a 2x2 transformation
    // matrix here, then figure out how to invert the matrix at compile time to keep unprojection
    // efficient and set up conversions between the probably integer element types and
    // the transformation matrix that needs to be floating point. LLVM should be able to optimize
    // away a 2x2 matrix inversion function called with const inputs.

    /// Transform `CellSpace` coordinates to this space.
    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2];

    /// Transform coordinates of this space to `CellSpace`.
    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2];
}

impl Transformation for CellSpace {
    type Element = i32;

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] { v.into() }
    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] { v.into() }
}

/// Projection from a different space into `CellSpace`.
pub trait Space {
    /// Project from local space to `CellSpace`.
    fn to_cell_space(self) -> CellVector;

    /// Project from `CellSpace` to local space.
    fn from_cell_space(cell: CellVector) -> Self;
}

impl<T, U> Space for TypedVector2D<T, U>
where
    T: Copy,
    U: Transformation<Element = T>,
{
    fn to_cell_space(self) -> CellVector { U::project(self).into() }
    fn from_cell_space(cell: CellVector) -> Self { U::unproject(cell).into() }
}

impl<T, U> Space for TypedVector3D<T, U>
where
    T: Copy + Default,
    U: Transformation<Element = T>,
{
    fn to_cell_space(self) -> CellVector {
        let v = [self.x, self.y];
        U::project(v).into()
    }
    fn from_cell_space(cell: CellVector) -> Self {
        let v = U::unproject(cell);
        vec3(v[0], v[1], Default::default())
    }
}
<|endoftext|>


<commit_before>extern crate rand;

const ROWS: u8 = 9;
const COLS: u8 = 12;
const TILES: u8 = 108;
const PLAYERS: u8 = 4;

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct Game {
    players: Vec<Player>,
    board: Board,
    turn: PlayerId,
    merge_decision: Option<PlayerId>
}

#[derive(RustcDecodable, RustcEncodable, Clone)]
struct Player {
    id: PlayerId,
    money: i32,
    shares: PlayerShares,
    tiles: Vec<Tile>
}

type PlayerId = u8;

#[derive(RustcDecodable, RustcEncodable, Clone)]
struct PlayerShares {
    luxor: u8,
    tower: u8,
    american: u8,
    festival: u8,
    worldwide: u8,
    continental: u8,
    imperial: u8
}

#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct Tile { row: u8, col: u8 }

#[derive(RustcDecodable, RustcEncodable, Clone)]
struct Board {
    slots: Vec<Slot>
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq)]
struct Slot {
    row: u8,
    col: u8,
    has_tile: bool,
    hotel: Option<Hotel>
}

#[derive(Debug)]
pub enum Action {
    PlaceTile { player: PlayerId, tile: Tile },
    HandleMergeStocks { hold: u8, sell: u8, trade: u8 },
    BuyStocks,
    DrawTile,
    EndGame
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq)]
enum Hotel { Luxor, Tower, American, Festival, Worldwide, Continental, Imperial }

fn empty_shares() -> PlayerShares {
    PlayerShares { luxor: 0, tower: 0, american: 0, festival: 0, worldwide: 0, continental: 0, imperial: 0 }
}

fn new_player(id: PlayerId, tiles: Vec<Tile>) -> Player {
    Player { id: id, money: 6000, shares: empty_shares(), tiles: tiles }
}

fn choose_tiles(tiles: Vec<Tile>, count: u8) -> (Vec<Tile>, Vec<Tile>) {
    let mut remaining_tiles = tiles;
    let mut random_tiles = Vec::new();
    for _ in 0..count {
        let random_index = rand::random::<usize>() % remaining_tiles.len();
        random_tiles.push(remaining_tiles.remove(random_index));
    }
    (random_tiles, remaining_tiles)
}

fn new_players(tiles: Vec<Tile>) -> Vec<Player>{
    let init_players: Vec<Player> = Vec::new();
    let (players, _) = (0..PLAYERS)
        .fold( (init_players, tiles), | (mut v, remaining), i | {
            let (player_tiles, new_remaining) = choose_tiles(remaining, 6);
            let player = new_player(i, player_tiles);
            v.push(player);
            (v, new_remaining)
        });
    players
}

fn has_tile_on_slot(tiles: & Vec<Tile>, row: u8, col: u8) -> bool {
    tiles.iter().any(|t| t.row == row && t.col == col)
}

fn initial_slots(starting_tiles: Vec<Tile>) -> Vec<Slot> {
    (0..ROWS).flat_map(|row| -> Vec<Slot> {
        (0..COLS).map(|col| {
            Slot {
                row: row,
                col: col,
                hotel: None,
                has_tile: has_tile_on_slot(&starting_tiles, row, col)
            }
        }).collect()
    }).collect()
}

pub fn initial_state() -> Game {
    let all_tiles = (0..TILES).map(|i| Tile { row: i / COLS, col: i % ROWS }).collect();
    let (starting_tiles, remaining_tiles) = choose_tiles(all_tiles, PLAYERS);
    let players = new_players(remaining_tiles);
    let slots = initial_slots(starting_tiles);
    Game {
        board: Board { slots: slots },
        players: players,
        turn: 1,
        merge_decision: None
    }
}

fn new_game_with_tiles(start_tiles: BoardTiles, player_tiles: PlayerTiles) -> Game {
    let (starting_tiles, remaining) = board_tiles_to_tiles(&start_tiles);
    let players = player_tiles
        .iter()
        .enumerate()
        .map(|(i, tiles)| {
            let _tiles = tiles
                .iter()
                .map(|&(r,c)| Tile { row: r as u8, col: c as u8 } )
                .collect();
            new_player(i as u8, _tiles)
        })
        .collect();
    let slots = initial_slots(starting_tiles);
    Game {
        board: Board { slots: slots },
        players: players,
        turn: 1,
        merge_decision: None
    }
}

fn board_tiles_to_tiles(tiles: &BoardTiles) -> (Vec<Tile>, Vec<Tile>) {
    let mut chosen = Vec::new();
    let mut others = Vec::new();
    for row in 0..tiles.len() {
        for col in 0..tiles[0].len() {
            let tile = Tile { row: row as u8, col: col as u8 };
            if tiles[row][col] == 1 {
                chosen.push(tile)
            } else {
                others.push(tile)
            }
        }
    }
    (chosen, others)
}

pub fn play_turn(game: &Game, action: Action) -> Game {
    match action {
        Action::PlaceTile { player: player, tile: tile } => {
            place_tile(game, player, &tile)
        }
        _ => panic!(format!("I don't know how to play a turn with action {:?}", action))
    }
}

fn place_tile(game: &Game, player: u8, tile: &Tile) -> Game {
    Game {
        board: place_tile_on_board(&game.board, &tile),
        players: game.players.clone(),
        turn: game.turn,
        merge_decision: game.merge_decision
    }
}

fn place_tile_on_board(board: &Board, tile: &Tile) -> Board {
    println!("Tile {:?}", tile);
    let slots = board.slots
        .iter()
        .map(|s| {
            if s.row == tile.row && s.col == tile.col {
                println!("Match! ({},{})", s.row, s.col);
                Slot { row: s.row, col: s.col, has_tile: true, hotel: s.hotel.clone() }
            } else {
                println!("No match! ({},{})", s.row, s.col);
                s.clone()
            }
        })
        .collect();
    Board { slots: slots }
}

#[test]
fn players_start_with_six_tiles() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.tiles.len(), 6);
    }
}

#[test]
fn players_start_with_6000_in_cash() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.money, 6000);
    }
}

#[test]
fn players_start_with_zero_shares() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.shares.luxor, 0);
        assert_eq!(player.shares.tower, 0);
        assert_eq!(player.shares.american, 0);
        assert_eq!(player.shares.festival, 0);
        assert_eq!(player.shares.worldwide, 0);
        assert_eq!(player.shares.continental, 0);
        assert_eq!(player.shares.imperial, 0);
    }
}

#[test]
fn game_starts_with_four_placed_tiles() {
    let state = initial_state();
    let board_tiles = state.board.slots.iter().filter(|s| s.has_tile).count();
    assert_eq!(board_tiles, 4)
}

type BoardTiles = [[i32; COLS as usize]; ROWS as usize];
type PlayerTiles = [[(i32,i32); 6]; PLAYERS as usize];

#[test]
fn placing_a_tile_adds_tile_to_board() {
    let start_tiles = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
    let end_tiles =   [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
    let player_tiles = [[ (0,0), (0,1), (0,2), (0,3), (0,4), (0,5) ],
                        [ (1,0), (1,1), (1,2), (1,3), (1,4), (1,5) ],
                        [ (2,2), (2,1), (2,2), (2,3), (2,4), (2,5) ],
                        [ (3,3), (3,1), (3,2), (3,3), (3,4), (3,5) ]];
    let state = new_game_with_tiles(start_tiles, player_tiles);
    let tile_to_place = Tile { row: 5, col: 11 };
    let action = Action::PlaceTile { player: 1, tile: tile_to_place };
    let state_after = play_turn(&state, action);
    assert_board_has_tiles(end_tiles, &state_after.board);
}

fn assert_board_has_tiles(expected_tiles: BoardTiles, actual_board: &Board) {
    let expected_board = tiles_to_board(&expected_tiles);
    let are_equal = expected_board.slots
        .iter()
        .zip(actual_board.slots.iter())
        .all(|(left, right)| *left == *right );
    let error_msg = format!("\n\nBoard did not have expected tiles.\n\nExpected tiles:\n{}\n\nActual tiles:\n{}\n\n", print_board(&expected_board), print_board(&actual_board));
    assert!(are_equal, error_msg);
}

fn tiles_to_board(tiles: &BoardTiles) -> Board {
    let board_tiles = tiles
        .iter()
        .enumerate()
        .flat_map(|(row, row_tiles)| {
            let slots: Vec<Slot> = row_tiles
                .iter()
                .enumerate()
                .map(|(col, val): (usize, &i32)| {
                    let has_tile = *val == 1;
                    Slot { row: row as u8, col: col as u8, has_tile: has_tile, hotel: None }
                })
                .collect();
            slots
        }).collect();
    Board { slots: board_tiles }
}

fn row_to_char<'a>(row: u8) -> &'a str {
    let mapping = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];
    mapping[row as usize]
}

fn print_tile(slot: &Slot) -> String{
    format!("{}{}", row_to_char(slot.row), slot.col + 1)
}

fn print_board(board: &Board) -> String {
    let mut string = String::new();
    string.push_str("   1  2  3  4  5  6  7  8  9  10 11 12\n");
    for row in board.slots.chunks(COLS as usize) {
        let row_char = row_to_char(row[0].row);
        string.push_str(&format!("{}  ", row_char));
        for slot in row {
            string.push_str(&format!("{:<2} ", if slot.has_tile { '\u{25FC}' } else { '\u{25FB}' }));
        }
        string.push_str(&format!("{}\n", row_char));
    }
    string.push_str("   1  2  3  4  5  6  7  8  9  10 11 12");
    string
}
<commit_msg>Put tests in tests module<commit_after>extern crate rand;

pub const ROWS: u8 = 9;
pub const COLS: u8 = 12;
pub const TILES: u8 = 108;
pub const PLAYERS: u8 = 4;

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct Game {
    players: Vec<Player>,
    board: Board,
    turn: PlayerId,
    merge_decision: Option<PlayerId>
}

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct Player {
    id: PlayerId,
    money: i32,
    shares: PlayerShares,
    tiles: Vec<Tile>
}

pub type PlayerId = u8;

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct PlayerShares {
    luxor: u8,
    tower: u8,
    american: u8,
    festival: u8,
    worldwide: u8,
    continental: u8,
    imperial: u8
}

#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
pub struct Tile { row: u8, col: u8 }

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct Board {
    slots: Vec<Slot>
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq)]
pub struct Slot {
    row: u8,
    col: u8,
    has_tile: bool,
    hotel: Option<Hotel>
}

#[derive(Debug)]
pub enum Action {
    PlaceTile { player: PlayerId, tile: Tile },
    HandleMergeStocks { hold: u8, sell: u8, trade: u8 },
    BuyStocks,
    DrawTile,
    EndGame
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq)]
pub enum Hotel { Luxor, Tower, American, Festival, Worldwide, Continental, Imperial }

fn empty_shares() -> PlayerShares {
    PlayerShares { luxor: 0, tower: 0, american: 0, festival: 0, worldwide: 0, continental: 0, imperial: 0 }
}

pub fn new_player(id: PlayerId, tiles: Vec<Tile>) -> Player {
    Player { id: id, money: 6000, shares: empty_shares(), tiles: tiles }
}

fn choose_tiles(tiles: Vec<Tile>, count: u8) -> (Vec<Tile>, Vec<Tile>) {
    let mut remaining_tiles = tiles;
    let mut random_tiles = Vec::new();
    for _ in 0..count {
        let random_index = rand::random::<usize>() % remaining_tiles.len();
        random_tiles.push(remaining_tiles.remove(random_index));
    }
    (random_tiles, remaining_tiles)
}

fn new_players(tiles: Vec<Tile>) -> Vec<Player>{
    let init_players: Vec<Player> = Vec::new();
    let (players, _) = (0..PLAYERS)
        .fold( (init_players, tiles), | (mut v, remaining), i | {
            let (player_tiles, new_remaining) = choose_tiles(remaining, 6);
            let player = new_player(i, player_tiles);
            v.push(player);
            (v, new_remaining)
        });
    players
}

fn has_tile_on_slot(tiles: & Vec<Tile>, row: u8, col: u8) -> bool {
    tiles.iter().any(|t| t.row == row && t.col == col)
}

pub fn initial_slots(starting_tiles: Vec<Tile>) -> Vec<Slot> {
    (0..ROWS).flat_map(|row| -> Vec<Slot> {
        (0..COLS).map(|col| {
            Slot {
                row: row,
                col: col,
                hotel: None,
                has_tile: has_tile_on_slot(&starting_tiles, row, col)
            }
        }).collect()
    }).collect()
}

pub fn initial_state() -> Game {
    let all_tiles = (0..TILES).map(|i| Tile { row: i / COLS, col: i % ROWS }).collect();
    let (starting_tiles, remaining_tiles) = choose_tiles(all_tiles, PLAYERS);
    let players = new_players(remaining_tiles);
    let slots = initial_slots(starting_tiles);
    Game {
        board: Board { slots: slots },
        players: players,
        turn: 1,
        merge_decision: None
    }
}

pub fn play_turn(game: &Game, action: Action) -> Game {
    match action {
        Action::PlaceTile { player: player, tile: tile } => {
            place_tile(game, player, &tile)
        }
        _ => panic!(format!("I don't know how to play a turn with action {:?}", action))
    }
}

fn place_tile(game: &Game, player: u8, tile: &Tile) -> Game {
    Game {
        board: place_tile_on_board(&game.board, &tile),
        players: game.players.clone(),
        turn: game.turn,
        merge_decision: game.merge_decision
    }
}

fn place_tile_on_board(board: &Board, tile: &Tile) -> Board {
    println!("Tile {:?}", tile);
    let slots = board.slots
        .iter()
        .map(|s| {
            if s.row == tile.row && s.col == tile.col {
                println!("Match! ({},{})", s.row, s.col);
                Slot { row: s.row, col: s.col, has_tile: true, hotel: s.hotel.clone() }
            } else {
                println!("No match! ({},{})", s.row, s.col);
                s.clone()
            }
        })
        .collect();
    Board { slots: slots }
}

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

    type BoardTiles = [[i32; COLS as usize]; ROWS as usize];
    type PlayerTiles = [[(i32,i32); 6]; PLAYERS as usize];

    #[test]
    fn players_start_with_six_tiles() {
        let state = initial_state();
        for player in state.players {
            assert_eq!(player.tiles.len(), 6);
        }
    }

    #[test]
    fn players_start_with_6000_in_cash() {
        let state = initial_state();
        for player in state.players {
            assert_eq!(player.money, 6000);
        }
    }

    #[test]
    fn players_start_with_zero_shares() {
        let state = initial_state();
        for player in state.players {
            assert_eq!(player.shares.luxor, 0);
            assert_eq!(player.shares.tower, 0);
            assert_eq!(player.shares.american, 0);
            assert_eq!(player.shares.festival, 0);
            assert_eq!(player.shares.worldwide, 0);
            assert_eq!(player.shares.continental, 0);
            assert_eq!(player.shares.imperial, 0);
        }
    }

    #[test]
    fn game_starts_with_four_placed_tiles() {
        let state = initial_state();
        let board_tiles = state.board.slots.iter().filter(|s| s.has_tile).count();
        assert_eq!(board_tiles, 4)
    }

    #[test]
    fn placing_a_tile_adds_tile_to_board() {
        let start_tiles = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
        let end_tiles =   [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
        let player_tiles = [[ (0,0), (0,1), (0,2), (0,3), (0,4), (0,5) ],
                            [ (1,0), (1,1), (1,2), (1,3), (1,4), (1,5) ],
                            [ (2,2), (2,1), (2,2), (2,3), (2,4), (2,5) ],
                            [ (3,3), (3,1), (3,2), (3,3), (3,4), (3,5) ]];
        let state = new_game_with_tiles(start_tiles, player_tiles);
        let tile_to_place = Tile { row: 5, col: 11 };
        let action = Action::PlaceTile { player: 1, tile: tile_to_place };
        let state_after = play_turn(&state, action);
        assert_boards_equal(&tiles_to_board(&end_tiles), &state_after.board);
    }

    fn new_game_with_tiles(start_tiles: BoardTiles, player_tiles: PlayerTiles) -> Game {
        let (starting_tiles, remaining) = board_tiles_to_tiles(&start_tiles);
        let players = player_tiles
            .iter()
            .enumerate()
            .map(|(i, tiles)| {
                let _tiles = tiles
                    .iter()
                    .map(|&(r,c)| Tile { row: r as u8, col: c as u8 } )
                    .collect();
                new_player(i as u8, _tiles)
            })
            .collect();
        let slots = initial_slots(starting_tiles);
        Game {
            board: Board { slots: slots },
            players: players,
            turn: 1,
            merge_decision: None
        }
    }

    fn board_tiles_to_tiles(tiles: &BoardTiles) -> (Vec<Tile>, Vec<Tile>) {
        let mut chosen = Vec::new();
        let mut others = Vec::new();
        for row in 0..tiles.len() {
            for col in 0..tiles[0].len() {
                let tile = Tile { row: row as u8, col: col as u8 };
                if tiles[row][col] == 1 {
                    chosen.push(tile)
                } else {
                    others.push(tile)
                }
            }
        }
        (chosen, others)
    }

    fn assert_boards_equal(expected: &Board, actual: &Board) {
        let are_equal = expected.slots
            .iter()
            .zip(actual.slots.iter())
            .all(|(left, right)| *left == *right );
        let error_msg = format!("\n\nBoard did not have expected tiles.\n\nExpected tiles:\n{}\n\nActual tiles:\n{}\n\n", print_board(&expected), print_board(&actual));
        assert!(are_equal, error_msg);
    }

    fn tiles_to_board(tiles: &BoardTiles) -> Board {
        let board_tiles = tiles
            .iter()
            .enumerate()
            .flat_map(|(row, row_tiles)| {
                let slots: Vec<Slot> = row_tiles
                    .iter()
                    .enumerate()
                    .map(|(col, val): (usize, &i32)| {
                        let has_tile = *val == 1;
                        Slot { row: row as u8, col: col as u8, has_tile: has_tile, hotel: None }
                    })
                    .collect();
                slots
            }).collect();
        Board { slots: board_tiles }
    }

    fn row_to_char<'a>(row: u8) -> &'a str {
        let mapping = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];
        mapping[row as usize]
    }

    fn print_tile(slot: &Slot) -> String{
        format!("{}{}", row_to_char(slot.row), slot.col + 1)
    }

    fn print_board(board: &Board) -> String {
        let mut string = String::new();
        string.push_str("   1  2  3  4  5  6  7  8  9  10 11 12\n");
        for row in board.slots.chunks(COLS as usize) {
            let row_char = row_to_char(row[0].row);
            string.push_str(&format!("{}  ", row_char));
            for slot in row {
                string.push_str(&format!("{:<2} ", if slot.has_tile { '\u{25FC}' } else { '\u{25FB}' }));
            }
            string.push_str(&format!("{}\n", row_char));
        }
        string.push_str("   1  2  3  4  5  6  7  8  9  10 11 12");
        string
    }
}
<|endoftext|>