<commit_before>
//! Utilities for loading and using textures

use std::io;
use std::ptr;
use std::fmt;
use std::error;
use std::path::{Path, PathBuf};
use std::fs::File;
use png;
use gl;
use gl::types::*;

/// A wraper around a OpenGL texture object which can be modified
#[derive(Debug)]
pub struct Texture {
    texture: GLuint,
    pub format: TextureFormat,
    pub width: u32,
    pub height: u32,
}

impl Texture {
    /// Creates a texture from a raw OpenGL handle and some additional data. Intended for internal
    /// use only, use with care!
    pub fn from_raw(texture: GLuint, format: TextureFormat, width: u32, height: u32) -> Texture {
        Texture {
            texture: texture,
            format: format,
            width: width,
            height: height,
        }
    }

    /// Creates a texture from a image file.
    pub fn from_file<P>(path: P) -> Result<Texture, TextureError> where P: AsRef<Path> {
        let mut texture = Texture::new();
        texture.load_file(path)?;
        Ok(texture)
    }

    /// Creates a new texture without any ascociated data
    pub fn new() -> Texture {
        let mut texture = 0;

        unsafe {
            gl::GenTextures(1, &mut texture);
            gl::BindTexture(gl::TEXTURE_2D, texture);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
        }

        Texture {
            texture: texture,
            format: TextureFormat::RGB_8,
            width: 0,
            height: 0,
        }
    }

    /// Attempts to load data from the given image file into this texture. Note that
    /// it is usually more convenient to create a new texture from a file using
    /// [`from_file(path)`](struct.Texture.html#method.from_file)
    ///
    /// # Example
    /// ```rust,no_run
    /// use gondola::texture::Texture;
    ///
    /// let mut texture = Texture::new();
    /// texture.load_file("assets/test.png").expect("Failed to load texture");
    /// ```
    pub fn load_file<P>(&mut self, path: P) -> Result<(), TextureError> where P: AsRef<Path> {
        let path = path.as_ref();
        let (info, data) = load_image(path)?;
        let texture_format = match (info.color_type, info.bit_depth) {
            (png::ColorType::RGBA, png::BitDepth::Eight) => TextureFormat::RGBA_8,
            (png::ColorType::RGB, png::BitDepth::Eight)  => TextureFormat::RGB_8,
            other => {
                let message = format!(
                    "Unsuported texture format ({:?}, {:?}) in \"{}\" ({}:{})",
                    other.0, other.1,
                    path.to_string_lossy(),
                    file!(), line!()
                );
                return Err(TextureError { source: path.to_owned(), error: io::Error::new(io::ErrorKind::Other, message) });
            }
        };
        self.load_data(&data, info.width, info.height, texture_format);
        Ok(())
    }

    /// Sets the data this texture points to
    pub fn load_data(&mut self, data: &[u8], width: u32, height: u32, format: TextureFormat) {
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, self.texture);
            gl::TexImage2D(gl::TEXTURE_2D, 0, // Mipmap level
                           format as GLint, // Internal format
                           width as GLsizei, height as GLsizei, 0, // Size and border
                           format.unsized_format(), // Data format
                           gl::UNSIGNED_BYTE, data.as_ptr() as *const GLvoid);
        }

        self.width = width;
        self.height = height;
        self.format = format;
    }

    /// Sets the data in a sub-region of this texture. The data is expected to be in the
    /// format this texture was initialized to. This texture needs to be initialized
    /// before this method can be used.
    /// Note that there is a debug assertion in place to ensure that the given region
    /// is within the bounds of this texture. If debug assertions are not enabled this
    /// function will return without taking any action.
    pub fn load_data_to_region(&mut self, data: &[u8], x: u32, y: u32, width: u32, height: u32) {
        if x + width > self.width && y + height > self.height {
            debug_assert!(false, "Invalid region passed ({}:{}) Region: (x: {}, y: {}, width: {}, height: {})",
                          file!(), line!(),
                          x, y, width, height);
            return;
        }
        unsafe {
            // OpenGL is allowed to expect rows in pixel data to be aligned
            // at powers of two. This ensures that any data will be accepted.
            gl::PixelStorei(gl::PACK_ALIGNMENT, 1);
            gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);

            gl::BindTexture(gl::TEXTURE_2D, self.texture);
            gl::TexSubImage2D(gl::TEXTURE_2D, 0,
                              x as GLint, y as GLint,
                              width as GLsizei, height as GLsizei,
                              self.format.unsized_format(), // It is unclear whether opengl allows a different format here
                              gl::UNSIGNED_BYTE, data.as_ptr() as *const GLvoid);
        }
    }

    /// Converts this texture to a empty texture of the given size. The contents
    /// of the texture after this operation are undefined.
    pub fn initialize(&mut self, width: u32, height: u32, format: TextureFormat) {
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, self.texture);
            gl::TexImage2D(gl::TEXTURE_2D, 0, // Mipmap level
                           format as GLint, // Internal format
                           width as GLsizei, height as GLsizei, 0, // Size and border
                           format.unsized_format(), // Data format
                           gl::UNSIGNED_BYTE, ptr::null());
        }

        self.width = width;
        self.height = height;
        self.format = format;
    }

    /// Binds this texture to the given texture unit.
    pub fn bind(&self, unit: u32) {
        unsafe {
            gl::ActiveTexture(gl::TEXTURE0 + unit);
            gl::BindTexture(gl::TEXTURE_2D, self.texture);
        }
    }

    /// Unbinds the texture at the given texture unit.
    pub fn unbind(unit: u32) {
        unsafe {
            gl::ActiveTexture(gl::TEXTURE0 + unit);
            gl::BindTexture(gl::TEXTURE_2D, 0);
        }
    }

    /// Sets the filter that is applied when this texture is rendered at a size larger
    /// or smaller sizes than the native size of the texture. A separate filter can be
    /// set for magnification and minification.
    pub fn set_filter(&mut self, mag: TextureFilter, min: TextureFilter) {
        unsafe {
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, mag as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, min as GLint);
        }
    }

    /// Sets the texture filter, allowing for a separate filter to be used when mipmapping
    pub fn set_mipmap_filter(&mut self, mag: TextureFilter, mipmap_mag: TextureFilter,
                             min: TextureFilter, mipmap_min: TextureFilter) {
        unsafe {
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, TextureFilter::mipmap_filter(mag, mipmap_mag) as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, TextureFilter::mipmap_filter(min, mipmap_min) as GLint);
        }
    }

    /// Sets the swizzle mask of this texture. The swizzle mask specifies how data stored
    /// in this texture is seen by other parts of OpenGL. This includes texture samplers
    /// in shaders. This is usefull when using textures with only one or two components
    /// per pixel.
    ///
    /// For example, given a texture with only a red component (That is, its
    /// format is `TextureFormat::R_8` or similar), a texture sampler in a shader will
    /// normaly get a value of type `(r, 0.0, 0.0, 1.0)`. By setting the swizzle mask
    /// to `(SwizzleComp::One, SwizzleComp::One, SwizzleComp::One, SwizzleComp::Red)`
    /// shaders will now see `(1.0, 1.0, 1.0, r)`.
    pub fn set_swizzle_mask(&mut self, masks: (SwizzleComp, SwizzleComp, SwizzleComp, SwizzleComp)) {
        unsafe {
            let masks = [masks.0 as GLint, masks.1 as GLint, masks.2 as GLint, masks.3 as GLint];
            gl::TexParameteriv(gl::TEXTURE_2D, gl::TEXTURE_SWIZZLE_RGBA, &masks as *const _);
        }
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.texture);
        }
    }
}

/// Loads image data from a file
fn load_image(path: &Path) -> Result<(png::OutputInfo, Vec<u8>), TextureError> {
    let file = match File::open(path) {
        Ok(file) => file,
        Err(err) => return Err(TextureError { source: path.to_owned(), error: err }),
    };

    let decoder = png::Decoder::new(file);

    let (info, mut reader) = match decoder.read_info() {
        Ok(result) => result,
        Err(err) => return Err(TextureError { source: path.to_owned(), error: err.into() }),
    };

    let mut buf = vec![0; info.buffer_size()];

    match reader.next_frame(&mut buf) {
        Ok(()) => {},
        Err(err) => return Err(TextureError { source: path.to_owned(), error: err.into() }),
    };

    Ok((info, buf))
}

/// Represents an OpenGL texture filter.
#[repr(u32)] // GLenum is u32
#[derive(Debug, Copy, Clone)]
pub enum TextureFilter {
    Nearest = gl::NEAREST,
    Linear  = gl::LINEAR
}
impl TextureFilter {
    /// Retrieves a OpenGL mipmap filter for mipmaping. The returned `GLenum` can
    /// be used in the same scenarios as ´TextureFilter::* as GLenum´
    fn mipmap_filter(normal: TextureFilter, mipmap: TextureFilter) -> GLenum {
        match normal {
            TextureFilter::Nearest => match mipmap {
                TextureFilter::Nearest => gl::NEAREST_MIPMAP_NEAREST,
                TextureFilter::Linear => gl::NEAREST_MIPMAP_LINEAR,
            },
            TextureFilter::Linear => match mipmap {
                TextureFilter::Nearest => gl::LINEAR_MIPMAP_NEAREST,
                TextureFilter::Linear => gl::LINEAR_MIPMAP_LINEAR,
            },
        }
    }
}

/// Represents a OpenGL texture format.
#[repr(u32)] // GLenum is u32
#[allow(non_camel_case_types, dead_code)]
#[derive(Debug, Copy, Clone)]
pub enum TextureFormat {
    RGBA_F32 = gl::RGBA32F,
    RGBA_F16 = gl::RGBA16F,
    RGB_F32  = gl::RGB32F,
    RGB_F16  = gl::RGB16F,
    R_F32    = gl::R32F,
    R_F16    = gl::R16F,

    RGBA_8   = gl::RGBA8,
    RGB_8    = gl::RGB8,
    R_8      = gl::R8,
}
impl TextureFormat {
    /// Retrieves the unsized version of the given format
    pub fn unsized_format(&self) -> GLenum {
        match *self {
            TextureFormat::RGBA_F32 | TextureFormat::RGBA_F16 | TextureFormat::RGBA_8 => gl::RGBA,
            TextureFormat::RGB_F32 | TextureFormat::RGB_F16 | TextureFormat::RGB_8 => gl::RGB,
            TextureFormat::R_F32 | TextureFormat::R_F16 | TextureFormat::R_8 => gl::RED,
        }
    }

    /// The OpenGL primitive associated with this color format.
    pub fn gl_primitive_enum(&self) -> GLenum {
        match *self {
            TextureFormat::RGBA_F32 | TextureFormat::RGB_F32 | TextureFormat::R_F32 => gl::FLOAT,
            TextureFormat::RGBA_F16 | TextureFormat::RGB_F16 | TextureFormat::R_F16 => gl::FLOAT,
            TextureFormat::RGBA_8 | TextureFormat::RGB_8 | TextureFormat::R_8 => gl::UNSIGNED_BYTE,
        }
    }

