<commit_before>use std::mem;
use std::ptr;
use std::marker::PhantomData;
use super::gl_lib as gl;
use super::gl_lib::types::*;
use super::context::Context;
use super::types::{DrawingMode, GLError};
use super::index_data::{IndexData, IndexDatum, IndexDatumType};

pub struct Buffer {
    gl_id: GLuint
}

impl Buffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Buffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteBuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_buffer(&self) -> Buffer {
        unsafe {
            let mut id : GLuint = mem::uninitialized();

            gl::GenBuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Buffer { gl_id: id }
        }
    }
}

#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum BufferDataUsage {
    STREAM_DRAW = gl::STREAM_DRAW as isize,
    STATIC_DRAW = gl::STATIC_DRAW as isize,
    DYNAMIC_DRAW = gl::DYNAMIC_DRAW as isize
}
pub use self::BufferDataUsage::*;

#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum BufferBindingTarget {
    ARRAY_BUFFER = gl::ARRAY_BUFFER as isize,
    ELEMENT_ARRAY_BUFFER = gl::ELEMENT_ARRAY_BUFFER as isize
}
pub use self::BufferBindingTarget::*;

pub trait BufferBinding {
    fn target(&self) -> BufferBindingTarget;

    fn buffer_bytes(&mut self, bytes: &[u8], usage: BufferDataUsage) {
        let ptr = bytes.as_ptr();
        let size = bytes.len() * mem::size_of::<u8>();
        unsafe {
            gl::BufferData(self.target() as GLenum,
                           size as GLsizeiptr,
                           ptr as *const GLvoid,
                           usage as GLenum);
            dbg_gl_error! {
                GLError::InvalidEnum => "Invalid `target` or `usage`",
                GLError::InvalidValue => "`size` is negative",
                GLError::InvalidOperation => "Object 0 is bound to buffer target",
                GLError::OutOfMemory => "Unable to create a large enough buffer",
                _ => "Unknown error"
            }
        }
    }
}

pub struct ArrayBufferBinding<'a> {
    phantom: PhantomData<&'a mut Buffer>
}

impl<'a> BufferBinding for ArrayBufferBinding<'a> {
    fn target(&self) -> BufferBindingTarget {
        ARRAY_BUFFER
    }
}

impl<'a> ArrayBufferBinding<'a> {
    pub unsafe fn vertex_attrib_pointer(&self,
                                        attrib: super::ProgramAttrib,
                                        components: i8,
                                        gl_type: super::DataType,
                                        normalized: bool,
                                        stride: usize,
                                        offset: usize)
    {
        debug_assert!(1 <= components && components <= 4);

        let gl_normalized = if normalized { gl::TRUE } else { gl::FALSE };
        gl::VertexAttribPointer(attrib.gl_index,
                                components as GLint,
                                gl_type as GLenum,
                                gl_normalized,
                                stride as GLsizei,
                                offset as *const GLvoid);
        dbg_gl_error! {
            GLError::InvalidEnum => "Illegal vertex attribute type",
            GLError::InvalidValue => "`stride` is negative, `size` is not in range, or `index` is >= GL_MAX_VERTEX_ATTRIBS",
            GLError::InvalidFramebufferOperation => "Currently bound framebuffer is not framebuffer complete",
            _ => "Unknown error"
        }
    }

    pub unsafe fn draw_arrays_range(&self,
                                    mode: DrawingMode,
                                    first: u32,
                                    count: usize)
    {
        gl::DrawArrays(mode as GLenum, first as GLint, count as GLsizei);
        dbg_gl_sanity_check! {
            GLError::InvalidEnum => "`mode` is not an accepted value",
            GLError::InvalidValue => "`count` is negative",
            _ => "Unknown error"
        }
    }

    pub unsafe fn draw_n_elements<I>(&self,
                                     mode: DrawingMode,
                                     count: usize,
                                     indicies: &[I])
        where I: IndexDatum, [I]: IndexData
    {
        debug_assert!(count <= indicies.len());

        let index_type: GLenum = match I::index_datum_type() {
            IndexDatumType::UnsignedByte => gl::UNSIGNED_BYTE,
            IndexDatumType::UnsignedShort => gl::UNSIGNED_SHORT
        };
        let ptr = indicies.index_bytes().as_ptr();
        gl::DrawElements(mode as GLenum,
                         count as GLsizei,
                         index_type,
                         mem::transmute(ptr));
        dbg_gl_error! {
            GLError::InvalidEnum => "`mode` or `type` is not an accepted value",
            GLError::InvalidValue => "`count` is negative",
            GLError::InvalidFramebufferOperation => "The current framebuffer is not framebuffer-complete",
            _ => "Unknown error"
        }
    }

    pub unsafe fn draw_elements<I>(&self,
                                   mode: DrawingMode,
                                   indicies: &[I])
        where I: IndexDatum, [I]: IndexData
    {
        self.draw_n_elements(mode, indicies.index_elements(), indicies);
    }
}

pub struct ElementArrayBufferBinding<'a> {
    phantom: PhantomData<&'a mut Buffer>
}

impl<'a> BufferBinding for ElementArrayBufferBinding<'a> {
    fn target(&self) -> BufferBindingTarget {
        ELEMENT_ARRAY_BUFFER
    }
}

pub struct ArrayBufferBinder;
impl ArrayBufferBinder {
    pub fn bind<'a>(&'a mut self, buffer: &mut Buffer)
        -> ArrayBufferBinding<'a>
    {
        let binding = ArrayBufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindBuffer(binding.target() as GLuint, buffer.gl_id);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not an allowed value",
                _ => "Unknown error"
            }
        }
        binding
    }
}

pub struct ElementArrayBufferBinder;
impl ElementArrayBufferBinder {
    pub fn bind<'a>(&'a mut self, buffer: &mut Buffer)
        -> ElementArrayBufferBinding<'a>
    {
        let binding = ElementArrayBufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindBuffer(binding.target() as GLuint, buffer.gl_id);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not an allowed value",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<commit_msg>Add `ArrayBufferBinding.draw_n_elements_buffered()`<commit_after>use std::mem;
use std::ptr;
use std::marker::PhantomData;
use super::gl_lib as gl;
use super::gl_lib::types::*;
use super::context::Context;
use super::types::{DrawingMode, GLError};
use super::index_data::{IndexData, IndexDatum, IndexDatumType};

pub struct Buffer {
    gl_id: GLuint
}

impl Buffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Buffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteBuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_buffer(&self) -> Buffer {
        unsafe {
            let mut id : GLuint = mem::uninitialized();

            gl::GenBuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Buffer { gl_id: id }
        }
    }
}

#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum BufferDataUsage {
    STREAM_DRAW = gl::STREAM_DRAW as isize,
    STATIC_DRAW = gl::STATIC_DRAW as isize,
    DYNAMIC_DRAW = gl::DYNAMIC_DRAW as isize
}
pub use self::BufferDataUsage::*;

#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum BufferBindingTarget {
    ARRAY_BUFFER = gl::ARRAY_BUFFER as isize,
    ELEMENT_ARRAY_BUFFER = gl::ELEMENT_ARRAY_BUFFER as isize
}
pub use self::BufferBindingTarget::*;

pub trait BufferBinding {
    fn target(&self) -> BufferBindingTarget;

    fn buffer_bytes(&mut self, bytes: &[u8], usage: BufferDataUsage) {
        let ptr = bytes.as_ptr();
        let size = bytes.len() * mem::size_of::<u8>();
        unsafe {
            gl::BufferData(self.target() as GLenum,
                           size as GLsizeiptr,
                           ptr as *const GLvoid,
                           usage as GLenum);
            dbg_gl_error! {
                GLError::InvalidEnum => "Invalid `target` or `usage`",
                GLError::InvalidValue => "`size` is negative",
                GLError::InvalidOperation => "Object 0 is bound to buffer target",
                GLError::OutOfMemory => "Unable to create a large enough buffer",
                _ => "Unknown error"
            }
        }
    }
}

pub struct ArrayBufferBinding<'a> {
    phantom: PhantomData<&'a mut Buffer>
}

impl<'a> BufferBinding for ArrayBufferBinding<'a> {
    fn target(&self) -> BufferBindingTarget {
        ARRAY_BUFFER
    }
}

impl<'a> ArrayBufferBinding<'a> {
    pub unsafe fn vertex_attrib_pointer(&self,
                                        attrib: super::ProgramAttrib,
                                        components: i8,
                                        gl_type: super::DataType,
                                        normalized: bool,
                                        stride: usize,
                                        offset: usize)
    {
        debug_assert!(1 <= components && components <= 4);

        let gl_normalized = if normalized { gl::TRUE } else { gl::FALSE };
        gl::VertexAttribPointer(attrib.gl_index,
                                components as GLint,
                                gl_type as GLenum,
                                gl_normalized,
                                stride as GLsizei,
                                offset as *const GLvoid);
        dbg_gl_error! {
            GLError::InvalidEnum => "Illegal vertex attribute type",
            GLError::InvalidValue => "`stride` is negative, `size` is not in range, or `index` is >= GL_MAX_VERTEX_ATTRIBS",
            GLError::InvalidFramebufferOperation => "Currently bound framebuffer is not framebuffer complete",
            _ => "Unknown error"
        }
    }

    pub unsafe fn draw_arrays_range(&self,
                                    mode: DrawingMode,
                                    first: u32,
                                    count: usize)
    {
        gl::DrawArrays(mode as GLenum, first as GLint, count as GLsizei);
        dbg_gl_sanity_check! {
            GLError::InvalidEnum => "`mode` is not an accepted value",
            GLError::InvalidValue => "`count` is negative",
            _ => "Unknown error"
        }
    }

    pub unsafe fn draw_n_elements_buffered(&self,
                                           _eab: &ElementArrayBufferBinding,
                                           mode: DrawingMode,
                                           count: usize,
                                           index_type: IndexDatumType)
    {
        let gl_index_type: GLenum = match index_type {
            IndexDatumType::UnsignedByte => gl::UNSIGNED_BYTE,
            IndexDatumType::UnsignedShort => gl::UNSIGNED_SHORT
        };
        gl::DrawElements(mode as GLenum,
                         count as GLsizei,
                         gl_index_type,
                         ptr::null());
        dbg_gl_error! {
            GLError::InvalidEnum => "`mode` or `type` is not an accepted value",
            GLError::InvalidValue => "`count` is negative",
            GLError::InvalidFramebufferOperation => "The current framebuffer is not framebuffer-complete",
            _ => "Unknown error"
        }
    }

