<commit_before>//! The `conf` module contains functions for loading and saving game
//! configurations.
//!
//! A [`Conf`](struct.Conf.html) struct is used to create a config file
//! which specifies hardware setup stuff, mostly video display settings.
//!
//! By default a ggez game will search its resource paths for a `/conf.toml`
//! file and load values from it when the [`Context`](../struct.Context.html) is created.  This file
//! must be complete (ie you cannot just fill in some fields and have the
//! rest be default) and provides a nice way to specify settings that
//! can be tweaked such as window resolution, multisampling options, etc.
//! If no file is found, it will create a `Conf` object from the settings
//! passed to the [`ContextBuilder`](../struct.ContextBuilder.html).

use std::io;
use toml;

use crate::error::GameResult;

/// Possible fullscreen modes.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum FullscreenType {
    /// Windowed mode.
    Windowed,
    /// Real fullscreen.
    True,
    /// Windowed fullscreen, generally preferred over real fullscreen
    /// these days 'cause it plays nicer with multiple monitors.
    Desktop,
}

/// A builder structure containing window settings
/// that can be set at runtime and changed with [`graphics::set_mode()`](../graphics/fn.set_mode.html).
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// WindowMode {
///     width: 800.0,
///     height: 600.0,
///     maximized: false,
///     fullscreen_type: FullscreenType::Windowed,
///     borderless: false,
///     min_width: 0.0,
///     max_width: 0.0,
///     min_height: 0.0,
///     max_height: 0.0,
///     hidpi: false,
///     resizable: false,
/// }
/// # , WindowMode::default());}
/// ```
#[derive(Debug, Copy, Clone, SmartDefault, Serialize, Deserialize, PartialEq)]
pub struct WindowMode {
    /// Window width
    #[default = 800.0]
    pub width: f32,
    /// Window height
    #[default = 600.0]
    pub height: f32,
    /// Whether or not to maximize the window
    #[default = false]
    pub maximized: bool,
    /// Fullscreen type
    #[default(FullscreenType::Windowed)]
    pub fullscreen_type: FullscreenType,
    /// Whether or not to show window decorations
    #[default = false]
    pub borderless: bool,
    /// Minimum width for resizable windows; 0 means no limit
    #[default = 0.0]
    pub min_width: f32,
    /// Minimum height for resizable windows; 0 means no limit
    #[default = 0.0]
    pub min_height: f32,
    /// Maximum width for resizable windows; 0 means no limit
    #[default = 0.0]
    pub max_width: f32,
    /// Maximum height for resizable windows; 0 means no limit
    #[default = 0.0]
    pub max_height: f32,
    /// Whether or not to scale all "pixel" coordinates to deal with
    /// high DPI screens.
    ///
    /// A very good overview of this is available in
    /// [the `winit` docs](https://docs.rs/winit/0.18.0/winit/dpi/index.html).
    /// If this is false (the default), one pixel in ggez equates to one
    /// physical pixel on the screen.  If it is `true`, then ggez will
    /// scale *all* pixel coordinates by the scaling factor returned by
    /// [`graphics::get_hidpi_factor()`](../graphics/fn.get_hidpi_factor.html).
    ///
    /// TODO: This is not implemented properly yet because winit is stubborn.
    #[default = false]
    pub hidpi: bool,
    /// Whether or not the window is resizable
    #[default = false]
    pub resizable: bool,
}

impl WindowMode {
    /// Set default window size, or screen resolution in true fullscreen mode.
    pub fn dimensions(mut self, width: f32, height: f32) -> Self {
        self.width = width;
        self.height = height;
        self
    }

    /// Set whether the window should be maximized.
    pub fn maximized(mut self, maximized: bool) -> Self {
        self.maximized = maximized;
        self
    }

    /// Set the fullscreen type.
    pub fn fullscreen_type(mut self, fullscreen_type: FullscreenType) -> Self {
        self.fullscreen_type = fullscreen_type;
        self
    }

    /// Set whether a window should be borderless in windowed mode.
    pub fn borderless(mut self, borderless: bool) -> Self {
        self.borderless = borderless;
        self
    }

    /// Set minimum window dimensions for windowed mode.
    pub fn min_dimensions(mut self, width: f32, height: f32) -> Self {
        self.min_width = width;
        self.min_height = height;
        self
    }

    /// Set maximum window dimensions for windowed mode.
    pub fn max_dimensions(mut self, width: f32, height: f32) -> Self {
        self.max_width = width;
        self.max_height = height;
        self
    }

    /// Set resizable.
    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }

    /// Sets whether or not to allow hidpi.
    pub fn hidpi(mut self, hidpi: bool) -> Self {
        self.hidpi = hidpi;
        self
    }
}

/// A builder structure containing window settings
/// that must be set at init time and cannot be changed afterwards.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// WindowSetup {
///     title: "An easy, good game".to_owned(),
///     samples: NumSamples::Zero,
///     vsync: true,
///     transparent: false,
///     icon: "".to_owned(),
///     srgb: true,
/// }
/// # , WindowSetup::default()); }
/// ```
#[derive(Debug, Clone, SmartDefault, Serialize, Deserialize, PartialEq)]
pub struct WindowSetup {
    /// The window title.
    #[default(String::from("An easy, good game"))]
    pub title: String,
    /// Number of samples to use for multisample anti-aliasing.
    #[default(NumSamples::Zero)]
    pub samples: NumSamples,
    /// Whether or not to enable vsync.
    #[default = true]
    pub vsync: bool,
    /// Whether or not should the window's background be transparent.
    ///
    /// TODO: I don't think I've ever even tried to use this???  Need tests!
    #[default = false]
    pub transparent: bool,
    /// A file path to the window's icon.
    /// It takes a path rooted in the `resources` directory (see the [`filesystem`](../filesystem/index.html)
    /// module for details), and an empty string results in a blank/default icon.
    #[default(String::new())]
    pub icon: String,
    /// Whether or not to enable sRGB (gamma corrected color)
    /// handling on the display.
    #[default = true]
    pub srgb: bool,
}

impl WindowSetup {
    /// Set window title.
    pub fn title(mut self, title: &str) -> Self {
        self.title = title.to_owned();
        self
    }

    /// Set number of samples to use for multisample anti-aliasing.
    pub fn samples(mut self, samples: NumSamples) -> Self {
        self.samples = samples;
        self
    }

    /// Set whether vsync is enabled.
    pub fn vsync(mut self, vsync: bool) -> Self {
        self.vsync = vsync;
        self
    }

    /// Set if window background should be transparent.
    ///
    /// TODO: Is this necessary?  Do we ever want this?
    pub fn transparent(mut self, transparent: bool) -> Self {
        self.transparent = transparent;
        self
    }

    /// Set the window's icon.
    pub fn icon(mut self, icon: &str) -> Self {
        self.icon = icon.to_owned();
        self
    }

    /// Set sRGB color mode.
    pub fn srgb(mut self, active: bool) -> Self {
        self.srgb = active;
        self
    }
}

/// Possible backends.
/// Currently, only OpenGL and OpenGL ES Core specs are supported,
/// but this lets you specify which to use as well as the version numbers.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// Backend::OpenGL {
///     major: 3,
///     minor: 2,
/// }
/// # , Backend::default()); }
/// ```
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, SmartDefault)]
#[serde(tag = "type")]
pub enum Backend {
    /// Defaults to OpenGL 3.2, which is supported by basically
    /// every machine since 2009 or so (apart from the ones that don't).
    #[default]
    OpenGL {
        /// OpenGL major version
        #[default = 3]
        major: u8,
        /// OpenGL minor version
        #[default = 2]
        minor: u8,
    },
    /// OpenGL ES, defaults to 3.0.  Used for phones and other mobile
    /// devices.  Using something older
    /// than 3.0 starts to running into sticky limitations, particularly
    /// with instanced drawing, but might be possible.
    OpenGLES {
        /// OpenGL ES major version
        #[default = 3]
        major: u8,
        /// OpenGL ES minor version
        #[default = 0]
        minor: u8,
    },
}

impl Backend {
    /// Set OpenGL version.
    pub fn version(self, new_major: u8, new_minor: u8) -> Self {
        match self {
            Backend::OpenGL { .. } => Backend::OpenGL {
                major: new_major,
                minor: new_minor,
            },
            Backend::OpenGLES { .. } => Backend::OpenGLES {
                major: new_major,
                minor: new_minor,
            },
        }
    }

    /// Use OpenGL
    pub fn gl(self) -> Self {
        match self {
            Backend::OpenGLES { major, minor } => Backend::OpenGL { major, minor },
            gl => gl,
        }
    }

    /// Use OpenGL ES
    pub fn gles(self) -> Self {
        match self {
            Backend::OpenGL { major, minor } => Backend::OpenGLES { major, minor },
            es => es,
        }
    }
}

/// The possible number of samples for multisample anti-aliasing.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum NumSamples {
    /// Multisampling disabled.
    Zero = 0,
    /// One sample
    One = 1,
    /// Two samples
    Two = 2,
    /// Four samples
    Four = 4,
    /// Eight samples
    Eight = 8,
    /// Sixteen samples
    Sixteen = 16,
}

impl NumSamples {
    /// Create a `NumSamples` from a number.
    /// Returns None if `i` is invalid.
    pub fn from_u32(i: u32) -> Option<NumSamples> {
        match i {
            0 => Some(NumSamples::Zero),
            1 => Some(NumSamples::One),
            2 => Some(NumSamples::Two),
            4 => Some(NumSamples::Four),
            8 => Some(NumSamples::Eight),
            16 => Some(NumSamples::Sixteen),
            _ => None,
        }
    }
}

/// Defines which submodules to enable in ggez.
/// If one tries to use a submodule that is not enabled,
/// it will panic.  Currently, not all subsystems can be
/// disabled.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// ModuleConf {
///     gamepad: true,
///     audio: true,
/// }
/// # , ModuleConf::default()); }
/// ```
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, SmartDefault)]
pub struct ModuleConf {
    /// The gamepad input module.
    #[default = true]
    pub gamepad: bool,
    /// The audio module.
    #[default = true]
    pub audio: bool,
}

impl ModuleConf {
    /// Sets whether or not to enable the gamepad input module.
    pub fn gamepad(mut self, gamepad: bool) -> Self {
        self.gamepad = gamepad;
        self
    }

    /// Sets whether or not to enable the audio module.
    pub fn audio(mut self, audio: bool) -> Self {
        self.audio = audio;
        self
    }
}

/// A structure containing configuration data
/// for the game engine.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// Conf {
///     window_mode: WindowMode::default(),
///     window_setup: WindowSetup::default(),
///     backend: Backend::default(),
///     modules: ModuleConf::default(),
/// }
/// # , Conf::default()); }
/// ```
#[derive(Serialize, Deserialize, Debug, PartialEq, SmartDefault, Clone)]
pub struct Conf {
    /// Window setting information that can be set at runtime
    pub window_mode: WindowMode,
    /// Window setting information that must be set at init-time
    pub window_setup: WindowSetup,
    /// Backend configuration
    pub backend: Backend,
    /// Which modules to enable.
    pub modules: ModuleConf,
}

impl Conf {
    /// Same as `Conf::default()`
    pub fn new() -> Self {
        Self::default()
    }