    /// The name of the OpenGL primitive associated with this color format.
    pub fn gl_primitive_enum_name(&self) -> &'static str {
        match *self {
            TextureFormat::RGBA_F32 | TextureFormat::RGB_F32 | TextureFormat::R_F32 => "GLfloat",
            TextureFormat::RGBA_F16 | TextureFormat::RGB_F16 | TextureFormat::R_F16 => "GLfloat",
            TextureFormat::RGBA_8 | TextureFormat::RGB_8 | TextureFormat::R_8 => "GLbyte",
        }
    }

    /// The number of components this color format has. For example, `RGB_8` has 3 components.
    pub fn components(&self) -> usize {
        match *self {
            TextureFormat::RGBA_F32 | TextureFormat::RGBA_F16 | TextureFormat::RGBA_8 => 4,
            TextureFormat::RGB_F32 | TextureFormat::RGB_F16 | TextureFormat::RGB_8 => 3,
            TextureFormat::R_F32 | TextureFormat::R_F16 | TextureFormat::R_8 => 1,
        }
    }
}

/// Components that a texture can be mapped to through swizzling. See
/// [`set_swizzle_mask`](struct.Texture.html#method.set_swizzle_mask)
/// for more info.
#[repr(u32)] // GLenum is u32
#[derive(Debug, Copy, Clone)]
pub enum SwizzleComp {
    Red     = gl::RED,
    Green   = gl::GREEN,
    Blue    = gl::BLUE,
    Alpha   = gl::ALPHA,
    One     = gl::ONE,
    Zero    = gl::ZERO,
}

/// A error which can occur during texture loading and creation.
#[derive(Debug)]
pub struct TextureError {
    source: PathBuf,
    error: io::Error,
}

impl error::Error for TextureError {
    fn description(&self) -> &str {
        self.error.description()
    }

    fn cause(&self) -> Option<&error::Error> {
        self.error.cause()
    }
}

impl fmt::Display for TextureError {
    fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "For texture \"{}\": ", self.source.to_string_lossy())?;
        self.error.fmt(&mut f)?;
        Ok(())
    }
}
<commit_msg>Facilitate async texture loading<commit_after>
//! Utilities for loading and using textures

use std::io;
use std::ptr;
use std::fmt;
use std::error;
use std::path::{Path, PathBuf};
use std::fs::File;
use png;
use gl;
use gl::types::*;

/// A wraper around a OpenGL texture object which can be modified
#[derive(Debug)]
pub struct Texture {
    texture: GLuint,
    pub format: TextureFormat,
    pub width: u32,
    pub height: u32,
}

impl Texture {
    /// Creates a texture from a raw OpenGL handle and some additional data. Intended for internal
    /// use only, use with care!
    pub fn from_raw(texture: GLuint, format: TextureFormat, width: u32, height: u32) -> Texture {
        Texture {
            texture: texture,
            format: format,
            width: width,
            height: height,
        }
    }

    /// Creates a texture from a image file.
    pub fn from_file<P>(path: P) -> Result<Texture, TextureError> where P: AsRef<Path> {
        let mut texture = Texture::new();
        texture.load_file(path)?;
        Ok(texture)
    }

    /// Creates a new texture without any ascociated data
    pub fn new() -> Texture {
        let mut texture = 0;

        unsafe {
            gl::GenTextures(1, &mut texture);
            gl::BindTexture(gl::TEXTURE_2D, texture);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
        }

        Texture {
            texture: texture,
            format: TextureFormat::RGB_8,
            width: 0,
            height: 0,
        }
    }

    /// Attempts to load data from the given image file into this texture. Note that
    /// it is usually more convenient to create a new texture from a file using
    /// [`from_file(path)`](struct.Texture.html#method.from_file)
    ///
    /// # Example
    /// ```rust,no_run
    /// use gondola::texture::Texture;
    ///
    /// let mut texture = Texture::new();
    /// texture.load_file("assets/test.png").expect("Failed to load texture");
    /// ```
    pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> Result<(), TextureError> {
        let path = path.as_ref();
        let RawImageData { info, buf } = RawImageData::from_file(path)?;
        let texture_format = match (info.color_type, info.bit_depth) {
            (png::ColorType::RGBA, png::BitDepth::Eight) => TextureFormat::RGBA_8,
            (png::ColorType::RGB, png::BitDepth::Eight)  => TextureFormat::RGB_8,
            other => {
                let message = format!(
                    "Unsuported texture format ({:?}, {:?}) in \"{}\" ({}:{})",
                    other.0, other.1,
                    path.to_string_lossy(),
                    file!(), line!()
                );
                return Err(TextureError { source: Some(path.to_owned()), error: io::Error::new(io::ErrorKind::Other, message) });
            }
        };
        self.load_data(&buf, info.width, info.height, texture_format);
        Ok(())
    }

    /// Attempts to load the given raw image data into this texture. For more info see
    /// [`RawImageData`].
    ///
    /// [`RawImageData`]: struct.RawImageData.html
    pub fn load_raw_image_data(&mut self, data: RawImageData) -> Result<(), TextureError> {
        let texture_format = match (data.info.color_type, data.info.bit_depth) {
            (png::ColorType::RGBA, png::BitDepth::Eight) => TextureFormat::RGBA_8,
            (png::ColorType::RGB, png::BitDepth::Eight)  => TextureFormat::RGB_8,
            other => {
                let message = format!(
                    "Unsuported texture format ({:?}, {:?}) ({}:{})",
                    other.0, other.1, file!(), line!()
                );
                return Err(TextureError { source: None, error: io::Error::new(io::ErrorKind::Other, message) });
            }
        };
        self.load_data(&data.buf, data.info.width, data.info.height, texture_format);
        Ok(())
    }

    /// Sets the data this texture points to
    pub fn load_data(&mut self, data: &[u8], width: u32, height: u32, format: TextureFormat) {
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, self.texture);
            gl::TexImage2D(gl::TEXTURE_2D, 0, // Mipmap level
                           format as GLint, // Internal format
                           width as GLsizei, height as GLsizei, 0, // Size and border
                           format.unsized_format(), // Data format
                           gl::UNSIGNED_BYTE, data.as_ptr() as *const GLvoid);
        }

        self.width = width;
        self.height = height;
        self.format = format;
    }

    /// Sets the data in a sub-region of this texture. The data is expected to be in the
    /// format this texture was initialized to. This texture needs to be initialized
    /// before this method can be used.
    /// Note that there is a debug assertion in place to ensure that the given region
    /// is within the bounds of this texture. If debug assertions are not enabled this
    /// function will return without taking any action.
    pub fn load_data_to_region(&mut self, data: &[u8], x: u32, y: u32, width: u32, height: u32) {
        if x + width > self.width && y + height > self.height {
            debug_assert!(false, "Invalid region passed ({}:{}) Region: (x: {}, y: {}, width: {}, height: {})",
                          file!(), line!(),
                          x, y, width, height);
            return;
        }
        unsafe {
            // OpenGL is allowed to expect rows in pixel data to be aligned
            // at powers of two. This ensures that any data will be accepted.
            gl::PixelStorei(gl::PACK_ALIGNMENT, 1);
            gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);

            gl::BindTexture(gl::TEXTURE_2D, self.texture);
            gl::TexSubImage2D(gl::TEXTURE_2D, 0,
                              x as GLint, y as GLint,
                              width as GLsizei, height as GLsizei,
                              self.format.unsized_format(), // It is unclear whether opengl allows a different format here
                              gl::UNSIGNED_BYTE, data.as_ptr() as *const GLvoid);
        }
    }

    /// Converts this texture to a empty texture of the given size. The contents
    /// of the texture after this operation are undefined.
    pub fn initialize(&mut self, width: u32, height: u32, format: TextureFormat) {
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, self.texture);
            gl::TexImage2D(gl::TEXTURE_2D, 0, // Mipmap level
                           format as GLint, // Internal format
                           width as GLsizei, height as GLsizei, 0, // Size and border
                           format.unsized_format(), // Data format
                           gl::UNSIGNED_BYTE, ptr::null());
        }

        self.width = width;
        self.height = height;
        self.format = format;
    }

    /// Binds this texture to the given texture unit.
    pub fn bind(&self, unit: u32) {
        unsafe {
            gl::ActiveTexture(gl::TEXTURE0 + unit);
            gl::BindTexture(gl::TEXTURE_2D, self.texture);
        }
    }

    /// Unbinds the texture at the given texture unit.
    pub fn unbind(unit: u32) {
        unsafe {
            gl::ActiveTexture(gl::TEXTURE0 + unit);
            gl::BindTexture(gl::TEXTURE_2D, 0);
        }
    }

    /// Sets the filter that is applied when this texture is rendered at a size larger
    /// or smaller sizes than the native size of the texture. A separate filter can be
    /// set for magnification and minification.
    pub fn set_filter(&mut self, mag: TextureFilter, min: TextureFilter) {
        unsafe {
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, mag as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, min as GLint);
        }
    }

    /// Sets the texture filter, allowing for a separate filter to be used when mipmapping
    pub fn set_mipmap_filter(&mut self, mag: TextureFilter, mipmap_mag: TextureFilter,
                             min: TextureFilter, mipmap_min: TextureFilter) {
        unsafe {
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, TextureFilter::mipmap_filter(mag, mipmap_mag) as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, TextureFilter::mipmap_filter(min, mipmap_min) as GLint);
        }
    }

    /// Sets the swizzle mask of this texture. The swizzle mask specifies how data stored
    /// in this texture is seen by other parts of OpenGL. This includes texture samplers
    /// in shaders. This is usefull when using textures with only one or two components
    /// per pixel.
    ///
    /// For example, given a texture with only a red component (That is, its
    /// format is `TextureFormat::R_8` or similar), a texture sampler in a shader will
    /// normaly get a value of type `(r, 0.0, 0.0, 1.0)`. By setting the swizzle mask
    /// to `(SwizzleComp::One, SwizzleComp::One, SwizzleComp::One, SwizzleComp::Red)`
    /// shaders will now see `(1.0, 1.0, 1.0, r)`.
    pub fn set_swizzle_mask(&mut self, masks: (SwizzleComp, SwizzleComp, SwizzleComp, SwizzleComp)) {
        unsafe {
            let masks = [masks.0 as GLint, masks.1 as GLint, masks.2 as GLint, masks.3 as GLint];
            gl::TexParameteriv(gl::TEXTURE_2D, gl::TEXTURE_SWIZZLE_RGBA, &masks as *const _);
        }
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.texture);
        }
    }
}

