<commit_before>//! # command
//!
//! Runs task commands/scripts.
//!

use log::Logger;
use rand::{Rng, thread_rng};
use std::env;
use std::env::current_dir;
use std::fs::{File, create_dir_all, remove_file};
use std::io::Error;
use std::io::prelude::*;
use std::process::{Command, ExitStatus, Stdio, exit};
use types::Step;

/// Validates the exit code code and if not 0 or unable to validate it, panic.
pub fn validate_exit_code(exit_status: Result<ExitStatus, Error>) {
    match exit_status {
        Ok(code) => {
            if !code.success() {
                match code.code() {
                    Some(value) => {
                        if value != 0 {
                            exit(value);
                        }
                    }
                    None => exit(1),
                }
            }
        }
        Err(error) => panic!("Error while executing command, error: {}", error),
    }
}

/// Runs the requested script text and panics in case of any script error.
pub fn run_script(
    logger: &Logger,
    script_lines: &Vec<String>,
) {
    let name = env!("CARGO_PKG_NAME");
    let file_name: String = thread_rng().gen_ascii_chars().take(10).collect();

    let mut file_path = env::temp_dir();
    file_path.push(name);

    // create parent directory
    match create_dir_all(&file_path) {
        Ok(_) => logger.verbose::<()>("Created temporary directory.", &[], None),
        Err(error) => logger.verbose("Unable to create temporary directory: ", &[&file_path.to_str().unwrap_or("???")], Some(error)),
    };

    file_path.push(file_name);
    if cfg!(windows) {
        file_path.set_extension("bat");
    } else {
        file_path.set_extension("sh");
    };

    let file_path_str = &file_path.to_str().unwrap_or("???");

    logger.verbose::<()>("Creating temporary script file: ", &[&file_path_str], None);

    let mut file = match File::create(&file_path) {
        Err(error) => {
            logger.error("Unable to create script file: ", &[&file_path_str], Some(&error));
            panic!("Unable to create script file, error: {}", error);
        }
        Ok(file) => file,
    };

    let mut text = script_lines.join("\n");

    let cwd_holder = match current_dir() {
        Ok(value) => value,
        Err(error) => {
            logger.error("Unable to get current working directory.", &[], Some(&error));
            panic!("Unable to get current working directory, error: {}", error);
        }
    };

    let cwd = match cwd_holder.to_str() {
        Some(cwd_str) => cwd_str.clone(),
        None => {
            logger.error::<()>("Unable to get current working directory.", &[], None);
            panic!("Unable to get current working directory");
        }
    };

    text.insert_str(0, "\n");
    text.insert_str(0, cwd);
    text.insert_str(0, "cd ");

    match file.write_all(text.as_bytes()) {
        Err(error) => {
            logger.error("Unable to write to script file: ", &[&file_path_str], Some(&error));
            panic!("Unable to write to script file, error: {}", error);
        }
        Ok(_) => logger.verbose::<()>("Written script file text:\n", &[&text], None),
    }

    let command = if cfg!(windows) {
        "cmd"
    } else {
        "sh"
    };

    let args_vector = if cfg!(windows) {
        vec!["-c".to_string(), file_path_str.to_string()]
    } else {
        vec![file_path_str.to_string()]
    };

    let args = Some(args_vector);

    run_command(&logger, &command, &args);

    match remove_file(&file_path_str) {
        Ok(_) => logger.verbose::<()>("Temporary file deleted: ", &[&file_path_str], None),
        Err(error) => logger.verbose("Unable to delete temporary file: ", &[&file_path_str], Some(error)),
    };
}

/// Runs the requested command and panics in case of any error.
pub fn run_command(
    logger: &Logger,
    command_string: &str,
    args: &Option<Vec<String>>,
) {
    logger.verbose::<()>("Execute Command: ", &[&command_string], None);
    let mut command = Command::new(&command_string);

    match *args {
        Some(ref args_vec) => {
            for arg in args_vec.iter() {
                command.arg(arg);
            }
        }
        None => logger.verbose::<()>("No command args defined.", &[], None),
    };

    command.stdin(Stdio::inherit()).stdout(Stdio::inherit()).stderr(Stdio::inherit());
    logger.info("Execute Command: ", &[], Some(&command));

    let exit_status = command.status();
    validate_exit_code(exit_status);
}

/// Runs the given task command and if not defined, the task script.
pub fn run(
    logger: &Logger,
    step: &Step,
) {
    match step.config.command {
        Some(ref command_string) => {
            run_command(&logger, &command_string, &step.config.args);
        }
        None => {
            match step.config.script {
                Some(ref script) => run_script(&logger, script),
                None => logger.verbose::<()>("No script defined.", &[], None),
            }
        }
    };
}

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

    #[test]
    #[should_panic]
    fn validate_exit_code_error() {
        validate_exit_code(Err(Error::new(ErrorKind::Other, "test")));
    }

    #[test]
    fn run_no_command() {
        let logger = log::create("error");
        let task = Task {
            install_crate: None,
            command: None,
            args: None,
            disabled: None,
            alias: None,
            install_script: None,
            script: None,
            dependencies: None
        };

        run(&logger, &task);
    }
}
<commit_msg>more tests<commit_after>//! # command
//!
//! Runs task commands/scripts.
//!

use log::Logger;
use rand::{Rng, thread_rng};
use std::env;
use std::env::current_dir;
use std::fs::{File, create_dir_all, remove_file};
use std::io::Error;
use std::io::prelude::*;
use std::process::{Command, ExitStatus, Stdio, exit};
use types::Step;

/// Validates the exit code code and if not 0 or unable to validate it, panic.
pub fn validate_exit_code(exit_status: Result<ExitStatus, Error>) {
    match exit_status {
        Ok(code) => {
            if !code.success() {
                match code.code() {
                    Some(value) => {
                        if value != 0 {
                            exit(value);
                        }
                    }
                    None => exit(1),
                }
            }
        }
        Err(error) => panic!("Error while executing command, error: {}", error),
    }
}

/// Runs the requested script text and panics in case of any script error.
pub fn run_script(
    logger: &Logger,
    script_lines: &Vec<String>,
) {
    let name = env!("CARGO_PKG_NAME");
    let file_name: String = thread_rng().gen_ascii_chars().take(10).collect();

    let mut file_path = env::temp_dir();
    file_path.push(name);

    // create parent directory
    match create_dir_all(&file_path) {
        Ok(_) => logger.verbose::<()>("Created temporary directory.", &[], None),
        Err(error) => logger.verbose("Unable to create temporary directory: ", &[&file_path.to_str().unwrap_or("???")], Some(error)),
    };

    file_path.push(file_name);
    if cfg!(windows) {
        file_path.set_extension("bat");
    } else {
        file_path.set_extension("sh");
    };

    let file_path_str = &file_path.to_str().unwrap_or("???");

    logger.verbose::<()>("Creating temporary script file: ", &[&file_path_str], None);

    let mut file = match File::create(&file_path) {
        Err(error) => {
            logger.error("Unable to create script file: ", &[&file_path_str], Some(&error));
            panic!("Unable to create script file, error: {}", error);
        }
        Ok(file) => file,
    };

    let mut text = script_lines.join("\n");

    let cwd_holder = match current_dir() {
        Ok(value) => value,
        Err(error) => {
            logger.error("Unable to get current working directory.", &[], Some(&error));
            panic!("Unable to get current working directory, error: {}", error);
        }
    };

    let cwd = match cwd_holder.to_str() {
        Some(cwd_str) => cwd_str.clone(),
        None => {
            logger.error::<()>("Unable to get current working directory.", &[], None);
            panic!("Unable to get current working directory");
        }
    };

    text.insert_str(0, "\n");
    text.insert_str(0, cwd);
    text.insert_str(0, "cd ");

    match file.write_all(text.as_bytes()) {
        Err(error) => {
            logger.error("Unable to write to script file: ", &[&file_path_str], Some(&error));
            panic!("Unable to write to script file, error: {}", error);
        }
        Ok(_) => logger.verbose::<()>("Written script file text:\n", &[&text], None),
    }

    let command = if cfg!(windows) {
        "cmd"
    } else {
        "sh"
    };

    let args_vector = if cfg!(windows) {
        vec!["-c".to_string(), file_path_str.to_string()]
    } else {
        vec![file_path_str.to_string()]
    };

    let args = Some(args_vector);

    run_command(&logger, &command, &args);

    match remove_file(&file_path_str) {
        Ok(_) => logger.verbose::<()>("Temporary file deleted: ", &[&file_path_str], None),
        Err(error) => logger.verbose("Unable to delete temporary file: ", &[&file_path_str], Some(error)),
    };
}

/// Runs the requested command and panics in case of any error.
pub fn run_command(
    logger: &Logger,
    command_string: &str,
    args: &Option<Vec<String>>,
) {
    logger.verbose::<()>("Execute Command: ", &[&command_string], None);
    let mut command = Command::new(&command_string);

    match *args {
        Some(ref args_vec) => {
            for arg in args_vec.iter() {
                command.arg(arg);
            }
        }
        None => logger.verbose::<()>("No command args defined.", &[], None),
    };

    command.stdin(Stdio::inherit()).stdout(Stdio::inherit()).stderr(Stdio::inherit());
    logger.info("Execute Command: ", &[], Some(&command));

    let exit_status = command.status();
    validate_exit_code(exit_status);
}

/// Runs the given task command and if not defined, the task script.
pub fn run(
    logger: &Logger,
    step: &Step,
) {
    match step.config.command {
        Some(ref command_string) => {
            run_command(&logger, &command_string, &step.config.args);
        }
        None => {
            match step.config.script {
                Some(ref script) => run_script(&logger, script),
                None => logger.verbose::<()>("No script defined.", &[], None),
            }
        }
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use log;
    use std::io::ErrorKind;
    use types::Task;

    #[test]
    #[should_panic]
    fn validate_exit_code_error() {
        validate_exit_code(Err(Error::new(ErrorKind::Other, "test")));
    }

    #[test]
    fn run_no_command() {
        let logger = log::create("error");
        let task = Task {
            install_crate: None,
            command: None,
            args: None,
            disabled: None,
            alias: None,
            install_script: None,
            script: None,
            dependencies: None
        };
        let step = Step { name: "test".to_string(), config: task };

        run(&logger, &step);
    }
}
<|endoftext|>


<commit_before>use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;

use half::f16;
use libc::{c_char, c_int};

use openexr_sys::*;

use cexr_type_aliases::*;
use error::*;
use Header;

/// Points to and describes in-memory image data for reading and writing.
///
/// `FrameBuffer` does not store any image data itself, but instead points to
/// and describes image data elsewhere in memory. Those descriptions are then
/// used by the input and output file types to know where in memory to read
/// from and write to when writing and reading files.
pub struct FrameBuffer<'a> {
    handle: *mut CEXR_FrameBuffer,
    dimensions: (usize, usize),
    _phantom_1: PhantomData<CEXR_FrameBuffer>,
    _phantom_2: PhantomData<&'a mut [u8]>,
}