    pub unsafe fn draw_n_elements<I>(&self,
                                     mode: DrawingMode,
                                     count: usize,
                                     indicies: &[I])
        where I: IndexDatum, [I]: IndexData
    {
        debug_assert!(count <= indicies.len());

        let index_type: GLenum = match I::index_datum_type() {
            IndexDatumType::UnsignedByte => gl::UNSIGNED_BYTE,
            IndexDatumType::UnsignedShort => gl::UNSIGNED_SHORT
        };
        let ptr = indicies.index_bytes().as_ptr();
        gl::DrawElements(mode as GLenum,
                         count as GLsizei,
                         index_type,
                         mem::transmute(ptr));
        dbg_gl_error! {
            GLError::InvalidEnum => "`mode` or `type` is not an accepted value",
            GLError::InvalidValue => "`count` is negative",
            GLError::InvalidFramebufferOperation => "The current framebuffer is not framebuffer-complete",
            _ => "Unknown error"
        }
    }

    pub unsafe fn draw_elements<I>(&self,
                                   mode: DrawingMode,
                                   indicies: &[I])
        where I: IndexDatum, [I]: IndexData
    {
        self.draw_n_elements(mode, indicies.index_elements(), indicies);
    }
}

pub struct ElementArrayBufferBinding<'a> {
    phantom: PhantomData<&'a mut Buffer>
}

impl<'a> BufferBinding for ElementArrayBufferBinding<'a> {
    fn target(&self) -> BufferBindingTarget {
        ELEMENT_ARRAY_BUFFER
    }
}

pub struct ArrayBufferBinder;
impl ArrayBufferBinder {
    pub fn bind<'a>(&'a mut self, buffer: &mut Buffer)
        -> ArrayBufferBinding<'a>
    {
        let binding = ArrayBufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindBuffer(binding.target() as GLuint, buffer.gl_id);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not an allowed value",
                _ => "Unknown error"
            }
        }
        binding
    }
}

pub struct ElementArrayBufferBinder;
impl ElementArrayBufferBinder {
    pub fn bind<'a>(&'a mut self, buffer: &mut Buffer)
        -> ElementArrayBufferBinding<'a>
    {
        let binding = ElementArrayBufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindBuffer(binding.target() as GLuint, buffer.gl_id);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not an allowed value",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<|endoftext|>


<commit_before>use std::collections::hashmap::HashMap;
use time;
use std::mem;
use sync::comm::Receiver;
use image::{GenericImage, SubImage, Pixel};
use image::{ImageBuf, Rgba};
use image;
use glfw;
use glfw::Context as _Context;
use gfx;
use gfx::{Device, DeviceHelper, ToSlice, CommandBuffer};
use gfx::{GlDevice};
use key;
use atlas::{AtlasBuilder, Atlas};
use util;
use color;
use color::{Rgb};
use geom::{V2};

static FONT_DATA: &'static [u8] = include_bin!("../assets/font.png");

pub struct Canvas {
    title: String,
    dim: [u32, ..2],
    frame_interval: Option<f64>,
    builder: AtlasBuilder,
    font_glyphs: HashMap<char, Image>,
}

/// Toplevel graphics drawing and input reading context.
impl Canvas {
    pub fn new() -> Canvas {
        let mut ret = Canvas {
            title: "window".to_string(),
            dim: [640, 360],
            frame_interval: None,
            builder: AtlasBuilder::new(),
            font_glyphs: HashMap::new(),
        };
        ret.init_font();
        ret
    }

    pub fn set_title(mut self, title: &str) -> Canvas {
        self.title = title.to_string();
        self
    }

    /// Set the frame rate.
    pub fn set_frame_interval(mut self, interval_s: f64) -> Canvas {
        assert!(interval_s > 0.00001);
        self.frame_interval = Some(interval_s);
        self
    }

    /// Set the resolution.
    pub fn set_dim(mut self, dim: [u32, ..2]) -> Canvas {
        self.dim = dim;
        self
    }

    pub fn add_image<P: Pixel<u8>, I: GenericImage<P>>(
        &mut self, image: I) -> Image {
        Image(self.builder.push(image))
    }

    /// Start running the engine, return an event iteration.
    pub fn run(&mut self) -> Context {
        Context::new(
            self.dim,
            self.title.as_slice(),
            Some(1.0 / 30.0),
            Atlas::new(&self.builder))
    }

    fn init_font(&mut self) {
        let mut font_sheet = util::color_key(
            &image::load_from_memory(FONT_DATA, image::PNG).unwrap(),
            0x80u8, 0x80u8, 0x80u8);
        for i in range(0u32, 96u32) {
            let x = 8u32 * (i % 16u32);
            let y = 8u32 * (i / 16u32);
            let glyph = self.add_image(SubImage::new(&mut font_sheet, x, y, 8, 8));
            self.font_glyphs.insert((i + 32) as u8 as char, glyph);
        }
    }
}

/// Interface to render to a live display.
pub struct Context {
    glfw: glfw::Glfw,
    window: glfw::Window,
    events: Receiver<(f64, glfw::WindowEvent)>,
    graphics: gfx::Graphics<gfx::GlDevice, gfx::GlCommandBuffer>,
    frame: gfx::Frame,

    state: State,
    frame_interval: Option<f64>,
    last_render_time: f64,
    atlas: Atlas,
    atlas_tex: Texture,
}

#[deriving(PartialEq)]
enum State {
    Normal,
    EndFrame,
}

impl Context {
    fn new(
        dim: [u32, ..2],
        title: &str,
        frame_interval: Option<f64>,
        atlas: Atlas) -> Context {

        let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
        let (window, events) = glfw
            .create_window(dim[0], dim[1],
            title.as_slice(), glfw::Windowed)
            .expect("Failed to open window");
        window.make_current();
        glfw.set_error_callback(glfw::FAIL_ON_ERRORS);
        window.set_key_polling(true);
        window.set_char_polling(true);

        let device = gfx::GlDevice::new(|s| window.get_proc_address(s));
        let mut graphics = gfx::Graphics::new(device);
        let frame = gfx::Frame::new(dim[0] as u16, dim[1] as u16);
        let atlas_tex = Texture::from_rgba8(&atlas.image, &mut graphics.device);

        Context {
            glfw: glfw,
            window: window,
            events: events,
            graphics: graphics,
            frame: frame,
            state: Normal,
            frame_interval: frame_interval,
            last_render_time: time::precise_time_s(),
            atlas: atlas,
            atlas_tex: atlas_tex,
        }
    }

    /// Clear the screen
    pub fn clear(&mut self, color: &Rgb) {
        self.graphics.clear(
            gfx::ClearData {
                color: color.to_array(),
                depth: 1.0,
                stencil: 0,
            }, gfx::Color, &self.frame);
    }

    /// Mess with drawy stuff
    pub fn draw_test(&mut self) {
        self.draw_image(V2(100, 100), Image(32), &color::ALICEBLUE);
    }

    pub fn draw_image(&mut self, offset: V2<int>, image: Image, color: &Rgb) {
        let Image(idx) = image;
        let bound = self.atlas.bounds[idx];

        let V2(u1, v1) = bound.min();
        let V2(u2, v2) = bound.max();

        // TODO: Position.
        let mesh = self.graphics.device.create_mesh([
            Vertex { pos: [0.0, 1.0], tex_coord: [u1, v1] },
            Vertex { pos: [1.0, 1.0], tex_coord: [u2, v1] },
            Vertex { pos: [0.0, 0.0], tex_coord: [u1, v2] },

            Vertex { pos: [1.0, 1.0], tex_coord: [u2, v1] },
            Vertex { pos: [0.0, 0.0], tex_coord: [u1, v2] },
            Vertex { pos: [1.0, 0.0], tex_coord: [u2, v2] },
        ]);

        let sampler_info = None; // TODO
        let params = ShaderParam {
            color: color.to_array(),
            s_texture: (self.atlas_tex.tex, sampler_info),
        };

        let slice = mesh.to_slice(gfx::TriangleList);
        let program = self.graphics.device.link_program(
            VERTEX_SRC.clone(), FRAGMENT_SRC.clone()).unwrap();
        let batch: gfx::batch::RefBatch<_ShaderParamLink, ShaderParam> = self.graphics.make_batch(
            &program, &mesh, slice, &gfx::DrawState::new()).unwrap();
        self.graphics.draw(&batch, &params, &self.frame);
    }
}

pub enum Event<'a> {
    /// Time to render the screen. Call your own render code on the Context
    /// value when you get this.
    Render(&'a mut Context),
    /// Some printable text entered. Data is whole strings to account for
    /// exotic input devices.
    Text(String),
    KeyPressed(key::Key),
    KeyReleased(key::Key),
}

impl<'a> Iterator<Event<'a>> for Context {
    fn next(&mut self) -> Option<Event<'a>> {
        // After a render event, control will return here on a new
        // iter call. Do post-render work here.
        if self.state == EndFrame {
            self.state = Normal;
            self.graphics.end_frame();
            self.window.swap_buffers();
        }

        loop {
            if self.window.should_close() {
                return None;
            }

            self.glfw.poll_events();

            match self.events.try_recv() {
                Ok((_, event)) => {
                    match event {
                        glfw::CharEvent(ch) => {
                            return Some(Text(String::from_char(1, ch)));
                        }
                        glfw::KeyEvent(k, _scan, action, _mods) => {
                            match key::translate_glfw_key(k).map(|k| {
                                if action == glfw::Press || action == glfw::Repeat {
                                    KeyPressed(k)
                                }
                                else {
                                    KeyReleased(k)
                                }
                            }) {
                                Some(e) => { return Some(e); }
                                _ => ()
                            }
                        }
                        // TODO Mouse events.
                        _ => ()
                    }
                }
                _ => ()
            }

            let t = time::precise_time_s();
            if self.frame_interval.map_or(true,
                |x| t - self.last_render_time >= x) {
                self.last_render_time = t;

                // Time to render, must return a handle to self.
                // XXX: Need unsafe hackery to get around lifetimes check.
                self.state = EndFrame;
                unsafe {
                    return Some(Render(mem::transmute(self)))
                }
            }
        }
    }
}

/// Drawable images stored in the Canvas.
#[deriving(Clone, PartialEq)]
pub struct Image(uint);

static VERTEX_SRC: gfx::ShaderSource = shaders! {
GLSL_120: b"
    #version 120

    uniform vec4 color;

    attribute vec2 a_pos;
    attribute vec2 a_tex_coord;

    // TODO: Make color a uniform argument.
    varying vec2 v_tex_coord;
    varying vec4 v_color;

    void main() {
        v_tex_coord = a_tex_coord;
        v_color = color;
        gl_Position = vec4(a_pos, 0.0, 1.0);
    }
"
};

static FRAGMENT_SRC: gfx::ShaderSource = shaders! {
GLSL_120: b"
    #version 120

    uniform sampler2D s_texture;

    varying vec2 v_tex_coord;
    varying vec4 v_color;

    void main() {
        vec4 tex_color = texture2D(s_texture, v_tex_coord);
        if (tex_color.a == 0.0) discard;
        gl_FragColor = v_color * tex_color;
    }
"
};

#[shader_param(Program)]
pub struct ShaderParam {
    pub color: [f32, ..4],
    pub s_texture: gfx::shade::TextureParam,
}

#[vertex_format]
struct Vertex {
    #[name = "a_pos"]
    pos: [f32, ..2],