/// Raw image data loaded from a png file. This data can then be loaded into a texture
/// using [`Texture::load_raw_image_data`]. When loading very large textures it can be
/// beneficial to load the raw image data from the texture on a separate thread, and then
/// pass it to a texture in the main thread for performance reasons.
///
/// Note that textures must allways be created in the same thread as they are used in, because
/// of OpenGL limitations. You can call [`RawImageData::from_file`] from anywhere, but only
/// ever create textures in the rendering tread (usually the main thread).
///
/// [`Texture::load_raw_image_data`]: struct.Texture.html#method.load_raw_image_data
/// [`RawImageData::from_file`]: struct.RawImageData.html#method.from_file
pub struct RawImageData {
    info: png::OutputInfo,
    buf: Vec<u8>,
}

impl RawImageData {
    /// Does not invoke any OpenGL functions, and can thus be called from any thread.
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<RawImageData, TextureError> {
        let path = path.as_ref();

        // Open file
        let file = match File::open(path) {
            Ok(file) => file,
            Err(err) => return Err(TextureError { source: Some(path.to_owned()), error: err }),
        };

        let decoder = png::Decoder::new(file);
        let (info, mut reader) = match decoder.read_info() {
            Ok(result) => result,
            Err(err) => return Err(TextureError { source: Some(path.to_owned()), error: err.into() }),
        };

        // Read data into buffer (This is what makes texture loading slow)
        let mut buf = vec![0; info.buffer_size()];
        match reader.next_frame(&mut buf) {
            Ok(()) => {},
            Err(err) => return Err(TextureError { source: Some(path.to_owned()), error: err.into() }),
        };

        Ok(RawImageData {
            info: info,
            buf: buf,
        })
    }
}

/// Represents an OpenGL texture filter.
#[repr(u32)] // GLenum is u32
#[derive(Debug, Copy, Clone)]
pub enum TextureFilter {
    Nearest = gl::NEAREST,
    Linear  = gl::LINEAR
}
impl TextureFilter {
    /// Retrieves a OpenGL mipmap filter for mipmaping. The returned `GLenum` can
    /// be used in the same scenarios as ´TextureFilter::* as GLenum´
    fn mipmap_filter(normal: TextureFilter, mipmap: TextureFilter) -> GLenum {
        match normal {
            TextureFilter::Nearest => match mipmap {
                TextureFilter::Nearest => gl::NEAREST_MIPMAP_NEAREST,
                TextureFilter::Linear => gl::NEAREST_MIPMAP_LINEAR,
            },
            TextureFilter::Linear => match mipmap {
                TextureFilter::Nearest => gl::LINEAR_MIPMAP_NEAREST,
                TextureFilter::Linear => gl::LINEAR_MIPMAP_LINEAR,
            },
        }
    }
}

/// Represents a OpenGL texture format.
#[repr(u32)] // GLenum is u32
#[allow(non_camel_case_types, dead_code)]
#[derive(Debug, Copy, Clone)]
pub enum TextureFormat {
    RGBA_F32 = gl::RGBA32F,
    RGBA_F16 = gl::RGBA16F,
    RGB_F32  = gl::RGB32F,
    RGB_F16  = gl::RGB16F,
    R_F32    = gl::R32F,
    R_F16    = gl::R16F,

    RGBA_8   = gl::RGBA8,
    RGB_8    = gl::RGB8,
    R_8      = gl::R8,
}
impl TextureFormat {
    /// Retrieves the unsized version of the given format
    pub fn unsized_format(&self) -> GLenum {
        match *self {
            TextureFormat::RGBA_F32 | TextureFormat::RGBA_F16 | TextureFormat::RGBA_8 => gl::RGBA,
            TextureFormat::RGB_F32 | TextureFormat::RGB_F16 | TextureFormat::RGB_8 => gl::RGB,
            TextureFormat::R_F32 | TextureFormat::R_F16 | TextureFormat::R_8 => gl::RED,
        }
    }

    /// The OpenGL primitive associated with this color format.
    pub fn gl_primitive_enum(&self) -> GLenum {
        match *self {
            TextureFormat::RGBA_F32 | TextureFormat::RGB_F32 | TextureFormat::R_F32 => gl::FLOAT,
            TextureFormat::RGBA_F16 | TextureFormat::RGB_F16 | TextureFormat::R_F16 => gl::FLOAT,
            TextureFormat::RGBA_8 | TextureFormat::RGB_8 | TextureFormat::R_8 => gl::UNSIGNED_BYTE,
        }
    }

    /// The name of the OpenGL primitive associated with this color format.
    pub fn gl_primitive_enum_name(&self) -> &'static str {
        match *self {
            TextureFormat::RGBA_F32 | TextureFormat::RGB_F32 | TextureFormat::R_F32 => "GLfloat",
            TextureFormat::RGBA_F16 | TextureFormat::RGB_F16 | TextureFormat::R_F16 => "GLfloat",
            TextureFormat::RGBA_8 | TextureFormat::RGB_8 | TextureFormat::R_8 => "GLbyte",
        }
    }

    /// The number of components this color format has. For example, `RGB_8` has 3 components.
    pub fn components(&self) -> usize {
        match *self {
            TextureFormat::RGBA_F32 | TextureFormat::RGBA_F16 | TextureFormat::RGBA_8 => 4,
            TextureFormat::RGB_F32 | TextureFormat::RGB_F16 | TextureFormat::RGB_8 => 3,
            TextureFormat::R_F32 | TextureFormat::R_F16 | TextureFormat::R_8 => 1,
        }
    }
}

/// Components that a texture can be mapped to through swizzling. See
/// [`set_swizzle_mask`](struct.Texture.html#method.set_swizzle_mask)
/// for more info.
#[repr(u32)] // GLenum is u32
#[derive(Debug, Copy, Clone)]
pub enum SwizzleComp {
    Red     = gl::RED,
    Green   = gl::GREEN,
    Blue    = gl::BLUE,
    Alpha   = gl::ALPHA,
    One     = gl::ONE,
    Zero    = gl::ZERO,
}

/// A error which can occur during texture loading and creation.
#[derive(Debug)]
pub struct TextureError {
    source: Option<PathBuf>,
    error: io::Error,
}

impl error::Error for TextureError {
    fn description(&self) -> &str {
        self.error.description()
    }

    fn cause(&self) -> Option<&error::Error> {
        self.error.cause()
    }
}

impl fmt::Display for TextureError {
    fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref source) = self.source {
            write!(f, "For texture \"{}\": ", source.to_string_lossy())?;
        }

        self.error.fmt(&mut f)?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Jerome Rasky <jerome@rasky.co>
//
// Licensed under the Apache License, version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     <http://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. See the
// License for the specific language concerning governing permissions and
// limitations under the License.
use std::io::prelude::*;

use std::io::BufReader;
use std::sync::Arc;
use std::sync::mpsc::Sender;
use std::sync::atomic::{AtomicBool, Ordering};
use std::fs::File;
use std::borrow::Borrow;
use std::thread::JoinHandle;
use std::collections::HashMap;
use std::rc::Rc;
use std::iter::FromIterator;

use std::env;
use std::io;
use std::thread;

use flx::{SearchBase, LineInfo};
use error::{StrError, StrResult};
use bis_c;

use types::*;
use constants::*;

pub fn start_threads(emit: Sender<Event>) -> StrResult<(JoinHandle<()>, Arc<AtomicBool>)> {
    // mask sigint
    trys!(bis_c::mask_sigint(), "Failed to mask SIGINT");

    // start the sigint thread
    let signal_emit = emit.clone();
    thread::spawn(move || {
        read_signals(signal_emit);
    });

    // start reading history
    let history_emit = emit.clone();
    thread::spawn(move || {
        read_history(history_emit);
    });

    // start the input thread
    let input_stop = Arc::new(AtomicBool::new(false));
    let stop = input_stop.clone();
    let input_thread = thread::spawn(|| read_input(emit, stop));

    Ok((input_thread, input_stop))
}

fn read_history(emit: Sender<Event>) {
    let history_path = match env::var("HISTFILE") {
        Ok(path) => path,
        Err(env::VarError::NotPresent) => {
            debug!("History file not found, defaulting to ~/.bash_history");
            "~/.bash_history".into()
        },
        Err(e) => {
            panic!("Failed to get history file path: {}", e);
        }
    };
    let input_file = BufReader::new(trysp!(File::open(history_path),
                                           "Cauld not open history file"));
    let mut count = 0.0;
    let mut set: HashMap<Rc<String>, LineInfo> = HashMap::new();

    for maybe_line in input_file.lines() {
        if let Ok(line) = maybe_line {
            let line = Rc::new(line);
            let item = set.entry(line.clone()).or_insert(LineInfo::new(line.as_str(), 0.0));
            let old_count = item.get_factor();
            item.set_factor(old_count + count);
            count += 1.0;
        }
    }

    // create the search base
    let base = SearchBase::from_iter(set.into_iter().map(|(_, info)| info));

    // if this fails, we can't search anything
    trysp!(emit.send(Event::SearchReady(base)),
           "Failed to emit search ready signal");
}

fn read_input(emit: Sender<Event>, stop: Arc<AtomicBool>) {
    // this thread is joined on quit, so none of its sends should fail
    let mut escape = None;

    // escape sequence tree, assume ANSI
    let mut control: HashMap<String, Option<Event>> = HashMap::default();
    control.insert(format!("["), None);
    control.insert(format!("[A"), Some(Event::KeyUp));
    control.insert(format!("[B"), Some(Event::KeyDown));

    // read characters
    for maybe_chr in io::stdin().chars() {
        match maybe_chr {
            Err(_) => {
                error!("Failed to read input, quitting");
                trysp!(emit.send(Event::Quit(false)), "Failed to emit quit signal");
                break;
            }
            Ok(ESC) => {
                escape = Some(String::default());
                trace!("Begin escape sequence");
            }
            Ok(chr) => {
                match escape {
                    None => {
                        if chr.is_control() {
                            match chr {
                                EOT => {
                                    // stop
                                    trysp!(emit.send(Event::Quit(false)),
                                           "Failed to send quit event");
                                }
                                CTRL_U => {
                                    // clear query
                                    trysp!(emit.send(Event::Clear), "Failed to send clear event");
                                }
                                '\n' => {
                                    // exit
                                    trysp!(emit.send(Event::Quit(true)),
                                           "Failed to send quit signal");
                                }
                                ESC => {
                                    // escape sequence
                                    escape = Some(format!(""));
                                }
                                BSPC => {
                                    // backspace
                                    trysp!(emit.send(Event::Backspace),
                                           "Failed to send backspace signal");
                                }
                                ch => {
                                    trace!("Unknown control character {:?}", ch);
                                    trysp!(emit.send(Event::Bell), "Failed to send bell event");
                                }
                            }
                        } else {
                            trysp!(emit.send(Event::Input(chr)), "Failed to send character");
                        }
                    }
                    Some(mut seq) => {
                        seq.push(chr);
                        trace!("Sequence: {:?}", seq);
                        match control.get(&seq) {
                            None => {
                                // no possible escape sequence like this
                                trysp!(emit.send(Event::Bell), "Failed to send bell event");
                                trysp!(emit.send(Event::Input(chr)), "Failed to send character");
                                escape = None;
                            }
                            Some(&None) => {
                                // keep going
                                escape = Some(seq);
                            }
                            Some(&Some(ref event)) => {
                                // send the appropriate event
                                let cloned = trysp!(event.maybe_clone()
                                                         .ok_or(StrError::new("Event {:?} \
                                                                               could not be \
                                                                               cloned",
                                                                              None)),
                                                    "Failed to create event");
                                trysp!(emit.send(cloned), "Failed to send escape event");
                                escape = None;
                            }
                        }
                    }
                }
            }
        }

        // check for requested stop
        if stop.load(Ordering::Relaxed) {
            debug!("Input thread exiting");
            break;
        }
    }
}