impl<'a> FrameBuffer<'a> {
    /// Creates an empty frame buffer with the given dimensions in pixels.
    ///
    /// `FrameBuffer` does not store any data, therefore its size in memory is
    /// independent of the dimensions specified here.
    pub fn new(width: usize, height: usize) -> Self {
        FrameBuffer {
            handle: unsafe { CEXR_FrameBuffer_new() },
            dimensions: (width, height),
            _phantom_1: PhantomData,
            _phantom_2: PhantomData,
        }
    }

    /// Return the dimensions of the frame buffer.
    pub fn dimensions(&self) -> (usize, usize) {
        self.dimensions
    }

    /// Insert a single channel.
    ///
    /// The channel will be given the name `name` and when reading from a file
    /// will be filled in with the value `fill` if there isn't any pixel data
    /// for that channel in the file.
    ///
    /// `data` is the memory for the channel and should contain precisely
    /// width * height elements, where width and height are the dimensions
    /// of the `FrameBuffer`.
    pub fn insert_channel<T: PixelData>(&mut self, name: &str, fill: f64, data: &'a mut [T]) {
        if data.len() != self.dimensions.0 * self.dimensions.1 {
            panic!("data size of {} elements cannot back {}x{} framebuffer",
                   data.len(),
                   self.dimensions.0,
                   self.dimensions.1);
        }
        let width = self.dimensions.0;
        unsafe {
            self.insert_raw(name,
                            T::pixel_type(),
                            data.as_mut_ptr() as *mut c_char,
                            (mem::size_of::<T>(), width * mem::size_of::<T>()),
                            (1, 1),
                            fill,
                            (false, false))
        };
    }

    /// Insert multiple channels from a slice of structs or tuples.
    ///
    /// The number of channels to be inserted is determined by the
    /// implementation of the `PixelStruct` trait on `T`.  `channels` should
    /// contain that number of elements, and each element is a tuple of the
    /// channel's name and default fill value.
    ///
    /// `data` is the memory for the channel and should contain precisely
    /// width * height elements, where width and height are the dimensions
    /// of the `FrameBuffer`.
    pub fn insert_pixels<T: PixelStruct>(&mut self, channels: &[(&str, f64)], data: &'a mut [T]) {
        if data.len() != self.dimensions.0 * self.dimensions.1 {
            panic!("data size of {} elements cannot back {}x{} framebuffer",
                   data.len(),
                   self.dimensions.0,
                   self.dimensions.1);
        }
        let width = self.dimensions.0;
        for (&(name, fill), (ty, offset)) in channels.iter().zip(T::channels()) {
            unsafe {
                self.insert_raw(name,
                                ty,
                                (data.as_mut_ptr() as *mut c_char).offset(offset as isize),
                                (mem::size_of::<T>(), width * mem::size_of::<T>()),
                                (1, 1),
                                fill,
                                (false, false))
            };
        }
    }

    /// The raw method for inserting a new channel.
    ///
    /// This is very unsafe: the other methods should be preferred unless you
    /// have a special use-case.
    ///
    /// This method corresponds directly to constructing and then inserting a
    /// "Slice" in the C++ OpenEXR library.  Please see its documentation for
    /// details.
    pub unsafe fn insert_raw(&mut self,
                             name: &str,
                             type_: PixelType,
                             base: *mut c_char,
                             stride: (usize, usize),
                             sampling: (c_int, c_int),
                             fill_value: f64,
                             tile_coords: (bool, bool)) {
        let c_name = CString::new(name).unwrap();
        CEXR_FrameBuffer_insert(self.handle,
                                c_name.as_ptr(),
                                type_,
                                base,
                                stride.0,
                                stride.1,
                                sampling.0,
                                sampling.1,
                                fill_value,
                                tile_coords.0 as c_int,
                                tile_coords.1 as c_int);
    }

    // These shouldn't be used outside of this crate, but due to
    // https://github.com/rust-lang/rfcs/pull/1422 not being stable
    // yet (should land in Rust 1.18), just hide from public
    // documentation for now.
    // TODO: once Rust 1.18 comes out, remove these functions and
    // just use direct field access via `pub(crate)`.
    #[doc(hidden)]
    pub fn handle(&self) -> *const CEXR_FrameBuffer {
        self.handle
    }

    #[doc(hidden)]
    pub fn handle_mut(&mut self) -> *mut CEXR_FrameBuffer {
        self.handle
    }

    // TODO: this should probably be part of Header.  It's only here
    // right now to allow access to both struct's internals, but it won't
    // have to be here for that once `pub(crate)` lands in Rust 1.18.
    pub fn validate_header_for_output(&self, header: &Header) -> Result<()> {
        let w = header.data_window();
        if (w.max.x - w.min.x) as usize != self.dimensions().0 - 1 ||
           (w.max.y - w.min.y) as usize != self.dimensions().1 - 1 {
            return Err(Error::Generic(format!("framebuffer size {}x{} does not \
                match output file dimensions {}x{}",
                                              self.dimensions().0,
                                              self.dimensions().1,
                                              w.max.x - w.min.x,
                                              w.max.y - w.min.y)));
        }

        Ok(())
    }
}

impl<'a> Drop for FrameBuffer<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_FrameBuffer_delete(self.handle) };
    }
}

/// Types that are bitwise- and semantically-identical to one of the
/// `PixelType` variants.
///
/// Implementing this trait on a type allows the type to be used directly by the
/// library to write data out to and read data in from EXR files.
///
/// NOTE: unless you really know what you're doing, you probably shouldn't
/// implement this for anything. It's already been implemented for the
/// relevant built-in Rust types.  The only exception might be implementing it
/// for alternative implementations of half floats.
pub unsafe trait PixelData {
    fn pixel_type() -> PixelType;
}

unsafe impl PixelData for u32 {
    fn pixel_type() -> PixelType {
        PixelType::UINT
    }
}

unsafe impl PixelData for f16 {
    fn pixel_type() -> PixelType {
        PixelType::HALF
    }
}

unsafe impl PixelData for f32 {
    fn pixel_type() -> PixelType {
        PixelType::FLOAT
    }
}

/// Types that contain components that are bitwise- and semantically-identical
/// to the `PixelType` variants.
///
/// The intended use of this is to allow e.g. a tuple or struct of RGB values
/// to be used directly by the library to write data out to and read data in
/// from EXR files.  This avoids having to create buffers of converted values.
///
/// # Examples
///
/// ```
/// use openexr::{PixelStruct, PixelType};
///
/// #[repr(C)]
/// #[derive(Copy, Clone)]
/// struct RGB {
///     r: f32,
///     g: f32,
///     b: f32,
/// }
///
/// unsafe impl PixelStruct for RGB {
///     fn channel_count() -> usize { 3 }
///     fn channel(i: usize) -> (PixelType, usize) {
///         [(PixelType::FLOAT, 0),
///          (PixelType::FLOAT, 4),
///          (PixelType::FLOAT, 8)][i]
///     }
/// }
/// ```
pub unsafe trait PixelStruct {
    /// Returns the number of channels in this type
    fn channel_count() -> usize;

    /// Returns the type and offset of channel `i`
    /// # Panics
    /// Will either panic or return garbage when `i >= channel_count()`.
    fn channel(i: usize) -> (PixelType, usize);

    /// Returns an iterator over the set of channels
    fn channels() -> PixelStructChannels {
        (0..Self::channel_count()).map(Self::channel)
    }
}

pub type PixelStructChannels = ::std::iter::Map<::std::ops::Range<usize>,
                                                fn(usize) -> (PixelType, usize)>;

unsafe impl<T: PixelData> PixelStruct for T {
    fn channel_count() -> usize {
        1
    }
    fn channel(_: usize) -> (PixelType, usize) {
        (T::pixel_type(), 0)
    }
}

macro_rules! offset_of {
    ($ty:ty, $field:tt) => {
        unsafe { &(*(0 as *const $ty)).$field as *const _ as usize }
    }
}

unsafe impl<A: PixelData> PixelStruct for (A,) {
    fn channel_count() -> usize {
        1
    }
    fn channel(_: usize) -> (PixelType, usize) {
        (A::pixel_type(), offset_of!(Self, 0))
    }
}

unsafe impl<A, B> PixelStruct for (A, B)
    where A: PixelData,
          B: PixelData
{
    fn channel_count() -> usize {
        2
    }
    fn channel(i: usize) -> (PixelType, usize) {
        [(A::pixel_type(), offset_of!(Self, 0)),
         (B::pixel_type(), offset_of!(Self, 1))][i]
    }
}

unsafe impl<A, B, C> PixelStruct for (A, B, C)
    where A: PixelData,
          B: PixelData,
          C: PixelData
{
    fn channel_count() -> usize {
        3
    }
    fn channel(i: usize) -> (PixelType, usize) {
        [(A::pixel_type(), offset_of!(Self, 0)),
         (B::pixel_type(), offset_of!(Self, 1)),
         (C::pixel_type(), offset_of!(Self, 2))]
            [i]
    }
}

unsafe impl<A, B, C, D> PixelStruct for (A, B, C, D)
    where A: PixelData,
          B: PixelData,
          C: PixelData,
          D: PixelData
{
    fn channel_count() -> usize {
        4
    }
    fn channel(i: usize) -> (PixelType, usize) {
        [(A::pixel_type(), offset_of!(Self, 0)),
         (B::pixel_type(), offset_of!(Self, 1)),
         (C::pixel_type(), offset_of!(Self, 2)),
         (D::pixel_type(), offset_of!(Self, 3))]
            [i]
    }
}

unsafe impl<T: PixelData> PixelStruct for [T; 1] {
    fn channel_count() -> usize {
        1
    }
    fn channel(_: usize) -> (PixelType, usize) {
        (T::pixel_type(), 0)
    }
}

unsafe impl<T: PixelData> PixelStruct for [T; 2] {
    fn channel_count() -> usize {
        2
    }
    fn channel(i: usize) -> (PixelType, usize) {
        (T::pixel_type(), i * mem::size_of::<T>())
    }
}

unsafe impl<T: PixelData> PixelStruct for [T; 3] {
    fn channel_count() -> usize {
        3
    }
    fn channel(i: usize) -> (PixelType, usize) {
        (T::pixel_type(), i * mem::size_of::<T>())
    }
}

unsafe impl<T: PixelData> PixelStruct for [T; 4] {
    fn channel_count() -> usize {
        4
    }
    fn channel(i: usize) -> (PixelType, usize) {
        (T::pixel_type(), i * mem::size_of::<T>())
    }
}
<commit_msg>Let FrameBuffer use the builder pattern.<commit_after>use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;

use half::f16;
use libc::{c_char, c_int};

use openexr_sys::*;

use cexr_type_aliases::*;
use error::*;
use Header;

/// Points to and describes in-memory image data for reading and writing.
///
/// `FrameBuffer` does not store any image data itself, but instead points to
/// and describes image data elsewhere in memory. Those descriptions are then
/// used by the input and output file types to know where in memory to read
/// from and write to when writing and reading files.
pub struct FrameBuffer<'a> {
    handle: *mut CEXR_FrameBuffer,
    dimensions: (usize, usize),
    _phantom_1: PhantomData<CEXR_FrameBuffer>,
    _phantom_2: PhantomData<&'a mut [u8]>,
}