    #[name = "a_tex_coord"]
    tex_coord: [f32, ..2],
}

impl Clone for Vertex {
    fn clone(&self) -> Vertex { *self }
}

struct Texture {
    tex: gfx::TextureHandle,
    width: u32,
    height: u32,
}

impl Texture {
    fn from_rgba8<D: Device<C>, C: CommandBuffer>(
        img: &ImageBuf<Rgba<u8>>,
        d: &mut D) -> Texture {
        let (w, h) = img.dimensions();
        let mut info = gfx::tex::TextureInfo::new();
        info.width = w as u16;
        info.height = h as u16;
        info.kind = gfx::tex::Texture2D;
        info.format = gfx::tex::RGBA8;

        let tex = d.create_texture(info).unwrap();
        d.update_texture(&tex, &info.to_image_info(), img.pixelbuf()).unwrap();

        Texture {
            tex: tex,
            width: w,
            height: h,
        }
    }
}
<commit_msg>Nearest neighbor texture filtering<commit_after>use std::collections::hashmap::HashMap;
use time;
use std::mem;
use sync::comm::Receiver;
use image::{GenericImage, SubImage, Pixel};
use image::{ImageBuf, Rgba};
use image;
use glfw;
use glfw::Context as _Context;
use gfx;
use gfx::tex;
use gfx::{Device, DeviceHelper, ToSlice, CommandBuffer};
use gfx::{GlDevice};
use key;
use atlas::{AtlasBuilder, Atlas};
use util;
use color;
use color::{Rgb};
use geom::{V2};

static FONT_DATA: &'static [u8] = include_bin!("../assets/font.png");

pub struct Canvas {
    title: String,
    dim: [u32, ..2],
    frame_interval: Option<f64>,
    builder: AtlasBuilder,
    font_glyphs: HashMap<char, Image>,
}

/// Toplevel graphics drawing and input reading context.
impl Canvas {
    pub fn new() -> Canvas {
        let mut ret = Canvas {
            title: "window".to_string(),
            dim: [640, 360],
            frame_interval: None,
            builder: AtlasBuilder::new(),
            font_glyphs: HashMap::new(),
        };
        ret.init_font();
        ret
    }

    pub fn set_title(mut self, title: &str) -> Canvas {
        self.title = title.to_string();
        self
    }

    /// Set the frame rate.
    pub fn set_frame_interval(mut self, interval_s: f64) -> Canvas {
        assert!(interval_s > 0.00001);
        self.frame_interval = Some(interval_s);
        self
    }

    /// Set the resolution.
    pub fn set_dim(mut self, dim: [u32, ..2]) -> Canvas {
        self.dim = dim;
        self
    }

    pub fn add_image<P: Pixel<u8>, I: GenericImage<P>>(
        &mut self, image: I) -> Image {
        Image(self.builder.push(image))
    }

    /// Start running the engine, return an event iteration.
    pub fn run(&mut self) -> Context {
        Context::new(
            self.dim,
            self.title.as_slice(),
            Some(1.0 / 30.0),
            Atlas::new(&self.builder))
    }

    fn init_font(&mut self) {
        let mut font_sheet = util::color_key(
            &image::load_from_memory(FONT_DATA, image::PNG).unwrap(),
            0x80u8, 0x80u8, 0x80u8);
        for i in range(0u32, 96u32) {
            let x = 8u32 * (i % 16u32);
            let y = 8u32 * (i / 16u32);
            let glyph = self.add_image(SubImage::new(&mut font_sheet, x, y, 8, 8));
            self.font_glyphs.insert((i + 32) as u8 as char, glyph);
        }
    }
}

/// Interface to render to a live display.
pub struct Context {
    glfw: glfw::Glfw,
    window: glfw::Window,
    events: Receiver<(f64, glfw::WindowEvent)>,
    graphics: gfx::Graphics<gfx::GlDevice, gfx::GlCommandBuffer>,
    frame: gfx::Frame,

    state: State,
    frame_interval: Option<f64>,
    last_render_time: f64,
    atlas: Atlas,
    atlas_tex: Texture,
}

#[deriving(PartialEq)]
enum State {
    Normal,
    EndFrame,
}

impl Context {
    fn new(
        dim: [u32, ..2],
        title: &str,
        frame_interval: Option<f64>,
        atlas: Atlas) -> Context {

        let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
        let (window, events) = glfw
            .create_window(dim[0], dim[1],
            title.as_slice(), glfw::Windowed)
            .expect("Failed to open window");
        window.make_current();
        glfw.set_error_callback(glfw::FAIL_ON_ERRORS);
        window.set_key_polling(true);
        window.set_char_polling(true);

        let device = gfx::GlDevice::new(|s| window.get_proc_address(s));
        let mut graphics = gfx::Graphics::new(device);
        let frame = gfx::Frame::new(dim[0] as u16, dim[1] as u16);
        let atlas_tex = Texture::from_rgba8(&atlas.image, &mut graphics.device);

        Context {
            glfw: glfw,
            window: window,
            events: events,
            graphics: graphics,
            frame: frame,
            state: Normal,
            frame_interval: frame_interval,
            last_render_time: time::precise_time_s(),
            atlas: atlas,
            atlas_tex: atlas_tex,
        }
    }

    /// Clear the screen
    pub fn clear(&mut self, color: &Rgb) {
        self.graphics.clear(
            gfx::ClearData {
                color: color.to_array(),
                depth: 1.0,
                stencil: 0,
            }, gfx::Color, &self.frame);
    }

    /// Mess with drawy stuff
    pub fn draw_test(&mut self) {
        self.draw_image(V2(100, 100), Image(32), &color::ALICEBLUE);
    }

    pub fn draw_image(&mut self, offset: V2<int>, image: Image, color: &Rgb) {
        let Image(idx) = image;
        let bound = self.atlas.bounds[idx];

        let V2(u1, v1) = bound.min();
        let V2(u2, v2) = bound.max();

        // TODO: Position.
        let mesh = self.graphics.device.create_mesh([
            Vertex { pos: [0.0, 1.0], tex_coord: [u1, v1] },
            Vertex { pos: [1.0, 1.0], tex_coord: [u2, v1] },
            Vertex { pos: [0.0, 0.0], tex_coord: [u1, v2] },

            Vertex { pos: [1.0, 1.0], tex_coord: [u2, v1] },
            Vertex { pos: [0.0, 0.0], tex_coord: [u1, v2] },
            Vertex { pos: [1.0, 0.0], tex_coord: [u2, v2] },
        ]);

        let sampler_info = Some(self.graphics.device.create_sampler(
            tex::SamplerInfo::new(tex::Scale, tex::Clamp)));
        let params = ShaderParam {
            color: color.to_array(),
            s_texture: (self.atlas_tex.tex, sampler_info),
        };

        let slice = mesh.to_slice(gfx::TriangleList);
        let program = self.graphics.device.link_program(
            VERTEX_SRC.clone(), FRAGMENT_SRC.clone()).unwrap();
        let batch: gfx::batch::RefBatch<_ShaderParamLink, ShaderParam> = self.graphics.make_batch(
            &program, &mesh, slice, &gfx::DrawState::new()).unwrap();
        self.graphics.draw(&batch, &params, &self.frame);
    }
}

pub enum Event<'a> {
    /// Time to render the screen. Call your own render code on the Context
    /// value when you get this.
    Render(&'a mut Context),
    /// Some printable text entered. Data is whole strings to account for
    /// exotic input devices.
    Text(String),
    KeyPressed(key::Key),
    KeyReleased(key::Key),
}

impl<'a> Iterator<Event<'a>> for Context {
    fn next(&mut self) -> Option<Event<'a>> {
        // After a render event, control will return here on a new
        // iter call. Do post-render work here.
        if self.state == EndFrame {
            self.state = Normal;
            self.graphics.end_frame();
            self.window.swap_buffers();
        }

        loop {
            if self.window.should_close() {
                return None;
            }

            self.glfw.poll_events();

            match self.events.try_recv() {
                Ok((_, event)) => {
                    match event {
                        glfw::CharEvent(ch) => {
                            return Some(Text(String::from_char(1, ch)));
                        }
                        glfw::KeyEvent(k, _scan, action, _mods) => {
                            match key::translate_glfw_key(k).map(|k| {
                                if action == glfw::Press || action == glfw::Repeat {
                                    KeyPressed(k)
                                }
                                else {
                                    KeyReleased(k)
                                }
                            }) {
                                Some(e) => { return Some(e); }
                                _ => ()
                            }
                        }
                        // TODO Mouse events.
                        _ => ()
                    }
                }
                _ => ()
            }

            let t = time::precise_time_s();
            if self.frame_interval.map_or(true,
                |x| t - self.last_render_time >= x) {
                self.last_render_time = t;

                // Time to render, must return a handle to self.
                // XXX: Need unsafe hackery to get around lifetimes check.
                self.state = EndFrame;
                unsafe {
                    return Some(Render(mem::transmute(self)))
                }
            }
        }
    }
}

/// Drawable images stored in the Canvas.
#[deriving(Clone, PartialEq)]
pub struct Image(uint);

static VERTEX_SRC: gfx::ShaderSource = shaders! {
GLSL_120: b"
    #version 120

    uniform vec4 color;

    attribute vec2 a_pos;
    attribute vec2 a_tex_coord;

    // TODO: Make color a uniform argument.
    varying vec2 v_tex_coord;
    varying vec4 v_color;

    void main() {
        v_tex_coord = a_tex_coord;
        v_color = color;
        gl_Position = vec4(a_pos, 0.0, 1.0);
    }
"
};

static FRAGMENT_SRC: gfx::ShaderSource = shaders! {
GLSL_120: b"
    #version 120

    uniform sampler2D s_texture;

    varying vec2 v_tex_coord;
    varying vec4 v_color;

    void main() {
        vec4 tex_color = texture2D(s_texture, v_tex_coord);
        if (tex_color.a == 0.0) discard;
        gl_FragColor = v_color * tex_color;
    }
"
};

#[shader_param(Program)]
pub struct ShaderParam {
    pub color: [f32, ..4],
    pub s_texture: gfx::shade::TextureParam,
}

#[vertex_format]
struct Vertex {
    #[name = "a_pos"]
    pos: [f32, ..2],

    #[name = "a_tex_coord"]
    tex_coord: [f32, ..2],
}

impl Clone for Vertex {
    fn clone(&self) -> Vertex { *self }
}

struct Texture {
    tex: gfx::TextureHandle,
    width: u32,
    height: u32,
}