fn read_signals(emit: Sender<Event>) {
    // wait for a sigint
    trysp!(bis_c::wait_sigint(), "Failed to wait for sigint");

    debug!("Caught sigint");

    // send a quit signal
    let _ = emit.send(Event::Quit(false));
    // might happen after events is closed, so don't fail
}

pub fn start_query(emit: Sender<Event>, base: Arc<SearchBase>, query: Arc<String>) {
    let result = base.query::<&String>(query.borrow(), MATCH_NUMBER);
    let _ = emit.send(Event::Match(result, query)).and_then(|_| {
        trace!("Finished query");
        Ok(())
    });
    // don't panic on fail send, events might be already closed
}
<commit_msg>Correctly expand to ~/.bash_history<commit_after>// Copyright 2015 Jerome Rasky <jerome@rasky.co>
//
// Licensed under the Apache License, version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     <http://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. See the
// License for the specific language concerning governing permissions and
// limitations under the License.
use std::io::prelude::*;

use std::io::BufReader;
use std::sync::Arc;
use std::sync::mpsc::Sender;
use std::sync::atomic::{AtomicBool, Ordering};
use std::fs::File;
use std::borrow::Borrow;
use std::thread::JoinHandle;
use std::collections::HashMap;
use std::rc::Rc;
use std::iter::FromIterator;

use std::env;
use std::io;
use std::thread;

use flx::{SearchBase, LineInfo};
use error::{StrError, StrResult};
use bis_c;

use types::*;
use constants::*;

pub fn start_threads(emit: Sender<Event>) -> StrResult<(JoinHandle<()>, Arc<AtomicBool>)> {
    // mask sigint
    trys!(bis_c::mask_sigint(), "Failed to mask SIGINT");

    // start the sigint thread
    let signal_emit = emit.clone();
    thread::spawn(move || {
        read_signals(signal_emit);
    });

    // start reading history
    let history_emit = emit.clone();
    thread::spawn(move || {
        read_history(history_emit);
    });

    // start the input thread
    let input_stop = Arc::new(AtomicBool::new(false));
    let stop = input_stop.clone();
    let input_thread = thread::spawn(|| read_input(emit, stop));

    Ok((input_thread, input_stop))
}

fn read_history(emit: Sender<Event>) {
    let history_path = match env::var("HISTFILE") {
        Ok(path) => path.into(),
        Err(env::VarError::NotPresent) => {
            debug!("History file not found, defaulting to ~/.bash_history");
            let mut home = env::home_dir().unwrap_or("".into());
            home.push(".bash_history");
            home
        },
        Err(e) => {
            panic!("Failed to get history file path: {}", e);
        }
    };

    trace!("History path: {:?}", history_path);

    let input_file = BufReader::new(trysp!(File::open(history_path),
                                           "Cauld not open history file"));
    let mut count = 0.0;
    let mut set: HashMap<Rc<String>, LineInfo> = HashMap::new();

    for maybe_line in input_file.lines() {
        if let Ok(line) = maybe_line {
            let line = Rc::new(line);
            let item = set.entry(line.clone()).or_insert(LineInfo::new(line.as_str(), 0.0));
            let old_count = item.get_factor();
            item.set_factor(old_count + count);
            count += 1.0;
        }
    }

    // create the search base
    let base = SearchBase::from_iter(set.into_iter().map(|(_, info)| info));

    // if this fails, we can't search anything
    trysp!(emit.send(Event::SearchReady(base)),
           "Failed to emit search ready signal");
}

fn read_input(emit: Sender<Event>, stop: Arc<AtomicBool>) {
    // this thread is joined on quit, so none of its sends should fail
    let mut escape = None;

    // escape sequence tree, assume ANSI
    let mut control: HashMap<String, Option<Event>> = HashMap::default();
    control.insert(format!("["), None);
    control.insert(format!("[A"), Some(Event::KeyUp));
    control.insert(format!("[B"), Some(Event::KeyDown));

    // read characters
    for maybe_chr in io::stdin().chars() {
        match maybe_chr {
            Err(_) => {
                error!("Failed to read input, quitting");
                trysp!(emit.send(Event::Quit(false)), "Failed to emit quit signal");
                break;
            }
            Ok(ESC) => {
                escape = Some(String::default());
                trace!("Begin escape sequence");
            }
            Ok(chr) => {
                match escape {
                    None => {
                        if chr.is_control() {
                            match chr {
                                EOT => {
                                    // stop
                                    trysp!(emit.send(Event::Quit(false)),
                                           "Failed to send quit event");
                                }
                                CTRL_U => {
                                    // clear query
                                    trysp!(emit.send(Event::Clear), "Failed to send clear event");
                                }
                                '\n' => {
                                    // exit
                                    trysp!(emit.send(Event::Quit(true)),
                                           "Failed to send quit signal");
                                }
                                ESC => {
                                    // escape sequence
                                    escape = Some(format!(""));
                                }
                                BSPC => {
                                    // backspace
                                    trysp!(emit.send(Event::Backspace),
                                           "Failed to send backspace signal");
                                }
                                ch => {
                                    trace!("Unknown control character {:?}", ch);
                                    trysp!(emit.send(Event::Bell), "Failed to send bell event");
                                }
                            }
                        } else {
                            trysp!(emit.send(Event::Input(chr)), "Failed to send character");
                        }
                    }
                    Some(mut seq) => {
                        seq.push(chr);
                        trace!("Sequence: {:?}", seq);
                        match control.get(&seq) {
                            None => {
                                // no possible escape sequence like this
                                trysp!(emit.send(Event::Bell), "Failed to send bell event");
                                trysp!(emit.send(Event::Input(chr)), "Failed to send character");
                                escape = None;
                            }
                            Some(&None) => {
                                // keep going
                                escape = Some(seq);
                            }
                            Some(&Some(ref event)) => {
                                // send the appropriate event
                                let cloned = trysp!(event.maybe_clone()
                                                         .ok_or(StrError::new("Event {:?} \
                                                                               could not be \
                                                                               cloned",
                                                                              None)),
                                                    "Failed to create event");
                                trysp!(emit.send(cloned), "Failed to send escape event");
                                escape = None;
                            }
                        }
                    }
                }
            }
        }

        // check for requested stop
        if stop.load(Ordering::Relaxed) {
            debug!("Input thread exiting");
            break;
        }
    }
}

fn read_signals(emit: Sender<Event>) {
    // wait for a sigint
    trysp!(bis_c::wait_sigint(), "Failed to wait for sigint");

    debug!("Caught sigint");

    // send a quit signal
    let _ = emit.send(Event::Quit(false));
    // might happen after events is closed, so don't fail
}

pub fn start_query(emit: Sender<Event>, base: Arc<SearchBase>, query: Arc<String>) {
    let result = base.query::<&String>(query.borrow(), MATCH_NUMBER);
    let _ = emit.send(Event::Match(result, query)).and_then(|_| {
        trace!("Finished query");
        Ok(())
    });
    // don't panic on fail send, events might be already closed
}
<|endoftext|>


<commit_before>use std::io::{self, Write};
use std::path::Path;
use std::sync::Arc;

use regex::bytes::Regex;
use term::{self, Terminal};
use term::color::*;
use term::terminfo::TermInfo;

use terminal::TerminfoTerminal;
use types::FileTypeDef;

use self::Writer::*;

/// Printer encapsulates all output logic for searching.
///
/// Note that we currently ignore all write errors. It's probably worthwhile
/// to fix this, but printers are only ever used for writes to stdout or
/// writes to memory, neither of which commonly fail.
pub struct Printer<W> {
    /// The underlying writer.
    wtr: Writer<W>,
    /// Whether anything has been printed to wtr yet.
    has_printed: bool,
    /// The string to use to separate non-contiguous runs of context lines.
    context_separator: Vec<u8>,
    /// The end-of-line terminator used by the printer. In general, eols are
    /// printed via the match directly, but occasionally we need to insert them
    /// ourselves (for example, to print a context separator).
    eol: u8,
    /// Whether to show file name as a heading or not.
    ///
    /// N.B. If with_filename is false, then this setting has no effect.
    heading: bool,
    /// Whether to suppress all output.
    quiet: bool,
    /// A string to use as a replacement of each match in a matching line.
    replace: Option<Vec<u8>>,
    /// Whether to prefix each match with the corresponding file name.
    with_filename: bool,
}

impl<W: Send + io::Write> Printer<W> {
    /// Create a new printer that writes to wtr.
    ///
    /// `color` should be true if the printer should try to use coloring.
    pub fn new(wtr: W, color: bool) -> Printer<W> {
        Printer {
            wtr: Writer::new(wtr, color),
            has_printed: false,
            context_separator: "--".to_string().into_bytes(),
            eol: b'\n',
            heading: false,
            quiet: false,
            replace: None,
            with_filename: false,
        }
    }

    /// Set the context separator. The default is `--`.
    pub fn context_separator(mut self, sep: Vec<u8>) -> Printer<W> {
        self.context_separator = sep;
        self
    }

    /// Set the end-of-line terminator. The default is `\n`.
    pub fn eol(mut self, eol: u8) -> Printer<W> {
        self.eol = eol;
        self
    }

    /// Whether to show file name as a heading or not.
    ///
    /// N.B. If with_filename is false, then this setting has no effect.
    pub fn heading(mut self, yes: bool) -> Printer<W> {
        self.heading = yes;
        self
    }

    /// When set, all output is suppressed.
    pub fn quiet(mut self, yes: bool) -> Printer<W> {
        self.quiet = yes;
        self
    }

    /// Replace every match in each matching line with the replacement string
    /// given.
    ///
    /// The replacement string syntax is documented here:
    /// https://doc.rust-lang.org/regex/regex/bytes/struct.Captures.html#method.expand
    pub fn replace(mut self, replacement: Vec<u8>) -> Printer<W> {
        self.replace = Some(replacement);
        self
    }

    /// When set, each match is prefixed with the file name that it came from.
    pub fn with_filename(mut self, yes: bool) -> Printer<W> {
        self.with_filename = yes;
        self
    }