impl<'a> FrameBuffer<'a> {
    /// Creates an empty frame buffer with the given dimensions in pixels.
    ///
    /// `FrameBuffer` does not store any data, therefore its size in memory is
    /// independent of the dimensions specified here.
    pub fn new(width: usize, height: usize) -> Self {
        FrameBuffer {
            handle: unsafe { CEXR_FrameBuffer_new() },
            dimensions: (width, height),
            _phantom_1: PhantomData,
            _phantom_2: PhantomData,
        }
    }

    /// Return the dimensions of the frame buffer.
    pub fn dimensions(&self) -> (usize, usize) {
        self.dimensions
    }

    /// Insert a single channel.
    ///
    /// The channel will be given the name `name` and when reading from a file
    /// will be filled in with the value `fill` if there isn't any pixel data
    /// for that channel in the file.
    ///
    /// `data` is the memory for the channel and should contain precisely
    /// width * height elements, where width and height are the dimensions
    /// of the `FrameBuffer`.
    pub fn insert_channel<T: PixelData>(&mut self, name: &str, fill: f64, data: &'a mut [T]) -> &mut Self {
        if data.len() != self.dimensions.0 * self.dimensions.1 {
            panic!("data size of {} elements cannot back {}x{} framebuffer",
                   data.len(),
                   self.dimensions.0,
                   self.dimensions.1);
        }
        let width = self.dimensions.0;
        unsafe {
            self.insert_raw(name,
                            T::pixel_type(),
                            data.as_mut_ptr() as *mut c_char,
                            (mem::size_of::<T>(), width * mem::size_of::<T>()),
                            (1, 1),
                            fill,
                            (false, false))
        };
        self
    }

    /// Insert multiple channels from a slice of structs or tuples.
    ///
    /// The number of channels to be inserted is determined by the
    /// implementation of the `PixelStruct` trait on `T`.  `channels` should
    /// contain that number of elements, and each element is a tuple of the
    /// channel's name and default fill value.
    ///
    /// `data` is the memory for the channel and should contain precisely
    /// width * height elements, where width and height are the dimensions
    /// of the `FrameBuffer`.
    pub fn insert_pixels<T: PixelStruct>(&mut self, channels: &[(&str, f64)], data: &'a mut [T]) -> &mut Self {
        if data.len() != self.dimensions.0 * self.dimensions.1 {
            panic!("data size of {} elements cannot back {}x{} framebuffer",
                   data.len(),
                   self.dimensions.0,
                   self.dimensions.1);
        }
        let width = self.dimensions.0;
        for (&(name, fill), (ty, offset)) in channels.iter().zip(T::channels()) {
            unsafe {
                self.insert_raw(name,
                                ty,
                                (data.as_mut_ptr() as *mut c_char).offset(offset as isize),
                                (mem::size_of::<T>(), width * mem::size_of::<T>()),
                                (1, 1),
                                fill,
                                (false, false))
            };
        }
        self
    }

    /// The raw method for inserting a new channel.
    ///
    /// This is very unsafe: the other methods should be preferred unless you
    /// have a special use-case.
    ///
    /// This method corresponds directly to constructing and then inserting a
    /// "Slice" in the C++ OpenEXR library.  Please see its documentation for
    /// details.
    pub unsafe fn insert_raw(&mut self,
                             name: &str,
                             type_: PixelType,
                             base: *mut c_char,
                             stride: (usize, usize),
                             sampling: (c_int, c_int),
                             fill_value: f64,
                             tile_coords: (bool, bool)) -> &mut Self {
        let c_name = CString::new(name).unwrap();
        CEXR_FrameBuffer_insert(self.handle,
                                c_name.as_ptr(),
                                type_,
                                base,
                                stride.0,
                                stride.1,
                                sampling.0,
                                sampling.1,
                                fill_value,
                                tile_coords.0 as c_int,
                                tile_coords.1 as c_int);
        self
    }

    // These shouldn't be used outside of this crate, but due to
    // https://github.com/rust-lang/rfcs/pull/1422 not being stable
    // yet (should land in Rust 1.18), just hide from public
    // documentation for now.
    // TODO: once Rust 1.18 comes out, remove these functions and
    // just use direct field access via `pub(crate)`.
    #[doc(hidden)]
    pub fn handle(&self) -> *const CEXR_FrameBuffer {
        self.handle
    }

    #[doc(hidden)]
    pub fn handle_mut(&mut self) -> *mut CEXR_FrameBuffer {
        self.handle
    }

    // TODO: this should probably be part of Header.  It's only here
    // right now to allow access to both struct's internals, but it won't
    // have to be here for that once `pub(crate)` lands in Rust 1.18.
    pub fn validate_header_for_output(&self, header: &Header) -> Result<()> {
        let w = header.data_window();
        if (w.max.x - w.min.x) as usize != self.dimensions().0 - 1 ||
           (w.max.y - w.min.y) as usize != self.dimensions().1 - 1 {
            return Err(Error::Generic(format!("framebuffer size {}x{} does not \
                match output file dimensions {}x{}",
                                              self.dimensions().0,
                                              self.dimensions().1,
                                              w.max.x - w.min.x,
                                              w.max.y - w.min.y)));
        }

        Ok(())
    }
}

impl<'a> Drop for FrameBuffer<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_FrameBuffer_delete(self.handle) };
    }
}

/// Types that are bitwise- and semantically-identical to one of the
/// `PixelType` variants.
///
/// Implementing this trait on a type allows the type to be used directly by the
/// library to write data out to and read data in from EXR files.
///
/// NOTE: unless you really know what you're doing, you probably shouldn't
/// implement this for anything. It's already been implemented for the
/// relevant built-in Rust types.  The only exception might be implementing it
/// for alternative implementations of half floats.
pub unsafe trait PixelData {
    fn pixel_type() -> PixelType;
}

unsafe impl PixelData for u32 {
    fn pixel_type() -> PixelType {
        PixelType::UINT
    }
}

unsafe impl PixelData for f16 {
    fn pixel_type() -> PixelType {
        PixelType::HALF
    }
}

unsafe impl PixelData for f32 {
    fn pixel_type() -> PixelType {
        PixelType::FLOAT
    }
}

/// Types that contain components that are bitwise- and semantically-identical
/// to the `PixelType` variants.
///
/// The intended use of this is to allow e.g. a tuple or struct of RGB values
/// to be used directly by the library to write data out to and read data in
/// from EXR files.  This avoids having to create buffers of converted values.
///
/// # Examples
///
/// ```
/// use openexr::{PixelStruct, PixelType};
///
/// #[repr(C)]
/// #[derive(Copy, Clone)]
/// struct RGB {
///     r: f32,
///     g: f32,
///     b: f32,
/// }
///
/// unsafe impl PixelStruct for RGB {
///     fn channel_count() -> usize { 3 }
///     fn channel(i: usize) -> (PixelType, usize) {
///         [(PixelType::FLOAT, 0),
///          (PixelType::FLOAT, 4),
///          (PixelType::FLOAT, 8)][i]
///     }
/// }
/// ```
pub unsafe trait PixelStruct {
    /// Returns the number of channels in this type
    fn channel_count() -> usize;

    /// Returns the type and offset of channel `i`
    /// # Panics
    /// Will either panic or return garbage when `i >= channel_count()`.
    fn channel(i: usize) -> (PixelType, usize);

    /// Returns an iterator over the set of channels
    fn channels() -> PixelStructChannels {
        (0..Self::channel_count()).map(Self::channel)
    }
}

pub type PixelStructChannels = ::std::iter::Map<::std::ops::Range<usize>,
                                                fn(usize) -> (PixelType, usize)>;

unsafe impl<T: PixelData> PixelStruct for T {
    fn channel_count() -> usize {
        1
    }
    fn channel(_: usize) -> (PixelType, usize) {
        (T::pixel_type(), 0)
    }
}

macro_rules! offset_of {
    ($ty:ty, $field:tt) => {
        unsafe { &(*(0 as *const $ty)).$field as *const _ as usize }
    }
}

unsafe impl<A: PixelData> PixelStruct for (A,) {
    fn channel_count() -> usize {
        1
    }
    fn channel(_: usize) -> (PixelType, usize) {
        (A::pixel_type(), offset_of!(Self, 0))
    }
}

unsafe impl<A, B> PixelStruct for (A, B)
    where A: PixelData,
          B: PixelData
{
    fn channel_count() -> usize {
        2
    }
    fn channel(i: usize) -> (PixelType, usize) {
        [(A::pixel_type(), offset_of!(Self, 0)),
         (B::pixel_type(), offset_of!(Self, 1))][i]
    }
}

unsafe impl<A, B, C> PixelStruct for (A, B, C)
    where A: PixelData,
          B: PixelData,
          C: PixelData
{
    fn channel_count() -> usize {
        3
    }
    fn channel(i: usize) -> (PixelType, usize) {
        [(A::pixel_type(), offset_of!(Self, 0)),
         (B::pixel_type(), offset_of!(Self, 1)),
         (C::pixel_type(), offset_of!(Self, 2))]
            [i]
    }
}

unsafe impl<A, B, C, D> PixelStruct for (A, B, C, D)
    where A: PixelData,
          B: PixelData,
          C: PixelData,
          D: PixelData
{
    fn channel_count() -> usize {
        4
    }
    fn channel(i: usize) -> (PixelType, usize) {
        [(A::pixel_type(), offset_of!(Self, 0)),
         (B::pixel_type(), offset_of!(Self, 1)),
         (C::pixel_type(), offset_of!(Self, 2)),
         (D::pixel_type(), offset_of!(Self, 3))]
            [i]
    }
}

unsafe impl<T: PixelData> PixelStruct for [T; 1] {
    fn channel_count() -> usize {
        1
    }
    fn channel(_: usize) -> (PixelType, usize) {
        (T::pixel_type(), 0)
    }
}

unsafe impl<T: PixelData> PixelStruct for [T; 2] {
    fn channel_count() -> usize {
        2
    }
    fn channel(i: usize) -> (PixelType, usize) {
        (T::pixel_type(), i * mem::size_of::<T>())
    }
}

unsafe impl<T: PixelData> PixelStruct for [T; 3] {
    fn channel_count() -> usize {
        3
    }
    fn channel(i: usize) -> (PixelType, usize) {
        (T::pixel_type(), i * mem::size_of::<T>())
    }
}

unsafe impl<T: PixelData> PixelStruct for [T; 4] {
    fn channel_count() -> usize {
        4
    }
    fn channel(i: usize) -> (PixelType, usize) {
        (T::pixel_type(), i * mem::size_of::<T>())
    }
}
<|endoftext|>


<commit_before>
use std::ffi::CStr;
use std::error::Error;
use std::fmt::{self,Display,Formatter};
use std::default::Default;

use libc::{c_int,c_ulong};

use super::device::{DeviceFileDescriptor,BlockMode,ReadWriteMode,DeviceResult,DeviceError};

#[allow(dead_code,non_camel_case_types,non_snake_case)]
mod ffi;

pub mod caps;

#[allow(dead_code,non_camel_case_types,non_snake_case)]
pub mod properties {
    use super::ffi as ffi;

    include!(concat!(env!("OUT_DIR"), "/frontend-properties.rs"));
}

pub struct Frontend {
    device: DeviceFileDescriptor
}