    /// Load a TOML file from the given `Read` and attempts to parse
    /// a `Conf` from it.
    pub fn from_toml_file<R: io::Read>(file: &mut R) -> GameResult<Conf> {
        let mut s = String::new();
        let _ = file.read_to_string(&mut s)?;
        let decoded = toml::from_str(&s)?;
        Ok(decoded)
    }

    /// Saves the `Conf` to the given `Write` object,
    /// formatted as TOML.
    pub fn to_toml_file<W: io::Write>(&self, file: &mut W) -> GameResult {
        let s = toml::to_vec(self)?;
        file.write_all(&s)?;
        Ok(())
    }

    /// Sets the window mode
    pub fn window_mode(mut self, window_mode: WindowMode) -> Self {
        self.window_mode = window_mode;
        self
    }

    /// Sets the backend
    pub fn backend(mut self, backend: Backend) -> Self {
        self.backend = backend;
        self
    }

    /// Sets the backend
    pub fn modules(mut self, modules: ModuleConf) -> Self {
        self.modules = modules;
        self
    }
}

#[cfg(test)]
mod tests {
    use crate::conf;

    /// Tries to encode and decode a `Conf` object
    /// and makes sure it gets the same result it had.
    #[test]
    fn headless_encode_round_trip() {
        let c1 = conf::Conf::new();
        let mut writer = Vec::new();
        let _c = c1.to_toml_file(&mut writer).unwrap();
        let mut reader = writer.as_slice();
        let c2 = conf::Conf::from_toml_file(&mut reader).unwrap();
        assert_eq!(c1, c2);
    }
}
<commit_msg>Disable gamepad by default on OSX.<commit_after>//! The `conf` module contains functions for loading and saving game
//! configurations.
//!
//! A [`Conf`](struct.Conf.html) struct is used to create a config file
//! which specifies hardware setup stuff, mostly video display settings.
//!
//! By default a ggez game will search its resource paths for a `/conf.toml`
//! file and load values from it when the [`Context`](../struct.Context.html) is created.  This file
//! must be complete (ie you cannot just fill in some fields and have the
//! rest be default) and provides a nice way to specify settings that
//! can be tweaked such as window resolution, multisampling options, etc.
//! If no file is found, it will create a `Conf` object from the settings
//! passed to the [`ContextBuilder`](../struct.ContextBuilder.html).

use std::io;
use toml;

use crate::error::GameResult;

/// Possible fullscreen modes.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum FullscreenType {
    /// Windowed mode.
    Windowed,
    /// Real fullscreen.
    True,
    /// Windowed fullscreen, generally preferred over real fullscreen
    /// these days 'cause it plays nicer with multiple monitors.
    Desktop,
}

/// A builder structure containing window settings
/// that can be set at runtime and changed with [`graphics::set_mode()`](../graphics/fn.set_mode.html).
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// WindowMode {
///     width: 800.0,
///     height: 600.0,
///     maximized: false,
///     fullscreen_type: FullscreenType::Windowed,
///     borderless: false,
///     min_width: 0.0,
///     max_width: 0.0,
///     min_height: 0.0,
///     max_height: 0.0,
///     hidpi: false,
///     resizable: false,
/// }
/// # , WindowMode::default());}
/// ```
#[derive(Debug, Copy, Clone, SmartDefault, Serialize, Deserialize, PartialEq)]
pub struct WindowMode {
    /// Window width
    #[default = 800.0]
    pub width: f32,
    /// Window height
    #[default = 600.0]
    pub height: f32,
    /// Whether or not to maximize the window
    #[default = false]
    pub maximized: bool,
    /// Fullscreen type
    #[default(FullscreenType::Windowed)]
    pub fullscreen_type: FullscreenType,
    /// Whether or not to show window decorations
    #[default = false]
    pub borderless: bool,
    /// Minimum width for resizable windows; 0 means no limit
    #[default = 0.0]
    pub min_width: f32,
    /// Minimum height for resizable windows; 0 means no limit
    #[default = 0.0]
    pub min_height: f32,
    /// Maximum width for resizable windows; 0 means no limit
    #[default = 0.0]
    pub max_width: f32,
    /// Maximum height for resizable windows; 0 means no limit
    #[default = 0.0]
    pub max_height: f32,
    /// Whether or not to scale all "pixel" coordinates to deal with
    /// high DPI screens.
    ///
    /// A very good overview of this is available in
    /// [the `winit` docs](https://docs.rs/winit/0.18.0/winit/dpi/index.html).
    /// If this is false (the default), one pixel in ggez equates to one
    /// physical pixel on the screen.  If it is `true`, then ggez will
    /// scale *all* pixel coordinates by the scaling factor returned by
    /// [`graphics::get_hidpi_factor()`](../graphics/fn.get_hidpi_factor.html).
    ///
    /// TODO: This is not implemented properly yet because winit is stubborn.
    #[default = false]
    pub hidpi: bool,
    /// Whether or not the window is resizable
    #[default = false]
    pub resizable: bool,
}

impl WindowMode {
    /// Set default window size, or screen resolution in true fullscreen mode.
    pub fn dimensions(mut self, width: f32, height: f32) -> Self {
        self.width = width;
        self.height = height;
        self
    }

    /// Set whether the window should be maximized.
    pub fn maximized(mut self, maximized: bool) -> Self {
        self.maximized = maximized;
        self
    }

    /// Set the fullscreen type.
    pub fn fullscreen_type(mut self, fullscreen_type: FullscreenType) -> Self {
        self.fullscreen_type = fullscreen_type;
        self
    }

    /// Set whether a window should be borderless in windowed mode.
    pub fn borderless(mut self, borderless: bool) -> Self {
        self.borderless = borderless;
        self
    }

    /// Set minimum window dimensions for windowed mode.
    pub fn min_dimensions(mut self, width: f32, height: f32) -> Self {
        self.min_width = width;
        self.min_height = height;
        self
    }

    /// Set maximum window dimensions for windowed mode.
    pub fn max_dimensions(mut self, width: f32, height: f32) -> Self {
        self.max_width = width;
        self.max_height = height;
        self
    }

    /// Set resizable.
    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }

    /// Sets whether or not to allow hidpi.
    pub fn hidpi(mut self, hidpi: bool) -> Self {
        self.hidpi = hidpi;
        self
    }
}

/// A builder structure containing window settings
/// that must be set at init time and cannot be changed afterwards.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// WindowSetup {
///     title: "An easy, good game".to_owned(),
///     samples: NumSamples::Zero,
///     vsync: true,
///     transparent: false,
///     icon: "".to_owned(),
///     srgb: true,
/// }
/// # , WindowSetup::default()); }
/// ```
#[derive(Debug, Clone, SmartDefault, Serialize, Deserialize, PartialEq)]
pub struct WindowSetup {
    /// The window title.
    #[default(String::from("An easy, good game"))]
    pub title: String,
    /// Number of samples to use for multisample anti-aliasing.
    #[default(NumSamples::Zero)]
    pub samples: NumSamples,
    /// Whether or not to enable vsync.
    #[default = true]
    pub vsync: bool,
    /// Whether or not should the window's background be transparent.
    ///
    /// TODO: I don't think I've ever even tried to use this???  Need tests!
    #[default = false]
    pub transparent: bool,
    /// A file path to the window's icon.
    /// It takes a path rooted in the `resources` directory (see the [`filesystem`](../filesystem/index.html)
    /// module for details), and an empty string results in a blank/default icon.
    #[default(String::new())]
    pub icon: String,
    /// Whether or not to enable sRGB (gamma corrected color)
    /// handling on the display.
    #[default = true]
    pub srgb: bool,
}

impl WindowSetup {
    /// Set window title.
    pub fn title(mut self, title: &str) -> Self {
        self.title = title.to_owned();
        self
    }

    /// Set number of samples to use for multisample anti-aliasing.
    pub fn samples(mut self, samples: NumSamples) -> Self {
        self.samples = samples;
        self
    }

    /// Set whether vsync is enabled.
    pub fn vsync(mut self, vsync: bool) -> Self {
        self.vsync = vsync;
        self
    }

    /// Set if window background should be transparent.
    ///
    /// TODO: Is this necessary?  Do we ever want this?
    pub fn transparent(mut self, transparent: bool) -> Self {
        self.transparent = transparent;
        self
    }

    /// Set the window's icon.
    pub fn icon(mut self, icon: &str) -> Self {
        self.icon = icon.to_owned();
        self
    }

    /// Set sRGB color mode.
    pub fn srgb(mut self, active: bool) -> Self {
        self.srgb = active;
        self
    }
}

/// Possible backends.
/// Currently, only OpenGL and OpenGL ES Core specs are supported,
/// but this lets you specify which to use as well as the version numbers.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// Backend::OpenGL {
///     major: 3,
///     minor: 2,
/// }
/// # , Backend::default()); }
/// ```
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, SmartDefault)]
#[serde(tag = "type")]
pub enum Backend {
    /// Defaults to OpenGL 3.2, which is supported by basically
    /// every machine since 2009 or so (apart from the ones that don't).
    #[default]
    OpenGL {
        /// OpenGL major version
        #[default = 3]
        major: u8,
        /// OpenGL minor version
        #[default = 2]
        minor: u8,
    },
    /// OpenGL ES, defaults to 3.0.  Used for phones and other mobile
    /// devices.  Using something older
    /// than 3.0 starts to running into sticky limitations, particularly
    /// with instanced drawing, but might be possible.
    OpenGLES {
        /// OpenGL ES major version
        #[default = 3]
        major: u8,
        /// OpenGL ES minor version
        #[default = 0]
        minor: u8,
    },
}

impl Backend {
    /// Set OpenGL version.
    pub fn version(self, new_major: u8, new_minor: u8) -> Self {
        match self {
            Backend::OpenGL { .. } => Backend::OpenGL {
                major: new_major,
                minor: new_minor,
            },
            Backend::OpenGLES { .. } => Backend::OpenGLES {
                major: new_major,
                minor: new_minor,
            },
        }
    }

    /// Use OpenGL
    pub fn gl(self) -> Self {
        match self {
            Backend::OpenGLES { major, minor } => Backend::OpenGL { major, minor },
            gl => gl,
        }
    }

    /// Use OpenGL ES
    pub fn gles(self) -> Self {
        match self {
            Backend::OpenGL { major, minor } => Backend::OpenGLES { major, minor },
            es => es,
        }
    }
}

/// The possible number of samples for multisample anti-aliasing.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum NumSamples {
    /// Multisampling disabled.
    Zero = 0,
    /// One sample
    One = 1,
    /// Two samples
    Two = 2,
    /// Four samples
    Four = 4,
    /// Eight samples
    Eight = 8,
    /// Sixteen samples
    Sixteen = 16,
}

impl NumSamples {
    /// Create a `NumSamples` from a number.
    /// Returns None if `i` is invalid.
    pub fn from_u32(i: u32) -> Option<NumSamples> {
        match i {
            0 => Some(NumSamples::Zero),
            1 => Some(NumSamples::One),
            2 => Some(NumSamples::Two),
            4 => Some(NumSamples::Four),
            8 => Some(NumSamples::Eight),
            16 => Some(NumSamples::Sixteen),
            _ => None,
        }
    }
}