    /// Returns true if and only if something has been printed.
    pub fn has_printed(&self) -> bool {
        self.has_printed
    }

    /// Flushes the underlying writer and returns it.
    pub fn into_inner(mut self) -> W {
        let _ = self.wtr.flush();
        self.wtr.into_inner()
    }

    /// Prints a type definition.
    pub fn type_def(&mut self, def: &FileTypeDef) {
        self.write(def.name().as_bytes());
        self.write(b": ");
        let mut first = true;
        for pat in def.patterns() {
            if !first {
                self.write(b", ");
            }
            self.write(pat.as_bytes());
            first = false;
        }
        self.write_eol();
    }

    /// Prints the given path.
    pub fn path<P: AsRef<Path>>(&mut self, path: P) {
        self.write(path.as_ref().to_string_lossy().as_bytes());
        self.write_eol();
    }

    /// Prints the given path and a count of the number of matches found.
    pub fn path_count<P: AsRef<Path>>(&mut self, path: P, count: u64) {
        if self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b":");
        }
        self.write(count.to_string().as_bytes());
        self.write_eol();
    }

    /// Prints the context separator.
    pub fn context_separate(&mut self) {
        // N.B. We can't use `write` here because of borrowing restrictions.
        if self.quiet {
            return;
        }
        if self.context_separator.is_empty() {
            return;
        }
        self.has_printed = true;
        let _ = self.wtr.write_all(&self.context_separator);
        let _ = self.wtr.write_all(&[self.eol]);
    }

    pub fn matched<P: AsRef<Path>>(
        &mut self,
        re: &Regex,
        path: P,
        buf: &[u8],
        start: usize,
        end: usize,
        line_number: Option<u64>,
    ) {
        if self.heading && self.with_filename && !self.has_printed {
            self.write_heading(path.as_ref());
        } else if !self.heading && self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b":");
        }
        if let Some(line_number) = line_number {
            self.line_number(line_number, b':');
        }
        if self.replace.is_some() {
            let line = re.replace_all(
                &buf[start..end], &**self.replace.as_ref().unwrap());
            self.write(&line);
        } else {
            self.write_match(re, &buf[start..end]);
        }
        if buf[start..end].last() != Some(&self.eol) {
            self.write_eol();
        }
    }

    pub fn write_match(&mut self, re: &Regex, buf: &[u8]) {
        if !self.wtr.is_color() {
            self.write(buf);
            return;
        }
        let mut last_written = 0;
        for (s, e) in re.find_iter(buf) {
            self.write(&buf[last_written..s]);
            let _ = self.wtr.fg(BRIGHT_RED);
            let _ = self.wtr.attr(term::Attr::Bold);
            self.write(&buf[s..e]);
            let _ = self.wtr.reset();
            last_written = e;
        }
        self.write(&buf[last_written..]);
    }

    pub fn context<P: AsRef<Path>>(
        &mut self,
        path: P,
        buf: &[u8],
        start: usize,
        end: usize,
        line_number: Option<u64>,
    ) {
        if self.heading && self.with_filename && !self.has_printed {
            self.write_heading(path.as_ref());
        } else if !self.heading && self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b"-");
        }
        if let Some(line_number) = line_number {
            self.line_number(line_number, b'-');
        }
        self.write(&buf[start..end]);
        if buf[start..end].last() != Some(&self.eol) {
            self.write_eol();
        }
    }

    fn write_heading<P: AsRef<Path>>(&mut self, path: P) {
        if self.wtr.is_color() {
            let _ = self.wtr.fg(GREEN);
        }
        self.write(path.as_ref().to_string_lossy().as_bytes());
        self.write_eol();
        if self.wtr.is_color() {
            let _ = self.wtr.reset();
        }
    }

    fn line_number(&mut self, n: u64, sep: u8) {
        if self.wtr.is_color() {
            let _ = self.wtr.fg(YELLOW);
        }
        self.write(n.to_string().as_bytes());
        if self.wtr.is_color() {
            let _ = self.wtr.reset();
        }
        self.write(&[sep]);
    }

    fn write(&mut self, buf: &[u8]) {
        if self.quiet {
            return;
        }
        self.has_printed = true;
        let _ = self.wtr.write_all(buf);
    }

    fn write_eol(&mut self) {
        let eol = self.eol;
        self.write(&[eol]);
    }
}

enum Writer<W> {
    Colored(TerminfoTerminal<W>),
    NoColor(W),
}

lazy_static! {
    static ref TERMINFO: Option<Arc<TermInfo>> = {
        match term::terminfo::TermInfo::from_env() {
            Ok(info) => Some(Arc::new(info)),
            Err(err) => {
                debug!("error loading terminfo for coloring: {}", err);
                None
            }
        }
    };
}

impl<W: Send + io::Write> Writer<W> {
    fn new(wtr: W, color: bool) -> Writer<W> {
        // If we want color, build a TerminfoTerminal and see if the current
        // environment supports coloring. If not, bail with NoColor. To avoid
        // losing our writer (ownership), do this the long way.
        if !color || TERMINFO.is_none() {
            return NoColor(wtr);
        }
        let info = TERMINFO.clone().unwrap();
        let tt = TerminfoTerminal::new_with_terminfo(wtr, info);
        if !tt.supports_color() {
            debug!("environment doesn't support coloring");
            return NoColor(tt.into_inner());
        }
        Colored(tt)
    }

    fn is_color(&self) -> bool {
        match *self {
            Colored(_) => true,
            NoColor(_) => false,
        }
    }

    fn map_result<F>(
        &mut self,
        mut f: F,
    ) -> term::Result<()>
    where F: FnMut(&mut TerminfoTerminal<W>) -> term::Result<()> {
        match *self {
            Colored(ref mut w) => f(w),
            NoColor(_) => Err(term::Error::NotSupported),
        }
    }

    fn map_bool<F>(
        &self,
        mut f: F,
    ) -> bool
    where F: FnMut(&TerminfoTerminal<W>) -> bool {
        match *self {
            Colored(ref w) => f(w),
            NoColor(_) => false,
        }
    }
}

impl<W: Send + io::Write> io::Write for Writer<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            Colored(ref mut w) => w.write(buf),
            NoColor(ref mut w) => w.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            Colored(ref mut w) => w.flush(),
            NoColor(ref mut w) => w.flush(),
        }
    }
}

impl<W: Send + io::Write> term::Terminal for Writer<W> {
    type Output = W;

    fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
        self.map_result(|w| w.fg(fg))
    }

    fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
        self.map_result(|w| w.bg(bg))
    }

    fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
        self.map_result(|w| w.attr(attr))
    }

    fn supports_attr(&self, attr: term::Attr) -> bool {
        self.map_bool(|w| w.supports_attr(attr))
    }

    fn reset(&mut self) -> term::Result<()> {
        self.map_result(|w| w.reset())
    }

    fn supports_reset(&self) -> bool {
        self.map_bool(|w| w.supports_reset())
    }

    fn supports_color(&self) -> bool {
        self.map_bool(|w| w.supports_color())
    }

    fn cursor_up(&mut self) -> term::Result<()> {
        self.map_result(|w| w.cursor_up())
    }

    fn delete_line(&mut self) -> term::Result<()> {
        self.map_result(|w| w.delete_line())
    }

    fn carriage_return(&mut self) -> term::Result<()> {
        self.map_result(|w| w.carriage_return())
    }

    fn get_ref(&self) -> &W {
        match *self {
            Colored(ref w) => w.get_ref(),
            NoColor(ref w) => w,
        }
    }

    fn get_mut(&mut self) -> &mut W {
        match *self {
            Colored(ref mut w) => w.get_mut(),
            NoColor(ref mut w) => w,
        }
    }

    fn into_inner(self) -> W {
        match self {
            Colored(w) => w.into_inner(),
            NoColor(w) => w,
        }
    }
}
<commit_msg>Fix yellow color to match ack.<commit_after>use std::io::{self, Write};
use std::path::Path;
use std::sync::Arc;

use regex::bytes::Regex;
use term::{self, Terminal};
use term::color::*;
use term::terminfo::TermInfo;

use terminal::TerminfoTerminal;
use types::FileTypeDef;

use self::Writer::*;

/// Printer encapsulates all output logic for searching.
///
/// Note that we currently ignore all write errors. It's probably worthwhile
/// to fix this, but printers are only ever used for writes to stdout or
/// writes to memory, neither of which commonly fail.
pub struct Printer<W> {
    /// The underlying writer.
    wtr: Writer<W>,
    /// Whether anything has been printed to wtr yet.
    has_printed: bool,
    /// The string to use to separate non-contiguous runs of context lines.
    context_separator: Vec<u8>,
    /// The end-of-line terminator used by the printer. In general, eols are
    /// printed via the match directly, but occasionally we need to insert them
    /// ourselves (for example, to print a context separator).
    eol: u8,
    /// Whether to show file name as a heading or not.
    ///
    /// N.B. If with_filename is false, then this setting has no effect.
    heading: bool,
    /// Whether to suppress all output.
    quiet: bool,
    /// A string to use as a replacement of each match in a matching line.
    replace: Option<Vec<u8>>,
    /// Whether to prefix each match with the corresponding file name.
    with_filename: bool,
}

impl<W: Send + io::Write> Printer<W> {
    /// Create a new printer that writes to wtr.
    ///
    /// `color` should be true if the printer should try to use coloring.
    pub fn new(wtr: W, color: bool) -> Printer<W> {
        Printer {
            wtr: Writer::new(wtr, color),
            has_printed: false,
            context_separator: "--".to_string().into_bytes(),
            eol: b'\n',
            heading: false,
            quiet: false,
            replace: None,
            with_filename: false,
        }
    }

    /// Set the context separator. The default is `--`.
    pub fn context_separator(mut self, sep: Vec<u8>) -> Printer<W> {
        self.context_separator = sep;
        self
    }

    /// Set the end-of-line terminator. The default is `\n`.
    pub fn eol(mut self, eol: u8) -> Printer<W> {
        self.eol = eol;
        self
    }

    /// Whether to show file name as a heading or not.
    ///
    /// N.B. If with_filename is false, then this setting has no effect.
    pub fn heading(mut self, yes: bool) -> Printer<W> {
        self.heading = yes;
        self
    }

    /// When set, all output is suppressed.
    pub fn quiet(mut self, yes: bool) -> Printer<W> {
        self.quiet = yes;
        self
    }

    /// Replace every match in each matching line with the replacement string
    /// given.
    ///
    /// The replacement string syntax is documented here:
    /// https://doc.rust-lang.org/regex/regex/bytes/struct.Captures.html#method.expand
    pub fn replace(mut self, replacement: Vec<u8>) -> Printer<W> {
        self.replace = Some(replacement);
        self
    }

    /// When set, each match is prefixed with the file name that it came from.
    pub fn with_filename(mut self, yes: bool) -> Printer<W> {
        self.with_filename = yes;
        self
    }