impl Frontend {
    pub fn open(filename: &str, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Frontend> {
        let device = try!(DeviceFileDescriptor::open(filename, rw_mode, block_mode));
        Ok(Frontend { device: device })
    }

    pub fn get_info(&self) -> DeviceResult<FrontendInfo> {
        let mut ffi_info: ffi::Struct_dvb_frontend_info = Default::default();
        try!(self.device.ioctl_pointer(ffi::FE_GET_INFO as c_ulong, &mut ffi_info));
        let c_name = unsafe { CStr::from_ptr(ffi_info.name.as_ptr()) };
        Ok(FrontendInfo {
            name: String::from_utf8_lossy(c_name.to_bytes()).into_owned(),
            frequency_min: ffi_info.frequency_min,
            frequency_max: ffi_info.frequency_max,
            frequency_stepsize: ffi_info.frequency_stepsize,
            frequency_tolerance: ffi_info.frequency_tolerance,
            symbol_rate_min: ffi_info.symbol_rate_min,
            symbol_rate_max: ffi_info.symbol_rate_max,
            symbol_rate_tolerance: ffi_info.symbol_rate_tolerance,
            // Is truncate a good choice?
            caps: caps::FrontendCaps::from_bits_truncate(ffi_info.caps),
        })
    }
    pub fn read_status(&self) -> DeviceResult<FrontendStatus> {
        let mut ffi_status: ffi::Enum_fe_status = 0;
        try!(self.device.ioctl_pointer(ffi::FE_READ_STATUS as c_ulong, &mut ffi_status));
        Ok(FrontendStatus {
            has_signal: ffi_status & ffi::FE_HAS_SIGNAL != 0,
            has_carrier: ffi_status & ffi::FE_HAS_CARRIER != 0,
            has_viterbi: ffi_status & ffi::FE_HAS_VITERBI != 0,
            has_sync: ffi_status & ffi::FE_HAS_SYNC != 0,
            has_lock: ffi_status & ffi::FE_HAS_LOCK != 0,
            timedout: ffi_status & ffi::FE_TIMEDOUT != 0,
            reinit: ffi_status & ffi::FE_REINIT != 0,
        })
    }
    pub fn set_properties(&self, properties: &[properties::SetPropertyValue]) -> DeviceResult<()> {
        unimplemented!();
    }

    pub fn get_properties(&self, properties: &[properties::GetProperty]) -> PropertyResult<Vec<properties::GetPropertyValue>> {
        let mut ffi_property_list: Vec<ffi::Struct_dtv_property> = properties.iter().map(
            |p| ffi::Struct_dtv_property { cmd: (*p).into(), ..Default::default() }
        ).collect();
        let mut ffi_properties = ffi::Struct_dtv_properties {
            num: ffi_property_list.len() as u32,
            props: ffi_property_list.as_mut_ptr()
        };
        try!(self.device.ioctl_pointer(ffi::FE_GET_PROPERTY as c_ulong, &mut ffi_properties));
        let mut values = vec!();
        for ffi_property in ffi_property_list {
            let value = try!(properties::get_property_value(ffi_property));
            values.push(value);
        }
        Ok(values)
    }

    pub fn diseqc_reset_overload(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::FE_DISEQC_RESET_OVERLOAD as c_ulong)
    }

    pub fn diseqc_send_master_cmd(&self, command: DiseqcMasterCommand) -> DeviceResult<()> {
        let mut ffi_cmd = ffi::Struct_dvb_diseqc_master_cmd {
            msg: command.msg,
            msg_len: command.msg_len
        };
        self.device.ioctl_pointer(ffi::FE_DISEQC_SEND_MASTER_CMD as c_ulong, &mut ffi_cmd)
    }

    /// The timeout parameter is not used by most drivers. (2015-06-17)
    pub fn diseqc_recv_slave_reply(&self, timeout: u32) -> DeviceResult<DiseqcSlaveReply> {
        let mut ffi_reply: ffi::Struct_dvb_diseqc_slave_reply = Default::default();
        ffi_reply.timeout = timeout as c_int;
        try!(self.device.ioctl_pointer(ffi::FE_DISEQC_RECV_SLAVE_REPLY as c_ulong, &mut ffi_reply));
        Ok(DiseqcSlaveReply {
            msg: ffi_reply.msg,
            msg_len: ffi_reply.msg_len
        })
    }

    pub fn diseqc_send_burst(&self, command: SecMiniCmd) -> DeviceResult<()> {
        let mut command = match command {
            SecMiniCmd::A => ffi::SEC_MINI_A,
            SecMiniCmd::B => ffi::SEC_MINI_B
        };
        self.device.ioctl_pointer(ffi::FE_DISEQC_SEND_BURST as c_ulong, &mut command)
    }

    pub fn set_tone(&self, tone: SecToneMode) -> DeviceResult<()> {
        let mut tone = match tone {
            SecToneMode::On => ffi::SEC_TONE_ON,
            SecToneMode::Off => ffi::SEC_TONE_OFF
        };
        self.device.ioctl_pointer(ffi::FE_SET_TONE as c_ulong, &mut tone)
    }

    pub fn set_voltage(&self, voltage: properties::Voltage) -> DeviceResult<()> {
        let voltage = match voltage {
            properties::Voltage::SecVoltage13 => ffi::SEC_VOLTAGE_13,
            properties::Voltage::SecVoltage18 => ffi::SEC_VOLTAGE_18,
            properties::Voltage::SecVoltageOff => ffi::SEC_VOLTAGE_OFF,
        };
        self.device.ioctl_value(ffi::FE_SET_VOLTAGE as c_ulong, voltage)
    }

    pub fn enable_high_lnb_voltage(&self, high: u32) -> DeviceResult<()> {
        self.device.ioctl_value(ffi::FE_ENABLE_HIGH_LNB_VOLTAGE as c_ulong, high)
    }

    pub fn set_frontend_tune_mode(&self, tune_mode: TuneMode) -> DeviceResult<()> {
        let value = match tune_mode {
            TuneMode::Normal => 0,
            TuneMode::OneShot => ffi::FE_TUNE_MODE_ONESHOT
        };
        self.device.ioctl_value(ffi::FE_SET_FRONTEND_TUNE_MODE as c_ulong, value)
    }
}

#[derive(Clone,Debug)]
pub struct FrontendInfo {
    pub name: String,
    pub frequency_min: u32,
    pub frequency_max: u32,
    pub frequency_stepsize: u32,
    pub frequency_tolerance: u32,
    pub symbol_rate_min: u32,
    pub symbol_rate_max: u32,
    pub symbol_rate_tolerance: u32,
    pub caps: caps::FrontendCaps,
}

#[derive(Copy,Clone,Debug)]
pub struct FrontendStatus {
    pub has_signal: bool,
    pub has_carrier: bool,
    pub has_viterbi: bool,
    pub has_sync: bool,
    pub has_lock: bool,
    pub timedout: bool,
    pub reinit: bool
}

#[derive(Copy,Clone,Debug)]
pub struct DiseqcSlaveReply {
    pub msg: [u8; 4],
    pub msg_len: u8
}

#[derive(Copy,Clone,Debug)]
pub struct DiseqcMasterCommand {
    pub msg: [u8; 6],
    pub msg_len: u8
}

#[derive(Copy,Clone,Debug)]
pub enum SecMiniCmd {
    A,
    B
}

#[derive(Copy,Clone,Debug)]
pub enum SecToneMode {
    On,
    Off
}

#[derive(Copy,Clone,Debug)]
pub enum TuneMode {
    Normal,
    OneShot
}

pub type PropertyResult<T> = Result<T, PropertyError>;

#[derive(Clone,Debug)]
pub enum PropertyError {
    DeviceError(DeviceError),
    PropertyMappingError(properties::PropertyMappingError)
}

impl Error for PropertyError {
    fn description(&self) -> &str {
        match *self {
            PropertyError::DeviceError(ref err) => err.description(),
            PropertyError::PropertyMappingError(ref err) => err.description()
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            PropertyError::DeviceError(ref err) => Some(err),
            PropertyError::PropertyMappingError(ref err) => Some(err)
        }
    }
}

impl From<DeviceError> for PropertyError {
    fn from(err: DeviceError) -> PropertyError {
        PropertyError::DeviceError(err)
    }
}

impl From<properties::PropertyMappingError> for PropertyError {
    fn from(err: properties::PropertyMappingError) -> PropertyError {
        PropertyError::PropertyMappingError(err)
    }
}

impl Display for PropertyError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "PropertyError: {}", self.description())
    }
}<commit_msg>Implement Frontend::set_property().<commit_after>
use std::ffi::CStr;
use std::error::Error;
use std::fmt::{self,Display,Formatter};
use std::default::Default;

use libc::{c_int,c_ulong};

use super::device::{DeviceFileDescriptor,BlockMode,ReadWriteMode,DeviceResult,DeviceError};

#[allow(dead_code,non_camel_case_types,non_snake_case)]
mod ffi;

pub mod caps;

#[allow(dead_code,non_camel_case_types,non_snake_case)]
pub mod properties {
    use super::ffi as ffi;

    include!(concat!(env!("OUT_DIR"), "/frontend-properties.rs"));
}

pub struct Frontend {
    device: DeviceFileDescriptor
}

impl Frontend {
    pub fn open(filename: &str, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Frontend> {
        let device = try!(DeviceFileDescriptor::open(filename, rw_mode, block_mode));
        Ok(Frontend { device: device })
    }

    pub fn get_info(&self) -> DeviceResult<FrontendInfo> {
        let mut ffi_info: ffi::Struct_dvb_frontend_info = Default::default();
        try!(self.device.ioctl_pointer(ffi::FE_GET_INFO as c_ulong, &mut ffi_info));
        let c_name = unsafe { CStr::from_ptr(ffi_info.name.as_ptr()) };
        Ok(FrontendInfo {
            name: String::from_utf8_lossy(c_name.to_bytes()).into_owned(),
            frequency_min: ffi_info.frequency_min,
            frequency_max: ffi_info.frequency_max,
            frequency_stepsize: ffi_info.frequency_stepsize,
            frequency_tolerance: ffi_info.frequency_tolerance,
            symbol_rate_min: ffi_info.symbol_rate_min,
            symbol_rate_max: ffi_info.symbol_rate_max,
            symbol_rate_tolerance: ffi_info.symbol_rate_tolerance,
            // Is truncate a good choice?
            caps: caps::FrontendCaps::from_bits_truncate(ffi_info.caps),
        })
    }
    pub fn read_status(&self) -> DeviceResult<FrontendStatus> {
        let mut ffi_status: ffi::Enum_fe_status = 0;
        try!(self.device.ioctl_pointer(ffi::FE_READ_STATUS as c_ulong, &mut ffi_status));
        Ok(FrontendStatus {
            has_signal: ffi_status & ffi::FE_HAS_SIGNAL != 0,
            has_carrier: ffi_status & ffi::FE_HAS_CARRIER != 0,
            has_viterbi: ffi_status & ffi::FE_HAS_VITERBI != 0,
            has_sync: ffi_status & ffi::FE_HAS_SYNC != 0,
            has_lock: ffi_status & ffi::FE_HAS_LOCK != 0,
            timedout: ffi_status & ffi::FE_TIMEDOUT != 0,
            reinit: ffi_status & ffi::FE_REINIT != 0,
        })
    }
    pub fn set_properties(&self, properties: &[properties::SetPropertyValue]) -> DeviceResult<()> {
        let mut ffi_property_list: Vec<ffi::Struct_dtv_property> = properties.iter().map(
            properties::set_property_value
        ).collect();
        let mut ffi_properties = ffi::Struct_dtv_properties {
            num: ffi_property_list.len() as u32,
            props: ffi_property_list.as_mut_ptr()
        };
        self.device.ioctl_pointer(ffi::FE_SET_PROPERTY as c_ulong, &mut ffi_properties)
    }

    pub fn get_properties(&self, properties: &[properties::GetProperty]) -> PropertyResult<Vec<properties::GetPropertyValue>> {
        let mut ffi_property_list: Vec<ffi::Struct_dtv_property> = properties.iter().map(
            |p| ffi::Struct_dtv_property { cmd: (*p).into(), ..Default::default() }
        ).collect();
        let mut ffi_properties = ffi::Struct_dtv_properties {
            num: ffi_property_list.len() as u32,
            props: ffi_property_list.as_mut_ptr()
        };
        try!(self.device.ioctl_pointer(ffi::FE_GET_PROPERTY as c_ulong, &mut ffi_properties));
        let mut values = vec!();
        for ffi_property in ffi_property_list {
            let value = try!(properties::get_property_value(ffi_property));
            values.push(value);
        }
        Ok(values)
    }

    pub fn diseqc_reset_overload(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::FE_DISEQC_RESET_OVERLOAD as c_ulong)
    }