/// Defines which submodules to enable in ggez.
/// If one tries to use a submodule that is not enabled,
/// it will panic.  Currently, not all subsystems can be
/// disabled.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// ModuleConf {
///     gamepad: true,
///     audio: true,
/// }
/// # , ModuleConf::default()); }
/// ```
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, SmartDefault)]
pub struct ModuleConf {
    // Gamepad is disabled by default on OSX
    // See issue #588 in general, #577 for specifics.
    /// The gamepad input module.
    #[cfg(target_os = "macos")]
    #[default = false]
    pub gamepad: bool,

    /// The gamepad input module.
    #[cfg(not(target_os = "macos"))]
    #[default = true]
    pub gamepad: bool,

    /// The audio module.
    #[default = true]
    pub audio: bool,
}

impl ModuleConf {
    /// Sets whether or not to enable the gamepad input module.
    pub fn gamepad(mut self, gamepad: bool) -> Self {
        self.gamepad = gamepad;
        self
    }

    /// Sets whether or not to enable the audio module.
    pub fn audio(mut self, audio: bool) -> Self {
        self.audio = audio;
        self
    }
}

/// A structure containing configuration data
/// for the game engine.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// Conf {
///     window_mode: WindowMode::default(),
///     window_setup: WindowSetup::default(),
///     backend: Backend::default(),
///     modules: ModuleConf::default(),
/// }
/// # , Conf::default()); }
/// ```
#[derive(Serialize, Deserialize, Debug, PartialEq, SmartDefault, Clone)]
pub struct Conf {
    /// Window setting information that can be set at runtime
    pub window_mode: WindowMode,
    /// Window setting information that must be set at init-time
    pub window_setup: WindowSetup,
    /// Backend configuration
    pub backend: Backend,
    /// Which modules to enable.
    pub modules: ModuleConf,
}

impl Conf {
    /// Same as `Conf::default()`
    pub fn new() -> Self {
        Self::default()
    }

    /// Load a TOML file from the given `Read` and attempts to parse
    /// a `Conf` from it.
    pub fn from_toml_file<R: io::Read>(file: &mut R) -> GameResult<Conf> {
        let mut s = String::new();
        let _ = file.read_to_string(&mut s)?;
        let decoded = toml::from_str(&s)?;
        Ok(decoded)
    }

    /// Saves the `Conf` to the given `Write` object,
    /// formatted as TOML.
    pub fn to_toml_file<W: io::Write>(&self, file: &mut W) -> GameResult {
        let s = toml::to_vec(self)?;
        file.write_all(&s)?;
        Ok(())
    }

    /// Sets the window mode
    pub fn window_mode(mut self, window_mode: WindowMode) -> Self {
        self.window_mode = window_mode;
        self
    }

    /// Sets the backend
    pub fn backend(mut self, backend: Backend) -> Self {
        self.backend = backend;
        self
    }

    /// Sets the backend
    pub fn modules(mut self, modules: ModuleConf) -> Self {
        self.modules = modules;
        self
    }
}

#[cfg(test)]
mod tests {
    use crate::conf;

    /// Tries to encode and decode a `Conf` object
    /// and makes sure it gets the same result it had.
    #[test]
    fn headless_encode_round_trip() {
        let c1 = conf::Conf::new();
        let mut writer = Vec::new();
        let _c = c1.to_toml_file(&mut writer).unwrap();
        let mut reader = writer.as_slice();
        let c2 = conf::Conf::from_toml_file(&mut reader).unwrap();
        assert_eq!(c1, c2);
    }
}
<|endoftext|>


<commit_before>use net2::TcpBuilder;
use net2::TcpStreamExt;
use std::fmt::Arguments;
use std::fs::File;
use std::fs;
use std::io::Read;
use std::io::Write;
use std::io;
use std::net::TcpStream;
use std::os::unix::io::{AsRawFd, RawFd};
use std::str;

use wire::{Cmd, Msg};
use wire;

pub struct Conn {
    nick: String,
    hostname: String,
    realname: String,

    /// servername to be used in PING messages. Read from 002 RPL_YOURHOST. `None` until 002.
    host: Option<String>,

    serv_addr: String,

    /// The TCP connection to the server.
    stream: TcpStream,

    status: ConnStatus,

    serv_name: String,

    /// _Partial_ messages collected here until they make a complete message.
    buf: Vec<u8>,

    /// A file to log incoming messages for debugging purposes. Only available
    /// when `debug_assertions` is available.
    log_file: Option<File>,
}

/// How many ticks to wait before sending a ping to the server.
const PING_TICKS: u8 = 10;
/// How many ticks to wait after sending a ping to the server to consider a disconnect.
const PONG_TICKS: u8 = 10;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum ConnStatus {
    /// Need to introduce self
    Introduce,
    PingPong {
        /// Ticks passed since last time we've heard from the server.
        /// Reset on each message. After `PING_TICKS` ticks we send a PING message and move to
        /// `WaitPong` state.
        ticks_passed: u8,
    },
    WaitPong {
        /// Ticks passed since we sent a PING to the server.
        /// After a message move to `PingPong` state. On timeout we reset the connection.
        ticks_passed: u8,
    },
}

#[derive(Debug)]
pub enum ConnEv {
    Disconnected,
    Err(io::Error),
    Msg(Msg),
}

fn init_stream(serv_addr: &str) -> TcpStream {
    let stream = TcpBuilder::new_v4().unwrap().to_tcp_stream().unwrap();
    stream.set_nonblocking(true).unwrap();
    // This will fail with EINPROGRESS
    let _ = stream.connect(serv_addr);
    stream
}

impl Conn {
    pub fn new(serv_addr: &str, serv_name: &str, nick: &str, hostname: &str, realname: &str) -> Conn {
        let log_file = {
            if cfg!(debug_assertions) {
                let _ = fs::create_dir("logs");
                Some(File::create(format!("logs/{}.txt", serv_addr)).unwrap())
            } else {
                None
            }
        };

        Conn {
            nick: nick.to_owned(),
            hostname: hostname.to_owned(),
            realname: realname.to_owned(),
            host: None,
            serv_addr: serv_addr.to_owned(),
            stream: init_stream(serv_addr),
            status: ConnStatus::Introduce,
            serv_name: serv_name.to_owned(),
            buf: vec![],
            log_file: log_file,
        }
    }

    pub fn reconnect(&mut self) {
        self.stream = init_stream(&self.serv_addr);
        self.status = ConnStatus::Introduce;
    }

    /// Get the RawFd, to be used with select() or other I/O multiplexer.
    pub fn get_raw_fd(&self) -> RawFd {
        self.stream.as_raw_fd()
    }

    pub fn get_serv_name(&self) -> &str {
        &self.serv_name
    }

}

impl Conn {

    ////////////////////////////////////////////////////////////////////////////
    // Tick handling

    pub fn tick(&mut self, evs: &mut Vec<ConnEv>, debug_out: &mut Write) {
        match self.status {
            ConnStatus::Introduce => {},
            ConnStatus::PingPong { ticks_passed } => {
                if ticks_passed + 1 == PING_TICKS {
                    match self.host {
                        None => {
                            writeln!(debug_out, "{}: Can't send PING, host unknown", self.serv_name).unwrap();
                        }
                        Some(ref host_) => {
                            writeln!(debug_out, "{}: Ping timeout, sending PING", self.serv_name).unwrap();
                            wire::ping(&mut self.stream, host_).unwrap();;
                        }
                    }
                    self.status = ConnStatus::WaitPong { ticks_passed: 0 };
                } else {
                    self.status = ConnStatus::PingPong { ticks_passed: ticks_passed + 1 };
                }
            }
            ConnStatus::WaitPong { ticks_passed } => {
                if ticks_passed + 1 == PONG_TICKS {
                    evs.push(ConnEv::Disconnected);
                    self.status = ConnStatus::Introduce;
                } else {
                    self.status = ConnStatus::WaitPong { ticks_passed: ticks_passed + 1 };
                }
            }
        }
    }

    fn reset_ticks(&mut self) {
        match self.status {
            ConnStatus::Introduce => {},
            _ => { self.status = ConnStatus::PingPong { ticks_passed: 0 }; }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Sending messages

    fn introduce(&mut self) {
        wire::user(&self.hostname, &self.realname, &mut self.stream).unwrap();
        wire::nick(&self.nick, &mut self.stream).unwrap();
    }

    ////////////////////////////////////////////////////////////////////////////
    // Receiving messages

    pub fn read_incoming_msg(&mut self, evs: &mut Vec<ConnEv>, debug_out: &mut Write) {
        let mut read_buf: [u8; 512] = [0; 512];

        // Handle disconnects
        match self.stream.read(&mut read_buf) {
            Err(err) => {
                evs.push(ConnEv::Err(err));
            }
            Ok(bytes_read) => {
                writeln!(debug_out, "{} read {:?} bytes", self.serv_name, bytes_read).unwrap();
                self.reset_ticks();
                self.add_to_msg_buf(&read_buf[ 0 .. bytes_read ]);
                self.handle_msgs(evs, debug_out);
                if bytes_read == 0 {
                    evs.push(ConnEv::Disconnected);
                }
            }
        }
    }

    fn add_to_msg_buf(&mut self, slice: &[u8]) {
        // Some invisible ASCII characters causing glitches on some terminals,
        // we filter those out here.
        self.buf.extend(slice.iter().filter(|c| **c != 0x1 /* SOH */ ||
                                                **c != 0x2 /* STX */ ||
                                                **c != 0x0 /* NUL */ ||
                                                **c != 0x4 /* EOT */ ));
    }

    fn handle_msgs(&mut self, evs: &mut Vec<ConnEv>, debug_out: &mut Write) {
        while let Some(msg) = Msg::read(&mut self.buf, &self.log_file) {
            self.handle_msg(msg, evs, debug_out);
        }
    }

    fn handle_msg(&mut self, msg: Msg, evs: &mut Vec<ConnEv>, debug_out: &mut Write) {
        if let &Msg { cmd: Cmd::PING { ref server }, .. } = &msg {
            wire::pong(server, &mut self.stream).unwrap();
        }

        if let ConnStatus::Introduce = self.status {
            self.introduce();
            self.status = ConnStatus::PingPong { ticks_passed: 0 };
        }

        if let &Msg { cmd: Cmd::Reply { num: 002, ref params }, .. } = &msg {
            // 002    RPL_YOURHOST
            //        "Your host is <servername>, running version <ver>"

            // An example <servername>: cherryh.freenode.net[149.56.134.238/8001]

            match parse_servername(params) {
                None => {
                    writeln!(debug_out, "{} Can't parse hostname from params: {:?}",
                             self.serv_name, params).unwrap();
                }
                Some(host) => {
                    writeln!(debug_out, "{} host: {}", self.serv_name, host).unwrap();
                    self.host = Some(host);
                }
            }
        }

        evs.push(ConnEv::Msg(msg));
    }
}

macro_rules! try_opt {
    ($expr:expr) => (match $expr {
        Option::Some(val) => val,
        Option::None => {
            return Option::None
        }
    })
}

/// Try to parse servername in a 002 RPL_YOURHOST reply
fn parse_servername(params: &[String]) -> Option<String> {
    let msg = try_opt!(params.get(1).or(params.get(0)));
    let slice1 = &msg[13..];
    let servername_ends = try_opt!(wire::find_byte(slice1.as_bytes(), b'['));
    Some((&slice1[..servername_ends]).to_owned())
}

impl Write for Conn {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stream.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stream.flush()
    }

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.stream.write_all(buf)
    }

    fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> {
        self.stream.write_fmt(fmt)
    }

    fn by_ref(&mut self) -> &mut Conn {
        self
    }
}

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

    #[test]
    fn test_parse_servername_1() {
        let args = vec!["tiny_test".to_owned(),
                        "Your host is adams.freenode.net[94.125.182.252/8001], \
                         running version ircd-seven-1.1.4".to_owned()];
        assert_eq!(parse_servername(&args), Some("adams.freenode.net".to_owned()));
    }

    #[test]
    fn test_parse_servername_2() {
        let args = vec!["Your host is adams.freenode.net[94.125.182.252/8001], \
                         running version ircd-seven-1.1.4".to_owned()];
        assert_eq!(parse_servername(&args), Some("adams.freenode.net".to_owned()));
    }
}
<commit_msg>Revert debug timeouts<commit_after>use net2::TcpBuilder;
use net2::TcpStreamExt;
use std::fmt::Arguments;
use std::fs::File;
use std::fs;
use std::io::Read;
use std::io::Write;
use std::io;
use std::net::TcpStream;
use std::os::unix::io::{AsRawFd, RawFd};
use std::str;

use wire::{Cmd, Msg};
use wire;

pub struct Conn {
    nick: String,
    hostname: String,
    realname: String,

    /// servername to be used in PING messages. Read from 002 RPL_YOURHOST. `None` until 002.
    host: Option<String>,

    serv_addr: String,

    /// The TCP connection to the server.
    stream: TcpStream,

    status: ConnStatus,

    serv_name: String,

    /// _Partial_ messages collected here until they make a complete message.
    buf: Vec<u8>,

    /// A file to log incoming messages for debugging purposes. Only available
    /// when `debug_assertions` is available.
    log_file: Option<File>,
}

/// How many ticks to wait before sending a ping to the server.
const PING_TICKS: u8 = 60;
/// How many ticks to wait after sending a ping to the server to consider a disconnect.
const PONG_TICKS: u8 = 60;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum ConnStatus {
    /// Need to introduce self
    Introduce,
    PingPong {
        /// Ticks passed since last time we've heard from the server.
        /// Reset on each message. After `PING_TICKS` ticks we send a PING message and move to
        /// `WaitPong` state.
        ticks_passed: u8,
    },
    WaitPong {
        /// Ticks passed since we sent a PING to the server.
        /// After a message move to `PingPong` state. On timeout we reset the connection.
        ticks_passed: u8,
    },
}

#[derive(Debug)]
pub enum ConnEv {
    Disconnected,
    Err(io::Error),
    Msg(Msg),
}

fn init_stream(serv_addr: &str) -> TcpStream {
    let stream = TcpBuilder::new_v4().unwrap().to_tcp_stream().unwrap();
    stream.set_nonblocking(true).unwrap();
    // This will fail with EINPROGRESS
    let _ = stream.connect(serv_addr);
    stream
}

impl Conn {
    pub fn new(serv_addr: &str, serv_name: &str, nick: &str, hostname: &str, realname: &str) -> Conn {
        let log_file = {
            if cfg!(debug_assertions) {
                let _ = fs::create_dir("logs");
                Some(File::create(format!("logs/{}.txt", serv_addr)).unwrap())
            } else {
                None
            }
        };

        Conn {
            nick: nick.to_owned(),
            hostname: hostname.to_owned(),
            realname: realname.to_owned(),
            host: None,
            serv_addr: serv_addr.to_owned(),
            stream: init_stream(serv_addr),
            status: ConnStatus::Introduce,
            serv_name: serv_name.to_owned(),
            buf: vec![],
            log_file: log_file,
        }
    }

    pub fn reconnect(&mut self) {
        self.stream = init_stream(&self.serv_addr);
        self.status = ConnStatus::Introduce;
    }

    /// Get the RawFd, to be used with select() or other I/O multiplexer.
    pub fn get_raw_fd(&self) -> RawFd {
        self.stream.as_raw_fd()
    }

    pub fn get_serv_name(&self) -> &str {
        &self.serv_name
    }

}

impl Conn {

    ////////////////////////////////////////////////////////////////////////////
    // Tick handling

    pub fn tick(&mut self, evs: &mut Vec<ConnEv>, debug_out: &mut Write) {
        match self.status {
            ConnStatus::Introduce => {},
            ConnStatus::PingPong { ticks_passed } => {
                if ticks_passed + 1 == PING_TICKS {
                    match self.host {
                        None => {
                            writeln!(debug_out, "{}: Can't send PING, host unknown", self.serv_name).unwrap();
                        }
                        Some(ref host_) => {
                            writeln!(debug_out, "{}: Ping timeout, sending PING", self.serv_name).unwrap();
                            wire::ping(&mut self.stream, host_).unwrap();;
                        }
                    }
                    self.status = ConnStatus::WaitPong { ticks_passed: 0 };
                } else {
                    self.status = ConnStatus::PingPong { ticks_passed: ticks_passed + 1 };
                }
            }
            ConnStatus::WaitPong { ticks_passed } => {
                if ticks_passed + 1 == PONG_TICKS {
                    evs.push(ConnEv::Disconnected);
                    self.status = ConnStatus::Introduce;
                } else {
                    self.status = ConnStatus::WaitPong { ticks_passed: ticks_passed + 1 };
                }
            }
        }
    }

    fn reset_ticks(&mut self) {
        match self.status {
            ConnStatus::Introduce => {},
            _ => { self.status = ConnStatus::PingPong { ticks_passed: 0 }; }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Sending messages

    fn introduce(&mut self) {
        wire::user(&self.hostname, &self.realname, &mut self.stream).unwrap();
        wire::nick(&self.nick, &mut self.stream).unwrap();
    }

    ////////////////////////////////////////////////////////////////////////////
    // Receiving messages

    pub fn read_incoming_msg(&mut self, evs: &mut Vec<ConnEv>, debug_out: &mut Write) {
        let mut read_buf: [u8; 512] = [0; 512];

        // Handle disconnects
        match self.stream.read(&mut read_buf) {
            Err(err) => {
                evs.push(ConnEv::Err(err));
            }
            Ok(bytes_read) => {
                writeln!(debug_out, "{} read {:?} bytes", self.serv_name, bytes_read).unwrap();
                self.reset_ticks();
                self.add_to_msg_buf(&read_buf[ 0 .. bytes_read ]);
                self.handle_msgs(evs, debug_out);
                if bytes_read == 0 {
                    evs.push(ConnEv::Disconnected);
                }
            }
        }
    }

    fn add_to_msg_buf(&mut self, slice: &[u8]) {
        // Some invisible ASCII characters causing glitches on some terminals,
        // we filter those out here.
        self.buf.extend(slice.iter().filter(|c| **c != 0x1 /* SOH */ ||
                                                **c != 0x2 /* STX */ ||
                                                **c != 0x0 /* NUL */ ||
                                                **c != 0x4 /* EOT */ ));
    }

    fn handle_msgs(&mut self, evs: &mut Vec<ConnEv>, debug_out: &mut Write) {
        while let Some(msg) = Msg::read(&mut self.buf, &self.log_file) {
            self.handle_msg(msg, evs, debug_out);
        }
    }

    fn handle_msg(&mut self, msg: Msg, evs: &mut Vec<ConnEv>, debug_out: &mut Write) {
        if let &Msg { cmd: Cmd::PING { ref server }, .. } = &msg {
            wire::pong(server, &mut self.stream).unwrap();
        }

        if let ConnStatus::Introduce = self.status {
            self.introduce();
            self.status = ConnStatus::PingPong { ticks_passed: 0 };
        }

        if let &Msg { cmd: Cmd::Reply { num: 002, ref params }, .. } = &msg {
            // 002    RPL_YOURHOST
            //        "Your host is <servername>, running version <ver>"

            // An example <servername>: cherryh.freenode.net[149.56.134.238/8001]

            match parse_servername(params) {
                None => {
                    writeln!(debug_out, "{} Can't parse hostname from params: {:?}",
                             self.serv_name, params).unwrap();
                }
                Some(host) => {
                    writeln!(debug_out, "{} host: {}", self.serv_name, host).unwrap();
                    self.host = Some(host);
                }
            }
        }

        evs.push(ConnEv::Msg(msg));
    }
}

macro_rules! try_opt {
    ($expr:expr) => (match $expr {
        Option::Some(val) => val,
        Option::None => {
            return Option::None
        }
    })
}

/// Try to parse servername in a 002 RPL_YOURHOST reply
fn parse_servername(params: &[String]) -> Option<String> {
    let msg = try_opt!(params.get(1).or(params.get(0)));
    let slice1 = &msg[13..];
    let servername_ends = try_opt!(wire::find_byte(slice1.as_bytes(), b'['));
    Some((&slice1[..servername_ends]).to_owned())
}

impl Write for Conn {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stream.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stream.flush()
    }

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.stream.write_all(buf)
    }

    fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> {
        self.stream.write_fmt(fmt)
    }

    fn by_ref(&mut self) -> &mut Conn {
        self
    }
}

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

    #[test]
    fn test_parse_servername_1() {
        let args = vec!["tiny_test".to_owned(),
                        "Your host is adams.freenode.net[94.125.182.252/8001], \
                         running version ircd-seven-1.1.4".to_owned()];
        assert_eq!(parse_servername(&args), Some("adams.freenode.net".to_owned()));
    }

    #[test]
    fn test_parse_servername_2() {
        let args = vec!["Your host is adams.freenode.net[94.125.182.252/8001], \
                         running version ircd-seven-1.1.4".to_owned()];
        assert_eq!(parse_servername(&args), Some("adams.freenode.net".to_owned()));
    }
}
<|endoftext|>


<commit_before>//! Make sure we detect erroneous constants post-monomorphization even when they are unused.
//! (https://github.com/rust-lang/miri/issues/1382)
#![feature(const_panic)]
#![feature(never_type)]
#![warn(warnings, const_err)]

struct PrintName<T>(T);
impl<T> PrintName<T> {
    const VOID: ! = panic!(); //~WARN any use of this value will cause an error
}

fn no_codegen<T>() {
    if false {
        let _ = PrintName::<T>::VOID; //~ERROR referenced constant has errors
    }
}
fn main() {
    no_codegen::<i32>();
}
<commit_msg>erroneous_const span now changes with inlining<commit_after>//! Make sure we detect erroneous constants post-monomorphization even when they are unused.
//! (https://github.com/rust-lang/miri/issues/1382)
// Inlining changes the error location
// compile-flags: -Zmir-opt-level=0
#![feature(const_panic)]
#![feature(never_type)]
#![warn(warnings, const_err)]

struct PrintName<T>(T);
impl<T> PrintName<T> {
    const VOID: ! = panic!(); //~WARN any use of this value will cause an error
}

fn no_codegen<T>() {
    if false {
        let _ = PrintName::<T>::VOID; //~ERROR referenced constant has errors
    }
}
fn main() {
    no_codegen::<i32>();
}
<|endoftext|>


<commit_before>//! Structs for reading a ZIP archive

use crc32::Crc32Reader;
use compression::CompressionMethod;
use spec;
use result::{ZipResult, ZipError};
use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
use flate2;
use flate2::FlateReadExt;
use podio::{ReadPodExt, LittleEndian};
use types::{ZipFileData, System};
use cp437::FromCp437;
use msdos_time::{TmMsDosExt, MsDosDateTime};