    /// Returns true if and only if something has been printed.
    pub fn has_printed(&self) -> bool {
        self.has_printed
    }

    /// Flushes the underlying writer and returns it.
    pub fn into_inner(mut self) -> W {
        let _ = self.wtr.flush();
        self.wtr.into_inner()
    }

    /// Prints a type definition.
    pub fn type_def(&mut self, def: &FileTypeDef) {
        self.write(def.name().as_bytes());
        self.write(b": ");
        let mut first = true;
        for pat in def.patterns() {
            if !first {
                self.write(b", ");
            }
            self.write(pat.as_bytes());
            first = false;
        }
        self.write_eol();
    }

    /// Prints the given path.
    pub fn path<P: AsRef<Path>>(&mut self, path: P) {
        self.write(path.as_ref().to_string_lossy().as_bytes());
        self.write_eol();
    }

    /// Prints the given path and a count of the number of matches found.
    pub fn path_count<P: AsRef<Path>>(&mut self, path: P, count: u64) {
        if self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b":");
        }
        self.write(count.to_string().as_bytes());
        self.write_eol();
    }

    /// Prints the context separator.
    pub fn context_separate(&mut self) {
        // N.B. We can't use `write` here because of borrowing restrictions.
        if self.quiet {
            return;
        }
        if self.context_separator.is_empty() {
            return;
        }
        self.has_printed = true;
        let _ = self.wtr.write_all(&self.context_separator);
        let _ = self.wtr.write_all(&[self.eol]);
    }

    pub fn matched<P: AsRef<Path>>(
        &mut self,
        re: &Regex,
        path: P,
        buf: &[u8],
        start: usize,
        end: usize,
        line_number: Option<u64>,
    ) {
        if self.heading && self.with_filename && !self.has_printed {
            self.write_heading(path.as_ref());
        } else if !self.heading && self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b":");
        }
        if let Some(line_number) = line_number {
            self.line_number(line_number, b':');
        }
        if self.replace.is_some() {
            let line = re.replace_all(
                &buf[start..end], &**self.replace.as_ref().unwrap());
            self.write(&line);
        } else {
            self.write_match(re, &buf[start..end]);
        }
        if buf[start..end].last() != Some(&self.eol) {
            self.write_eol();
        }
    }

    pub fn write_match(&mut self, re: &Regex, buf: &[u8]) {
        if !self.wtr.is_color() {
            self.write(buf);
            return;
        }
        let mut last_written = 0;
        for (s, e) in re.find_iter(buf) {
            self.write(&buf[last_written..s]);
            let _ = self.wtr.fg(BRIGHT_RED);
            let _ = self.wtr.attr(term::Attr::Bold);
            self.write(&buf[s..e]);
            let _ = self.wtr.reset();
            last_written = e;
        }
        self.write(&buf[last_written..]);
    }

    pub fn context<P: AsRef<Path>>(
        &mut self,
        path: P,
        buf: &[u8],
        start: usize,
        end: usize,
        line_number: Option<u64>,
    ) {
        if self.heading && self.with_filename && !self.has_printed {
            self.write_heading(path.as_ref());
        } else if !self.heading && self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b"-");
        }
        if let Some(line_number) = line_number {
            self.line_number(line_number, b'-');
        }
        self.write(&buf[start..end]);
        if buf[start..end].last() != Some(&self.eol) {
            self.write_eol();
        }
    }

    fn write_heading<P: AsRef<Path>>(&mut self, path: P) {
        if self.wtr.is_color() {
            let _ = self.wtr.fg(GREEN);
        }
        self.write(path.as_ref().to_string_lossy().as_bytes());
        self.write_eol();
        if self.wtr.is_color() {
            let _ = self.wtr.reset();
        }
    }

    fn line_number(&mut self, n: u64, sep: u8) {
        if self.wtr.is_color() {
            let _ = self.wtr.fg(YELLOW);
            let _ = self.wtr.attr(term::Attr::Bold);
        }
        self.write(n.to_string().as_bytes());
        if self.wtr.is_color() {
            let _ = self.wtr.reset();
        }
        self.write(&[sep]);
    }

    fn write(&mut self, buf: &[u8]) {
        if self.quiet {
            return;
        }
        self.has_printed = true;
        let _ = self.wtr.write_all(buf);
    }

    fn write_eol(&mut self) {
        let eol = self.eol;
        self.write(&[eol]);
    }
}

enum Writer<W> {
    Colored(TerminfoTerminal<W>),
    NoColor(W),
}

lazy_static! {
    static ref TERMINFO: Option<Arc<TermInfo>> = {
        match term::terminfo::TermInfo::from_env() {
            Ok(info) => Some(Arc::new(info)),
            Err(err) => {
                debug!("error loading terminfo for coloring: {}", err);
                None
            }
        }
    };
}

impl<W: Send + io::Write> Writer<W> {
    fn new(wtr: W, color: bool) -> Writer<W> {
        // If we want color, build a TerminfoTerminal and see if the current
        // environment supports coloring. If not, bail with NoColor. To avoid
        // losing our writer (ownership), do this the long way.
        if !color || TERMINFO.is_none() {
            return NoColor(wtr);
        }
        let info = TERMINFO.clone().unwrap();
        let tt = TerminfoTerminal::new_with_terminfo(wtr, info);
        if !tt.supports_color() {
            debug!("environment doesn't support coloring");
            return NoColor(tt.into_inner());
        }
        Colored(tt)
    }

    fn is_color(&self) -> bool {
        match *self {
            Colored(_) => true,
            NoColor(_) => false,
        }
    }

    fn map_result<F>(
        &mut self,
        mut f: F,
    ) -> term::Result<()>
    where F: FnMut(&mut TerminfoTerminal<W>) -> term::Result<()> {
        match *self {
            Colored(ref mut w) => f(w),
            NoColor(_) => Err(term::Error::NotSupported),
        }
    }

    fn map_bool<F>(
        &self,
        mut f: F,
    ) -> bool
    where F: FnMut(&TerminfoTerminal<W>) -> bool {
        match *self {
            Colored(ref w) => f(w),
            NoColor(_) => false,
        }
    }
}

impl<W: Send + io::Write> io::Write for Writer<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            Colored(ref mut w) => w.write(buf),
            NoColor(ref mut w) => w.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            Colored(ref mut w) => w.flush(),
            NoColor(ref mut w) => w.flush(),
        }
    }
}

impl<W: Send + io::Write> term::Terminal for Writer<W> {
    type Output = W;

    fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
        self.map_result(|w| w.fg(fg))
    }

    fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
        self.map_result(|w| w.bg(bg))
    }

    fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
        self.map_result(|w| w.attr(attr))
    }

    fn supports_attr(&self, attr: term::Attr) -> bool {
        self.map_bool(|w| w.supports_attr(attr))
    }

    fn reset(&mut self) -> term::Result<()> {
        self.map_result(|w| w.reset())
    }

    fn supports_reset(&self) -> bool {
        self.map_bool(|w| w.supports_reset())
    }

    fn supports_color(&self) -> bool {
        self.map_bool(|w| w.supports_color())
    }

    fn cursor_up(&mut self) -> term::Result<()> {
        self.map_result(|w| w.cursor_up())
    }

    fn delete_line(&mut self) -> term::Result<()> {
        self.map_result(|w| w.delete_line())
    }

    fn carriage_return(&mut self) -> term::Result<()> {
        self.map_result(|w| w.carriage_return())
    }

    fn get_ref(&self) -> &W {
        match *self {
            Colored(ref w) => w.get_ref(),
            NoColor(ref w) => w,
        }
    }

    fn get_mut(&mut self) -> &mut W {
        match *self {
            Colored(ref mut w) => w.get_mut(),
            NoColor(ref mut w) => w,
        }
    }

    fn into_inner(self) -> W {
        match self {
            Colored(w) => w.into_inner(),
            NoColor(w) => w,
        }
    }
}
<|endoftext|>


<commit_before>use {Value, Response};
use parser::parse_response;
use error::RequestError;
use utils::escape_xml;

use reqwest::{Body, Client, RequestBuilder, StatusCode};
use reqwest::header::{ContentType, ContentLength, UserAgent};