    pub fn diseqc_send_master_cmd(&self, command: DiseqcMasterCommand) -> DeviceResult<()> {
        let mut ffi_cmd = ffi::Struct_dvb_diseqc_master_cmd {
            msg: command.msg,
            msg_len: command.msg_len
        };
        self.device.ioctl_pointer(ffi::FE_DISEQC_SEND_MASTER_CMD as c_ulong, &mut ffi_cmd)
    }

    /// The timeout parameter is not used by most drivers. (2015-06-17)
    pub fn diseqc_recv_slave_reply(&self, timeout: u32) -> DeviceResult<DiseqcSlaveReply> {
        let mut ffi_reply: ffi::Struct_dvb_diseqc_slave_reply = Default::default();
        ffi_reply.timeout = timeout as c_int;
        try!(self.device.ioctl_pointer(ffi::FE_DISEQC_RECV_SLAVE_REPLY as c_ulong, &mut ffi_reply));
        Ok(DiseqcSlaveReply {
            msg: ffi_reply.msg,
            msg_len: ffi_reply.msg_len
        })
    }

    pub fn diseqc_send_burst(&self, command: SecMiniCmd) -> DeviceResult<()> {
        let mut command = match command {
            SecMiniCmd::A => ffi::SEC_MINI_A,
            SecMiniCmd::B => ffi::SEC_MINI_B
        };
        self.device.ioctl_pointer(ffi::FE_DISEQC_SEND_BURST as c_ulong, &mut command)
    }

    pub fn set_tone(&self, tone: SecToneMode) -> DeviceResult<()> {
        let mut tone = match tone {
            SecToneMode::On => ffi::SEC_TONE_ON,
            SecToneMode::Off => ffi::SEC_TONE_OFF
        };
        self.device.ioctl_pointer(ffi::FE_SET_TONE as c_ulong, &mut tone)
    }

    pub fn set_voltage(&self, voltage: properties::Voltage) -> DeviceResult<()> {
        let voltage = match voltage {
            properties::Voltage::SecVoltage13 => ffi::SEC_VOLTAGE_13,
            properties::Voltage::SecVoltage18 => ffi::SEC_VOLTAGE_18,
            properties::Voltage::SecVoltageOff => ffi::SEC_VOLTAGE_OFF,
        };
        self.device.ioctl_value(ffi::FE_SET_VOLTAGE as c_ulong, voltage)
    }

    pub fn enable_high_lnb_voltage(&self, high: u32) -> DeviceResult<()> {
        self.device.ioctl_value(ffi::FE_ENABLE_HIGH_LNB_VOLTAGE as c_ulong, high)
    }

    pub fn set_frontend_tune_mode(&self, tune_mode: TuneMode) -> DeviceResult<()> {
        let value = match tune_mode {
            TuneMode::Normal => 0,
            TuneMode::OneShot => ffi::FE_TUNE_MODE_ONESHOT
        };
        self.device.ioctl_value(ffi::FE_SET_FRONTEND_TUNE_MODE as c_ulong, value)
    }
}

#[derive(Clone,Debug)]
pub struct FrontendInfo {
    pub name: String,
    pub frequency_min: u32,
    pub frequency_max: u32,
    pub frequency_stepsize: u32,
    pub frequency_tolerance: u32,
    pub symbol_rate_min: u32,
    pub symbol_rate_max: u32,
    pub symbol_rate_tolerance: u32,
    pub caps: caps::FrontendCaps,
}

#[derive(Copy,Clone,Debug)]
pub struct FrontendStatus {
    pub has_signal: bool,
    pub has_carrier: bool,
    pub has_viterbi: bool,
    pub has_sync: bool,
    pub has_lock: bool,
    pub timedout: bool,
    pub reinit: bool
}

#[derive(Copy,Clone,Debug)]
pub struct DiseqcSlaveReply {
    pub msg: [u8; 4],
    pub msg_len: u8
}

#[derive(Copy,Clone,Debug)]
pub struct DiseqcMasterCommand {
    pub msg: [u8; 6],
    pub msg_len: u8
}

#[derive(Copy,Clone,Debug)]
pub enum SecMiniCmd {
    A,
    B
}

#[derive(Copy,Clone,Debug)]
pub enum SecToneMode {
    On,
    Off
}

#[derive(Copy,Clone,Debug)]
pub enum TuneMode {
    Normal,
    OneShot
}

pub type PropertyResult<T> = Result<T, PropertyError>;

#[derive(Clone,Debug)]
pub enum PropertyError {
    DeviceError(DeviceError),
    PropertyMappingError(properties::PropertyMappingError)
}

impl Error for PropertyError {
    fn description(&self) -> &str {
        match *self {
            PropertyError::DeviceError(ref err) => err.description(),
            PropertyError::PropertyMappingError(ref err) => err.description()
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            PropertyError::DeviceError(ref err) => Some(err),
            PropertyError::PropertyMappingError(ref err) => Some(err)
        }
    }
}

impl From<DeviceError> for PropertyError {
    fn from(err: DeviceError) -> PropertyError {
        PropertyError::DeviceError(err)
    }
}

impl From<properties::PropertyMappingError> for PropertyError {
    fn from(err: properties::PropertyMappingError) -> PropertyError {
        PropertyError::PropertyMappingError(err)
    }
}

impl Display for PropertyError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "PropertyError: {}", self.description())
    }
}<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use serde::ser::Serialize;
use serde_json::value::{Value, to_value};

use errors::{Result as TeraResult, ResultExt};

/// The struct that holds the context of a template rendering.
///
/// Light wrapper around a `BTreeMap` for easier insertions of Serializable
/// values
#[derive(Debug, Clone)]
pub struct Context {
    data: BTreeMap<String, Value>,
}

impl Context {
    /// Initializes an empty context
    pub fn new() -> Context {
        Context {
            data: BTreeMap::new()
        }
    }

    /// Converts the `val` parameter to `Value` and insert it into the context
    ///
    /// ```rust,ignore
    /// let mut context = Context::new();
    /// // user is an instance of a struct implementing `Serialize`
    /// context.add("number_users", 42);
    /// ```
    pub fn add<T: Serialize>(&mut self, key: &str, val: &T) {
        self.data.insert(key.to_owned(), to_value(val).unwrap());
    }

    #[doc(hidden)]
    pub fn as_json(&self) -> TeraResult<Value> {
        to_value(&self.data).chain_err(|| "Failed to convert data to JSON")
    }
}

impl Default for Context {
    fn default() -> Context {
        Context::new()
    }
}

pub trait ValueRender {
    fn render(&self) -> String;
}

// Needed to render variables
// From handlebars-rust
impl ValueRender for Value {
    fn render(&self) -> String {
        match *self {
            Value::String(ref s) => s.clone(),
            Value::Number(ref i) => i.to_string(),
            Value::Bool(i) => i.to_string(),
            Value::Null => "".to_owned(),
            Value::Array(ref a) => {
                let mut buf = String::new();
                buf.push('[');
                for i in a.iter() {
                    buf.push_str(i.render().as_ref());
                    buf.push_str(", ");
                }
                buf.push(']');
                buf
            },
            Value::Object(_) => "[object]".to_owned()
        }
    }
}

pub trait ValueNumber {
    fn to_number(&self) -> Result<f64, ()>;
}
// Needed for all the maths
// Convert everything to f64, seems like a terrible idea
impl ValueNumber for Value {
    fn to_number(&self) -> Result<f64, ()> {
        match *self {
            Value::Number(ref i) => Ok(i.as_f64().unwrap()),
            _ => Err(())
        }
    }
}

// From handlebars-rust
pub trait ValueTruthy {
    fn is_truthy(&self) -> bool;
}

impl ValueTruthy for Value {
    fn is_truthy(&self) -> bool {
        match *self {
            Value::Number(ref i) => {
                if i.is_i64() {
                    return i.as_i64().unwrap() != 0;
                }
                if i.is_u64() {
                    return i.as_u64().unwrap() != 0;
                }
                let f = i.as_f64().unwrap();
                f != 0.0 || !f.is_nan()
            },
            Value::Bool(ref i) => *i,
            Value::Null => false,
            Value::String(ref i) => !i.is_empty(),
            Value::Array(ref i) => !i.is_empty(),
            Value::Object(ref i) => !i.is_empty()
        }
    }
}

/// Converts a dotted path to a json pointer one
#[inline]
pub fn get_json_pointer(key: &str) -> String {
    ["/", &key.replace(".", "/")].join("")
}
<commit_msg>Add extend method to Context struct.<commit_after>use std::collections::BTreeMap;

use serde::ser::Serialize;
use serde_json::value::{Value, to_value};

use errors::{Result as TeraResult, ResultExt};

/// The struct that holds the context of a template rendering.
///
/// Light wrapper around a `BTreeMap` for easier insertions of Serializable
/// values
#[derive(Debug, Clone)]
pub struct Context {
    data: BTreeMap<String, Value>,
}

impl Context {
    /// Initializes an empty context
    pub fn new() -> Context {
        Context {
            data: BTreeMap::new()
        }
    }

    /// Converts the `val` parameter to `Value` and insert it into the context
    ///
    /// ```rust,ignore
    /// let mut context = Context::new();
    /// // user is an instance of a struct implementing `Serialize`
    /// context.add("number_users", 42);
    /// ```
    pub fn add<T: Serialize>(&mut self, key: &str, val: &T) {
        self.data.insert(key.to_owned(), to_value(val).unwrap());
    }

    #[doc(hidden)]
    pub fn as_json(&self) -> TeraResult<Value> {
        to_value(&self.data).chain_err(|| "Failed to convert data to JSON")
    }