#[cfg(feature = "bzip2")]
use bzip2::read::BzDecoder;

mod ffi {
    pub const S_IFDIR: u32 = 0o0040000;
    pub const S_IFREG: u32 = 0o0100000;
}

/// Wrapper for reading the contents of a ZIP file.
///
/// ```
/// fn doit() -> zip::result::ZipResult<()>
/// {
///     use std::io::prelude::*;
///
///     // For demonstration purposes we read from an empty buffer.
///     // Normally a File object would be used.
///     let buf: &[u8] = &[0u8; 128];
///     let mut reader = std::io::Cursor::new(buf);
///
///     let mut zip = try!(zip::ZipArchive::new(reader));
///
///     for i in 0..zip.len()
///     {
///         let mut file = zip.by_index(i).unwrap();
///         println!("Filename: {}", file.name());
///         let first_byte = try!(file.bytes().next().unwrap());
///         println!("{}", first_byte);
///     }
///     Ok(())
/// }
///
/// println!("Result: {:?}", doit());
/// ```
#[derive(Debug)]
pub struct ZipArchive<R: Read + io::Seek>
{
    reader: R,
    files: Vec<ZipFileData>,
    names_map: HashMap<String, usize>,
}

enum ZipFileReader<'a> {
    Stored(Crc32Reader<io::Take<&'a mut Read>>),
    Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
    #[cfg(feature = "bzip2")]
    Bzip2(Crc32Reader<BzDecoder<io::Take<&'a mut Read>>>),
}

/// A struct for reading a zip file
pub struct ZipFile<'a> {
    data: &'a ZipFileData,
    reader: ZipFileReader<'a>,
}

fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
{
    Err(ZipError::UnsupportedArchive(detail))
}

impl<R: Read+io::Seek> ZipArchive<R>
{
    /// Opens a Zip archive and parses the central directory
    pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
        let footer = try!(spec::CentralDirectoryEnd::find_and_parse(&mut reader));

        if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }

        let directory_start = footer.central_directory_offset as u64;
        let number_of_files = footer.number_of_files_on_this_disk as usize;

        let mut files = Vec::with_capacity(number_of_files);
        let mut names_map = HashMap::new();

        try!(reader.seek(io::SeekFrom::Start(directory_start)));
        for _ in 0 .. number_of_files
        {
            let file = try!(central_header_to_zip_file(&mut reader));
            names_map.insert(file.file_name.clone(), files.len());
            files.push(file);
        }

        Ok(ZipArchive { reader: reader, files: files, names_map: names_map })
    }

    /// Number of files contained in this zip.
    ///
    /// ```
    /// fn iter() {
    ///     let mut zip = zip::ZipArchive::new(std::io::Cursor::new(vec![])).unwrap();
    ///
    ///     for i in 0..zip.len() {
    ///         let mut file = zip.by_index(i).unwrap();
    ///         // Do something with file i
    ///     }
    /// }
    /// ```
    pub fn len(&self) -> usize
    {
        self.files.len()
    }

    /// Search for a file entry by name
    pub fn by_name<'a>(&'a mut self, name: &str) -> ZipResult<ZipFile<'a>>
    {
        let index = match self.names_map.get(name) {
            Some(index) => *index,
            None => { return Err(ZipError::FileNotFound); },
        };
        self.by_index(index)
    }

    /// Get a contained file by index
    pub fn by_index<'a>(&'a mut self, file_number: usize) -> ZipResult<ZipFile<'a>>
    {
        if file_number >= self.files.len() { return Err(ZipError::FileNotFound); }
        let ref data = self.files[file_number];
        let pos = data.data_start;

        if data.encrypted
        {
            return unsupported_zip_error("Encrypted files are not supported")
        }

        try!(self.reader.seek(io::SeekFrom::Start(pos)));
        let limit_reader = (self.reader.by_ref() as &mut Read).take(data.compressed_size);

        let reader = match data.compression_method
        {
            CompressionMethod::Stored =>
            {
                ZipFileReader::Stored(Crc32Reader::new(
                    limit_reader,
                    data.crc32))
            },
            CompressionMethod::Deflated =>
            {
                let deflate_reader = limit_reader.deflate_decode();
                ZipFileReader::Deflated(Crc32Reader::new(
                    deflate_reader,
                    data.crc32))
            },
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 =>
            {
                let bzip2_reader = BzDecoder::new(limit_reader);
                ZipFileReader::Bzip2(Crc32Reader::new(
                    bzip2_reader,
                    data.crc32))
            },
            _ => return unsupported_zip_error("Compression method not supported"),
        };
        Ok(ZipFile { reader: reader, data: data })
    }

    /// Unwrap and return the inner reader object
    ///
    /// The position of the reader is undefined.
    pub fn into_inner(self) -> R
    {
        self.reader
    }
}

fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFileData>
{
    // Parse central header
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
    }

    let version_made_by = try!(reader.read_u16::<LittleEndian>());
    let _version_to_extract = try!(reader.read_u16::<LittleEndian>());
    let flags = try!(reader.read_u16::<LittleEndian>());
    let encrypted = flags & 1 == 1;
    let is_utf8 = flags & (1 << 11) != 0;
    let compression_method = try!(reader.read_u16::<LittleEndian>());
    let last_mod_time = try!(reader.read_u16::<LittleEndian>());
    let last_mod_date = try!(reader.read_u16::<LittleEndian>());
    let crc32 = try!(reader.read_u32::<LittleEndian>());
    let compressed_size = try!(reader.read_u32::<LittleEndian>());
    let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let _disk_number = try!(reader.read_u16::<LittleEndian>());
    let _internal_file_attributes = try!(reader.read_u16::<LittleEndian>());
    let external_file_attributes = try!(reader.read_u32::<LittleEndian>());
    let offset = try!(reader.read_u32::<LittleEndian>()) as u64;
    let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
    let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
    let file_comment_raw  = try!(ReadPodExt::read_exact(reader, file_comment_length));

    let file_name = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
        false => file_name_raw.clone().from_cp437(),
    };
    let file_comment = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
        false => file_comment_raw.from_cp437(),
    };

    // Remember end of central header
    let return_position = try!(reader.seek(io::SeekFrom::Current(0)));

    // Parse local header
    try!(reader.seek(io::SeekFrom::Start(offset)));
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid local file header"))
    }

    try!(reader.seek(io::SeekFrom::Current(22)));
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let magic_and_header = 4 + 22 + 2 + 2;
    let data_start = offset + magic_and_header + file_name_length + extra_field_length;

    // Construct the result
    let mut result = ZipFileData
    {
        system: System::from_u8((version_made_by >> 8) as u8),
        version_made_by: version_made_by as u8,
        encrypted: encrypted,
        compression_method: CompressionMethod::from_u16(compression_method),
        last_modified_time: try!(::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date))),
        crc32: crc32,
        compressed_size: compressed_size as u64,
        uncompressed_size: uncompressed_size as u64,
        file_name: file_name,
        file_name_raw: file_name_raw,
        file_comment: file_comment,
        header_start: offset,
        data_start: data_start,
        external_attributes: external_file_attributes,
    };

    match parse_extra_field(&mut result, &*extra_field) {
        Ok(..) | Err(ZipError::Io(..)) => {},
        Err(e) => try!(Err(e)),
    }

    // Go back after the central header
    try!(reader.seek(io::SeekFrom::Start(return_position)));

    Ok(result)
}

fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
{
    let mut reader = io::Cursor::new(data);

    while (reader.position() as usize) < data.len()
    {
        let kind = try!(reader.read_u16::<LittleEndian>());
        let len = try!(reader.read_u16::<LittleEndian>());
        match kind
        {
            _ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
        };
    }
    Ok(())
}

/// Methods for retreiving information on zip files
impl<'a> ZipFile<'a> {
    fn get_reader(&mut self) -> &mut Read {
        match self.reader {
           ZipFileReader::Stored(ref mut r) => r as &mut Read,
           ZipFileReader::Deflated(ref mut r) => r as &mut Read,
           #[cfg(feature = "bzip2")]
           ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
        }
    }
    /// Get the version of the file
    pub fn version_made_by(&self) -> (u8, u8) {
        (self.data.version_made_by / 10, self.data.version_made_by % 10)
    }
    /// Get the name of the file
    pub fn name(&self) -> &str {
        &*self.data.file_name
    }
    /// Get the name of the file, in the raw (internal) byte representation.
    pub fn name_raw(&self) -> &[u8] {
        &*self.data.file_name_raw
    }
    /// Get the comment of the file
    pub fn comment(&self) -> &str {
        &*self.data.file_comment
    }
    /// Get the compression method used to store the file
    pub fn compression(&self) -> CompressionMethod {
        self.data.compression_method
    }
    /// Get the size of the file in the archive
    pub fn compressed_size(&self) -> u64 {
        self.data.compressed_size
    }
    /// Get the size of the file when uncompressed
    pub fn size(&self) -> u64 {
        self.data.uncompressed_size
    }
    /// Get the time the file was last modified
    pub fn last_modified(&self) -> ::time::Tm {
        self.data.last_modified_time
    }
    /// Get unix mode for the file
    pub fn unix_mode(&self) -> Option<u32> {
        match self.data.system {
            System::Unix => {
                Some(self.data.external_attributes >> 16)
            },
            System::Dos => {
                // Interpret MSDOS directory bit
                let mut mode = if 0x10 == (self.data.external_attributes & 0x10) {
                    ffi::S_IFDIR | 0o0775
                } else {
                    ffi::S_IFREG | 0o0664
                };
                if 0x01 == (self.data.external_attributes & 0x01) {
                    // Read-only bit; strip write permissions
                    mode &= 0o0555;
                }
                Some(mode)
            },
            _ => None,
        }
    }
    /// Get the CRC32 hash of the original file
    pub fn crc32(&self) -> u32 {
        self.data.crc32
    }
}

impl<'a> Read for ZipFile<'a> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.get_reader().read(buf)
     }
}
<commit_msg>read_single<commit_after>//! Structs for reading a ZIP archive

use crc32::Crc32Reader;
use compression::CompressionMethod;
use spec;
use result::{ZipResult, ZipError};
use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
use flate2;
use flate2::FlateReadExt;
use podio::{ReadPodExt, LittleEndian};
use types::{ZipFileData, System};
use cp437::FromCp437;
use msdos_time::{TmMsDosExt, MsDosDateTime};

#[cfg(feature = "bzip2")]
use bzip2::read::BzDecoder;

mod ffi {
    pub const S_IFDIR: u32 = 0o0040000;
    pub const S_IFREG: u32 = 0o0100000;
}

/// Wrapper for reading the contents of a ZIP file.
///
/// ```
/// fn doit() -> zip::result::ZipResult<()>
/// {
///     use std::io::prelude::*;
///
///     // For demonstration purposes we read from an empty buffer.
///     // Normally a File object would be used.
///     let buf: &[u8] = &[0u8; 128];
///     let mut reader = std::io::Cursor::new(buf);
///
///     let mut zip = try!(zip::ZipArchive::new(reader));
///
///     for i in 0..zip.len()
///     {
///         let mut file = zip.by_index(i).unwrap();
///         println!("Filename: {}", file.name());
///         let first_byte = try!(file.bytes().next().unwrap());
///         println!("{}", first_byte);
///     }
///     Ok(())
/// }
///
/// println!("Result: {:?}", doit());
/// ```
#[derive(Debug)]
pub struct ZipArchive<R: Read + io::Seek>
{
    reader: R,
    files: Vec<ZipFileData>,
    names_map: HashMap<String, usize>,
}