use std::io::{self, Cursor, Write};
use std::collections::BTreeMap;

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<Value>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, value: T) -> Self {
        self.args.push(value.into());
        self
    }

    /// Calls the method using the given `Client`.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(&self, client: &Client, url: &str) -> RequestResult {
        self.call_with(client, url, |_| {})
    }

    /// Calls the method, giving the closure a chance to amend the reqwest
    /// `RequestBuilder` before sending.
    pub fn call_with<F>(&self, client: &Client, url: &str, cb: F) -> RequestResult
        where F: Fn(&mut RequestBuilder)
    {
        // First, build the body XML
        let mut body = Vec::new();
        // This unwrap never panics as we are using `Vec<u8>` as a `Write` implementor,
        // and not doing anything else that could return an `Err` in `write_as_xml()`.
        self.write_as_xml(&mut body).unwrap();

        // Send XML-RPC request
        let mut builder = client.post(url);
        builder
            .header(UserAgent::new("Rust xmlrpc"))
            .header(ContentType("text/xml; charset=utf-8".parse().unwrap()))
            .header(ContentLength(body.len() as u64))
            .body(Body::new(Cursor::new(body)));
        cb(&mut builder);
        let mut response = builder.send()?;

        // FIXME Check that the response headers are correct
        if response.status() != StatusCode::Ok {
            Err(RequestError::HttpStatus(format!("{}", response.status())))
        } else {
            // Read the response and parse it
            // FIXME `BufRead`?
            Ok(parse_response(&mut response)?)
        }
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        write!(fmt, r#"<?xml version="1.0" encoding="utf-8"?>"#)?;
        write!(fmt, r#"<methodCall>"#)?;
        write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name))?;
        write!(fmt, r#"    <params>"#)?;
        for value in &self.args {
            write!(fmt, r#"        <param>"#)?;
            value.write_as_xml(fmt)?;
            write!(fmt, r#"        </param>"#)?;
        }
        write!(fmt, r#"    </params>"#)?;
        write!(fmt, r#"</methodCall>"#)?;
        Ok(())
    }

    /// Serialize this `Request` into an XML-RPC struct that can be passed to
    /// the [`system.multicall`](https://mirrors.talideon.com/articles/multicall.html)
    /// XML-RPC method, specifically a struct with two fields:
    ///
    /// * `methodName`: the request name
    /// * `params`: the request arguments
    pub fn into_multicall_struct(self) -> Value {
        let mut multicall_struct: BTreeMap<String, Value> = BTreeMap::new();

        multicall_struct.insert("methodName".into(), self.name.into());
        multicall_struct.insert("params".into(), Value::Array(self.args));

        Value::Struct(multicall_struct)
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<commit_msg>Derive Debug and Clone for Request.<commit_after>use {Value, Response};
use parser::parse_response;
use error::RequestError;
use utils::escape_xml;

use reqwest::{Body, Client, RequestBuilder, StatusCode};
use reqwest::header::{ContentType, ContentLength, UserAgent};

use std::io::{self, Cursor, Write};
use std::collections::BTreeMap;

/// A request to call a procedure.
#[derive(Clone, Debug)]
pub struct Request<'a> {
    name: &'a str,
    args: Vec<Value>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, value: T) -> Self {
        self.args.push(value.into());
        self
    }

    /// Calls the method using the given `Client`.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(&self, client: &Client, url: &str) -> RequestResult {
        self.call_with(client, url, |_| {})
    }

    /// Calls the method, giving the closure a chance to amend the reqwest
    /// `RequestBuilder` before sending.
    pub fn call_with<F>(&self, client: &Client, url: &str, cb: F) -> RequestResult
        where F: Fn(&mut RequestBuilder)
    {
        // First, build the body XML
        let mut body = Vec::new();
        // This unwrap never panics as we are using `Vec<u8>` as a `Write` implementor,
        // and not doing anything else that could return an `Err` in `write_as_xml()`.
        self.write_as_xml(&mut body).unwrap();

        // Send XML-RPC request
        let mut builder = client.post(url);
        builder
            .header(UserAgent::new("Rust xmlrpc"))
            .header(ContentType("text/xml; charset=utf-8".parse().unwrap()))
            .header(ContentLength(body.len() as u64))
            .body(Body::new(Cursor::new(body)));
        cb(&mut builder);
        let mut response = builder.send()?;

        // FIXME Check that the response headers are correct
        if response.status() != StatusCode::Ok {
            Err(RequestError::HttpStatus(format!("{}", response.status())))
        } else {
            // Read the response and parse it
            // FIXME `BufRead`?
            Ok(parse_response(&mut response)?)
        }
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        write!(fmt, r#"<?xml version="1.0" encoding="utf-8"?>"#)?;
        write!(fmt, r#"<methodCall>"#)?;
        write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name))?;
        write!(fmt, r#"    <params>"#)?;
        for value in &self.args {
            write!(fmt, r#"        <param>"#)?;
            value.write_as_xml(fmt)?;
            write!(fmt, r#"        </param>"#)?;
        }
        write!(fmt, r#"    </params>"#)?;
        write!(fmt, r#"</methodCall>"#)?;
        Ok(())
    }

    /// Serialize this `Request` into an XML-RPC struct that can be passed to
    /// the [`system.multicall`](https://mirrors.talideon.com/articles/multicall.html)
    /// XML-RPC method, specifically a struct with two fields:
    ///
    /// * `methodName`: the request name
    /// * `params`: the request arguments
    pub fn into_multicall_struct(self) -> Value {
        let mut multicall_struct: BTreeMap<String, Value> = BTreeMap::new();

        multicall_struct.insert("methodName".into(), self.name.into());
        multicall_struct.insert("params".into(), Value::Array(self.args));

        Value::Struct(multicall_struct)
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<|endoftext|>


<commit_before>//! jsonrpc request
use serde::de::{Deserialize, Deserializer, SeqVisitor, MapVisitor, Error};
use serde_json::value;
use super::{Id, Params, Version, Value};

/// Represents jsonrpc request which is a method call.
#[derive(Debug, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MethodCall {
	/// A String specifying the version of the JSON-RPC protocol.
	/// MUST be exactly "2.0".
	pub jsonrpc: Version,
	/// A String containing the name of the method to be invoked.
	pub method: String,
	/// A Structured value that holds the parameter values to be used
	/// during the invocation of the method. This member MAY be omitted.
	pub params: Option<Params>,
	/// An identifier established by the Client that MUST contain a String,
	/// Number, or NULL value if included. If it is not included it is assumed
	/// to be a notification.
	pub id: Id,
}

/// Represents jsonrpc request which is a notification.
#[derive(Debug, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Notification {
	/// A String specifying the version of the JSON-RPC protocol.
	/// MUST be exactly "2.0".
	pub jsonrpc: Version,
	/// A String containing the name of the method to be invoked.
	pub method: String,
	/// A Structured value that holds the parameter values to be used
	/// during the invocation of the method. This member MAY be omitted.
	pub params: Option<Params>
}

/// Represents single jsonrpc call.
#[derive(Debug, PartialEq)]
pub enum Call {
	MethodCall(MethodCall),
	Notification(Notification),
	Invalid
}

impl Deserialize for Call {
	fn deserialize<D>(deserializer: &mut D) -> Result<Call, D::Error>
	where D: Deserializer {
		let v = try!(Value::deserialize(deserializer));
		Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Call::Notification)
			.or_else(|_| Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Call::MethodCall))
			.map_err(|_| Error::custom("")) // types must match
			.or_else(|_: D::Error | Ok(Call::Invalid))
	}
}

/// Represents jsonrpc request.
#[derive(Debug, PartialEq)]
pub enum Request {
	Single(Call),
	Batch(Vec<Call>)
}

impl Deserialize for Request {
	fn deserialize<D>(deserializer: &mut D) -> Result<Request, D::Error>
	where D: Deserializer {
		let v = try!(Value::deserialize(deserializer));
		Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Request::Batch)
			.or_else(|_| Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Request::Single))
			.map_err(|_| Error::custom("")) // unreachable, but types must match
	}
}

#[test]
fn method_call_serialize() {
	use serde_json;
	use serde_json::Value;

	let m = MethodCall {
		jsonrpc: Version::V2,
		method: "update".to_string(),
		params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)])),
		id: Id::Num(1)
	};

	let serialized = serde_json::to_string(&m).unwrap();

	assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"update","params":[1,2],"id":1}"#);

}

#[test]
fn notification_serialize() {
	use serde_json;
	use serde_json::Value;

	let n = Notification {
		jsonrpc: Version::V2,
		method: "update".to_string(),
		params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)]))
	};

	let serialized = serde_json::to_string(&n).unwrap();

	assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"update","params":[1,2]}"#);
}

#[test]
fn notification_deserialize() {
	use serde_json;
	use serde_json::Value;

	let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1,2]}"#;
	let deserialized: Notification = serde_json::from_str(s).unwrap();

	assert_eq!(deserialized, Notification {
		jsonrpc: Version::V2,
		method: "update".to_string(),
		params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)]))
	});

	let s = r#"{"jsonrpc": "2.0", "method": "foobar"}"#;
	let deserialized: Notification = serde_json::from_str(s).unwrap();

	assert_eq!(deserialized, Notification {
		jsonrpc: Version::V2,
		method: "foobar".to_string(),
		params: None
	});

	let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1,2], "id": 1}"#;
	let deserialized: Result<Notification, _> = serde_json::from_str(s);
	assert!(deserialized.is_err())
}

#[test]
fn call_deserialize() {
	use serde_json;

	let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1]}"#;
	let deserialized: Call = serde_json::from_str(s).unwrap();
	assert_eq!(deserialized, Call::Notification(Notification {
		jsonrpc: Version::V2,
		method: "update".to_owned(),
		params: Some(Params::Array(vec![Value::U64(1)]))
	}));

	let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1], "id": 1}"#;
	let deserialized: Call = serde_json::from_str(s).unwrap();
	assert_eq!(deserialized, Call::MethodCall(MethodCall {
		jsonrpc: Version::V2,
		method: "update".to_owned(),
		params: Some(Params::Array(vec![Value::U64(1)])),
		id: Id::Num(1)
	}));
}

#[test]
fn request_deserialize_batch() {
	use serde_json;

	let s = r#"[1, {"jsonrpc": "2.0", "method": "update", "params": [1,2], "id": 1},{"jsonrpc": "2.0", "method": "update", "params": [1]}]"#;
	let deserialized: Request = serde_json::from_str(s).unwrap();
	assert_eq!(deserialized, Request::Batch(vec![
		Call::Invalid,
		Call::MethodCall(MethodCall {
			jsonrpc: Version::V2,
			method: "update".to_owned(),
			params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)])),
			id: Id::Num(1)
		}),
		Call::Notification(Notification {
			jsonrpc: Version::V2,
			method: "update".to_owned(),
			params: Some(Params::Array(vec![Value::U64(1)]))
		})
	]))
}
<commit_msg>Impl Serialize to Call + Test<commit_after>//! jsonrpc request
use serde::de::{Deserialize, Deserializer, SeqVisitor, MapVisitor, Error};
use serde::ser::{Serialize, Serializer};
use serde_json::value;
use super::{Id, Params, Version, Value, ErrorCode};

/// Represents jsonrpc request which is a method call.
#[derive(Debug, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MethodCall {
	/// A String specifying the version of the JSON-RPC protocol.
	/// MUST be exactly "2.0".
	pub jsonrpc: Version,
	/// A String containing the name of the method to be invoked.
	pub method: String,
	/// A Structured value that holds the parameter values to be used
	/// during the invocation of the method. This member MAY be omitted.
	pub params: Option<Params>,
	/// An identifier established by the Client that MUST contain a String,
	/// Number, or NULL value if included. If it is not included it is assumed
	/// to be a notification.
	pub id: Id,
}

/// Represents jsonrpc request which is a notification.
#[derive(Debug, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Notification {
	/// A String specifying the version of the JSON-RPC protocol.
	/// MUST be exactly "2.0".
	pub jsonrpc: Version,
	/// A String containing the name of the method to be invoked.
	pub method: String,
	/// A Structured value that holds the parameter values to be used
	/// during the invocation of the method. This member MAY be omitted.
	pub params: Option<Params>
}

/// Represents single jsonrpc call.
#[derive(Debug, PartialEq)]
pub enum Call {
	MethodCall(MethodCall),
	Notification(Notification),
	Invalid
}

impl Serialize for Call {
	fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
	where S: Serializer {
		match * self {
			Call::MethodCall(ref m) => m.serialize(serializer),
			Call::Notification(ref n) => n.serialize(serializer),
			Call::Invalid => ErrorCode::InvalidRequest.serialize(serializer)
		}
	}
}

impl Deserialize for Call {
	fn deserialize<D>(deserializer: &mut D) -> Result<Call, D::Error>
	where D: Deserializer {
		let v = try!(Value::deserialize(deserializer));
		Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Call::Notification)
			.or_else(|_| Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Call::MethodCall))
			.map_err(|_| Error::custom("")) // types must match
			.or_else(|_: D::Error | Ok(Call::Invalid))
	}
}

/// Represents jsonrpc request.
#[derive(Debug, PartialEq)]
pub enum Request {
	Single(Call),
	Batch(Vec<Call>)
}

impl Deserialize for Request {
	fn deserialize<D>(deserializer: &mut D) -> Result<Request, D::Error>
	where D: Deserializer {
		let v = try!(Value::deserialize(deserializer));
		Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Request::Batch)
			.or_else(|_| Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Request::Single))
			.map_err(|_| Error::custom("")) // unreachable, but types must match
	}
}