    /// Appends the data of the `source` parameter to `self` overwriting existing keys.
    /// The source context will be dropped
    ///
    /// ```rust,ignore
    /// let mut target = Context::new();
    /// target.add("a", 1);
    /// target.add("b", 2);
    /// let mut source = Context::new();
    /// source.add("b", 3);
    /// source.add("d", 4);
    /// target.extend(source);
    /// ```
    pub fn extend(&mut self, mut source: Context) {
        self.data.append(&mut source.data);
    }
}

impl Default for Context {
    fn default() -> Context {
        Context::new()
    }
}

pub trait ValueRender {
    fn render(&self) -> String;
}

// Needed to render variables
// From handlebars-rust
impl ValueRender for Value {
    fn render(&self) -> String {
        match *self {
            Value::String(ref s) => s.clone(),
            Value::Number(ref i) => i.to_string(),
            Value::Bool(i) => i.to_string(),
            Value::Null => "".to_owned(),
            Value::Array(ref a) => {
                let mut buf = String::new();
                buf.push('[');
                for i in a.iter() {
                    buf.push_str(i.render().as_ref());
                    buf.push_str(", ");
                }
                buf.push(']');
                buf
            },
            Value::Object(_) => "[object]".to_owned()
        }
    }
}

pub trait ValueNumber {
    fn to_number(&self) -> Result<f64, ()>;
}
// Needed for all the maths
// Convert everything to f64, seems like a terrible idea
impl ValueNumber for Value {
    fn to_number(&self) -> Result<f64, ()> {
        match *self {
            Value::Number(ref i) => Ok(i.as_f64().unwrap()),
            _ => Err(())
        }
    }
}

// From handlebars-rust
pub trait ValueTruthy {
    fn is_truthy(&self) -> bool;
}

impl ValueTruthy for Value {
    fn is_truthy(&self) -> bool {
        match *self {
            Value::Number(ref i) => {
                if i.is_i64() {
                    return i.as_i64().unwrap() != 0;
                }
                if i.is_u64() {
                    return i.as_u64().unwrap() != 0;
                }
                let f = i.as_f64().unwrap();
                f != 0.0 || !f.is_nan()
            },
            Value::Bool(ref i) => *i,
            Value::Null => false,
            Value::String(ref i) => !i.is_empty(),
            Value::Array(ref i) => !i.is_empty(),
            Value::Object(ref i) => !i.is_empty()
        }
    }
}

/// Converts a dotted path to a json pointer one
#[inline]
pub fn get_json_pointer(key: &str) -> String {
    ["/", &key.replace(".", "/")].join("")
}

#[test]
fn test_extend() {
    let mut target = Context::new();
    target.add("a", &1);
    target.add("b", &2);
    let mut source = Context::new();
    source.add("b", &3);
    source.add("c", &4);
    target.extend(source);
    assert_eq!(*target.data.get("a").unwrap(), to_value(1).unwrap());
    assert_eq!(*target.data.get("b").unwrap(), to_value(3).unwrap());
    assert_eq!(*target.data.get("c").unwrap(), to_value(4).unwrap());
}
<|endoftext|>


<commit_before>//! # `context` - manage a valid repository.

use std::ops::{Deref, DerefMut};
use std::fmt;
use std::path::{Path, PathBuf};

use chrono::prelude::*;
use futures::future::{self, Either};
use futures::prelude::*;
use futures::stream;
use futures::sync::mpsc::{self, Sender};
use futures_cpupool::CpuPool;
use globset::GlobSet;
use memmap::{Mmap, Protection};

use {BATCH_FUTURE_BUFFER_SIZE, WRITE_FUTURE_BUFFER_SIZE};
use arc_slice::{self, ArcSlice};
use errors::*;
use index::Cached;
use marshal::{ObjectHash, Marshaller, Hashed, CommitObject, DirTree};
use repository::Repository;
use split::SliceChunker;
use store::{Store, Empty};
use trace::Trace;

/// A context for marshalling and local operations on a repository. `RemoteContext`s must be built
/// from a `Context`.
///
/// `Context` may optionally be supplied with a type `T` implementing `Trace`. This "trace object"
/// is useful for doing things like tracking the progress of long-running operations.
pub struct Context<'a, T: Trace, S: Store> {
    repository: &'a mut Repository,

    trace: T,
    store: S,

    marshal_pool: CpuPool,

    marshal_tx: Sender<Hashed>,
    writes: Box<Future<Item = (), Error = Error> + Send>,
}

impl<'a, T: Trace, S: Store> Deref for Context<'a, T, S> {
    type Target = Repository;

    fn deref(&self) -> &Self::Target {
        &*self.repository
    }
}

impl<'a, T: Trace, S: Store> DerefMut for Context<'a, T, S> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut *self.repository
    }
}

impl<'a, T: Trace + fmt::Debug, S: Store + fmt::Debug> fmt::Debug for Context<'a, T, S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Context")
            .field("repository", &self.repository)
            .field("trace", &self.trace)
            .field("store", &self.store)
            .field("marshal_pool", &self.marshal_pool)
            .field("marshal_tx", &self.marshal_tx)
            .finish()
    }
}

impl<'a, T: Trace, S: Store> Context<'a, T, S> {
    /// Create a context from a loaded repository, with a supplied trace object.
    pub fn new(
        repository: &'a mut Repository,
        trace: T,
        store: S,
        marshal_pool: &CpuPool,
        io_pool: &CpuPool,
    ) -> Self {
        let (marshal_tx, marshal_rx) = mpsc::channel(BATCH_FUTURE_BUFFER_SIZE);

        let writes = {
            let trace = trace.clone();
            let store = store.clone();
            let writes_unboxed = marshal_rx
                .map_err(|()| unreachable!("mpsc receivers never error"))
                .map(move |hashed: Hashed| {
                    let hash = *hashed.as_hash();
                    let trace = trace.clone();

                    trace.on_write_object_start(&hash);
                    store.write_object(hashed).map(move |fresh| {
                        trace.on_write_object_finish(&hash, fresh);
                    })
                })
                .buffer_unordered(WRITE_FUTURE_BUFFER_SIZE)
                .for_each(|_| Ok(()));

            Box::new(io_pool.spawn(writes_unboxed))
        };

        Self {
            repository,

            trace,
            store,

            marshal_pool: marshal_pool.clone(),

            marshal_tx,
            writes,
        }
    }

    pub fn split_file<P: AsRef<Path>>(
        &self,
        path: P,
    ) -> Box<Stream<Item = ArcSlice, Error = Error> + Send> {
        let trace = self.trace.clone();
        let slice_res = Mmap::open_path(path, Protection::Read).map(|mmap| {
            trace.on_split_begin(mmap.len() as u64);
            arc_slice::mapped(mmap)
        });

        let stream_future = {
            async_block! {
                let mut offset = 0u64;
                let slices = SliceChunker::new(slice_res?).inspect(move |chunk| {
                    trace.on_split_chunk(offset, chunk);
                    offset += chunk.len() as u64;
                });

                Ok(stream::iter_ok(slices))
            }
        };

        Box::new(stream_future.flatten_stream())
    }

    pub fn hash_file<U>(&self, stream: U) -> Box<Future<Item = ObjectHash, Error = Error> + Send>
    where
        U: Stream<Item = ArcSlice, Error = Error> + Send + 'static,
    {
        let marshal_tx = self.marshal_tx.clone();
        let marshaller = Marshaller::with_trace(marshal_tx, self.trace.clone());

        Box::new(self.marshal_pool.spawn(marshaller.process_chunks(stream)))
    }

    pub fn hash_subtree<U>(&self, stream: U) -> Box<Future<Item = ObjectHash, Error = Error> + Send>
    where
        U: Stream<Item = (PathBuf, ObjectHash), Error = Error> + Send + 'static,
    {
        let marshal_tx = self.marshal_tx.clone();
        let marshaller = Marshaller::with_trace(marshal_tx, self.trace.clone());
        let hash_future = stream
            .map(|(path, hash)| (path, Some(hash)))
            .collect()
            .and_then(|entries| {
                DirTree::delta(Empty, None, entries).map_err(|err| {
                    Error::with_chain(Error::from_kind(ErrorKind::DirTreeDelta), err)
                })
            })
            .and_then(move |dir_tree| marshaller.process_dir_tree(dir_tree));

        Box::new(self.marshal_pool.spawn(hash_future))
    }

    pub fn hash_commit(
        &mut self,
        include_opt: Option<&GlobSet>,
        exclude_opt: Option<&GlobSet>,
        parents: Vec<ObjectHash>,
        message: String,
        timestamp: DateTime<Utc>,
    ) -> Box<Future<Item = ObjectHash, Error = Error> + Send> {
        let marshaller = Marshaller::with_trace(self.marshal_tx.clone(), self.trace.clone());

        let subtree_future = {
            let entries_iter = self.index.iter()
                .filter(|&(path, entry)| {
                    let is_included = include_opt
                        .map(|include| include.is_match(path))
                        .unwrap_or(true);
                    let is_excluded = exclude_opt
                        .map(|exclude| exclude.is_match(path))
                        .unwrap_or(false);

                    (is_included || entry.added || entry.tracked) && !is_excluded
                })
                .map(|(path, entry)| {
                    match entry.get() {
                        Some(Cached::Hashed(object_hash)) => Either::A(future::ok((path.to_owned(), Some(object_hash)))),
                        Some(Cached::Removed) => Either::A(future::ok((path.to_owned(), None))),

                        // If the file has no hash in the cache *or* has an invalid cache entry, we must
                        // split and hash it.
                        Some(Cached::Unhashed) | None => {
                            let path = path.to_owned();
                            let chunk_stream = self.split_file(&path);
                            let hash_future = self.hash_file(chunk_stream);

                            Either::B(hash_future.map(|object_hash| (path, Some(object_hash))))
                        }
                    }
                });

            let marshaller = marshaller.clone();
            let store = self.store.clone();
            let root = self.refs.head_hash().cloned();
            stream::futures_unordered(entries_iter)
                .collect()
                .and_then(move |ops| DirTree::delta(store, root, ops))
                .and_then(move |dir_tree| marshaller.process_dir_tree(dir_tree))
        };
        let commit_future = subtree_future.and_then(move |subtree| {
            marshaller.process(CommitObject {
                subtree,
                parents,
                message,
                timestamp,
            })
        });

        Box::new(commit_future)
    }

    pub fn store(&self) -> &S {
        &self.store
    }

    pub fn close(self) -> Box<Future<Item = (), Error = Error> + Send> {
        self.writes
    }
}
<commit_msg>context.rs: Update index after hashing a new commit<commit_after>//! # `context` - manage a valid repository.

use std::ops::{Deref, DerefMut};
use std::fmt;
use std::path::{Path, PathBuf};

use chrono::prelude::*;
use futures::future::{self, Either};
use futures::prelude::*;
use futures::stream;
use futures::sync::mpsc::{self, Sender, Receiver};
use futures_cpupool::CpuPool;
use globset::GlobSet;
use memmap::{Mmap, Protection};