enum ZipFileReader<'a> {
    Stored(Crc32Reader<io::Take<&'a mut Read>>),
    Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
    #[cfg(feature = "bzip2")]
    Bzip2(Crc32Reader<BzDecoder<io::Take<&'a mut Read>>>),
}

/// A struct for reading a zip file
pub struct ZipFile<'a> {
    data: &'a ZipFileData,
    reader: ZipFileReader<'a>,
}

fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
{
    Err(ZipError::UnsupportedArchive(detail))
}

fn make_reader<'a>(
    compression_method: ::compression::CompressionMethod,
    crc32: u32,
    reader: io::Take<&'a mut io::Read>)
        -> ZipResult<ZipFileReader<'a>> {

    match compression_method {
        CompressionMethod::Stored =>
        {
            Ok(ZipFileReader::Stored(Crc32Reader::new(
                reader,
                crc32)))
        },
        CompressionMethod::Deflated =>
        {
            let deflate_reader = reader.deflate_decode();
            Ok(ZipFileReader::Deflated(Crc32Reader::new(
                deflate_reader,
                crc32)))
        },
        #[cfg(feature = "bzip2")]
        CompressionMethod::Bzip2 =>
        {
            let bzip2_reader = BzDecoder::new(reader);
            Ok(ZipFileReader::Bzip2(Crc32Reader::new(
                bzip2_reader,
                crc32)))
        },
        _ => unsupported_zip_error("Compression method not supported"),
    }
}

impl<R: Read+io::Seek> ZipArchive<R>
{
    /// Opens a Zip archive and parses the central directory
    pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
        let footer = try!(spec::CentralDirectoryEnd::find_and_parse(&mut reader));

        if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }

        let directory_start = footer.central_directory_offset as u64;
        let number_of_files = footer.number_of_files_on_this_disk as usize;

        let mut files = Vec::with_capacity(number_of_files);
        let mut names_map = HashMap::new();

        try!(reader.seek(io::SeekFrom::Start(directory_start)));
        for _ in 0 .. number_of_files
        {
            let file = try!(central_header_to_zip_file(&mut reader));
            names_map.insert(file.file_name.clone(), files.len());
            files.push(file);
        }

        Ok(ZipArchive { reader: reader, files: files, names_map: names_map })
    }

    /// Number of files contained in this zip.
    ///
    /// ```
    /// fn iter() {
    ///     let mut zip = zip::ZipArchive::new(std::io::Cursor::new(vec![])).unwrap();
    ///
    ///     for i in 0..zip.len() {
    ///         let mut file = zip.by_index(i).unwrap();
    ///         // Do something with file i
    ///     }
    /// }
    /// ```
    pub fn len(&self) -> usize
    {
        self.files.len()
    }

    /// Search for a file entry by name
    pub fn by_name<'a>(&'a mut self, name: &str) -> ZipResult<ZipFile<'a>>
    {
        let index = match self.names_map.get(name) {
            Some(index) => *index,
            None => { return Err(ZipError::FileNotFound); },
        };
        self.by_index(index)
    }

    /// Get a contained file by index
    pub fn by_index<'a>(&'a mut self, file_number: usize) -> ZipResult<ZipFile<'a>>
    {
        if file_number >= self.files.len() { return Err(ZipError::FileNotFound); }
        let ref data = self.files[file_number];
        let pos = data.data_start;

        if data.encrypted
        {
            return unsupported_zip_error("Encrypted files are not supported")
        }

        try!(self.reader.seek(io::SeekFrom::Start(pos)));
        let limit_reader = (self.reader.by_ref() as &mut Read).take(data.compressed_size);

        Ok(ZipFile { reader: try!(make_reader(data.compression_method, data.crc32, limit_reader)), data: data })
    }

    /// Unwrap and return the inner reader object
    ///
    /// The position of the reader is undefined.
    pub fn into_inner(self) -> R
    {
        self.reader
    }
}

fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFileData>
{
    // Parse central header
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
    }

    let version_made_by = try!(reader.read_u16::<LittleEndian>());
    let _version_to_extract = try!(reader.read_u16::<LittleEndian>());
    let flags = try!(reader.read_u16::<LittleEndian>());
    let encrypted = flags & 1 == 1;
    let is_utf8 = flags & (1 << 11) != 0;
    let compression_method = try!(reader.read_u16::<LittleEndian>());
    let last_mod_time = try!(reader.read_u16::<LittleEndian>());
    let last_mod_date = try!(reader.read_u16::<LittleEndian>());
    let crc32 = try!(reader.read_u32::<LittleEndian>());
    let compressed_size = try!(reader.read_u32::<LittleEndian>());
    let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let _disk_number = try!(reader.read_u16::<LittleEndian>());
    let _internal_file_attributes = try!(reader.read_u16::<LittleEndian>());
    let external_file_attributes = try!(reader.read_u32::<LittleEndian>());
    let offset = try!(reader.read_u32::<LittleEndian>()) as u64;
    let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
    let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
    let file_comment_raw  = try!(ReadPodExt::read_exact(reader, file_comment_length));

    let file_name = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
        false => file_name_raw.clone().from_cp437(),
    };
    let file_comment = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
        false => file_comment_raw.from_cp437(),
    };

    // Remember end of central header
    let return_position = try!(reader.seek(io::SeekFrom::Current(0)));

    // Parse local header
    try!(reader.seek(io::SeekFrom::Start(offset)));
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid local file header"))
    }

    try!(reader.seek(io::SeekFrom::Current(22)));
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let magic_and_header = 4 + 22 + 2 + 2;
    let data_start = offset + magic_and_header + file_name_length + extra_field_length;

    // Construct the result
    let mut result = ZipFileData
    {
        system: System::from_u8((version_made_by >> 8) as u8),
        version_made_by: version_made_by as u8,
        encrypted: encrypted,
        compression_method: CompressionMethod::from_u16(compression_method),
        last_modified_time: try!(::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date))),
        crc32: crc32,
        compressed_size: compressed_size as u64,
        uncompressed_size: uncompressed_size as u64,
        file_name: file_name,
        file_name_raw: file_name_raw,
        file_comment: file_comment,
        header_start: offset,
        data_start: data_start,
        external_attributes: external_file_attributes,
    };

    match parse_extra_field(&mut result, &*extra_field) {
        Ok(..) | Err(ZipError::Io(..)) => {},
        Err(e) => try!(Err(e)),
    }

    // Go back after the central header
    try!(reader.seek(io::SeekFrom::Start(return_position)));

    Ok(result)
}

fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
{
    let mut reader = io::Cursor::new(data);

    while (reader.position() as usize) < data.len()
    {
        let kind = try!(reader.read_u16::<LittleEndian>());
        let len = try!(reader.read_u16::<LittleEndian>());
        match kind
        {
            _ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
        };
    }
    Ok(())
}

fn get_reader<'a>(reader: &'a mut ZipFileReader) -> &'a mut Read {
    match *reader {
        ZipFileReader::Stored(ref mut r) => r as &mut Read,
        ZipFileReader::Deflated(ref mut r) => r as &mut Read,
        #[cfg(feature = "bzip2")]
        ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
    }
}

/// Methods for retreiving information on zip files
impl<'a> ZipFile<'a> {
    fn get_reader(&mut self) -> &mut Read {
        get_reader(&mut self.reader)
    }
    /// Get the version of the file
    pub fn version_made_by(&self) -> (u8, u8) {
        (self.data.version_made_by / 10, self.data.version_made_by % 10)
    }
    /// Get the name of the file
    pub fn name(&self) -> &str {
        &*self.data.file_name
    }
    /// Get the name of the file, in the raw (internal) byte representation.
    pub fn name_raw(&self) -> &[u8] {
        &*self.data.file_name_raw
    }
    /// Get the comment of the file
    pub fn comment(&self) -> &str {
        &*self.data.file_comment
    }
    /// Get the compression method used to store the file
    pub fn compression(&self) -> CompressionMethod {
        self.data.compression_method
    }
    /// Get the size of the file in the archive
    pub fn compressed_size(&self) -> u64 {
        self.data.compressed_size
    }
    /// Get the size of the file when uncompressed
    pub fn size(&self) -> u64 {
        self.data.uncompressed_size
    }
    /// Get the time the file was last modified
    pub fn last_modified(&self) -> ::time::Tm {
        self.data.last_modified_time
    }
    /// Get unix mode for the file
    pub fn unix_mode(&self) -> Option<u32> {
        match self.data.system {
            System::Unix => {
                Some(self.data.external_attributes >> 16)
            },
            System::Dos => {
                // Interpret MSDOS directory bit
                let mut mode = if 0x10 == (self.data.external_attributes & 0x10) {
                    ffi::S_IFDIR | 0o0775
                } else {
                    ffi::S_IFREG | 0o0664
                };
                if 0x01 == (self.data.external_attributes & 0x01) {
                    // Read-only bit; strip write permissions
                    mode &= 0o0555;
                }
                Some(mode)
            },
            _ => None,
        }
    }
    /// Get the CRC32 hash of the original file
    pub fn crc32(&self) -> u32 {
        self.data.crc32
    }
}

impl<'a> Read for ZipFile<'a> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.get_reader().read(buf)
     }
}

pub struct ZipEntry<'a> {
    pub name: String,
    pub modified: ::time::Tm,
    reader: ZipFileReader<'a>,
}

impl<'a> ZipEntry<'a> {
    pub fn get_reader(&mut self) -> &mut Read {
        get_reader(&mut self.reader)
    }
}

pub fn read_single<'a, R: io::Read>(reader: &'a mut R) -> ZipResult<Option<ZipEntry>> {
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
    {
        return if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE {
            Err(ZipError::InvalidArchive("Invalid local file header"))
        } else {
            Ok(None)
        };
    }

    let version_made_by = try!(reader.read_u16::<LittleEndian>());
    let flags = try!(reader.read_u16::<LittleEndian>());
    let encrypted = flags & 1 == 1;
    let is_utf8 = flags & (1 << 11) != 0;
    let compression_method = CompressionMethod::from_u16(try!(reader.read_u16::<LittleEndian>()));
    let last_mod_time = try!(reader.read_u16::<LittleEndian>());
    let last_mod_date = try!(reader.read_u16::<LittleEndian>());
    let crc32 = try!(reader.read_u32::<LittleEndian>());
    let compressed_size = try!(reader.read_u32::<LittleEndian>());
    let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;

    let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
    let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));

    let file_name = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
        false => file_name_raw.clone().from_cp437(),
    };

    let limit_reader = (reader as &'a mut io::Read).take(compressed_size as u64);

    Ok(Some(ZipEntry { name: file_name,
        modified: try!(::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date))),
        reader: try!(make_reader(compression_method, crc32, limit_reader))
    }))
}

<|endoftext|>


<commit_before>// The tokenizer ("reader", in Scheme parlance) for Unseemly.

extern crate regex;

use name::*;

custom_derive! {
    #[derive(Debug,PartialEq,Eq,Clone,Copy,Reifiable)]
    pub enum DelimChar { Paren, SquareBracket, CurlyBracket }
}