#[test]
fn method_call_serialize() {
	use serde_json;
	use serde_json::Value;

	let m = MethodCall {
		jsonrpc: Version::V2,
		method: "update".to_string(),
		params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)])),
		id: Id::Num(1)
	};

	let serialized = serde_json::to_string(&m).unwrap();
	assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"update","params":[1,2],"id":1}"#);
}

#[test]
fn notification_serialize() {
	use serde_json;
	use serde_json::Value;

	let n = Notification {
		jsonrpc: Version::V2,
		method: "update".to_string(),
		params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)]))
	};

	let serialized = serde_json::to_string(&n).unwrap();
	assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"update","params":[1,2]}"#);
}

#[test]
fn call_serialize() {
	use serde_json;
	use serde_json::Value;

	let n = Call::Notification(Notification {
		jsonrpc: Version::V2,
		method: "update".to_owned(),
		params: Some(Params::Array(vec![Value::U64(1)]))
	});

	let serialized = serde_json::to_string(&n).unwrap();
	assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"update","params":[1]}"#);
}

#[test]
fn notification_deserialize() {
	use serde_json;
	use serde_json::Value;

	let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1,2]}"#;
	let deserialized: Notification = serde_json::from_str(s).unwrap();

	assert_eq!(deserialized, Notification {
		jsonrpc: Version::V2,
		method: "update".to_string(),
		params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)]))
	});

	let s = r#"{"jsonrpc": "2.0", "method": "foobar"}"#;
	let deserialized: Notification = serde_json::from_str(s).unwrap();

	assert_eq!(deserialized, Notification {
		jsonrpc: Version::V2,
		method: "foobar".to_string(),
		params: None
	});

	let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1,2], "id": 1}"#;
	let deserialized: Result<Notification, _> = serde_json::from_str(s);
	assert!(deserialized.is_err())
}

#[test]
fn call_deserialize() {
	use serde_json;

	let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1]}"#;
	let deserialized: Call = serde_json::from_str(s).unwrap();
	assert_eq!(deserialized, Call::Notification(Notification {
		jsonrpc: Version::V2,
		method: "update".to_owned(),
		params: Some(Params::Array(vec![Value::U64(1)]))
	}));

	let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1], "id": 1}"#;
	let deserialized: Call = serde_json::from_str(s).unwrap();
	assert_eq!(deserialized, Call::MethodCall(MethodCall {
		jsonrpc: Version::V2,
		method: "update".to_owned(),
		params: Some(Params::Array(vec![Value::U64(1)])),
		id: Id::Num(1)
	}));
}

#[test]
fn request_deserialize_batch() {
	use serde_json;

	let s = r#"[1, {"jsonrpc": "2.0", "method": "update", "params": [1,2], "id": 1},{"jsonrpc": "2.0", "method": "update", "params": [1]}]"#;
	let deserialized: Request = serde_json::from_str(s).unwrap();
	assert_eq!(deserialized, Request::Batch(vec![
		Call::Invalid,
		Call::MethodCall(MethodCall {
			jsonrpc: Version::V2,
			method: "update".to_owned(),
			params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)])),
			id: Id::Num(1)
		}),
		Call::Notification(Notification {
			jsonrpc: Version::V2,
			method: "update".to_owned(),
			params: Some(Params::Array(vec![Value::U64(1)]))
		})
	]))
}
<|endoftext|>


<commit_before>//! Module for decoding RFC 2047 strings
// use for to_ascii_lowercase
use std::ascii::AsciiExt;
use std::num::from_str_radix;
use rustc_serialize::base64::FromBase64;

use encoding::label::encoding_from_whatwg_label;
use encoding::DecoderTrap;

/// Decode an RFC 2047 string (`s`) into a Rust String.
///
/// Will accept either "Q" encoding (RFC 2047 Section 4.2) or
/// "B" encoding (BASE64)
#[unstable]
pub fn decode_rfc2047(s: &str) -> Option<String> {
    let parts: Vec<&str> = s.split('?').collect();
    if parts.len() != 5 || parts[0] != "=" || parts[4] != "=" {
        None
    } else {
        let charset = parts[1].to_ascii_lowercase();
        let encoding = parts[2].to_ascii_lowercase();
        let content = parts[3];

        let bytes = match encoding.as_slice() {
            "q" => decode_q_encoding(content),
            "b" => decode_base64_encoding(content),
            _ => panic!("Unknown encoding type"),
        };

        // XXX: Relies on WHATWG labels, rather than MIME labels for
        // charset. Consider adding mapping upstream.
        let decoder = encoding_from_whatwg_label(charset.as_slice());

        match (bytes, decoder) {
            (Ok(b), Some(d)) => {
                d.decode(&b[], DecoderTrap::Replace).ok()
            }
            _ => None,
        }
    }
}

#[stable]
pub fn decode_q_encoding(s: &str) -> Result<Vec<u8>, String> {
    let mut result = Vec::new();

    let mut pos = 0us;

    while pos < s.len() {
        let c = s.char_range_at(pos);
        pos = match c.ch {
            '=' => {
                let mut inner_pos = c.next;
                let mut hex_string = String::new();
                for _ in range(0us, 2) {
                    let hex_digit_char = s.char_range_at(inner_pos);
                    hex_string.push(hex_digit_char.ch);
                    inner_pos = hex_digit_char.next;
                }
                // = followed by a newline means a continuation
                if hex_string.as_slice() != "\r\n" {
                    match from_str_radix(hex_string.as_slice(), 16us) {
                        Some(char_val) => { result.push(char_val) },
                        None => { return Err(format!("'{}' is not a hex number", hex_string)) },
                    }
                }
                inner_pos
            }
            _ => {
                result.push(c.ch as u8);
                c.next
            }
        }
    }

    Ok(result)
}

fn decode_base64_encoding(s: &str) -> Result<Vec<u8>, String> {
    match s.from_base64() {
        Ok(bytes) => Ok(bytes),
        Err(_) => Err("Failed to base64 decode".to_string()),
    }
}

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

    struct DecodeTest<'s> {
        input: &'s str,
        output: &'s str,
    }

    #[test]
    fn test_decode() {
        let tests = [
            DecodeTest {
                input: "=?ISO-8859-1?Q?Test=20text?=",
                output: "Test text"
            },
            DecodeTest {
                input: "=?ISO-8859-1?b?VGVzdCB0ZXh0?=",
                output: "Test text"
            },
            DecodeTest {
                input: "=?utf-8?b?44GT44KT44Gr44Gh44Gv44CC?=",
                output: "こんにちは。"
            },
        ];

        for t in tests.iter() {
            assert_eq!(decode_rfc2047(t.input).unwrap(), t.output.to_string());
        }
    }

    #[test]
    fn test_decode_failure() {
        let tests = [
            // Invalid base64
            "=?ISO-8859-1?b?-?=",
            // Not valid RFC 2047
            "=?Doesn't end with equals",
            // Unknown charset
            "=?NOCHARSET?q?foo?=",
        ];

        for t in tests.iter() {
            println!("{}", t);
            assert!(decode_rfc2047(*t).is_none());
        }
    }
}
<commit_msg>Switch to new range syntax.<commit_after>//! Module for decoding RFC 2047 strings
// use for to_ascii_lowercase
use std::ascii::AsciiExt;
use std::num::from_str_radix;
use rustc_serialize::base64::FromBase64;

use encoding::label::encoding_from_whatwg_label;
use encoding::DecoderTrap;

/// Decode an RFC 2047 string (`s`) into a Rust String.
///
/// Will accept either "Q" encoding (RFC 2047 Section 4.2) or
/// "B" encoding (BASE64)
#[unstable]
pub fn decode_rfc2047(s: &str) -> Option<String> {
    let parts: Vec<&str> = s.split('?').collect();
    if parts.len() != 5 || parts[0] != "=" || parts[4] != "=" {
        None
    } else {
        let charset = parts[1].to_ascii_lowercase();
        let encoding = parts[2].to_ascii_lowercase();
        let content = parts[3];

        let bytes = match encoding.as_slice() {
            "q" => decode_q_encoding(content),
            "b" => decode_base64_encoding(content),
            _ => panic!("Unknown encoding type"),
        };

        // XXX: Relies on WHATWG labels, rather than MIME labels for
        // charset. Consider adding mapping upstream.
        let decoder = encoding_from_whatwg_label(charset.as_slice());

        match (bytes, decoder) {
            (Ok(b), Some(d)) => {
                d.decode(&b[], DecoderTrap::Replace).ok()
            }
            _ => None,
        }
    }
}

#[stable]
pub fn decode_q_encoding(s: &str) -> Result<Vec<u8>, String> {
    let mut result = Vec::new();

    let mut pos = 0us;

    while pos < s.len() {
        let c = s.char_range_at(pos);
        pos = match c.ch {
            '=' => {
                let mut inner_pos = c.next;
                let mut hex_string = String::new();
                for _ in 0..2 {
                    let hex_digit_char = s.char_range_at(inner_pos);
                    hex_string.push(hex_digit_char.ch);
                    inner_pos = hex_digit_char.next;
                }
                // = followed by a newline means a continuation
                if hex_string.as_slice() != "\r\n" {
                    match from_str_radix(hex_string.as_slice(), 16us) {
                        Some(char_val) => { result.push(char_val) },
                        None => { return Err(format!("'{}' is not a hex number", hex_string)) },
                    }
                }
                inner_pos
            }
            _ => {
                result.push(c.ch as u8);
                c.next
            }
        }
    }

    Ok(result)
}

fn decode_base64_encoding(s: &str) -> Result<Vec<u8>, String> {
    match s.from_base64() {
        Ok(bytes) => Ok(bytes),
        Err(_) => Err("Failed to base64 decode".to_string()),
    }
}

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

    struct DecodeTest<'s> {
        input: &'s str,
        output: &'s str,
    }

    #[test]
    fn test_decode() {
        let tests = [
            DecodeTest {
                input: "=?ISO-8859-1?Q?Test=20text?=",
                output: "Test text"
            },
            DecodeTest {
                input: "=?ISO-8859-1?b?VGVzdCB0ZXh0?=",
                output: "Test text"
            },
            DecodeTest {
                input: "=?utf-8?b?44GT44KT44Gr44Gh44Gv44CC?=",
                output: "こんにちは。"
            },
        ];

        for t in tests.iter() {
            assert_eq!(decode_rfc2047(t.input).unwrap(), t.output.to_string());
        }
    }

    #[test]
    fn test_decode_failure() {
        let tests = [
            // Invalid base64
            "=?ISO-8859-1?b?-?=",
            // Not valid RFC 2047
            "=?Doesn't end with equals",
            // Unknown charset
            "=?NOCHARSET?q?foo?=",
        ];

        for t in tests.iter() {
            println!("{}", t);
            assert!(decode_rfc2047(*t).is_none());
        }
    }
}
<|endoftext|>