use {BATCH_FUTURE_BUFFER_SIZE, WRITE_FUTURE_BUFFER_SIZE};
use arc_slice::{self, ArcSlice};
use errors::*;
use index::Cached;
use marshal::{ObjectHash, Marshaller, Hashed, CommitObject, DirTree};
use repository::Repository;
use split::SliceChunker;
use store::{Store, Empty};
use trace::Trace;

/// A context for marshalling and local operations on a repository. `RemoteContext`s must be built
/// from a `Context`.
///
/// `Context` may optionally be supplied with a type `T` implementing `Trace`. This "trace object"
/// is useful for doing things like tracking the progress of long-running operations.
pub struct Context<'a, T: Trace, S: Store> {
    repository: &'a mut Repository,

    trace: T,
    store: S,

    marshal_pool: CpuPool,

    marshal_tx: Sender<Hashed>,
    writes: Box<Future<Item = (), Error = Error> + Send>,

    index_tx: Sender<(PathBuf, ObjectHash)>,
    index_rx: Receiver<(PathBuf, ObjectHash)>,
}

impl<'a, T: Trace, S: Store> Deref for Context<'a, T, S> {
    type Target = Repository;

    fn deref(&self) -> &Self::Target {
        &*self.repository
    }
}

impl<'a, T: Trace, S: Store> DerefMut for Context<'a, T, S> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut *self.repository
    }
}

impl<'a, T: Trace + fmt::Debug, S: Store + fmt::Debug> fmt::Debug for Context<'a, T, S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Context")
            .field("repository", &self.repository)
            .field("trace", &self.trace)
            .field("store", &self.store)
            .field("marshal_pool", &self.marshal_pool)
            .field("marshal_tx", &self.marshal_tx)
            .finish()
    }
}

impl<'a, T: Trace, S: Store> Context<'a, T, S> {
    /// Create a context from a loaded repository, with a supplied trace object.
    pub fn new(
        repository: &'a mut Repository,
        trace: T,
        store: S,
        marshal_pool: &CpuPool,
        io_pool: &CpuPool,
    ) -> Self {
        let (marshal_tx, marshal_rx) = mpsc::channel(BATCH_FUTURE_BUFFER_SIZE);
        let (index_tx, index_rx) = mpsc::channel(BATCH_FUTURE_BUFFER_SIZE);

        let writes = {
            let trace = trace.clone();
            let store = store.clone();
            let writes_unboxed = marshal_rx
                .map_err(|()| unreachable!("mpsc receivers never error"))
                .map(move |hashed: Hashed| {
                    let hash = *hashed.as_hash();
                    let trace = trace.clone();

                    trace.on_write_object_start(&hash);
                    store.write_object(hashed).map(move |fresh| {
                        trace.on_write_object_finish(&hash, fresh);
                    })
                })
                .buffer_unordered(WRITE_FUTURE_BUFFER_SIZE)
                .for_each(|_| Ok(()));

            Box::new(io_pool.spawn(writes_unboxed))
        };

        Self {
            repository,

            trace,
            store,

            marshal_pool: marshal_pool.clone(),

            marshal_tx,
            writes,

            index_tx,
            index_rx,
        }
    }

    pub fn split_file<P: AsRef<Path>>(
        &self,
        path: P,
    ) -> Box<Stream<Item = ArcSlice, Error = Error> + Send> {
        let trace = self.trace.clone();
        let slice_res = Mmap::open_path(path, Protection::Read).map(|mmap| {
            trace.on_split_begin(mmap.len() as u64);
            arc_slice::mapped(mmap)
        });

        let stream_future = {
            async_block! {
                let mut offset = 0u64;
                let slices = SliceChunker::new(slice_res?).inspect(move |chunk| {
                    trace.on_split_chunk(offset, chunk);
                    offset += chunk.len() as u64;
                });

                Ok(stream::iter_ok(slices))
            }
        };

        Box::new(stream_future.flatten_stream())
    }

    pub fn hash_file<U>(&self, stream: U) -> Box<Future<Item = ObjectHash, Error = Error> + Send>
    where
        U: Stream<Item = ArcSlice, Error = Error> + Send + 'static,
    {
        let marshal_tx = self.marshal_tx.clone();
        let marshaller = Marshaller::with_trace(marshal_tx, self.trace.clone());

        Box::new(self.marshal_pool.spawn(marshaller.process_chunks(stream)))
    }

    pub fn hash_subtree<U>(&self, stream: U) -> Box<Future<Item = ObjectHash, Error = Error> + Send>
    where
        U: Stream<Item = (PathBuf, ObjectHash), Error = Error> + Send + 'static,
    {
        let marshal_tx = self.marshal_tx.clone();
        let marshaller = Marshaller::with_trace(marshal_tx, self.trace.clone());
        let hash_future = stream
            .map(|(path, hash)| (path, Some(hash)))
            .collect()
            .and_then(|entries| {
                DirTree::delta(Empty, None, entries).map_err(|err| {
                    Error::with_chain(Error::from_kind(ErrorKind::DirTreeDelta), err)
                })
            })
            .and_then(move |dir_tree| marshaller.process_dir_tree(dir_tree));

        Box::new(self.marshal_pool.spawn(hash_future))
    }

    pub fn hash_commit(
        &self,
        include_opt: Option<&GlobSet>,
        exclude_opt: Option<&GlobSet>,
        parents: Vec<ObjectHash>,
        message: String,
        timestamp: DateTime<Utc>,
    ) -> Box<Future<Item = ObjectHash, Error = Error> + Send> {
        let marshaller = Marshaller::with_trace(self.marshal_tx.clone(), self.trace.clone());

        let subtree_future = {
            let entries_iter = self.index.iter()
                .filter(|&(path, entry)| {
                    let is_included = include_opt
                        .map(|include| include.is_match(path))
                        .unwrap_or(false);
                    let is_excluded = exclude_opt
                        .map(|exclude| exclude.is_match(path))
                        .unwrap_or(false);

                    (is_included || entry.added || entry.tracked) && !is_excluded
                })
                .map(|(path, entry)| {
                    match entry.get() {
                        Some(Cached::Hashed(object_hash)) => Either::A(future::ok((path.to_owned(), Some(object_hash)))),
                        Some(Cached::Removed) => Either::A(future::ok((path.to_owned(), None))),

                        // If the file has no hash in the cache *or* has an invalid cache entry, we must
                        // split and hash it.
                        Some(Cached::Unhashed) | None => {
                            let path = path.to_owned();
                            let chunk_stream = self.split_file(&path);
                            let index_tx = self.index_tx.clone();
                            let hash_future = self.hash_file(chunk_stream);

                            Either::B(hash_future.and_then(|object_hash| {
                                index_tx
                                    .send((path.clone(), object_hash))
                                    .map(move |_| (path, Some(object_hash)))
                                    .map_err(|_| Error::from_kind(ErrorKind::Absurd))
                            }))
                        }
                    }
                });

            let marshaller = marshaller.clone();
            let store = self.store.clone();
            let root = self.refs.head().cloned();
            stream::futures_unordered(entries_iter)
                .collect()
                .and_then(move |ops| DirTree::delta(store, root, ops))
                .and_then(move |dir_tree| marshaller.process_dir_tree(dir_tree))
        };

        let commit_future = subtree_future.and_then(move |subtree| {
            marshaller.process(CommitObject {
                subtree,
                parents,
                message,
                timestamp,
            })
        });

        Box::new(self.marshal_pool.spawn(commit_future))
    }

    pub fn store(&self) -> &S {
        &self.store
    }

    pub fn close(self) -> Box<Future<Item = (), Error = Error> + Send + 'a> {
        let repository = self.repository;
        let close_future = self.writes.join(
            self.index_rx.map_err(|_| Error::from_kind(ErrorKind::Absurd)).for_each(move |(path, object_hash)| {
                println!("\n\nCleaning entry: ({}, {})\n", path.display(), object_hash);
                repository.index.clean(path, object_hash)
            }),
        ).map(|((), ())| ());

        Box::new(close_future)
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Ident, Span, TokenStream};
use std::fmt::Display;
use syn::{
    parse::{Error, Result},
    spanned::Spanned,
    Attribute, Data, DeriveInput, Fields, Lit, Meta, MetaNameValue, NestedMeta,
};