impl DelimChar {
    pub fn open(self) -> char {
        match self {
            Paren => '(',
            SquareBracket => '[',
            CurlyBracket => '{',
        }
    }
    pub fn close(self) -> char {
        match self {
            Paren => ')',
            SquareBracket => ']',
            CurlyBracket => '}',
        }
    }
}

use self::DelimChar::*;

#[derive(Debug, PartialEq, Eq)]
pub struct TokenTree {
    pub t: Vec<Token>,
}

pub type Token = Name;

impl Token {
    pub fn is_just(&self, s: &str) -> bool { self.is(s) }
}

impl TokenTree {
    pub fn to_string(&self) -> String {
        let mut result = "".to_owned();
        for t in &self.t {
            result += " ";
            result += &t.orig_sp();
        }
        result
    }
}

// A token may start with an open delimiter, or end with a close delmiter,
// but otherwise may not contain delimiters

const nondelim: &str = r"[^\[\]\(\)\{\}\s]";
const open: &str = r"[\[\(\{]";
const close: &str = r"[\]\)\}]";

pub fn delim(s: &str) -> DelimChar {
    match s {
        "(" | ")" => Paren,
        "[" | "]" => SquareBracket,
        "{" | "}" => CurlyBracket,
        _ => panic!("not a delimiter!"),
    }
}

pub fn read_tokens(s: &str) -> Result<TokenTree, String> {
    lazy_static! {
        static ref token : regex::Regex =
            regex::Regex::new(format!(r"(?P<open_all>(?P<main_o>{nd}*)(?P<open>{o}))|((?P<close>{c})(?P<main_c>{nd}*))|(?P<normal>{nd}+)",
                                      o = open, c = close, nd = nondelim)
                                      .as_str()).unwrap();
    }
    let mut flat_tokens = token.captures_iter(s);

    fn read_token_tree<'a>(
        flat_tokens: &mut regex::CaptureMatches<'a, 'a>,
    ) -> Result<(TokenTree, Option<(DelimChar, &'a str)>), String> {
        let mut this_level: Vec<Token> = vec![];
        loop {
            match flat_tokens.next() {
                None => return Ok((TokenTree { t: this_level }, None)),
                Some(c) => {
                    if let Some(normal) = c.name("normal") {
                        this_level.push(n(normal.as_str()));
                    } else if let (Some(_main), Some(o_del), Some(all)) =
                        (c.name("main_o"), c.name("open"), c.name("open_all"))
                    {
                        let (mut inside, last) = read_token_tree(flat_tokens)?;

                        if let Some(last) = last {
                            if format!("{}{}", last.1, o_del.as_str()) == all.as_str() {
                                this_level.push(n(all.as_str()));
                                this_level.append(&mut inside.t);
                                this_level.push(n(&format!(
                                    "{}{}",
                                    delim(o_del.as_str()).close(),
                                    last.1
                                )));
                            } else {
                                return Err(format!(
                                    "Unmatched delimiter names: \"{}\" is closed by \"{}\". \
                                     Remember(this tokenizer is weird)Remember",
                                    all.as_str(),
                                    last.1
                                ));
                            }
                        } else {
                            return Err(format!(
                                "Unclosed delimiter at EOF: \"{}\"",
                                o_del.as_str()
                            ));
                        }
                    } else if let (Some(main), Some(c_del)) = (c.name("main_c"), c.name("close")) {
                        return Ok((
                            TokenTree { t: this_level },
                            Some((delim(c_del.as_str()), main.as_str())),
                        ));
                    } else {
                        icp!()
                    }
                }
            }
        }
    }

    let (tt, leftover) = read_token_tree(&mut flat_tokens)?;

    match leftover {
        None => Ok(tt),
        Some(l) => Err(format!("Read error: leftover {:#?}", l)),
    }
}

#[test]
fn simple_reading() {
    assert_eq!(read_tokens(""), Ok(tokens!()));
    assert_eq!(read_tokens("asdf"), Ok(tokens!("asdf")));
    assert_eq!(
        read_tokens("a s d-f d - f && a\na    8888"),
        Ok(tokens!("a" "s" "d-f" "d" "-" "f" "&&" "a" "a" "8888"))
    );
}
#[test]
fn nested_reading() {
    assert_eq!(read_tokens("()"), Ok(tokens!(("";))));
    assert_eq!(read_tokens("a ()"), Ok(tokens!("a" ("";))));
    assert_eq!(read_tokens("(b)"), Ok(tokens!(("";"b"))));
    assert_eq!(read_tokens("f x(c)x"), Ok(tokens!("f" ("x";"c"))));
    assert_eq!(read_tokens("f x[d]x"), Ok(tokens!("f" ["x";"d"])));
    assert_eq!(read_tokens("$-[()]$- x"), Ok(tokens!(["$-";("";)] "x")));
    assert_eq!(
        read_tokens("(#(5 6)# -[yy foo()foo aa]-)"),
        Ok(tokens!((""; ("#"; "5" "6") ["-"; "yy" ("foo";) "aa"])))
    )
}
<commit_msg>Remove the old reader.<commit_after>use name::*;

custom_derive! {
    #[derive(Debug,PartialEq,Eq,Clone,Copy,Reifiable)]
    pub enum DelimChar { Paren, SquareBracket, CurlyBracket }
}

impl DelimChar {
    pub fn open(self) -> char {
        match self {
            Paren => '(',
            SquareBracket => '[',
            CurlyBracket => '{',
        }
    }
    pub fn close(self) -> char {
        match self {
            Paren => ')',
            SquareBracket => ']',
            CurlyBracket => '}',
        }
    }
}

use self::DelimChar::*;

#[derive(Debug, PartialEq, Eq)]
pub struct TokenTree {
    pub t: Vec<Token>,
}

pub type Token = Name;

impl Token {
    pub fn is_just(&self, s: &str) -> bool { self.is(s) }
}

impl TokenTree {
    pub fn to_string(&self) -> String {
        let mut result = "".to_owned();
        for t in &self.t {
            result += " ";
            result += &t.orig_sp();
        }
        result
    }
}