impl Texture {
    fn from_rgba8<D: Device<C>, C: CommandBuffer>(
        img: &ImageBuf<Rgba<u8>>,
        d: &mut D) -> Texture {
        let (w, h) = img.dimensions();
        let mut info = gfx::tex::TextureInfo::new();
        info.width = w as u16;
        info.height = h as u16;
        info.kind = gfx::tex::Texture2D;
        info.format = gfx::tex::RGBA8;

        let tex = d.create_texture(info).unwrap();
        d.update_texture(&tex, &info.to_image_info(), img.pixelbuf()).unwrap();

        Texture {
            tex: tex,
            width: w,
            height: h,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fleetspeak_proto::common::{Message, Address};
use prost;
use prost_types;
use std::error::Error;
use std::io::{Read, Write, Result};
use std::marker::{Send, Sync};

pub struct Connection<'r, 'w, R, W> {
    pub input: &'r mut R,
    pub output: &'w mut W,
    buf: Vec<u8>,
}

impl<'r, 'w, R: Read, W: Write> Connection<'r, 'w, R, W> {

    pub fn new(input: &'r mut R, output: &'w mut W) -> Self {
        Connection {
            input: input,
            output: output,
            buf: vec!(0; 2 * 1024 * 1024), // TODO: Magic.
        }
    }

    pub fn send<M>(&mut self, service: &str, kind: &str, data: M) -> Result<()>
    where
        M: prost::Message,
    {
        self.encode(data)?;

        let msg = Message {
            message_type: kind.to_string(),
            destination: Some(Address {
                service_name: service.to_string(),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: self.buf.to_vec(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn receive<M>(&mut self) -> Result<M>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = match msg.data {
            Some(data) => data,
            None => return Ok(Default::default()),
        };

        prost::Message::decode(&data.value[..]).map_err(invalid_data_error)
    }

    fn emit(&mut self, msg: Message) -> Result<()> {
        let len = prost::Message::encoded_len(&msg);
        self.encode(msg)?;

        self.output.write_u32::<LittleEndian>(len as u32)?;
        self.output.write(&self.buf[..len])?;
        self.output.write_u32::<LittleEndian>(0xf1ee1001)?; // TODO: Magic.

        Ok(())
    }

    fn collect(&mut self) -> Result<Message> {
        let len = self.input.read_i32::<LittleEndian>()? as usize;
        self.input.read_exact(&mut self.buf[..len])?;

        prost::Message::decode(&self.buf[..len]).map_err(invalid_data_error)
    }

    fn encode<M>(&mut self, msg: M) -> Result<()>
    where
        M: prost::Message,
    {
        msg.encode(&mut self.buf).map_err(invalid_data_error)
    }
}

fn invalid_data_error<E>(err: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    return std::io::Error::new(std::io::ErrorKind::InvalidData, err);
}
<commit_msg>Refactor magic numbers to named constants.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fleetspeak_proto::common::{Message, Address};
use prost;
use prost_types;
use std::io::{Read, Write, Result};
use std::marker::{Send, Sync};

pub struct Connection<'r, 'w, R, W> {
    pub input: &'r mut R,
    pub output: &'w mut W,
    buf: Vec<u8>,
}

impl<'r, 'w, R: Read, W: Write> Connection<'r, 'w, R, W> {

    pub fn new(input: &'r mut R, output: &'w mut W) -> Self {
        Connection {
            input: input,
            output: output,
            buf: vec!(0; MAX_BUF_SIZE),
        }
    }

    pub fn send<M>(&mut self, service: &str, kind: &str, data: M) -> Result<()>
    where
        M: prost::Message,
    {
        self.encode(data)?;

        let msg = Message {
            message_type: kind.to_string(),
            destination: Some(Address {
                service_name: service.to_string(),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: self.buf.to_vec(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn receive<M>(&mut self) -> Result<M>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = match msg.data {
            Some(data) => data,
            None => return Ok(Default::default()),
        };

        prost::Message::decode(&data.value[..]).map_err(invalid_data_error)
    }

    fn emit(&mut self, msg: Message) -> Result<()> {
        let len = prost::Message::encoded_len(&msg);
        self.encode(msg)?;

        self.output.write_u32::<LittleEndian>(len as u32)?;
        self.output.write(&self.buf[..len])?;
        self.output.write_u32::<LittleEndian>(MAGIC)?;

        Ok(())
    }

    fn collect(&mut self) -> Result<Message> {
        let len = self.input.read_i32::<LittleEndian>()? as usize;
        self.input.read_exact(&mut self.buf[..len])?;

        prost::Message::decode(&self.buf[..len]).map_err(invalid_data_error)
    }

    fn encode<M>(&mut self, msg: M) -> Result<()>
    where
        M: prost::Message,
    {
        msg.encode(&mut self.buf).map_err(invalid_data_error)
    }
}

fn invalid_data_error<E>(err: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    return std::io::Error::new(std::io::ErrorKind::InvalidData, err);
}

const MAGIC: u32 = 0xf1ee1001;
const MAX_BUF_SIZE: usize = 2 * 1024 * 1024;
<|endoftext|>


<commit_before>extern crate libc;
extern crate std;
use self::std::prelude::*;
use self::std::os::{errno, page_size, MemoryMap, MapReadable, MapWritable,
                    MapNonStandardFlags};

extern "C" {
  #[link_name = "lwt_stack_register"]
  fn stack_register(start: *const u8, end: *const u8) -> libc::c_uint;
  #[link_name = "lwt_stack_deregister"]
  fn stack_deregister(id: libc::c_uint);
}

pub struct Stack {
  buf: MemoryMap,
  valgrind_id: libc::c_uint
}

const STACK_FLAGS: libc::c_int = libc::MAP_STACK
                               | libc::MAP_PRIVATE
                               | libc::MAP_ANON;

impl Stack {
  pub fn new(size: uint) -> Stack {
    let buf = match MemoryMap::new(size, &[MapReadable, MapWritable,
                                   MapNonStandardFlags(STACK_FLAGS)]) {
      Ok(map) => map,
      Err(e) => panic!("mmap for stack of size {} failed: {}", size, e)
    };

    if !protect_last_page(&buf) {
      panic!("Could not memory-protect guard page. stack={}, errno={}",
             buf.data(), errno());
    }

    let valgrind_id = unsafe {
      stack_register(buf.data().offset(buf.len() as int) as *const _,
                     buf.data() as *const _)
    };

    Stack {
      buf: buf,
      valgrind_id: valgrind_id
    }
  }
}

fn protect_last_page(stack: &MemoryMap) -> bool {
  unsafe {
    let last_page = stack.data() as *mut libc::c_void;
    libc::mprotect(last_page, page_size() as libc::size_t,
                   libc::PROT_NONE) != -1
  }
}

impl Drop for Stack {
  fn drop(&mut self) {
    unsafe {
      stack_deregister(self.valgrind_id);
    }
  }
}

impl Stack {
  pub fn top(&mut self) -> *mut u8 {
    unsafe {
      self.buf.data().offset(self.buf.len() as int)
    }
  }

  pub fn limit(&self) -> *const u8 {
    unsafe {
      self.buf.data().offset(page_size() as int) as *const _
    }
  }
}
<commit_msg>Rust updates (MapOption fixes)<commit_after>extern crate libc;
extern crate std;
use self::std::prelude::*;
use self::std::os::{errno, page_size, MemoryMap};
use self::std::os::MapOption::{MapReadable, MapWritable, MapNonStandardFlags};

extern "C" {
  #[link_name = "lwt_stack_register"]
  fn stack_register(start: *const u8, end: *const u8) -> libc::c_uint;
  #[link_name = "lwt_stack_deregister"]
  fn stack_deregister(id: libc::c_uint);
}

pub struct Stack {
  buf: MemoryMap,
  valgrind_id: libc::c_uint
}

const STACK_FLAGS: libc::c_int = libc::MAP_STACK
                               | libc::MAP_PRIVATE
                               | libc::MAP_ANON;

impl Stack {
  pub fn new(size: uint) -> Stack {
    let buf = match MemoryMap::new(size, &[MapReadable, MapWritable,
                                   MapNonStandardFlags(STACK_FLAGS)]) {
      Ok(map) => map,
      Err(e) => panic!("mmap for stack of size {} failed: {}", size, e)
    };

    if !protect_last_page(&buf) {
      panic!("Could not memory-protect guard page. stack={}, errno={}",
             buf.data(), errno());
    }

    let valgrind_id = unsafe {
      stack_register(buf.data().offset(buf.len() as int) as *const _,
                     buf.data() as *const _)
    };

    Stack {
      buf: buf,
      valgrind_id: valgrind_id
    }
  }
}

fn protect_last_page(stack: &MemoryMap) -> bool {
  unsafe {
    let last_page = stack.data() as *mut libc::c_void;
    libc::mprotect(last_page, page_size() as libc::size_t,
                   libc::PROT_NONE) != -1
  }
}

impl Drop for Stack {
  fn drop(&mut self) {
    unsafe {
      stack_deregister(self.valgrind_id);
    }
  }
}

impl Stack {
  pub fn top(&mut self) -> *mut u8 {
    unsafe {
      self.buf.data().offset(self.buf.len() as int)
    }
  }

  pub fn limit(&self) -> *const u8 {
    unsafe {
      self.buf.data().offset(page_size() as int) as *const _
    }
  }
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;
extern crate pkg_config;

const GRPC_VERSION: &'static str = "1.3.0";

#[cfg(not(feature = "static-link"))]
mod imp {
    use gcc;
    use pkg_config;

    use super::GRPC_VERSION;

    pub fn build_or_link_grpc(cc: &mut gcc::Config) {
        if let Ok(lib) = pkg_config::Config::new()
               .atleast_version(GRPC_VERSION)
               .probe("grpc_unsecure") {
            for inc_path in lib.include_paths {
                cc.include(inc_path);
            }
        } else {
            panic!("can't find a dynamic grpc library");
        }
    }
}

#[cfg(feature = "static-link")]
mod imp {
    use std::env;
    use std::fs::DirBuilder;
    use std::process::Command;

    use cmake;
    use gcc;

    use super::GRPC_VERSION;

    const ZLIB_VERSION: &'static str = "1.2.8";
    const BORINGSSL_COMMIT_HASH: &'static str = "78684e5b222645828ca302e56b40b9daff2b2d27";
    const CARES_TAG: &'static str = "cares-1_12_0";

    struct FakeGitSubmodule {
        url: String,
        base_name: String,
        to_dir: String,
    }

    impl FakeGitSubmodule {
        fn new_with_hash(account: &str,
                         repo: &str,
                         commit_hash: &str,
                         to_dir: &str)
                         -> FakeGitSubmodule {
            FakeGitSubmodule {
                url: format!("https://github.com/{}/{}/archive/{}.tar.gz",
                             account,
                             repo,
                             commit_hash),
                base_name: format!("{}-{}", repo, commit_hash),
                to_dir: to_dir.to_owned(),
            }
        }

        fn new_with_tag(account: &str, repo: &str, tag: &str, to_dir: &str) -> FakeGitSubmodule {
            FakeGitSubmodule {
                url: format!("https://github.com/{}/{}/archive/{}.tar.gz",
                             account,
                             repo,
                             tag),
                base_name: format!("{}-{}", repo, tag),
                to_dir: to_dir.to_owned(),
            }
        }

        fn new_with_semver(account: &str, repo: &str, ver: &str, to_dir: &str) -> FakeGitSubmodule {
            FakeGitSubmodule {
                url: format!("https://github.com/{}/{}/archive/v{}.tar.gz",
                             account,
                             repo,
                             ver),
                base_name: format!("{}-{}", repo, ver),
                to_dir: to_dir.to_owned(),
            }
        }

        fn init(&self) -> Result<(), String> {
            let out_dir = env::var_os("OUT_DIR").unwrap();
            let tgz_file_name = format!("{}.tar.gz", self.base_name);
            try!(Command::new("wget")
                     .args(&["-q", "-c", "-O", &tgz_file_name, &self.url])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to execute wget: {}", err))
                     .and_then(|status| if status.success() {
                                   Ok(())
                               } else {
                                   Err(format!("wget exit with {}", status))
                               }));

            try!(Command::new("tar")
                     .args(&["zxf", &tgz_file_name])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to execute tar: {}", err))
                     .and_then(|status| if status.success() {
                                   Ok(())
                               } else {
                                   Err(format!("tar exit with {}", status))
                               }));
            // clean base dir
            try!(Command::new("rm")
                     .args(&["-r", &self.to_dir])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to clean dir: {}", err)));

            Command::new("mv")
                .args(&[&self.base_name, &self.to_dir])
                .current_dir(&out_dir)
                .status()
                .map_err(|err| format!("failed to mv: {}", err))
                .and_then(|status| if status.success() {
                              Ok(())
                          } else {
                              Err(format!("mv exit with {}", status))
                          })
        }
    }

    pub fn build_or_link_grpc(cc: &mut gcc::Config) {
        let out_dir = env::var("OUT_DIR").expect("Can't access OUT_DIR");
        DirBuilder::new()
            .recursive(true)
            .create(format!("{}/{}", out_dir, "grpc"))
            .unwrap();

        FakeGitSubmodule::new_with_semver("grpc", "grpc", GRPC_VERSION, "grpc")
            .init()
            .unwrap();

        FakeGitSubmodule::new_with_semver("madler", "zlib", ZLIB_VERSION, "grpc/third_party/zlib")
            .init()
            .unwrap();

        FakeGitSubmodule::new_with_hash("google",
                                        "boringssl",
                                        BORINGSSL_COMMIT_HASH,
                                        "grpc/third_party/boringssl")
                .init()
                .unwrap();

        FakeGitSubmodule::new_with_tag("c-ares",
                                       "c-ares",
                                       CARES_TAG,
                                       "grpc/third_party/cares/cares")
                .init()
                .unwrap();

        // fix multiple _main symbols
        let _ = Command::new("rm")
            .current_dir(&format!("{}/grpc/third_party/cares/cares", out_dir))
            .args(&["acountry.c", "adig.c", "ahost.c"])
            .status();

        let dst = cmake::Config::new(format!("{}/grpc", out_dir))
            .build_target("grpc")
            .build();

        println!("cargo:rustc-link-search=native={}/build", dst.display());
        println!("cargo:rustc-link-search=native={}/build/third_party/cares",
                 dst.display());
        println!("cargo:rustc-link-search=native={}/build/third_party/zlib",
                 dst.display());

        println!("cargo:rustc-link-lib=static=z");
        println!("cargo:rustc-link-lib=static=cares");
        println!("cargo:rustc-link-lib=static=gpr");
        println!("cargo:rustc-link-lib=static=grpc");

        cc.include(format!("{}/grpc/include", out_dir));
    }
}

fn main() {
    let mut cc = gcc::Config::new();

    imp::build_or_link_grpc(&mut cc);

    cc.file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<commit_msg>*: address comment<commit_after>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;
extern crate pkg_config;

const GRPC_VERSION: &'static str = "1.3.0";

#[cfg(not(feature = "static-link"))]
mod imp {
    use gcc;
    use pkg_config;

    use super::GRPC_VERSION;

    pub fn build_or_link_grpc(cc: &mut gcc::Config) {
        if let Ok(lib) = pkg_config::Config::new()
               .atleast_version(GRPC_VERSION)
               .probe("grpc_unsecure") {
            for inc_path in lib.include_paths {
                cc.include(inc_path);
            }
        } else {
            panic!("can't find a dynamic grpc library");
        }
    }
}

#[cfg(feature = "static-link")]
mod imp {
    use std::env;
    use std::fs::DirBuilder;
    use std::process::Command;

    use cmake;
    use gcc;

    use super::GRPC_VERSION;

    const ZLIB_VERSION: &'static str = "1.2.8";
    const BORINGSSL_COMMIT_HASH: &'static str = "78684e5b222645828ca302e56b40b9daff2b2d27";
    const CARES_TAG: &'static str = "cares-1_12_0";

    struct GithubRepoFetcher {
        url: String,
        base_name: String,
        to_dir: String,
    }

    impl GithubRepoFetcher {
        fn new_with_hash(account: &str,
                         repo: &str,
                         commit_hash: &str,
                         to_dir: &str)
                         -> GithubRepoFetcher {
            GithubRepoFetcher {
                url: format!("https://github.com/{}/{}/archive/{}.tar.gz",
                             account,
                             repo,
                             commit_hash),
                base_name: format!("{}-{}", repo, commit_hash),
                to_dir: to_dir.to_owned(),
            }
        }

        fn new_with_tag(account: &str, repo: &str, tag: &str, to_dir: &str) -> GithubRepoFetcher {
            GithubRepoFetcher {
                url: format!("https://github.com/{}/{}/archive/{}.tar.gz",
                             account,
                             repo,
                             tag),
                base_name: format!("{}-{}", repo, tag),
                to_dir: to_dir.to_owned(),
            }
        }

        fn new_with_semver(account: &str, repo: &str, ver: &str, to_dir: &str) -> GithubRepoFetcher {
            GithubRepoFetcher {
                url: format!("https://github.com/{}/{}/archive/v{}.tar.gz",
                             account,
                             repo,
                             ver),
                base_name: format!("{}-{}", repo, ver),
                to_dir: to_dir.to_owned(),
            }
        }

        fn fetch(&self) -> Result<(), String> {
            let out_dir = env::var_os("OUT_DIR").unwrap();
            let tgz_file_name = format!("{}.tar.gz", self.base_name);
            try!(Command::new("wget")
                     .args(&["-q", "-c", "-O", &tgz_file_name, &self.url])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to execute wget: {}", err))
                     .and_then(|status| if status.success() {
                                   Ok(())
                               } else {
                                   Err(format!("wget exit with {}", status))
                               }));

            try!(Command::new("tar")
                     .args(&["zxf", &tgz_file_name])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to execute tar: {}", err))
                     .and_then(|status| if status.success() {
                                   Ok(())
                               } else {
                                   Err(format!("tar exit with {}", status))
                               }));
            // clean base dir
            try!(Command::new("rm")
                     .args(&["-r", &self.to_dir])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to clean dir: {}", err)));

            Command::new("mv")
                .args(&[&self.base_name, &self.to_dir])
                .current_dir(&out_dir)
                .status()
                .map_err(|err| format!("failed to mv: {}", err))
                .and_then(|status| if status.success() {
                              Ok(())
                          } else {
                              Err(format!("mv exit with {}", status))
                          })
        }
    }

    pub fn build_or_link_grpc(cc: &mut gcc::Config) {
        let out_dir = env::var("OUT_DIR").expect("Can't access OUT_DIR");
        DirBuilder::new()
            .recursive(true)
            .create(format!("{}/{}", out_dir, "grpc"))
            .unwrap();

        GithubRepoFetcher::new_with_semver("grpc", "grpc", GRPC_VERSION, "grpc")
            .fetch()
            .unwrap();

        GithubRepoFetcher::new_with_semver("madler", "zlib", ZLIB_VERSION, "grpc/third_party/zlib")
            .fetch()
            .unwrap();

        GithubRepoFetcher::new_with_hash("google",
                                        "boringssl",
                                        BORINGSSL_COMMIT_HASH,
                                        "grpc/third_party/boringssl")
                .fetch()
                .unwrap();

        GithubRepoFetcher::new_with_tag("c-ares",
                                       "c-ares",
                                       CARES_TAG,
                                       "grpc/third_party/cares/cares")
                .fetch()
                .unwrap();

        // fix multiple _main symbols
        let _ = Command::new("rm")
            .current_dir(&format!("{}/grpc/third_party/cares/cares", out_dir))
            .args(&["acountry.c", "adig.c", "ahost.c"])
            .status();

        let dst = cmake::Config::new(format!("{}/grpc", out_dir))
            .build_target("grpc")
            .build();

        println!("cargo:rustc-link-search=native={}/build", dst.display());
        println!("cargo:rustc-link-search=native={}/build/third_party/cares",
                 dst.display());
        println!("cargo:rustc-link-search=native={}/build/third_party/zlib",
                 dst.display());

        println!("cargo:rustc-link-lib=static=z");
        println!("cargo:rustc-link-lib=static=cares");
        println!("cargo:rustc-link-lib=static=gpr");
        println!("cargo:rustc-link-lib=static=grpc");

        cc.include(format!("{}/grpc/include", out_dir));
    }
}

fn main() {
    let mut cc = gcc::Config::new();

    imp::build_or_link_grpc(&mut cc);

    cc.file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<|endoftext|>


<commit_before>use std::fs::{self, File};
use std::collections::HashMap;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use liquid::Value;
use rss;
use jsonfeed::Feed;
use jsonfeed;

use datetime;
use document::Document;
use error::{ErrorKind, Result};
use config::{Config, SortOrder};
use files::FilesBuilder;
use frontmatter;

/// The primary build function that transforms a directory into a site
pub fn build(config: &Config) -> Result<()> {
    trace!("Build configuration: {:?}", config);

    let source = Path::new(&config.source);
    let dest = Path::new(&config.dest);

    let template_extensions: Vec<&OsStr> = config
        .template_extensions
        .iter()
        .map(OsStr::new)
        .collect();

    let layouts = source.join(&config.layouts);
    let mut layouts_cache = HashMap::new();
    let posts_path = source.join(&config.posts);

    debug!("Layouts directory: {:?}", layouts);
    debug!("Posts directory: {:?}", posts_path);
    debug!("Draft mode enabled: {}", config.include_drafts);
    if config.include_drafts {
        debug!("Draft directory: {:?}", config.drafts);
    }

    let mut documents = vec![];

    let ignore_dest = {
        let rel_dest = dest.strip_prefix(source);
        if let Ok(rel_dest) = rel_dest {
            let ignore_dest = rel_dest.join("**/*");
            let ignore_dest = ignore_dest
                .to_str()
                .ok_or_else(|| format!("Cannot convert pathname {:?} to UTF-8", rel_dest))?
                .to_owned();
            Some(ignore_dest)
        } else {
            None
        }
    };

    let mut page_files = FilesBuilder::new(source)?;
    page_files
        .add_ignore(&format!("!{}", config.posts))?
        .add_ignore(&format!("!{}/**", config.posts))?
        .add_ignore(&format!("{}/**/_*", config.posts))?
        .add_ignore(&format!("{}/**/_*/**", config.posts))?;
    for line in &config.ignore {
        page_files.add_ignore(line.as_str())?;
    }
    if let Some(ref ignore_dest) = ignore_dest {
        page_files.add_ignore(ignore_dest)?;
    }
    let page_files = page_files.build()?;
    for file_path in page_files
            .files()
            .filter(|p| {
                        template_extensions
                            .contains(&p.extension().unwrap_or_else(|| OsStr::new("")))
                    }) {
        // if the document is in the posts folder it's considered a post
        let src_path = source.join(file_path.as_path());
        let is_post = src_path.starts_with(posts_path.as_path());

        let mut default_front = frontmatter::FrontmatterBuilder::new()
            .set_post(is_post)
            .set_draft(false)
            .set_excerpt_separator(config.excerpt_separator.clone());
        if is_post {
            default_front = default_front.set_permalink(config.post_path.clone());
        }

        let doc = Document::parse(source, &file_path, &file_path, default_front)?;
        if !doc.front.is_draft || config.include_drafts {
            documents.push(doc);
        }
    }

    if config.include_drafts {
        let drafts_root = source.join(&config.drafts);
        let mut draft_files = FilesBuilder::new(drafts_root.as_path())?;
        for line in &config.ignore {
            draft_files.add_ignore(line.as_str())?;
        }
        let draft_files = draft_files.build()?;
        for file_path in draft_files
                .files()
                .filter(|p| {
                            template_extensions
                                .contains(&p.extension().unwrap_or_else(|| OsStr::new("")))
                        }) {
            let new_path = posts_path.join(&file_path);
            let new_path = new_path
                .strip_prefix(source)
                .expect("Entry not in source folder");

            let is_post = true;

            let mut default_front = frontmatter::FrontmatterBuilder::new()
                .set_post(is_post)
                .set_draft(true)
                .set_excerpt_separator(config.excerpt_separator.clone());
            if is_post {
                default_front = default_front.set_permalink(config.post_path.clone());
            }

            let doc = Document::parse(&drafts_root, &file_path, new_path, default_front)?;
            documents.push(doc);
        }
    }

    // January 1, 1970 0:00:00 UTC, the beginning of time
    let default_date = datetime::DateTime::default();

    let (mut posts, documents): (Vec<Document>, Vec<Document>) =
        documents.into_iter().partition(|x| x.front.is_post);

    // sort documents by date, if there's no date (none was provided or it couldn't be read) then
    // fall back to the default date
    posts.sort_by(|a, b| {
                      b.front
                          .published_date
                          .unwrap_or(default_date)
                          .cmp(&a.front.published_date.unwrap_or(default_date))
                  });

    match config.post_order {
        SortOrder::Asc => posts.reverse(),
        SortOrder::Desc => (),
    }

    // collect all posts attributes to pass them to other posts for rendering
    let simple_posts_data: Vec<Value> = posts
        .iter()
        .map(|x| Value::Object(x.attributes.clone()))
        .collect();

    trace!("Generating posts");
    for (i, post) in &mut posts.iter_mut().enumerate() {
        trace!("Generating {}", post.url_path);

        // posts are in reverse date order, so previous post is the next in the list (+1)
        if let Some(previous) = simple_posts_data.get(i + 1) {
            post.attributes
                .insert("previous".to_owned(), previous.clone());
        }
        if i >= 1 {
            if let Some(next) = simple_posts_data.get(i - 1) {
                post.attributes.insert("next".to_owned(), next.clone());
            }
        }

        for dump in config.dump.iter().filter(|d| d.is_doc()) {
            trace!("Dumping {:?}", dump);
            let (content, ext) = post.render_dump(*dump)?;
            let mut file_path = post.file_path.clone();
            let file_name = file_path
                .file_stem()
                .and_then(|p| p.to_str())
                .expect("page must have file name")
                .to_owned();
            let file_name = format!("_{}.{}.{}", file_name, dump, ext);
            file_path.set_file_name(file_name);
            trace!("Generating {:?}", file_path);
            create_document_file(content, &file_path, dest)?;
        }

        let mut context = post.get_render_context(&simple_posts_data);

        post.render_excerpt(&mut context, source, &config.syntax_highlight.theme)?;
        let post_html = post.render(&mut context,
                                    source,
                                    &layouts,
                                    &mut layouts_cache,
                                    &config.syntax_highlight.theme)?;
        create_document_file(post_html, &post.file_path, dest)?;
    }

    // check if we should create an RSS file and create it!
    if let Some(ref path) = config.rss {
        try!(create_rss(path, dest, config, &posts));
    }
    // check if we should create an jsonfeed file and create it!
    if let Some(ref path) = config.jsonfeed {
        try!(create_jsonfeed(path, dest, config, &posts));
    }

    // during post rendering additional attributes such as content were
    // added to posts. collect them so that non-post documents can access them
    let posts_data: Vec<Value> = posts
        .into_iter()
        .map(|x| Value::Object(x.attributes))
        .collect();

    trace!("Generating other documents");
    for mut doc in documents {
        trace!("Generating {}", doc.url_path);

        for dump in config.dump.iter().filter(|d| d.is_doc()) {
            trace!("Dumping {:?}", dump);
            let (content, ext) = doc.render_dump(*dump)?;
            let mut file_path = doc.file_path.clone();
            let file_name = file_path
                .file_stem()
                .and_then(|p| p.to_str())
                .expect("page must have file name")
                .to_owned();
            let file_name = format!("_{}.{}.{}", file_name, dump, ext);
            file_path.set_file_name(file_name);
            trace!("Generating {:?}", file_path);
            create_document_file(content, &file_path, dest)?;
        }

        let mut context = doc.get_render_context(&posts_data);
        let doc_html = doc.render(&mut context,
                                  source,
                                  &layouts,
                                  &mut layouts_cache,
                                  &config.syntax_highlight.theme)?;
        create_document_file(doc_html, doc.file_path, dest)?;
    }

    // copy all remaining files in the source to the destination
    {
        info!("Copying remaining assets");
        let mut asset_files = FilesBuilder::new(source)?;
        for line in &config.ignore {
            asset_files.add_ignore(line.as_str())?;
        }
        if let Some(ref ignore_dest) = ignore_dest {
            asset_files.add_ignore(ignore_dest)?;
        }
        let asset_files = asset_files.build()?;
        for file_path in asset_files
                .files()
                .filter(|p| {
                            !template_extensions
                                 .contains(&p.extension().unwrap_or_else(|| OsStr::new("")))
                        }) {
            {
                let parent_dir = file_path.parent();
                if let Some(parent_dir) = parent_dir {
                    let parent_dir = dest.join(parent_dir);
                    fs::create_dir_all(parent_dir.as_path())?;
                    debug!("Created new directory {:?}", parent_dir);
                }
            }
            let src_file = source.join(file_path.as_path());
            let dest_file = dest.join(file_path);

            fs::copy(src_file.as_path(), dest_file.as_path())
                .map_err(|e| format!("Could not copy {:?} into {:?}: {}", src_file, dest_file, e))?;
            debug!("Copied {:?} to {:?}", src_file, dest_file);
        }
    }

    Ok(())
}

// creates a new RSS file with the contents of the site blog
fn create_rss(path: &str, dest: &Path, config: &Config, posts: &[Document]) -> Result<()> {
    match (&config.name, &config.description, &config.link) {
        // these three fields are mandatory in the RSS standard
        (&Some(ref name), &Some(ref description), &Some(ref link)) => {
            trace!("Generating RSS data");

            let items: Result<Vec<rss::Item>> = posts.iter().map(|doc| doc.to_rss(link)).collect();
            let items = items?;

            let channel = rss::ChannelBuilder::default()
                .title(name.to_owned())
                .link(link.to_owned())
                .description(description.to_owned())
                .items(items)
                .build()?;

            let rss_string = channel.to_string();
            trace!("RSS data: {}", rss_string);

            let rss_path = dest.join(path);

            let mut rss_file = File::create(&rss_path)?;
            rss_file
                .write_all(r#"<?xml version="1.0" encoding="UTF-8"?>"#.as_bytes())?;
            rss_file.write_all(&rss_string.into_bytes())?;
            rss_file.write_all("\n".as_bytes())?;

            info!("Created RSS file at {}", rss_path.display());
            Ok(())
        }
        _ => Err(ErrorKind::ConfigFileMissingFields.into()),
    }
}
// creates a new jsonfeed file with the contents of the site blog
fn create_jsonfeed(path: &str, dest: &Path, config: &Config, posts: &[Document]) -> Result<()> {
    match (&config.name, &config.description, &config.link) {
        (&Some(ref name), &Some(ref desc), &Some(ref link)) => {
            trace!("Generating jsonfeed data");

            let jsonitems = posts.iter().map(|doc| doc.to_jsonfeed(link)).collect();

            let feed = Feed {
                title: name.to_string(),
                items: jsonitems,
                home_page_url: Some(link.to_string()),
                description: Some(desc.to_string()),
                ..Default::default()
            };

            let jsonfeed_string = jsonfeed::to_string(&feed).unwrap();
            let jsonfeed_path = dest.join(path);
            let mut jsonfeed_file = try!(File::create(&jsonfeed_path));
            try!(jsonfeed_file.write_all(&jsonfeed_string.into_bytes()));

            info!("Created jsonfeed file at {}", jsonfeed_path.display());
            Ok(())
        }
        _ => Err(ErrorKind::ConfigFileMissingFields.into()),
    }
}

fn create_document_file<S: AsRef<str>, T: AsRef<Path>, R: Into<PathBuf>>(content: S,
                                                                         relpath: T,
                                                                         dest: R)
                                                                         -> Result<()> {
    create_document_file_internal(content.as_ref(), relpath.as_ref(), dest.into())
}

fn create_document_file_internal(content: &str, relpath: &Path, dest: PathBuf) -> Result<()> {
    // construct target path
    let mut file_path = dest;
    file_path.push(relpath);

    // create target directories if any exist
    if let Some(parent) = file_path.parent() {
        fs::create_dir_all(parent)
            .map_err(|e| format!("Could not create {:?}: {}", parent, e))?;
    }

    let mut file = File::create(&file_path)
        .map_err(|e| format!("Could not create {:?}: {}", file_path, e))?;

    file.write_all(content.as_bytes())?;
    info!("Created {}", file_path.display());
    Ok(())
}
<commit_msg>chore: Fix clippy problems<commit_after>use std::fs::{self, File};
use std::collections::HashMap;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use liquid::Value;
use rss;
use jsonfeed::Feed;
use jsonfeed;

use datetime;
use document::Document;
use error::{ErrorKind, Result};
use config::{Config, SortOrder};
use files::FilesBuilder;
use frontmatter;

/// The primary build function that transforms a directory into a site
pub fn build(config: &Config) -> Result<()> {
    trace!("Build configuration: {:?}", config);

    let source = Path::new(&config.source);
    let dest = Path::new(&config.dest);

    let template_extensions: Vec<&OsStr> = config
        .template_extensions
        .iter()
        .map(OsStr::new)
        .collect();

    let layouts = source.join(&config.layouts);
    let mut layouts_cache = HashMap::new();
    let posts_path = source.join(&config.posts);

    debug!("Layouts directory: {:?}", layouts);
    debug!("Posts directory: {:?}", posts_path);
    debug!("Draft mode enabled: {}", config.include_drafts);
    if config.include_drafts {
        debug!("Draft directory: {:?}", config.drafts);
    }

    let mut documents = vec![];

    let ignore_dest = {
        let rel_dest = dest.strip_prefix(source);
        if let Ok(rel_dest) = rel_dest {
            let ignore_dest = rel_dest.join("**/*");
            let ignore_dest = ignore_dest
                .to_str()
                .ok_or_else(|| format!("Cannot convert pathname {:?} to UTF-8", rel_dest))?
                .to_owned();
            Some(ignore_dest)
        } else {
            None
        }
    };

    let mut page_files = FilesBuilder::new(source)?;
    page_files
        .add_ignore(&format!("!{}", config.posts))?
        .add_ignore(&format!("!{}/**", config.posts))?
        .add_ignore(&format!("{}/**/_*", config.posts))?
        .add_ignore(&format!("{}/**/_*/**", config.posts))?;
    for line in &config.ignore {
        page_files.add_ignore(line.as_str())?;
    }
    if let Some(ref ignore_dest) = ignore_dest {
        page_files.add_ignore(ignore_dest)?;
    }
    let page_files = page_files.build()?;
    for file_path in page_files
            .files()
            .filter(|p| {
                        template_extensions
                            .contains(&p.extension().unwrap_or_else(|| OsStr::new("")))
                    }) {
        // if the document is in the posts folder it's considered a post
        let src_path = source.join(file_path.as_path());
        let is_post = src_path.starts_with(posts_path.as_path());

        let mut default_front = frontmatter::FrontmatterBuilder::new()
            .set_post(is_post)
            .set_draft(false)
            .set_excerpt_separator(config.excerpt_separator.clone());
        if is_post {
            default_front = default_front.set_permalink(config.post_path.clone());
        }

        let doc = Document::parse(source, &file_path, &file_path, default_front)?;
        if !doc.front.is_draft || config.include_drafts {
            documents.push(doc);
        }
    }

    if config.include_drafts {
        let drafts_root = source.join(&config.drafts);
        let mut draft_files = FilesBuilder::new(drafts_root.as_path())?;
        for line in &config.ignore {
            draft_files.add_ignore(line.as_str())?;
        }
        let draft_files = draft_files.build()?;
        for file_path in draft_files
                .files()
                .filter(|p| {
                            template_extensions
                                .contains(&p.extension().unwrap_or_else(|| OsStr::new("")))
                        }) {
            let new_path = posts_path.join(&file_path);
            let new_path = new_path
                .strip_prefix(source)
                .expect("Entry not in source folder");

            let is_post = true;

            let mut default_front = frontmatter::FrontmatterBuilder::new()
                .set_post(is_post)
                .set_draft(true)
                .set_excerpt_separator(config.excerpt_separator.clone());
            if is_post {
                default_front = default_front.set_permalink(config.post_path.clone());
            }

            let doc = Document::parse(&drafts_root, &file_path, new_path, default_front)?;
            documents.push(doc);
        }
    }

    // January 1, 1970 0:00:00 UTC, the beginning of time
    let default_date = datetime::DateTime::default();

    let (mut posts, documents): (Vec<Document>, Vec<Document>) =
        documents.into_iter().partition(|x| x.front.is_post);

    // sort documents by date, if there's no date (none was provided or it couldn't be read) then
    // fall back to the default date
    posts.sort_by(|a, b| {
                      b.front
                          .published_date
                          .unwrap_or(default_date)
                          .cmp(&a.front.published_date.unwrap_or(default_date))
                  });

    match config.post_order {
        SortOrder::Asc => posts.reverse(),
        SortOrder::Desc => (),
    }

    // collect all posts attributes to pass them to other posts for rendering
    let simple_posts_data: Vec<Value> = posts
        .iter()
        .map(|x| Value::Object(x.attributes.clone()))
        .collect();

    trace!("Generating posts");
    for (i, post) in &mut posts.iter_mut().enumerate() {
        trace!("Generating {}", post.url_path);

        // posts are in reverse date order, so previous post is the next in the list (+1)
        if let Some(previous) = simple_posts_data.get(i + 1) {
            post.attributes
                .insert("previous".to_owned(), previous.clone());
        }
        if i >= 1 {
            if let Some(next) = simple_posts_data.get(i - 1) {
                post.attributes.insert("next".to_owned(), next.clone());
            }
        }

        for dump in config.dump.iter().filter(|d| d.is_doc()) {
            trace!("Dumping {:?}", dump);
            let (content, ext) = post.render_dump(*dump)?;
            let mut file_path = post.file_path.clone();
            let file_name = file_path
                .file_stem()
                .and_then(|p| p.to_str())
                .expect("page must have file name")
                .to_owned();
            let file_name = format!("_{}.{}.{}", file_name, dump, ext);
            file_path.set_file_name(file_name);
            trace!("Generating {:?}", file_path);
            create_document_file(content, &file_path, dest)?;
        }

        let mut context = post.get_render_context(&simple_posts_data);

        post.render_excerpt(&mut context, source, &config.syntax_highlight.theme)?;
        let post_html = post.render(&mut context,
                                    source,
                                    &layouts,
                                    &mut layouts_cache,
                                    &config.syntax_highlight.theme)?;
        create_document_file(post_html, &post.file_path, dest)?;
    }

    // check if we should create an RSS file and create it!
    if let Some(ref path) = config.rss {
        try!(create_rss(path, dest, config, &posts));
    }
    // check if we should create an jsonfeed file and create it!
    if let Some(ref path) = config.jsonfeed {
        try!(create_jsonfeed(path, dest, config, &posts));
    }

    // during post rendering additional attributes such as content were
    // added to posts. collect them so that non-post documents can access them
    let posts_data: Vec<Value> = posts
        .into_iter()
        .map(|x| Value::Object(x.attributes))
        .collect();

    trace!("Generating other documents");
    for mut doc in documents {
        trace!("Generating {}", doc.url_path);

        for dump in config.dump.iter().filter(|d| d.is_doc()) {
            trace!("Dumping {:?}", dump);
            let (content, ext) = doc.render_dump(*dump)?;
            let mut file_path = doc.file_path.clone();
            let file_name = file_path
                .file_stem()
                .and_then(|p| p.to_str())
                .expect("page must have file name")
                .to_owned();
            let file_name = format!("_{}.{}.{}", file_name, dump, ext);
            file_path.set_file_name(file_name);
            trace!("Generating {:?}", file_path);
            create_document_file(content, &file_path, dest)?;
        }

        let mut context = doc.get_render_context(&posts_data);
        let doc_html = doc.render(&mut context,
                                  source,
                                  &layouts,
                                  &mut layouts_cache,
                                  &config.syntax_highlight.theme)?;
        create_document_file(doc_html, doc.file_path, dest)?;
    }

    // copy all remaining files in the source to the destination
    {
        info!("Copying remaining assets");
        let mut asset_files = FilesBuilder::new(source)?;
        for line in &config.ignore {
            asset_files.add_ignore(line.as_str())?;
        }
        if let Some(ref ignore_dest) = ignore_dest {
            asset_files.add_ignore(ignore_dest)?;
        }
        let asset_files = asset_files.build()?;
        for file_path in asset_files
                .files()
                .filter(|p| {
                            !template_extensions
                                 .contains(&p.extension().unwrap_or_else(|| OsStr::new("")))
                        }) {
            {
                let parent_dir = file_path.parent();
                if let Some(parent_dir) = parent_dir {
                    let parent_dir = dest.join(parent_dir);
                    fs::create_dir_all(parent_dir.as_path())?;
                    debug!("Created new directory {:?}", parent_dir);
                }
            }
            let src_file = source.join(file_path.as_path());
            let dest_file = dest.join(file_path);

            fs::copy(src_file.as_path(), dest_file.as_path())
                .map_err(|e| format!("Could not copy {:?} into {:?}: {}", src_file, dest_file, e))?;
            debug!("Copied {:?} to {:?}", src_file, dest_file);
        }
    }

    Ok(())
}

// creates a new RSS file with the contents of the site blog
fn create_rss(path: &str, dest: &Path, config: &Config, posts: &[Document]) -> Result<()> {
    match (&config.name, &config.description, &config.link) {
        // these three fields are mandatory in the RSS standard
        (&Some(ref name), &Some(ref description), &Some(ref link)) => {
            trace!("Generating RSS data");

            let items: Result<Vec<rss::Item>> = posts.iter().map(|doc| doc.to_rss(link)).collect();
            let items = items?;

            let channel = rss::ChannelBuilder::default()
                .title(name.to_owned())
                .link(link.to_owned())
                .description(description.to_owned())
                .items(items)
                .build()?;

            let rss_string = channel.to_string();
            trace!("RSS data: {}", rss_string);

            let rss_path = dest.join(path);

            let mut rss_file = File::create(&rss_path)?;
            rss_file
                .write_all(br#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
            rss_file.write_all(&rss_string.into_bytes())?;
            rss_file.write_all(b"\n")?;

            info!("Created RSS file at {}", rss_path.display());
            Ok(())
        }
        _ => Err(ErrorKind::ConfigFileMissingFields.into()),
    }
}
// creates a new jsonfeed file with the contents of the site blog
fn create_jsonfeed(path: &str, dest: &Path, config: &Config, posts: &[Document]) -> Result<()> {
    match (&config.name, &config.description, &config.link) {
        (&Some(ref name), &Some(ref desc), &Some(ref link)) => {
            trace!("Generating jsonfeed data");

            let jsonitems = posts.iter().map(|doc| doc.to_jsonfeed(link)).collect();

            let feed = Feed {
                title: name.to_string(),
                items: jsonitems,
                home_page_url: Some(link.to_string()),
                description: Some(desc.to_string()),
                ..Default::default()
            };

            let jsonfeed_string = jsonfeed::to_string(&feed).unwrap();
            let jsonfeed_path = dest.join(path);
            let mut jsonfeed_file = try!(File::create(&jsonfeed_path));
            try!(jsonfeed_file.write_all(&jsonfeed_string.into_bytes()));

            info!("Created jsonfeed file at {}", jsonfeed_path.display());
            Ok(())
        }
        _ => Err(ErrorKind::ConfigFileMissingFields.into()),
    }
}

fn create_document_file<S: AsRef<str>, T: AsRef<Path>, R: Into<PathBuf>>(content: S,
                                                                         relpath: T,
                                                                         dest: R)
                                                                         -> Result<()> {
    create_document_file_internal(content.as_ref(), relpath.as_ref(), dest.into())
}

fn create_document_file_internal(content: &str, relpath: &Path, dest: PathBuf) -> Result<()> {
    // construct target path
    let mut file_path = dest;
    file_path.push(relpath);

    // create target directories if any exist
    if let Some(parent) = file_path.parent() {
        fs::create_dir_all(parent)
            .map_err(|e| format!("Could not create {:?}: {}", parent, e))?;
    }

    let mut file = File::create(&file_path)
        .map_err(|e| format!("Could not create {:?}: {}", file_path, e))?;

    file.write_all(content.as_bytes())?;
    info!("Created {}", file_path.display());
    Ok(())
}
<|endoftext|>


<commit_before>//! Color definitions.
//!
//! This module should contain all colors used in the game. Later on, we could
//! replace it with a customizeable version, where people could write themes
//! (e.g. in TOML or YAML format).
//!
//! Make sure to contain only semantic names (e.g. "primary" or "background",
//! not "red" or "green").

pub const BLACK: [f32; 4] = [0.0, 0.0,  0.0, 1.0];
pub const YELLOW:[f32; 4] = [1.0, 1.0,  0.22, 1.0];
pub const ORANGE:[f32; 4] = [1.0, 0.61, 0.22, 1.0];
pub const RED:[f32; 4] = [1.0, 0.22, 0.22, 1.0];
pub const LIGHT_BLUE:[f32; 4] = [0.22, 0.22, 1.0, 1.0];
pub const BLUE:[f32; 4] = [0.0, 0.0, 1.0, 1.0];

pub struct Player {
    pub primary: [f32; 4],
    pub secondary: [f32; 4],
}

pub const PLAYERS: [Player; 3] = [
    Player{ primary: ORANGE, secondary: YELLOW },
    Player{ primary: RED, secondary: ORANGE },
    Player{ primary: BLUE, secondary: LIGHT_BLUE },
];
<commit_msg>colors: Add white<commit_after>//! Color definitions.
//!
//! This module should contain all colors used in the game. Later on, we could
//! replace it with a customizeable version, where people could write themes
//! (e.g. in TOML or YAML format).
//!
//! Make sure to contain only semantic names (e.g. "primary" or "background",
//! not "red" or "green").

pub const BLACK: [f32; 4] = [0.0, 0.0,  0.0, 1.0];
pub const WHITE: [f32; 4] = [1.0, 1.0,  1.0, 1.0];
pub const YELLOW:[f32; 4] = [1.0, 1.0,  0.22, 1.0];
pub const ORANGE:[f32; 4] = [1.0, 0.61, 0.22, 1.0];
pub const RED:[f32; 4] = [1.0, 0.22, 0.22, 1.0];
pub const LIGHT_BLUE:[f32; 4] = [0.22, 0.22, 1.0, 1.0];
pub const BLUE:[f32; 4] = [0.0, 0.0, 1.0, 1.0];

pub struct Player {
    pub primary: [f32; 4],
    pub secondary: [f32; 4],
}

pub const PLAYERS: [Player; 3] = [
    Player{ primary: ORANGE, secondary: YELLOW },
    Player{ primary: RED, secondary: ORANGE },
    Player{ primary: BLUE, secondary: LIGHT_BLUE },
];
<|endoftext|>


<commit_before>//! Module containing color-related functions using the `colored` crate.
use colored::{Color, ColoredString, Colorize};
use log::LogLevel;

/// Extension crate allowing the use of `.colored` on LogLevels.
pub trait ColoredLogLevel {
    /// Colors this log level with the given color.
    fn colored(&self, color: Color) -> ColoredString;
}

fn new_colored_string(input: String, color: Color) -> ColoredString {
    let cs = ColoredString::from(input.as_str());
    cs.color(color)
}

/// Configuration specifying colors a log level can be colored as.
#[derive(Copy, Clone)]
pub struct ColoredLogLevelConfig {
    /// The color to color logs with the [`Error`] level.
    ///
    /// [`Error`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Error
    pub error: Color,
    /// The color to color logs with the [`Warn`] level.
    ///
    /// [`Warn`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Warn
    pub warn: Color,
    /// The color to color logs with the [`Info`] level.
    ///
    /// [`Info`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Info
    pub info: Color,
    /// The color to color logs with the [`Debug`] level.
    ///
    /// [`Debug`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Debug
    pub debug: Color,
    /// The color to color logs with the [`Trace`] level.
    ///
    /// [`Trace`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Trace
    pub trace: Color,
}

impl ColoredLogLevelConfig {
    /// Creates a new ColoredLogLevelConfig with the given colors for each log level.
    ///
    /// Use [`ColoredLogLevelConfig::default`] for default colors.
    ///
    /// [`ColoredLogLevelConfig::default`]: #method.default
    #[inline]
    pub fn new(error: Color, warn: Color, info: Color, debug: Color, trace: Color) -> Self {
        ColoredLogLevelConfig {
            trace: trace,
            error: error,
            warn: warn,
            debug: debug,
            info: info,
        }
    }

    /// Retrieves the default configuration. This has:
    ///
    /// - [`Error`] as [`Color::Red`]
    /// - [`Warn`] as [`Color::Yellow`]
    /// - [`Info`] as [`Color::White`]
    /// - [`Debug`] as [`Color::White`]
    /// - [`Trace`] as [`Color::White`]
    ///
    /// [`Error`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Error
    /// [`Warn`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Warn
    /// [`Info`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Info
    /// [`Debug`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Debug
    /// [`Trace`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Trace
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow
    /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red
    pub fn default() -> Self {
        ColoredLogLevelConfig {
            error: Color::Red,
            warn: Color::Yellow,
            debug: Color::White,
            info: Color::White,
            trace: Color::White,
        }
    }

    /// Colors the given log level with the correct color.
    ///
    /// This will output ANSI escapes correctly coloring the log level when printed
    /// to a Unix terminal. Due to behavior of the [`colored`] crate, this does not
    /// function on Windows terminals.
    ///
    /// [`colored`]: https://github.com/mackwic/colored
    pub fn color(&self, level: LogLevel) -> ColoredString {
        level.colored(self.get_color(&level))
    }

    /// Retrieves the color that a log level should be colored as.
    pub fn get_color(&self, level: &LogLevel) -> Color {
        match *level {
            LogLevel::Error => self.error,
            LogLevel::Warn => self.warn,
            LogLevel::Info => self.info,
            LogLevel::Debug => self.debug,
            LogLevel::Trace => self.trace,
        }
    }
}

impl ColoredLogLevel for LogLevel {
    fn colored(&self, color: Color) -> ColoredString {
        let s = format!("{:?}", self);

        new_colored_string(s, color)
    }
}
<commit_msg>Replace constructor-style creation for ColoredLogLevelConfig with builder-style<commit_after>//! Module containing color-related functions using the `colored` crate.
use colored::{Color, ColoredString, Colorize};
use log::LogLevel;

/// Extension crate allowing the use of `.colored` on LogLevels.
pub trait ColoredLogLevel {
    /// Colors this log level with the given color.
    fn colored(&self, color: Color) -> ColoredString;
}

fn new_colored_string(input: String, color: Color) -> ColoredString {
    let cs = ColoredString::from(input.as_str());
    cs.color(color)
}

/// Configuration specifying colors a log level can be colored as.
#[derive(Copy, Clone)]
pub struct ColoredLogLevelConfig {
    /// The color to color logs with the [`Error`] level.
    ///
    /// [`Error`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Error
    pub error: Color,
    /// The color to color logs with the [`Warn`] level.
    ///
    /// [`Warn`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Warn
    pub warn: Color,
    /// The color to color logs with the [`Info`] level.
    ///
    /// [`Info`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Info
    pub info: Color,
    /// The color to color logs with the [`Debug`] level.
    ///
    /// [`Debug`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Debug
    pub debug: Color,
    /// The color to color logs with the [`Trace`] level.
    ///
    /// [`Trace`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Trace
    pub trace: Color,
}

impl ColoredLogLevelConfig {
    /// Creates a new ColoredLogLevelConfig with the default colors.
    ///
    /// This matches the behavior of [`ColoredLogLevelConfig::default`].
    ///
    /// [`ColoredLogLevelConfig::default`]: #method.default
    #[inline]
    pub fn new() -> Self {
        Self::default()
    }

    /// Overrides the [`Error`] level color with the given color.
    ///
    /// The default color is [`Color::Red`].
    ///
    /// [`Error`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Error
    /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red
    pub fn error(&mut self, error: Color) -> &mut Self {
        self.error = error;
        self
    }

    /// Overrides the [`Warn`] level color with the given color.
    ///
    /// The default color is [`Color::Yellow`].
    ///
    /// [`Warn`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Warn
    /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow
    pub fn warn(&mut self, warn: Color) -> &mut Self {
        self.warn = warn;
        self
    }

    /// Overrides the [`Info`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Info`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Info
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn info(&mut self, info: Color) -> &mut Self {
        self.info = info;
        self
    }

    /// Overrides the [`Debug`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Debug`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Debug
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn debug(&mut self, debug: Color) -> &mut Self {
        self.debug = debug;
        self
    }

    /// Overrides the [`Trace`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Trace`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Trace
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn trace(&mut self, trace: Color) -> &mut Self {
        self.trace = trace;
        self
    }

    /// Retrieves the default configuration. This has:
    ///
    /// - [`Error`] as [`Color::Red`]
    /// - [`Warn`] as [`Color::Yellow`]
    /// - [`Info`] as [`Color::White`]
    /// - [`Debug`] as [`Color::White`]
    /// - [`Trace`] as [`Color::White`]
    ///
    /// [`Error`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Error
    /// [`Warn`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Warn
    /// [`Info`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Info
    /// [`Debug`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Debug
    /// [`Trace`]: https://docs.rs/log/0.3/log/enum.LogLevel.html#variant.Trace
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow
    /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red
    pub fn default() -> Self {
        ColoredLogLevelConfig {
            error: Color::Red,
            warn: Color::Yellow,
            debug: Color::White,
            info: Color::White,
            trace: Color::White,
        }
    }

    /// Colors the given log level with the correct color.
    ///
    /// This will output ANSI escapes correctly coloring the log level when printed
    /// to a Unix terminal. Due to behavior of the [`colored`] crate, this does not
    /// function on Windows terminals.
    ///
    /// [`colored`]: https://github.com/mackwic/colored
    pub fn color(&self, level: LogLevel) -> ColoredString {
        level.colored(self.get_color(&level))
    }

    /// Retrieves the color that a log level should be colored as.
    pub fn get_color(&self, level: &LogLevel) -> Color {
        match *level {
            LogLevel::Error => self.error,
            LogLevel::Warn => self.warn,
            LogLevel::Info => self.info,
            LogLevel::Debug => self.debug,
            LogLevel::Trace => self.trace,
        }
    }
}

impl ColoredLogLevel for LogLevel {
    fn colored(&self, color: Color) -> ColoredString {
        let s = format!("{:?}", self);

        new_colored_string(s, color)
    }
}
<|endoftext|>