/// Provides the hook to expand `#[derive(Display)]` into an implementation of `From`
pub fn expand(input: &DeriveInput, trait_name: &str) -> Result<TokenStream> {
    let trait_ident = Ident::new(trait_name, Span::call_site());
    let trait_path = &quote!(::std::fmt::#trait_ident);
    let trait_attr = match trait_name {
        "Display" => "display",
        "Binary" => "binary",
        "Octal" => "octal",
        "LowerHex" => "lower_hex",
        "UpperHex" => "upper_hex",
        "LowerExp" => "lower_exp",
        "UpperExp" => "upper_exp",
        "Pointer" => "pointer",
        _ => unimplemented!(),
    };

    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let name = &input.ident;

    let arms = State {
        trait_path,
        trait_attr,
        input,
    }
    .get_match_arms()?;

    Ok(quote! {
        impl #impl_generics #trait_path for #name #ty_generics #where_clause
        {
            #[inline]
            fn fmt(&self, _derive_more_Display_formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                match self {
                    #arms
                    _ => Ok(()) // This is needed for empty enums
                }
            }
        }
    })
}

struct State<'a, 'b> {
    trait_path: &'b TokenStream,
    trait_attr: &'static str,
    input: &'a DeriveInput,
}

impl<'a, 'b> State<'a, 'b> {
    fn get_proper_syntax(&self) -> impl Display {
        format!(
            r#"Proper syntax: #[{}(fmt = "My format", "arg1", "arg2")]"#,
            self.trait_attr
        )
    }
    fn get_matcher(&self, fields: &Fields) -> TokenStream {
        match fields {
            Fields::Unit => TokenStream::new(),
            Fields::Unnamed(fields) => {
                let fields: TokenStream = (0..fields.unnamed.len())
                    .map(|n| {
                        let i = Ident::new(&format!("_{}", n), Span::call_site());
                        quote!(#i,)
                    })
                    .collect();
                quote!((#fields))
            }
            Fields::Named(fields) => {
                let fields: TokenStream = fields
                    .named
                    .iter()
                    .map(|f| {
                        let i = f.ident.as_ref().unwrap();
                        quote!(#i,)
                    })
                    .collect();
                quote!({#fields})
            }
        }
    }
    fn find_meta(&self, attrs: &[Attribute]) -> Result<Option<Meta>> {
        let mut it = attrs
            .iter()
            .filter_map(|a| a.interpret_meta())
            .filter(|m| m.name() == self.trait_attr);

        let meta = it.next();
        if it.next().is_some() {
            Err(Error::new(meta.span(), "Too many formats given"))
        } else {
            Ok(meta)
        }
    }
    fn get_meta_fmt(&self, meta: Meta) -> Result<TokenStream> {
        let list = match &meta {
            Meta::List(list) => list,
            _ => return Err(Error::new(meta.span(), self.get_proper_syntax())),
        };

        let fmt = match &list.nested[0] {
            NestedMeta::Meta(Meta::NameValue(MetaNameValue {
                ident,
                lit: Lit::Str(s),
                ..
            }))
                if ident == "fmt" =>
            {
                s
            }
            _ => return Err(Error::new(list.nested[0].span(), self.get_proper_syntax())),
        };

        let args = list
            .nested
            .iter()
            .skip(1) // skip fmt = "..."
            .try_fold(TokenStream::new(), |args, arg| {
                let arg = match arg {
                    NestedMeta::Literal(Lit::Str(s)) => s,
                    NestedMeta::Meta(Meta::Word(i)) => {
                        return Ok(quote_spanned!(list.span()=> #args #i,))
                    }
                    _ => return Err(Error::new(arg.span(), self.get_proper_syntax())),
                };
                let arg: TokenStream = match arg.parse() {
                    Ok(arg) => arg,
                    Err(e) => return Err(Error::new(arg.span(), e)),
                };
                Ok(quote_spanned!(list.span()=> #args #arg,))
            })?;

        Ok(quote_spanned!(meta.span()=> write!(_derive_more_Display_formatter, #fmt, #args)))
    }
    fn infer_fmt(&self, fields: &Fields, name: &Ident) -> Result<TokenStream> {
        let fields = match fields {
            Fields::Unit => {
                return Ok(quote!(write!(
                    _derive_more_Display_formatter,
                    stringify!(#name)
                )));
            }
            Fields::Named(fields) => &fields.named,
            Fields::Unnamed(fields) => &fields.unnamed,
        };
        if fields.len() == 0 {
            return Ok(quote!(write!(
                _derive_more_Display_formatter,
                stringify!(#name)
            )));
        } else if fields.len() > 1 {
            return Err(Error::new(
                fields.span(),
                "Can not automatically infer format for types with more than 1 field",
            ));
        }

        let trait_path = self.trait_path;
        if let Some(ident) = &fields.iter().next().as_ref().unwrap().ident {
            Ok(quote!(#trait_path::fmt(#ident, _derive_more_Display_formatter)))
        } else {
            Ok(quote!(#trait_path::fmt(_0, _derive_more_Display_formatter)))
        }
    }
    fn get_match_arms(&self) -> Result<TokenStream> {
        match &self.input.data {
            Data::Enum(e) => {
                if let Some(meta) = self.find_meta(&self.input.attrs)? {
                    let fmt = self.get_meta_fmt(meta)?;
                    e.variants.iter().try_for_each(|v| {
                        if let Some(meta) = self.find_meta(&v.attrs)? {
                            Err(Error::new(
                                meta.span(),
                                "Can not have a format on the variant when the whole enum has one",
                            ))
                        } else {
                            Ok(())
                        }
                    })?;
                    Ok(quote_spanned!(self.input.span()=> _ => #fmt,))
                } else {
                    e.variants.iter().try_fold(TokenStream::new(), |arms, v| {
                        let matcher = self.get_matcher(&v.fields);
                        let fmt = if let Some(meta) = self.find_meta(&v.attrs)? {
                            self.get_meta_fmt(meta)?
                        } else {
                            self.infer_fmt(&v.fields, &v.ident)?
                        };
                        let name = &self.input.ident;
                        let v_name = &v.ident;
                        Ok(quote_spanned!(self.input.span()=> #arms #name::#v_name #matcher => #fmt,))
                    })
                }
            }
            Data::Struct(s) => {
                let matcher = self.get_matcher(&s.fields);
                let fmt = if let Some(meta) = self.find_meta(&self.input.attrs)? {
                    self.get_meta_fmt(meta)?
                } else {
                    self.infer_fmt(&s.fields, &self.input.ident)?
                };
                let name = &self.input.ident;
                Ok(quote_spanned!(self.input.span()=> #name #matcher => #fmt,))
            }
            Data::Union(_) => {
                let meta = self.find_meta(&self.input.attrs)?.ok_or(Error::new(
                    self.input.span(),
                    "Can not automatically infer format for unions",
                ))?;
                let fmt = self.get_meta_fmt(meta)?;
                Ok(quote_spanned!(self.input.span()=> _ => #fmt,))
            }
        }
    }
}
<commit_msg>Suppress `unused_variables` warnings<commit_after>use proc_macro2::{Ident, Span, TokenStream};
use std::fmt::Display;
use syn::{
    parse::{Error, Result},
    spanned::Spanned,
    Attribute, Data, DeriveInput, Fields, Lit, Meta, MetaNameValue, NestedMeta,
};

/// Provides the hook to expand `#[derive(Display)]` into an implementation of `From`
pub fn expand(input: &DeriveInput, trait_name: &str) -> Result<TokenStream> {
    let trait_ident = Ident::new(trait_name, Span::call_site());
    let trait_path = &quote!(::std::fmt::#trait_ident);
    let trait_attr = match trait_name {
        "Display" => "display",
        "Binary" => "binary",
        "Octal" => "octal",
        "LowerHex" => "lower_hex",
        "UpperHex" => "upper_hex",
        "LowerExp" => "lower_exp",
        "UpperExp" => "upper_exp",
        "Pointer" => "pointer",
        _ => unimplemented!(),
    };

    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let name = &input.ident;

    let arms = State {
        trait_path,
        trait_attr,
        input,
    }
    .get_match_arms()?;

    Ok(quote! {
        impl #impl_generics #trait_path for #name #ty_generics #where_clause
        {
            #[allow(unused_variables)]
            #[inline]
            fn fmt(&self, _derive_more_Display_formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                match self {
                    #arms
                    _ => Ok(()) // This is needed for empty enums
                }
            }
        }
    })
}

struct State<'a, 'b> {
    trait_path: &'b TokenStream,
    trait_attr: &'static str,
    input: &'a DeriveInput,
}

impl<'a, 'b> State<'a, 'b> {
    fn get_proper_syntax(&self) -> impl Display {
        format!(
            r#"Proper syntax: #[{}(fmt = "My format", "arg1", "arg2")]"#,
            self.trait_attr
        )
    }
    fn get_matcher(&self, fields: &Fields) -> TokenStream {
        match fields {
            Fields::Unit => TokenStream::new(),
            Fields::Unnamed(fields) => {
                let fields: TokenStream = (0..fields.unnamed.len())
                    .map(|n| {
                        let i = Ident::new(&format!("_{}", n), Span::call_site());
                        quote!(#i,)
                    })
                    .collect();
                quote!((#fields))
            }
            Fields::Named(fields) => {
                let fields: TokenStream = fields
                    .named
                    .iter()
                    .map(|f| {
                        let i = f.ident.as_ref().unwrap();
                        quote!(#i,)
                    })
                    .collect();
                quote!({#fields})
            }
        }
    }
    fn find_meta(&self, attrs: &[Attribute]) -> Result<Option<Meta>> {
        let mut it = attrs
            .iter()
            .filter_map(|a| a.interpret_meta())
            .filter(|m| m.name() == self.trait_attr);

        let meta = it.next();
        if it.next().is_some() {
            Err(Error::new(meta.span(), "Too many formats given"))
        } else {
            Ok(meta)
        }
    }
    fn get_meta_fmt(&self, meta: Meta) -> Result<TokenStream> {
        let list = match &meta {
            Meta::List(list) => list,
            _ => return Err(Error::new(meta.span(), self.get_proper_syntax())),
        };

        let fmt = match &list.nested[0] {
            NestedMeta::Meta(Meta::NameValue(MetaNameValue {
                ident,
                lit: Lit::Str(s),
                ..
            }))
                if ident == "fmt" =>
            {
                s
            }
            _ => return Err(Error::new(list.nested[0].span(), self.get_proper_syntax())),
        };

        let args = list
            .nested
            .iter()
            .skip(1) // skip fmt = "..."
            .try_fold(TokenStream::new(), |args, arg| {
                let arg = match arg {
                    NestedMeta::Literal(Lit::Str(s)) => s,
                    NestedMeta::Meta(Meta::Word(i)) => {
                        return Ok(quote_spanned!(list.span()=> #args #i,))
                    }
                    _ => return Err(Error::new(arg.span(), self.get_proper_syntax())),
                };
                let arg: TokenStream = match arg.parse() {
                    Ok(arg) => arg,
                    Err(e) => return Err(Error::new(arg.span(), e)),
                };
                Ok(quote_spanned!(list.span()=> #args #arg,))
            })?;

        Ok(quote_spanned!(meta.span()=> write!(_derive_more_Display_formatter, #fmt, #args)))
    }
    fn infer_fmt(&self, fields: &Fields, name: &Ident) -> Result<TokenStream> {
        let fields = match fields {
            Fields::Unit => {
                return Ok(quote!(write!(
                    _derive_more_Display_formatter,
                    stringify!(#name)
                )));
            }
            Fields::Named(fields) => &fields.named,
            Fields::Unnamed(fields) => &fields.unnamed,
        };
        if fields.len() == 0 {
            return Ok(quote!(write!(
                _derive_more_Display_formatter,
                stringify!(#name)
            )));
        } else if fields.len() > 1 {
            return Err(Error::new(
                fields.span(),
                "Can not automatically infer format for types with more than 1 field",
            ));
        }

        let trait_path = self.trait_path;
        if let Some(ident) = &fields.iter().next().as_ref().unwrap().ident {
            Ok(quote!(#trait_path::fmt(#ident, _derive_more_Display_formatter)))
        } else {
            Ok(quote!(#trait_path::fmt(_0, _derive_more_Display_formatter)))
        }
    }
    fn get_match_arms(&self) -> Result<TokenStream> {
        match &self.input.data {
            Data::Enum(e) => {
                if let Some(meta) = self.find_meta(&self.input.attrs)? {
                    let fmt = self.get_meta_fmt(meta)?;
                    e.variants.iter().try_for_each(|v| {
                        if let Some(meta) = self.find_meta(&v.attrs)? {
                            Err(Error::new(
                                meta.span(),
                                "Can not have a format on the variant when the whole enum has one",
                            ))
                        } else {
                            Ok(())
                        }
                    })?;
                    Ok(quote_spanned!(self.input.span()=> _ => #fmt,))
                } else {
                    e.variants.iter().try_fold(TokenStream::new(), |arms, v| {
                        let matcher = self.get_matcher(&v.fields);
                        let fmt = if let Some(meta) = self.find_meta(&v.attrs)? {
                            self.get_meta_fmt(meta)?
                        } else {
                            self.infer_fmt(&v.fields, &v.ident)?
                        };
                        let name = &self.input.ident;
                        let v_name = &v.ident;
                        Ok(quote_spanned!(self.input.span()=> #arms #name::#v_name #matcher => #fmt,))
                    })
                }
            }
            Data::Struct(s) => {
                let matcher = self.get_matcher(&s.fields);
                let fmt = if let Some(meta) = self.find_meta(&self.input.attrs)? {
                    self.get_meta_fmt(meta)?
                } else {
                    self.infer_fmt(&s.fields, &self.input.ident)?
                };
                let name = &self.input.ident;
                Ok(quote_spanned!(self.input.span()=> #name #matcher => #fmt,))
            }
            Data::Union(_) => {
                let meta = self.find_meta(&self.input.attrs)?.ok_or(Error::new(
                    self.input.span(),
                    "Can not automatically infer format for unions",
                ))?;
                let fmt = self.get_meta_fmt(meta)?;
                Ok(quote_spanned!(self.input.span()=> _ => #fmt,))
            }
        }
    }
}
<|endoftext|>