pub fn delim(s: &str) -> DelimChar {
    match s {
        "(" | ")" => Paren,
        "[" | "]" => SquareBracket,
        "{" | "}" => CurlyBracket,
        _ => panic!("not a delimiter!"),
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]

#[plugin]
#[no_link]
extern crate json_macros;

extern crate "rustc-serialize" as rustc_serialize;

use rustc_serialize::Encodable;
use rustc_serialize::json::{self, PrettyEncoder};

#[derive(RustcDecodable, RustcEncodable)]
struct Photo {
    url: String,
    dimensions: (u32, u32),
}

#[derive(RustcDecodable, RustcEncodable)]
struct User {
    name: String,
    post_count: u32,
    likes_burgers: bool,
    avatar: Option<Photo>,
}

fn main() {
    println!("24 days of Rust - json (day 6)");
    println!("{}", json::encode(&42));
    println!("{}", json::encode(&vec!["to", "be", "or", "not", "to", "be"]));
    println!("{}", json::encode(&Some(true)));
    let user = User {
        name: "Zbyszek".to_string(),
        post_count: 100u32,
        likes_burgers: true,
        avatar: Some(Photo {
            url: "http://lorempixel.com/160/160/".to_string(),
            dimensions: (160u32, 160u32),
        }),
    };
    println!("{:?}", json::encode(&user));
    let mut buffer: Vec<u8> = Vec::new();
    {
        let mut encoder = PrettyEncoder::new(&mut buffer);
        user.encode(&mut encoder).ok().expect("JSON encode error");
    }
    let encoded = String::from_utf8(buffer).unwrap();
    println!("{}", encoded);
    let incoming_request = "{\"name\":\"John\",\"post_count\":2,\"likes_burgers\":false,\"avatar\":null}";
    let decoded: User = json::decode(incoming_request).unwrap();
    println!("My name is {} and I {} burgers",
        decoded.name, if decoded.likes_burgers { "love" } else { "don't like" });
    assert!(decoded.avatar.is_none());
    let new_request = "{\"id\":64,\"title\":\"24days\",\"stats\":{\"pageviews\":1500}}";
    if let Ok(request_json) = json::Json::from_str(new_request) {
        if let Some(ref stats) = request_json.find("stats") {
            if let Some(ref pageviews) = stats.find("pageviews") {
                println!("Pageviews: {}", pageviews);
            }
        }
    }
    let config = json!({
        "hostname": "localhost",
        "port": 6543,
        "allowed_methods": ["get", "post"],
        "limits": {
            "bandwidth": (500 * 16),
            "rate": null
        }
    });
    println!("Configuration: {}", config);
    println!("Bandwidth: {}", config.search("bandwidth").unwrap());
}
<commit_msg>Use Debug instead of Display for json::encode result.<commit_after>#![feature(plugin)]

#[plugin]
#[no_link]
extern crate json_macros;

extern crate "rustc-serialize" as rustc_serialize;

use rustc_serialize::Encodable;
use rustc_serialize::json::{self, PrettyEncoder};

#[derive(RustcDecodable, RustcEncodable)]
struct Photo {
    url: String,
    dimensions: (u32, u32),
}

#[derive(RustcDecodable, RustcEncodable)]
struct User {
    name: String,
    post_count: u32,
    likes_burgers: bool,
    avatar: Option<Photo>,
}

fn main() {
    println!("24 days of Rust - json (day 6)");
    println!("{:?}", json::encode(&42));
    println!("{:?}", json::encode(&vec!["to", "be", "or", "not", "to", "be"]));
    println!("{:?}", json::encode(&Some(true)));
    let user = User {
        name: "Zbyszek".to_string(),
        post_count: 100u32,
        likes_burgers: true,
        avatar: Some(Photo {
            url: "http://lorempixel.com/160/160/".to_string(),
            dimensions: (160u32, 160u32),
        }),
    };
    println!("{:?}", json::encode(&user));
    let mut buffer: Vec<u8> = Vec::new();
    {
        let mut encoder = PrettyEncoder::new(&mut buffer);
        user.encode(&mut encoder).ok().expect("JSON encode error");
    }
    let encoded = String::from_utf8(buffer).unwrap();
    println!("{}", encoded);
    let incoming_request = "{\"name\":\"John\",\"post_count\":2,\"likes_burgers\":false,\"avatar\":null}";
    let decoded: User = json::decode(incoming_request).unwrap();
    println!("My name is {} and I {} burgers",
        decoded.name, if decoded.likes_burgers { "love" } else { "don't like" });
    assert!(decoded.avatar.is_none());
    let new_request = "{\"id\":64,\"title\":\"24days\",\"stats\":{\"pageviews\":1500}}";
    if let Ok(request_json) = json::Json::from_str(new_request) {
        if let Some(ref stats) = request_json.find("stats") {
            if let Some(ref pageviews) = stats.find("pageviews") {
                println!("Pageviews: {}", pageviews);
            }
        }
    }
    let config = json!({
        "hostname": "localhost",
        "port": 6543,
        "allowed_methods": ["get", "post"],
        "limits": {
            "bandwidth": (500 * 16),
            "rate": null
        }
    });
    println!("Configuration: {}", config);
    println!("Bandwidth: {}", config.search("bandwidth").unwrap());
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use rustc_hash::FxHashMap;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr, SpaceSeparator, StringWithSeparator};
use shakmaty::{
    fen::Fen,
    san::San,
    uci::Uci,
    variant::{Variant, VariantPosition},
    zobrist::Zobrist,
    ByColor, CastlingMode, Chess, Color, Outcome, Position, Setup,
};
use tokio::sync::Mutex;

use crate::{
    api::{Error, LilaVariant},
    db::Database,
    model::{
        GameId, GamePlayer, Key, KeyBuilder, LaxDate, LichessEntry, LichessGame, MastersEntry,
        MastersGameWithId, Mode, Speed,
    },
    util::ByColorDef,
};

const MAX_PLIES: usize = 50;

#[derive(Clone)]
pub struct MastersImporter {
    db: Arc<Database>,
    mutex: Arc<Mutex<()>>,
}

impl MastersImporter {
    pub fn new(db: Arc<Database>) -> MastersImporter {
        MastersImporter {
            db,
            mutex: Arc::new(Mutex::new(())),
        }
    }

    pub async fn import(&self, body: MastersGameWithId) -> Result<(), Error> {
        if body.game.players.white.rating / 2 + body.game.players.black.rating / 2 < 2200 {
            return Err(Error::RejectedImport(body.id));
        }

        let _guard = self.mutex.lock();
        let masters_db = self.db.masters();
        if masters_db
            .has_game(body.id)
            .expect("check for masters game")
        {
            return Err(Error::DuplicateGame(body.id));
        }

        let mut without_loops: FxHashMap<Key, (Uci, Color)> =
            FxHashMap::with_capacity_and_hasher(body.game.moves.len(), Default::default());
        let mut pos: Zobrist<Chess, u128> = Zobrist::default();
        let mut final_key = None;
        for uci in &body.game.moves {
            let key = KeyBuilder::masters()
                .with_zobrist(Variant::Chess, pos.zobrist_hash())
                .with_year(body.game.date.year());
            final_key = Some(key.clone());
            let m = uci.to_move(&pos)?;
            without_loops.insert(key, (Uci::from_chess960(&m), pos.turn()));
            pos.play_unchecked(&m);
        }

        if let Some(final_key) = final_key {
            if masters_db.has(final_key).expect("check for masters entry") {
                return Err(Error::DuplicateGame(body.id));
            }
        }

        let mut batch = masters_db.batch();
        batch.put_game(body.id, &body.game);
        for (key, (uci, turn)) in without_loops {
            batch.merge(
                key,
                MastersEntry::new_single(
                    uci,
                    body.id,
                    Outcome::from_winner(body.game.winner),
                    body.game.players.by_color(turn).rating,
                    body.game.players.by_color(!turn).rating,
                ),
            );
        }

        batch.commit().expect("commit masters game");
        Ok(())
    }
}

#[serde_as]
#[derive(Deserialize)]
pub struct LichessGameImport {
    variant: Option<LilaVariant>,
    speed: Speed,
    #[serde_as(as = "Option<DisplayFromStr>")]
    fen: Option<Fen>,
    #[serde_as(as = "DisplayFromStr")]
    id: GameId,
    #[serde_as(as = "DisplayFromStr")]
    date: LaxDate,
    #[serde(flatten, with = "ByColorDef")]
    players: ByColor<GamePlayer>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    winner: Option<Color>,
    #[serde_as(as = "StringWithSeparator<SpaceSeparator, San>")]
    moves: Vec<San>,
}

#[derive(Clone)]
pub struct LichessImporter {
    db: Arc<Database>,
    mutex: Arc<Mutex<()>>,
}

impl LichessImporter {
    pub fn new(db: Arc<Database>) -> LichessImporter {
        LichessImporter {
            db,
            mutex: Arc::new(Mutex::new(())),
        }
    }

    pub async fn import(&self, game: LichessGameImport) -> Result<(), Error> {
        let _guard = self.mutex.lock();

        let lichess_db = self.db.lichess();

        if lichess_db
            .game(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed_lichess)
        {
            log::warn!("lichess game {} already imported", game.id);
            return Ok(());
        }

        let month = match game.date.month() {
            Some(month) => month,
            None => {
                log::error!("lichess game {} missing month", game.id);
                return Err(Error::RejectedImport(game.id));
            }
        };
        let outcome = Outcome::from_winner(game.winner);
        let variant = Variant::from(game.variant.unwrap_or_default());

        let mut pos: Zobrist<_, u128> = Zobrist::new(match game.fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960)?,
            None => VariantPosition::new(variant),
        });

        let mut without_loops: FxHashMap<Key, (Uci, Color)> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());
        for (ply, san) in game.moves.into_iter().enumerate() {
            if ply >= MAX_PLIES {
                break;
            }

            let m = san.to_move(&pos)?;
            without_loops.insert(
                KeyBuilder::lichess()
                    .with_zobrist(variant, pos.zobrist_hash())
                    .with_month(month),
                (Uci::from_chess960(&m), pos.turn()),
            );
            pos.play_unchecked(&m);
        }

        let mut batch = lichess_db.batch();
        batch.merge_game(
            game.id,
            LichessGame {
                mode: Mode::Rated,
                indexed_player: Default::default(),
                indexed_lichess: true,
                outcome,
                players: game.players.clone(),
                month,
                speed: game.speed,
            },
        );
        for (key, (uci, turn)) in without_loops {
            batch.merge_lichess(
                key,
                LichessEntry::new_single(
                    uci,
                    game.speed,
                    game.id,
                    outcome,
                    game.players.by_color(turn).rating,
                    game.players.by_color(!turn).rating,
                ),
            );
        }

        batch.commit().expect("commit lichess game");
        Ok(())
    }
}
<commit_msg>scrutinize dates of master game imports<commit_after>use std::sync::Arc;

use rustc_hash::FxHashMap;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr, SpaceSeparator, StringWithSeparator};
use shakmaty::{
    fen::Fen,
    san::San,
    uci::Uci,
    variant::{Variant, VariantPosition},
    zobrist::Zobrist,
    ByColor, CastlingMode, Chess, Color, Outcome, Position, Setup,
};
use tokio::sync::Mutex;

use crate::{
    api::{Error, LilaVariant},
    db::Database,
    model::{
        GameId, GamePlayer, Key, KeyBuilder, LaxDate, LichessEntry, LichessGame, MastersEntry,
        MastersGameWithId, Mode, Speed,
    },
    util::ByColorDef,
};

const MAX_PLIES: usize = 50;

#[derive(Clone)]
pub struct MastersImporter {
    db: Arc<Database>,
    mutex: Arc<Mutex<()>>,
}

impl MastersImporter {
    pub fn new(db: Arc<Database>) -> MastersImporter {
        MastersImporter {
            db,
            mutex: Arc::new(Mutex::new(())),
        }
    }

    pub async fn import(&self, body: MastersGameWithId) -> Result<(), Error> {
        if body.game.players.white.rating / 2 + body.game.players.black.rating / 2 < 2200 {
            return Err(Error::RejectedImport(body.id));
        }

        let year = u16::from(body.game.date.year());
        if year < 1952 || 2021 < year {
            return Err(Error::RejectedImport(body.id));
        }

        let _guard = self.mutex.lock();
        let masters_db = self.db.masters();
        if masters_db
            .has_game(body.id)
            .expect("check for masters game")
        {
            return Err(Error::DuplicateGame(body.id));
        }

        let mut without_loops: FxHashMap<Key, (Uci, Color)> =
            FxHashMap::with_capacity_and_hasher(body.game.moves.len(), Default::default());
        let mut pos: Zobrist<Chess, u128> = Zobrist::default();
        let mut final_key = None;
        for uci in &body.game.moves {
            let key = KeyBuilder::masters()
                .with_zobrist(Variant::Chess, pos.zobrist_hash())
                .with_year(body.game.date.year());
            final_key = Some(key.clone());
            let m = uci.to_move(&pos)?;
            without_loops.insert(key, (Uci::from_chess960(&m), pos.turn()));
            pos.play_unchecked(&m);
        }

        if let Some(final_key) = final_key {
            if masters_db.has(final_key).expect("check for masters entry") {
                return Err(Error::DuplicateGame(body.id));
            }
        }

        let mut batch = masters_db.batch();
        batch.put_game(body.id, &body.game);
        for (key, (uci, turn)) in without_loops {
            batch.merge(
                key,
                MastersEntry::new_single(
                    uci,
                    body.id,
                    Outcome::from_winner(body.game.winner),
                    body.game.players.by_color(turn).rating,
                    body.game.players.by_color(!turn).rating,
                ),
            );
        }

        batch.commit().expect("commit masters game");
        Ok(())
    }
}

#[serde_as]
#[derive(Deserialize)]
pub struct LichessGameImport {
    variant: Option<LilaVariant>,
    speed: Speed,
    #[serde_as(as = "Option<DisplayFromStr>")]
    fen: Option<Fen>,
    #[serde_as(as = "DisplayFromStr")]
    id: GameId,
    #[serde_as(as = "DisplayFromStr")]
    date: LaxDate,
    #[serde(flatten, with = "ByColorDef")]
    players: ByColor<GamePlayer>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    winner: Option<Color>,
    #[serde_as(as = "StringWithSeparator<SpaceSeparator, San>")]
    moves: Vec<San>,
}

#[derive(Clone)]
pub struct LichessImporter {
    db: Arc<Database>,
    mutex: Arc<Mutex<()>>,
}

impl LichessImporter {
    pub fn new(db: Arc<Database>) -> LichessImporter {
        LichessImporter {
            db,
            mutex: Arc::new(Mutex::new(())),
        }
    }

    pub async fn import(&self, game: LichessGameImport) -> Result<(), Error> {
        let _guard = self.mutex.lock();

        let lichess_db = self.db.lichess();

        if lichess_db
            .game(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed_lichess)
        {
            log::warn!("lichess game {} already imported", game.id);
            return Ok(());
        }

        let month = match game.date.month() {
            Some(month) => month,
            None => {
                log::error!("lichess game {} missing month", game.id);
                return Err(Error::RejectedImport(game.id));
            }
        };
        let outcome = Outcome::from_winner(game.winner);
        let variant = Variant::from(game.variant.unwrap_or_default());

        let mut pos: Zobrist<_, u128> = Zobrist::new(match game.fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960)?,
            None => VariantPosition::new(variant),
        });

        let mut without_loops: FxHashMap<Key, (Uci, Color)> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());
        for (ply, san) in game.moves.into_iter().enumerate() {
            if ply >= MAX_PLIES {
                break;
            }

            let m = san.to_move(&pos)?;
            without_loops.insert(
                KeyBuilder::lichess()
                    .with_zobrist(variant, pos.zobrist_hash())
                    .with_month(month),
                (Uci::from_chess960(&m), pos.turn()),
            );
            pos.play_unchecked(&m);
        }

        let mut batch = lichess_db.batch();
        batch.merge_game(
            game.id,
            LichessGame {
                mode: Mode::Rated,
                indexed_player: Default::default(),
                indexed_lichess: true,
                outcome,
                players: game.players.clone(),
                month,
                speed: game.speed,
            },
        );
        for (key, (uci, turn)) in without_loops {
            batch.merge_lichess(
                key,
                LichessEntry::new_single(
                    uci,
                    game.speed,
                    game.id,
                    outcome,
                    game.players.by_color(turn).rating,
                    game.players.by_color(!turn).rating,
                ),
            );
        }

        batch.commit().expect("commit lichess game");
        Ok(())
    }
}
<|endoftext|>