<commit_before>use std::env;
use std::fs;
use std::io::{Error as IoError, Read, Write};
use std::path::PathBuf;

use ansi_term::{Color, Style};
use app_dirs::{get_app_root, AppDataType};
use log::debug;
use serde_derive::{Deserialize, Serialize};
use toml;

use crate::error::TealdeerError::{self, ConfigError};

pub const CONFIG_FILE_NAME: &str = "config.toml";

fn default_underline() -> bool {
    false
}

fn default_bold() -> bool {
    false
}

#[serde(rename_all = "lowercase")]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum RawColor {
    Black,
    Red,
    Green,
    Yellow,
    Blue,
    Purple,
    Cyan,
    White,
}

impl From<RawColor> for Color {
    fn from(raw_color: RawColor) -> Self {
        match raw_color {
            RawColor::Black => Color::Black,
            RawColor::Red => Color::Red,
            RawColor::Green => Color::Green,
            RawColor::Yellow => Color::Yellow,
            RawColor::Blue => Color::Blue,
            RawColor::Purple => Color::Purple,
            RawColor::Cyan => Color::Cyan,
            RawColor::White => Color::White,
        }
    }
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyle {
    pub foreground: Option<RawColor>,
    pub background: Option<RawColor>,
    #[serde(default = "default_underline")]
    pub underline: bool,
    #[serde(default = "default_bold")]
    pub bold: bool,
}

impl Default for RawStyle {
    fn default() -> Self {
        Self {
            foreground: None,
            background: None,
            underline: false,
            bold: false,
        }
    }
} // impl RawStyle

impl From<RawStyle> for Style {
    fn from(raw_style: RawStyle) -> Self {
        let mut style = Self::default();

        if let Some(foreground) = raw_style.foreground {
            style = style.fg(Color::from(foreground));
        }

        if let Some(background) = raw_style.background {
            style = style.on(Color::from(background));
        }

        if raw_style.underline {
            style = style.underline();
        }

        if raw_style.bold {
            style = style.bold();
        }

        style
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyleConfig {
    #[serde(default)]
    pub description: RawStyle,
    #[serde(default)]
    pub command_name: RawStyle,
    #[serde(default)]
    pub example_text: RawStyle,
    #[serde(default)]
    pub example_code: RawStyle,
    #[serde(default)]
    pub example_variable: RawStyle,
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawDisplayConfig {
    #[serde(default)]
    pub use_pager: bool,
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawConfig {
    style: RawStyleConfig,
    display: RawDisplayConfig,
}

impl RawConfig {
    fn new() -> Self {
        let mut raw_config = Self::default();

        // Set default config
        raw_config.style.example_text.foreground = Some(RawColor::Green);
        raw_config.style.command_name.foreground = Some(RawColor::Cyan);
        raw_config.style.example_code.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.underline = true;
        raw_config.display.use_pager = false;

        raw_config
    }
} // impl RawConfig

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct StyleConfig {
    pub description: Style,
    pub command_name: Style,
    pub example_text: Style,
    pub example_code: Style,
    pub example_variable: Style,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DisplayConfig {
    pub use_pager: bool,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Config {
    pub style: StyleConfig,
    pub display: DisplayConfig,
}

impl From<RawConfig> for Config {
    fn from(raw_config: RawConfig) -> Self {
        Self {
            style: StyleConfig {
                command_name: raw_config.style.command_name.into(),
                description: raw_config.style.description.into(),
                example_text: raw_config.style.example_text.into(),
                example_code: raw_config.style.example_code.into(),
                example_variable: raw_config.style.example_variable.into(),
            },
            display: DisplayConfig {
                use_pager: false,
            },
        }
    }
}

#[allow(clippy::needless_pass_by_value)]
fn map_io_err_to_config_err(e: IoError) -> TealdeerError {
    ConfigError(format!("Io Error: {}", e))
}

impl Config {
    pub fn load(enable_styles: bool) -> Result<Self, TealdeerError> {
        debug!("Loading config");

        // Determine path
        let config_file_path = get_config_path()
            .map_err(|e| ConfigError(format!("Could not determine config path: {}", e)))?;

        // Load config
        let raw_config: RawConfig = if config_file_path.exists() && config_file_path.is_file() {
            let mut config_file =
                fs::File::open(config_file_path).map_err(map_io_err_to_config_err)?;
            let mut contents = String::new();
            let _ = config_file
                .read_to_string(&mut contents)
                .map_err(map_io_err_to_config_err)?;
            toml::from_str(&contents)
                .map_err(|err| ConfigError(format!("Failed to parse config file: {}", err)))?
        } else {
            RawConfig::new()
        };

        Ok(if enable_styles {
            Self::from(raw_config)
        } else {
            Self {
                style: StyleConfig {
                    command_name: Style::default(),
                    description: Style::default(),
                    example_text: Style::default(),
                    example_code: Style::default(),
                    example_variable: Style::default(),
                },
                display: DisplayConfig {
                    use_pager: false,
                }
            }
        })
    }
} // impl Config

/// Return the path to the config directory.
///
/// The config dir path can be overridden using the `TEALDEER_CONFIG_DIR` env
/// variable. Otherwise, the user config directory is returned.
///
/// Note that this function does not verify whether the directory at that
/// loation exists, or is a directory.
pub fn get_config_dir() -> Result<PathBuf, TealdeerError> {
    // Allow overriding the config directory by setting the
    // $TEALDEER_CONFIG_DIR env variable.
    if let Ok(value) = env::var("TEALDEER_CONFIG_DIR") {
        return Ok(PathBuf::from(value));
    };

    // Otherwise, fall back to the user config directory.
    match get_app_root(AppDataType::UserConfig, &crate::APP_INFO) {
        Ok(dirs) => Ok(dirs),
        Err(_) => Err(ConfigError(
            "Could not determine the user config directory.".into(),
        )),
    }
}

/// Return the path to the config file.
///
/// Note that this function does not verify whether the file at that location
/// exists, or is a file.
pub fn get_config_path() -> Result<PathBuf, TealdeerError> {
    let config_dir = get_config_dir()?;
    let config_file_path = config_dir.join(CONFIG_FILE_NAME);
    Ok(config_file_path)
}

/// Create default config file.
pub fn make_default_config() -> Result<PathBuf, TealdeerError> {
    let config_dir = get_config_dir()?;

    // Ensure that config directory exists
    if !config_dir.exists() {
        if let Err(e) = fs::create_dir_all(&config_dir) {
            return Err(ConfigError(format!(
                "Could not create config directory: {}",
                e
            )));
        }
    } else if !config_dir.is_dir() {
        return Err(ConfigError(format!(
            "Config directory could not be created: {} already exists but is not a directory",
            config_dir.to_string_lossy(),
        )));
    }

    // Ensure that a config file doesn't get overwritten
    let config_file_path = config_dir.join(CONFIG_FILE_NAME);
    if config_file_path.is_file() {
        return Err(ConfigError(format!(
            "A configuration file already exists at {}, no action was taken.",
            config_file_path.to_str().unwrap()
        )));
    }

    // Create default config
    let serialized_config = toml::to_string(&RawConfig::new())
        .map_err(|err| ConfigError(format!("Failed to serialize default config: {}", err)))?;

    // Write default config
    let mut config_file = fs::File::create(&config_file_path).map_err(map_io_err_to_config_err)?;
    let _wc = config_file
        .write(serialized_config.as_bytes())
        .map_err(map_io_err_to_config_err)?;

    Ok(config_file_path)
}

#[test]
fn test_serialize_deserialize() {
    let raw_config = RawConfig::new();
    let serialized = toml::to_string(&raw_config).unwrap();
    let deserialized: RawConfig = toml::from_str(&serialized).unwrap();
    assert_eq!(raw_config, deserialized);
}
<commit_msg>Ensure that all config keys are optional<commit_after>use std::env;
use std::fs;
use std::io::{Error as IoError, Read, Write};
use std::path::PathBuf;

use ansi_term::{Color, Style};
use app_dirs::{get_app_root, AppDataType};
use log::debug;
use serde_derive::{Deserialize, Serialize};
use toml;

use crate::error::TealdeerError::{self, ConfigError};

pub const CONFIG_FILE_NAME: &str = "config.toml";

fn default_underline() -> bool {
    false
}

fn default_bold() -> bool {
    false
}

#[serde(rename_all = "lowercase")]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum RawColor {
    Black,
    Red,
    Green,
    Yellow,
    Blue,
    Purple,
    Cyan,
    White,
}

impl From<RawColor> for Color {
    fn from(raw_color: RawColor) -> Self {
        match raw_color {
            RawColor::Black => Color::Black,
            RawColor::Red => Color::Red,
            RawColor::Green => Color::Green,
            RawColor::Yellow => Color::Yellow,
            RawColor::Blue => Color::Blue,
            RawColor::Purple => Color::Purple,
            RawColor::Cyan => Color::Cyan,
            RawColor::White => Color::White,
        }
    }
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyle {
    pub foreground: Option<RawColor>,
    pub background: Option<RawColor>,
    #[serde(default = "default_underline")]
    pub underline: bool,
    #[serde(default = "default_bold")]
    pub bold: bool,
}

impl Default for RawStyle {
    fn default() -> Self {
        Self {
            foreground: None,
            background: None,
            underline: false,
            bold: false,
        }
    }
} // impl RawStyle

impl From<RawStyle> for Style {
    fn from(raw_style: RawStyle) -> Self {
        let mut style = Self::default();

        if let Some(foreground) = raw_style.foreground {
            style = style.fg(Color::from(foreground));
        }

        if let Some(background) = raw_style.background {
            style = style.on(Color::from(background));
        }

        if raw_style.underline {
            style = style.underline();
        }

        if raw_style.bold {
            style = style.bold();
        }

        style
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyleConfig {
    #[serde(default)]
    pub description: RawStyle,
    #[serde(default)]
    pub command_name: RawStyle,
    #[serde(default)]
    pub example_text: RawStyle,
    #[serde(default)]
    pub example_code: RawStyle,
    #[serde(default)]
    pub example_variable: RawStyle,
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawDisplayConfig {
    #[serde(default)]
    pub use_pager: bool,
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawConfig {
    #[serde(default)]
    style: RawStyleConfig,
    #[serde(default)]
    display: RawDisplayConfig,
}

impl RawConfig {
    fn new() -> Self {
        let mut raw_config = Self::default();

        // Set default config
        raw_config.style.example_text.foreground = Some(RawColor::Green);
        raw_config.style.command_name.foreground = Some(RawColor::Cyan);
        raw_config.style.example_code.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.underline = true;
        raw_config.display.use_pager = false;

        raw_config
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct StyleConfig {
    pub description: Style,
    pub command_name: Style,
    pub example_text: Style,
    pub example_code: Style,
    pub example_variable: Style,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DisplayConfig {
    pub use_pager: bool,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Config {
    pub style: StyleConfig,
    pub display: DisplayConfig,
}

impl From<RawConfig> for Config {
    fn from(raw_config: RawConfig) -> Self {
        Self {
            style: StyleConfig {
                command_name: raw_config.style.command_name.into(),
                description: raw_config.style.description.into(),
                example_text: raw_config.style.example_text.into(),
                example_code: raw_config.style.example_code.into(),
                example_variable: raw_config.style.example_variable.into(),
            },
            display: DisplayConfig {
                use_pager: false,
            },
        }
    }
}

#[allow(clippy::needless_pass_by_value)]
fn map_io_err_to_config_err(e: IoError) -> TealdeerError {
    ConfigError(format!("Io Error: {}", e))
}

impl Config {
    pub fn load(enable_styles: bool) -> Result<Self, TealdeerError> {
        debug!("Loading config");

        // Determine path
        let config_file_path = get_config_path()
            .map_err(|e| ConfigError(format!("Could not determine config path: {}", e)))?;

        // Load config
        let raw_config: RawConfig = if config_file_path.exists() && config_file_path.is_file() {
            let mut config_file =
                fs::File::open(config_file_path).map_err(map_io_err_to_config_err)?;
            let mut contents = String::new();
            let _ = config_file
                .read_to_string(&mut contents)
                .map_err(map_io_err_to_config_err)?;
            toml::from_str(&contents)
                .map_err(|err| ConfigError(format!("Failed to parse config file: {}", err)))?
        } else {
            RawConfig::new()
        };

        Ok(if enable_styles {
            Self::from(raw_config)
        } else {
            Self {
                style: StyleConfig {
                    command_name: Style::default(),
                    description: Style::default(),
                    example_text: Style::default(),
                    example_code: Style::default(),
                    example_variable: Style::default(),
                },
                display: DisplayConfig {
                    use_pager: false,
                }
            }
        })
    }
} // impl Config

/// Return the path to the config directory.
///
/// The config dir path can be overridden using the `TEALDEER_CONFIG_DIR` env
/// variable. Otherwise, the user config directory is returned.
///
/// Note that this function does not verify whether the directory at that
/// loation exists, or is a directory.
pub fn get_config_dir() -> Result<PathBuf, TealdeerError> {
    // Allow overriding the config directory by setting the
    // $TEALDEER_CONFIG_DIR env variable.
    if let Ok(value) = env::var("TEALDEER_CONFIG_DIR") {
        return Ok(PathBuf::from(value));
    };

    // Otherwise, fall back to the user config directory.
    match get_app_root(AppDataType::UserConfig, &crate::APP_INFO) {
        Ok(dirs) => Ok(dirs),
        Err(_) => Err(ConfigError(
            "Could not determine the user config directory.".into(),
        )),
    }
}

/// Return the path to the config file.
///
/// Note that this function does not verify whether the file at that location
/// exists, or is a file.
pub fn get_config_path() -> Result<PathBuf, TealdeerError> {
    let config_dir = get_config_dir()?;
    let config_file_path = config_dir.join(CONFIG_FILE_NAME);
    Ok(config_file_path)
}

/// Create default config file.
pub fn make_default_config() -> Result<PathBuf, TealdeerError> {
    let config_dir = get_config_dir()?;

    // Ensure that config directory exists
    if !config_dir.exists() {
        if let Err(e) = fs::create_dir_all(&config_dir) {
            return Err(ConfigError(format!(
                "Could not create config directory: {}",
                e
            )));
        }
    } else if !config_dir.is_dir() {
        return Err(ConfigError(format!(
            "Config directory could not be created: {} already exists but is not a directory",
            config_dir.to_string_lossy(),
        )));
    }

    // Ensure that a config file doesn't get overwritten
    let config_file_path = config_dir.join(CONFIG_FILE_NAME);
    if config_file_path.is_file() {
        return Err(ConfigError(format!(
            "A configuration file already exists at {}, no action was taken.",
            config_file_path.to_str().unwrap()
        )));
    }

    // Create default config
    let serialized_config = toml::to_string(&RawConfig::new())
        .map_err(|err| ConfigError(format!("Failed to serialize default config: {}", err)))?;

    // Write default config
    let mut config_file = fs::File::create(&config_file_path).map_err(map_io_err_to_config_err)?;
    let _wc = config_file
        .write(serialized_config.as_bytes())
        .map_err(map_io_err_to_config_err)?;

    Ok(config_file_path)
}

#[test]
fn test_serialize_deserialize() {
    let raw_config = RawConfig::new();
    let serialized = toml::to_string(&raw_config).unwrap();
    let deserialized: RawConfig = toml::from_str(&serialized).unwrap();
    assert_eq!(raw_config, deserialized);
}
<|endoftext|>


<commit_before>use serde::Deserialize;

use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;

#[derive(Deserialize)]
struct Sections {
    #[serde(default)]
    expand: Config,
}

#[derive(Deserialize, Default)]
pub struct Config {
    pub theme: Option<String>,
    pub color: Option<String>,
    #[serde(default)]
    pub pager: bool,
}

pub fn deserialize() -> Config {
    try_deserialize().unwrap_or_default()
}

fn try_deserialize() -> Option<Config> {
    let cargo_home = env::var_os("CARGO_HOME")?;
    let config_names = ["config", "config.toml"];
    let config_path = config_names
        .iter()
        .map(|name| PathBuf::from(cargo_home.clone()).join(name))
        .find(|path| path.exists())?;

    let content = fs::read(&config_path).ok()?;

    let full_config: Sections = match toml::from_slice(&content) {
        Ok(config) => config,
        Err(err) => {
            let _ = writeln!(
                &mut io::stderr(),
                "Warning: {}: {}",
                config_path.display(),
                err
            );
            return None;
        }
    };

    Some(full_config.expand)
}
<commit_msg>Touch up PR 109 path creation<commit_after>use serde::Deserialize;

use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;

#[derive(Deserialize)]
struct Sections {
    #[serde(default)]
    expand: Config,
}

#[derive(Deserialize, Default)]
pub struct Config {
    pub theme: Option<String>,
    pub color: Option<String>,
    #[serde(default)]
    pub pager: bool,
}

pub fn deserialize() -> Config {
    try_deserialize().unwrap_or_default()
}

fn try_deserialize() -> Option<Config> {
    let cargo_home = env::var_os("CARGO_HOME").map(PathBuf::from)?;
    let config_names = ["config", "config.toml"];
    let config_path = config_names
        .iter()
        .map(|name| cargo_home.join(name))
        .find(|path| path.exists())?;

    let content = fs::read(&config_path).ok()?;

    let full_config: Sections = match toml::from_slice(&content) {
        Ok(config) => config,
        Err(err) => {
            let _ = writeln!(
                &mut io::stderr(),
                "Warning: {}: {}",
                config_path.display(),
                err
            );
            return None;
        }
    };

    Some(full_config.expand)
}
<|endoftext|>


<commit_before>use crate::file::{FileType, SaveDir, SaveFile};
use serde::de::{self, Deserializer, Visitor};
use serde::ser::Serializer;
use serde_derive::{Deserialize, Serialize};
use std::ops::Mul;
use std::path::PathBuf;
use std::result;

#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
    pub series_dir: PathBuf,
    pub reset_dates_on_rewatch: bool,
    pub episode: EpisodeConfig,
}

impl Config {
    pub fn new<P>(series_dir: P) -> Config
    where
        P: Into<PathBuf>,
    {
        Config {
            series_dir: series_dir.into(),
            reset_dates_on_rewatch: false,
            episode: EpisodeConfig::new(),
        }
    }
}

impl SaveFile for Config {
    fn filename() -> &'static str {
        "config.toml"
    }

    fn save_dir() -> SaveDir {
        SaveDir::Config
    }

    fn file_type() -> FileType {
        FileType::Toml
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct EpisodeConfig {
    #[serde(rename = "percent_watched_to_progress")]
    pub pcnt_must_watch: Percentage,
    pub seconds_before_next: f32,
}

impl EpisodeConfig {
    pub fn new() -> EpisodeConfig {
        EpisodeConfig {
            pcnt_must_watch: Percentage::new(50.0),
            seconds_before_next: 5.0,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub struct Percentage(#[serde(with = "Percentage")] f32);

impl Percentage {
    pub fn new(value: f32) -> Percentage {
        Percentage(value / 100.0)
    }

    fn deserialize<'de, D>(de: D) -> result::Result<f32, D::Error>
    where
        D: Deserializer<'de>,
    {
        use std::fmt;

        struct PercentageVisitor;

        impl<'de> Visitor<'de> for PercentageVisitor {
            type Value = f32;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("a positive percentage number")
            }

            fn visit_f32<E>(self, value: f32) -> result::Result<Self::Value, E>
            where
                E: de::Error,
            {
                if value.is_sign_negative() {
                    return Err(E::custom(format!(
                        "percentage must be greater than 0: {}",
                        value
                    )));
                }

                Ok(value / 100.0)
            }

            fn visit_f64<E>(self, value: f64) -> result::Result<Self::Value, E>
            where
                E: de::Error,
            {
                if value.is_sign_negative() {
                    return Err(E::custom(format!(
                        "percentage must be greater than 0: {}",
                        value
                    )));
                }

                Ok(value as f32 / 100.0)
            }
        }

        de.deserialize_f32(PercentageVisitor)
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    fn serialize<S>(value: &f32, ser: S) -> result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        ser.serialize_f32(value * 100.0)
    }

    #[inline(always)]
    pub fn as_multiplier(self) -> f32 {
        self.0
    }
}

impl Mul<Percentage> for f32 {
    type Output = f32;

    fn mul(self, other: Percentage) -> Self::Output {
        self * other.as_multiplier()
    }
}
<commit_msg>[config] Default seconds_before_next: 5.0 -> 3.0<commit_after>use crate::file::{FileType, SaveDir, SaveFile};
use serde::de::{self, Deserializer, Visitor};
use serde::ser::Serializer;
use serde_derive::{Deserialize, Serialize};
use std::ops::Mul;
use std::path::PathBuf;
use std::result;

#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
    pub series_dir: PathBuf,
    pub reset_dates_on_rewatch: bool,
    pub episode: EpisodeConfig,
}

impl Config {
    pub fn new<P>(series_dir: P) -> Config
    where
        P: Into<PathBuf>,
    {
        Config {
            series_dir: series_dir.into(),
            reset_dates_on_rewatch: false,
            episode: EpisodeConfig::new(),
        }
    }
}

impl SaveFile for Config {
    fn filename() -> &'static str {
        "config.toml"
    }

    fn save_dir() -> SaveDir {
        SaveDir::Config
    }

    fn file_type() -> FileType {
        FileType::Toml
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct EpisodeConfig {
    #[serde(rename = "percent_watched_to_progress")]
    pub pcnt_must_watch: Percentage,
    pub seconds_before_next: f32,
}

impl EpisodeConfig {
    pub fn new() -> EpisodeConfig {
        EpisodeConfig {
            pcnt_must_watch: Percentage::new(50.0),
            seconds_before_next: 3.0,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub struct Percentage(#[serde(with = "Percentage")] f32);

impl Percentage {
    pub fn new(value: f32) -> Percentage {
        Percentage(value / 100.0)
    }

    fn deserialize<'de, D>(de: D) -> result::Result<f32, D::Error>
    where
        D: Deserializer<'de>,
    {
        use std::fmt;

        struct PercentageVisitor;

        impl<'de> Visitor<'de> for PercentageVisitor {
            type Value = f32;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("a positive percentage number")
            }

            fn visit_f32<E>(self, value: f32) -> result::Result<Self::Value, E>
            where
                E: de::Error,
            {
                if value.is_sign_negative() {
                    return Err(E::custom(format!(
                        "percentage must be greater than 0: {}",
                        value
                    )));
                }

                Ok(value / 100.0)
            }

            fn visit_f64<E>(self, value: f64) -> result::Result<Self::Value, E>
            where
                E: de::Error,
            {
                if value.is_sign_negative() {
                    return Err(E::custom(format!(
                        "percentage must be greater than 0: {}",
                        value
                    )));
                }

                Ok(value as f32 / 100.0)
            }
        }

        de.deserialize_f32(PercentageVisitor)
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    fn serialize<S>(value: &f32, ser: S) -> result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        ser.serialize_f32(value * 100.0)
    }

    #[inline(always)]
    pub fn as_multiplier(self) -> f32 {
        self.0
    }
}

impl Mul<Percentage> for f32 {
    type Output = f32;

    fn mul(self, other: Percentage) -> Self::Output {
        self * other.as_multiplier()
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use types::{Sectors, SectorOffset};

pub const DBUS_TIMEOUT: i32 = 10000; // millieconds

pub const REDUNDANCY: usize = 1;
pub const MIN_BLK_DEVS: usize = 2;

const MEGA: u64 = 1024 * 1024;
const GIGA: u64 = MEGA * 1024;
const TERA: u64 = GIGA * 1024;

// Before increasing, either check if md-raid is good for wider, or
// change zone creation to limit width
pub const MAX_BLK_DEVS: usize = 8;

pub const SECTOR_SIZE: u64 = 512;
pub const HEADER_SIZE: u64 = 512;
pub const MDA_ZONE_SIZE: u64 = MEGA;
pub const MDA_ZONE_SECTORS: Sectors = Sectors(MDA_ZONE_SIZE / SECTOR_SIZE);
pub const MDAX_ZONE_SECTORS: Sectors = Sectors(1020);
pub const MDAA_ZONE_OFFSET: SectorOffset = SectorOffset(8);
pub const MDAB_ZONE_OFFSET: SectorOffset = SectorOffset(1028);

pub const FRO_MAGIC: &'static [u8] = b"!IamFroy0\x86\xffGO\x02^\x41";
pub const STRIPE_SECTORS: Sectors = Sectors(MEGA / SECTOR_SIZE);

// No devs smaller than around a gig
const MIN_DATA_ZONE_SIZE: u64 = GIGA;
pub const MIN_DATA_ZONE_SECTORS: Sectors = Sectors(MIN_DATA_ZONE_SIZE / SECTOR_SIZE);
pub const MIN_DEV_SIZE: u64 = MIN_DATA_ZONE_SIZE + (2 * MDA_ZONE_SIZE);

// But also no larger than 1 TiB
const MAX_DATA_ZONE_SIZE: u64 = TERA;
pub const MAX_DATA_ZONE_SECTORS: Sectors = Sectors(MAX_DATA_ZONE_SIZE / SECTOR_SIZE);

//const MIN_DEV_SECTORS: u64 = MIN_DEV_SIZE / SECTOR_SIZE;

pub const MAX_REGIONS: u64 = (2 * MEGA);
const DEFAULT_REGION_SIZE: u64 = (4 * MEGA);
pub const DEFAULT_REGION_SECTORS: Sectors = Sectors(DEFAULT_REGION_SIZE / SECTOR_SIZE);

// Try to create this many raids in a Froyodev. Too many is wasteful
// but too few limits reshape options (at least until dn-raid supports
// reshape)
pub const IDEAL_RAID_COUNT: usize = 10;

pub const DATA_BLOCK_SIZE: Sectors = Sectors(MEGA / SECTOR_SIZE);
pub const TPOOL_LOW_WATER_BLOCKS: u64 = 512; // 512MiB

pub const TPOOL_INITIAL_META_SECTORS: Sectors = Sectors(4 * MEGA / SECTOR_SIZE);
pub const TPOOL_INITIAL_DATA_SECTORS: Sectors = Sectors(2 * GIGA / SECTOR_SIZE);
pub const TPOOL_EXTEND_SECTORS: Sectors = Sectors(GIGA / SECTOR_SIZE);

pub const THIN_INITIAL_SECTORS: Sectors = Sectors(128 * GIGA / SECTOR_SIZE);
<commit_msg>Increase DBus timeout to 20sec<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use types::{Sectors, SectorOffset};

pub const DBUS_TIMEOUT: i32 = 20000; // millieconds

pub const REDUNDANCY: usize = 1;
pub const MIN_BLK_DEVS: usize = 2;

const MEGA: u64 = 1024 * 1024;
const GIGA: u64 = MEGA * 1024;
const TERA: u64 = GIGA * 1024;

// Before increasing, either check if md-raid is good for wider, or
// change zone creation to limit width
pub const MAX_BLK_DEVS: usize = 8;

pub const SECTOR_SIZE: u64 = 512;
pub const HEADER_SIZE: u64 = 512;
pub const MDA_ZONE_SIZE: u64 = MEGA;
pub const MDA_ZONE_SECTORS: Sectors = Sectors(MDA_ZONE_SIZE / SECTOR_SIZE);
pub const MDAX_ZONE_SECTORS: Sectors = Sectors(1020);
pub const MDAA_ZONE_OFFSET: SectorOffset = SectorOffset(8);
pub const MDAB_ZONE_OFFSET: SectorOffset = SectorOffset(1028);

pub const FRO_MAGIC: &'static [u8] = b"!IamFroy0\x86\xffGO\x02^\x41";
pub const STRIPE_SECTORS: Sectors = Sectors(MEGA / SECTOR_SIZE);

// No devs smaller than around a gig
const MIN_DATA_ZONE_SIZE: u64 = GIGA;
pub const MIN_DATA_ZONE_SECTORS: Sectors = Sectors(MIN_DATA_ZONE_SIZE / SECTOR_SIZE);
pub const MIN_DEV_SIZE: u64 = MIN_DATA_ZONE_SIZE + (2 * MDA_ZONE_SIZE);

// But also no larger than 1 TiB
const MAX_DATA_ZONE_SIZE: u64 = TERA;
pub const MAX_DATA_ZONE_SECTORS: Sectors = Sectors(MAX_DATA_ZONE_SIZE / SECTOR_SIZE);

//const MIN_DEV_SECTORS: u64 = MIN_DEV_SIZE / SECTOR_SIZE;

pub const MAX_REGIONS: u64 = (2 * MEGA);
const DEFAULT_REGION_SIZE: u64 = (4 * MEGA);
pub const DEFAULT_REGION_SECTORS: Sectors = Sectors(DEFAULT_REGION_SIZE / SECTOR_SIZE);

// Try to create this many raids in a Froyodev. Too many is wasteful
// but too few limits reshape options (at least until dn-raid supports
// reshape)
pub const IDEAL_RAID_COUNT: usize = 10;

pub const DATA_BLOCK_SIZE: Sectors = Sectors(MEGA / SECTOR_SIZE);
pub const TPOOL_LOW_WATER_BLOCKS: u64 = 512; // 512MiB

pub const TPOOL_INITIAL_META_SECTORS: Sectors = Sectors(4 * MEGA / SECTOR_SIZE);
pub const TPOOL_INITIAL_DATA_SECTORS: Sectors = Sectors(2 * GIGA / SECTOR_SIZE);
pub const TPOOL_EXTEND_SECTORS: Sectors = Sectors(GIGA / SECTOR_SIZE);

pub const THIN_INITIAL_SECTORS: Sectors = Sectors(128 * GIGA / SECTOR_SIZE);
<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use sys::{MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() != 0 }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() != 0 }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()) }.to_string_lossy()
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) != 0 }
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0"
        }
    }
}
<commit_msg>Small style nit<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use sys::{MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() != 0 }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() != 0 }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) != 0 }
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0"
        }
    }
}
<|endoftext|>


<commit_before>use rand::Rng;
use domination::Domination;
use multi_objective::MultiObjective;
use population::{Individual, UnratedPopulation, RatedPopulation, RankedPopulation};
use selection::SelectSolutions;
use time;

pub struct DriverConfig {
    pub mu: usize,
    pub lambda: usize,
    pub k: usize,
    pub ngen: usize,
    pub num_objectives: usize,
    pub parallel_weight: f64,
}

pub trait Driver: Sync
{
    type GENOME: Clone + Sync + Send; // XXX: clone?
    type FIT: MultiObjective + Domination + Clone + Send; // XXX: clone?
    type SELECTION: SelectSolutions<Individual<Self::GENOME, Self::FIT>, Self::FIT>;

    fn random_genome<R>(&self, rng: &mut R) -> Self::GENOME where R: Rng;
    fn initial_population<R>(&self, rng: &mut R, n: usize) -> UnratedPopulation<Self::GENOME, Self::FIT> where R: Rng {
        let mut pop = UnratedPopulation::new();
        for _ in 0..n {
            pop.push(self.random_genome(rng));
        }
        assert!(pop.len() == n);
        return pop;
    }
    fn fitness(&self, ind: &Self::GENOME) -> Self::FIT;
    // XXX: Make use of Fitness!
    fn mate<R>(&self, rng: &mut R, parent1: &Self::GENOME, parent2: &Self::GENOME) -> Self::GENOME where R: Rng;
    fn is_solution(&self, _ind: &Self::GENOME, _fit: &Self::FIT) -> bool {
        false
    }

    /// This can be used to update certain objectives in relation to the whole population.
    fn population_metric(&self, _population: &mut RatedPopulation<Self::GENOME, Self::FIT>) {
    }

    fn run<R, L>(&self,
                 rng: &mut R,
                 config: &DriverConfig,
                 selection: &Self::SELECTION,
                 logger: &L)
                 -> RankedPopulation<Self::GENOME, Self::FIT>
        where R: Rng,
              L: Fn(usize, u64, usize, &RankedPopulation<Self::GENOME, Self::FIT>)
    {
        // this is generation 0. it's empty
        let mut time_last = time::precise_time_ns();
        let mut parents = RankedPopulation::<Self::GENOME, Self::FIT>::new();
        let mut offspring = self.initial_population(rng, config.mu);
        assert!(offspring.len() == config.mu);
        let mut gen: usize = 0;

        loop {
            let rated_offspring = offspring.rate_in_parallel(&|ind| self.fitness(ind), config.parallel_weight);
            let mut next_generation = parents.merge(rated_offspring);
            // apply a population metric on the whole population
            self.population_metric(&mut next_generation);
            parents = next_generation.select(config.mu, config.num_objectives, selection, rng);

            let mut found_solutions = 0;
            parents.all(&mut |ind, fit| {
                if self.is_solution(ind, fit) {
                    found_solutions += 1;
                }
            });

            let now = time::precise_time_ns();
            let duration = now - time_last;
            time_last = now;

            logger(gen, duration, found_solutions, &parents);

            if found_solutions > 0 {
                break;
            }

            gen += 1;
            if gen > config.ngen {
                break;
            }

            offspring = parents.reproduce(rng,
                                          config.lambda,
                                          config.k,
                                          &|rng, p1, p2| self.mate(rng, p1, p2));
        }

        return parents;
    }
}
<commit_msg>Add merge_and_select() and reproduce() methods to Driver, so that someone can easily implement run() himself.<commit_after>use rand::Rng;
use domination::Domination;
use multi_objective::MultiObjective;
use population::{Individual, UnratedPopulation, RatedPopulation, RankedPopulation};
use selection::SelectSolutions;
use time;

pub struct DriverConfig {
    pub mu: usize,
    pub lambda: usize,
    pub k: usize,
    pub ngen: usize,
    pub num_objectives: usize,
    pub parallel_weight: f64,
}

pub trait Driver: Sync
{
    type GENOME: Clone + Sync + Send; // XXX: clone?
    type FIT: MultiObjective + Domination + Clone + Send; // XXX: clone?
    type SELECTION: SelectSolutions<Individual<Self::GENOME, Self::FIT>, Self::FIT>;

    fn random_genome<R>(&self, rng: &mut R) -> Self::GENOME where R: Rng;
    fn initial_population<R>(&self,
                             rng: &mut R,
                             n: usize)
                             -> UnratedPopulation<Self::GENOME, Self::FIT>
        where R: Rng
    {
        let mut pop = UnratedPopulation::new();
        for _ in 0..n {
            pop.push(self.random_genome(rng));
        }
        assert!(pop.len() == n);
        return pop;
    }
    fn fitness(&self, ind: &Self::GENOME) -> Self::FIT;

    // XXX: Make use of Fitness!
    fn mate<R>(&self, rng: &mut R, parent1: &Self::GENOME, parent2: &Self::GENOME) -> Self::GENOME
        where R: Rng;

    fn is_solution(&self, _ind: &Self::GENOME, _fit: &Self::FIT) -> bool {
        false
    }

    /// This can be used to update certain objectives in relation to the whole population.
    fn population_metric(&self, _population: &mut RatedPopulation<Self::GENOME, Self::FIT>) {}

    fn reproduce<R>(&self,
                    parents: &RankedPopulation<Self::GENOME, Self::FIT>,
                    rng: &mut R,
                    config: &DriverConfig)
                    -> UnratedPopulation<Self::GENOME, Self::FIT>
        where R: Rng
    {
        parents.reproduce(rng,
                          config.lambda,
                          config.k,
                          &|rng, p1, p2| self.mate(rng, p1, p2))
    }

    /// merge the parent population with the offspring population and select `config.mu`
    /// individuals according to `selection`.
    fn merge_and_select<R>(&self,
                           parents: RankedPopulation<Self::GENOME, Self::FIT>,
                           offspring: UnratedPopulation<Self::GENOME, Self::FIT>,
                           rng: &mut R,
                           config: &DriverConfig,
                           selection: &Self::SELECTION)
                           -> RankedPopulation<Self::GENOME, Self::FIT>
        where R: Rng
    {
        let rated_offspring = offspring.rate_in_parallel(&|ind| self.fitness(ind),
                                                         config.parallel_weight);
        let mut next_generation = parents.merge(rated_offspring);

        // apply a population metric on the whole population
        self.population_metric(&mut next_generation);

        next_generation.select(config.mu, config.num_objectives, selection, rng)
    }

    fn run<R, L>(&self,
                 rng: &mut R,
                 config: &DriverConfig,
                 selection: &Self::SELECTION,
                 logger: &L)
                 -> RankedPopulation<Self::GENOME, Self::FIT>
        where R: Rng,
              L: Fn(usize,
                    u64,
                    usize,
                    &RankedPopulation<Self::GENOME, Self::FIT>)

    {
        // this is generation 0. it's empty
        let mut time_last = time::precise_time_ns();
        let mut parents = RankedPopulation::<Self::GENOME, Self::FIT>::new();
        let mut offspring = self.initial_population(rng, config.mu);
        assert!(offspring.len() == config.mu);
        let mut gen: usize = 0;

        loop {
            parents = self.merge_and_select(parents, offspring, rng, config, selection);

            let mut found_solutions = 0;
            parents.all(&mut |ind, fit| {
                if self.is_solution(ind, fit) {
                    found_solutions += 1;
                }
            });

            let now = time::precise_time_ns();
            let duration = now - time_last;
            time_last = now;

            logger(gen, duration, found_solutions, &parents);

            if found_solutions > 0 {
                break;
            }

            gen += 1;
            if gen > config.ngen {
                break;
            }

            offspring = self.reproduce(&parents, rng, config);
        }

        return parents;
    }
}
<|endoftext|>


<commit_before>//! Representations of various client errors

use std::io::Error as IoError;
use hyper::Error as HttpError;
use hyper::status::StatusCode;
use rustc_serialize::json::{DecoderError, EncoderError, ParserError};

#[derive(Debug)]
pub enum Error {
    Decoding(DecoderError),
    Encoding(EncoderError),
    Parse(ParserError),
    Http(HttpError),
    IO(IoError),
    Fault {
        code: StatusCode,
        message: String,
    },
}

impl From<ParserError> for Error {
    fn from(error: ParserError) -> Error {
        Error::Parse(error)
    }
}

impl From<DecoderError> for Error {
    fn from(error: DecoderError) -> Error {
        Error::Decoding(error)
    }
}

impl From<EncoderError> for Error {
    fn from(error: EncoderError) -> Error {
        Error::Encoding(error)
    }
}

impl From<HttpError> for Error {
    fn from(error: HttpError) -> Error {
        Error::Http(error)
    }
}

impl From<IoError> for Error {
    fn from(error: IoError) -> Error {
        Error::IO(error)
    }
}
<commit_msg>Implement the standard Error trait<commit_after>//! Representations of various client errors

use std::error::Error as ErrorTrait;
use std::io::Error as IoError;
use std::fmt;
use hyper::Error as HttpError;
use hyper::status::StatusCode;
use rustc_serialize::json::{DecoderError, EncoderError, ParserError};

#[derive(Debug)]
pub enum Error {
    Decoding(DecoderError),
    Encoding(EncoderError),
    Parse(ParserError),
    Http(HttpError),
    IO(IoError),
    Fault {
        code: StatusCode,
        message: String,
    },
}

impl From<ParserError> for Error {
    fn from(error: ParserError) -> Error {
        Error::Parse(error)
    }
}

impl From<DecoderError> for Error {
    fn from(error: DecoderError) -> Error {
        Error::Decoding(error)
    }
}

impl From<EncoderError> for Error {
    fn from(error: EncoderError) -> Error {
        Error::Encoding(error)
    }
}

impl From<HttpError> for Error {
    fn from(error: HttpError) -> Error {
        Error::Http(error)
    }
}

impl From<IoError> for Error {
    fn from(error: IoError) -> Error {
        Error::IO(error)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Docker Error: "));
        match self {
            &Error::Decoding(ref err) => return err.fmt(f),
            &Error::Encoding(ref err) => return err.fmt(f),
            &Error::Parse(ref err) => return err.fmt(f),
            &Error::Http(ref err) => return err.fmt(f),
            &Error::IO(ref err) => return err.fmt(f),
            &Error::Fault { code, .. } => return write!(f, "{}", code),
        };
    }
}

impl ErrorTrait for Error {
    fn description(&self) -> &str {
        "Shiplift Error"
    }

    fn cause(&self) -> Option<&ErrorTrait> {
        match self {
            &Error::Decoding(ref err) => Some(err),
            &Error::Encoding(ref err) => Some(err),
            &Error::Parse(ref err) => Some(err),
            &Error::Http(ref err) => Some(err),
            &Error::IO(ref err) => Some(err),
            _ => None
        }
    }
}
<|endoftext|>


<commit_before>//! Define table formatting utilities

use std::io::{Write, Error};

use super::utils::NEWLINE;

/// Alignment for cell's content
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum Alignment  {
	LEFT,
	CENTER,
	RIGHT
}

/// Position of a line separator in a table
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum LinePosition {
	Top,
	Title,
	Intern,
	Bottom
}

/// Position of a column separator in a row
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum ColumnPosition {
	Left,
	Intern,
	Right
}

/// Contains the character used for printing a line separator
#[derive(Clone, Debug)]
pub struct LineSeparator {
	/// Line separator
	line: char,
	/// Internal junction separator
	junc: char,
	/// Left junction separator
	ljunc: char,
	/// Right junction separator
	rjunc: char
}

impl LineSeparator {

	/// Create a new line separator instance where `line` is the character used to separate 2 lines
	/// and `junc` is the one used for junctions between columns and lines
	pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator {
		return LineSeparator{line: line, junc: junc, ljunc: ljunc, rjunc: rjunc};
	}

	/// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
	pub fn print<T: Write+?Sized>(&self, out: &mut T, col_width: &[usize], colsep: bool, lborder: bool, rborder: bool) -> Result<(), Error> {
		if lborder {
			try!(out.write_all(&[self.ljunc as u8]));
		}
		let mut iter = col_width.into_iter().peekable();
		while let Some(width) = iter.next() {
			try!(out.write_all(&vec![self.line as u8; width+2]));
			if colsep && iter.peek().is_some() {
				try!(out.write_all(&[self.junc as u8]));
			}
		}
		if rborder {
			try!(out.write_all(&[self.rjunc as u8]));
		}
		return out.write_all(NEWLINE);
	}
}

/// Contains the table formatting rules
#[derive(Clone, Debug)]
pub struct TableFormat {
	/// Optional column separator character
	csep: Option<char>,
	/// Optional left border character
	lborder: Option<char>,
	/// Optional right border character
	rborder: Option<char>,
	/// Optional internal line separator
	lsep: Option<LineSeparator>,
	/// Optional title line separator
	tsep: Option<LineSeparator>,
	/// Optional top line separator
	top_sep: Option<LineSeparator>,
	/// Optional bottom line separator
	bottom_sep: Option<LineSeparator>
}

impl TableFormat {

	/// Create a new TableFormat.
	///
	/// `csep` is the character used for separating columns.
	/// `lsep` is an optional `LineSeparator` defining how to separate lines.
	/// `tsep` is an optional `LineSeparator` defining the format of the separator after the title line (if set).
	/// If `tsep` is set to `None`, then `lsep` will be used, f it's set.
	pub fn new(csep: Option<char>, lsep: Option<LineSeparator>, tsep: Option<LineSeparator>) -> TableFormat {
		 return TableFormat{
			 csep: csep,
			 lborder: None,
			 rborder: None,
			 lsep: lsep,
			 tsep: tsep,
			 top_sep: None,
			 bottom_sep: None
		 };
	}

	fn get_sep_for_line(&self, pos: LinePosition) -> &Option<LineSeparator> {
		return match pos {
			LinePosition::Intern => return &self.lsep,
			LinePosition::Top => return &self.top_sep,
			LinePosition::Bottom => return &self.bottom_sep,
			LinePosition::Title => match &self.tsep {
				s @ &Some(_) => s,
				&None => &self.lsep
			}
		};
	}

	/// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
	pub fn print_line_separator<T: Write+?Sized>(&self, out: &mut T, col_width: &[usize], pos: LinePosition) -> Result<(), Error> {
		return match *self.get_sep_for_line(pos) {
			Some(ref l) => l.print(out, col_width, self.csep.is_some(), self.lborder.is_some(), self.rborder.is_some()),
			None => Ok(())
		};
	}

	/// Print a column separator
	fn get_column_separator(&self, pos: ColumnPosition) -> Option<char> {
		return match pos {
			ColumnPosition::Left => self.lborder,
			ColumnPosition::Intern => self.csep,
			ColumnPosition::Right => self.rborder
		};
	}

	pub fn print_column_separator<T: Write+?Sized>(&self, out: &mut T, pos: ColumnPosition) -> Result<(), Error> {
		return match self.get_column_separator(pos) {
			Some(s) => out.write_all(&[s as u8]),
			None => Ok(())
		};
	}
}

/// A line separator mad of `-` and `+`
pub const MINUS_PLUS_SEP: LineSeparator = LineSeparator{line: '-', junc: '+', ljunc: '+', rjunc: '+'};
/// A line separator mad of `=` and `+`
pub const EQU_PLUS_SEP: LineSeparator = LineSeparator{line: '=', junc: '+', ljunc: '+', rjunc: '+'};

/// Default table format, printing a table like this :
///
/// ```text
/// +----+----+
/// | T1 | T2 |
/// +====+====+
/// |    |    |
/// +----+----+
/// |    |    |
/// +----+----+
/// ```
pub const FORMAT_DEFAULT: TableFormat = TableFormat{csep: Some('|'), lborder: Some('|'), rborder: Some('|'), lsep: Some(MINUS_PLUS_SEP), tsep: Some(EQU_PLUS_SEP), top_sep: Some(MINUS_PLUS_SEP), bottom_sep: Some(MINUS_PLUS_SEP)};

/// Similar to `FORMAT_DEFAULT` but without special separator after title line
pub const FORMAT_NO_TITLE: TableFormat = TableFormat{csep: Some('|'), lborder: Some('|'), rborder: Some('|'), lsep: Some(MINUS_PLUS_SEP), tsep: Some(MINUS_PLUS_SEP), top_sep: Some(MINUS_PLUS_SEP), bottom_sep: Some(MINUS_PLUS_SEP)};

/// With no line separator, but with title separator
pub const FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = TableFormat{csep: Some('|'), lborder: Some('|'), rborder: Some('|'), lsep: None, tsep: Some(MINUS_PLUS_SEP), top_sep: Some(MINUS_PLUS_SEP), bottom_sep: Some(MINUS_PLUS_SEP)};

/// With no line or title separator
pub const FORMAT_NO_LINESEP: TableFormat = TableFormat{csep: Some('|'), lborder: Some('|'), rborder: Some('|'), lsep: None, tsep: None, top_sep: None, bottom_sep: None};

/// No column seprarator
pub const FORMAT_NO_COLSEP: TableFormat = TableFormat{csep: None, lborder: None, rborder: None, lsep: Some(MINUS_PLUS_SEP), tsep: Some(EQU_PLUS_SEP), top_sep: Some(MINUS_PLUS_SEP), bottom_sep: Some(MINUS_PLUS_SEP)};

/// Format for printing a table without any separators (only alignment)
pub const FORMAT_NO_BORDER: TableFormat = TableFormat{csep: None, lborder: None, rborder: None, lsep: None, tsep: None, top_sep: None, bottom_sep: None};
<commit_msg>Implemented Default for TableFormat and LineSeparator<commit_after>//! Define table formatting utilities

use std::io::{Write, Error};

use super::utils::NEWLINE;

/// Alignment for cell's content
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum Alignment  {
	LEFT,
	CENTER,
	RIGHT
}

/// Position of a line separator in a table
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum LinePosition {
	Top,
	Title,
	Intern,
	Bottom
}

/// Position of a column separator in a row
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum ColumnPosition {
	Left,
	Intern,
	Right
}

/// Contains the character used for printing a line separator
#[derive(Clone, Debug)]
pub struct LineSeparator {
	/// Line separator
	line: char,
	/// Internal junction separator
	junc: char,
	/// Left junction separator
	ljunc: char,
	/// Right junction separator
	rjunc: char
}

impl LineSeparator {

	/// Create a new line separator instance where `line` is the character used to separate 2 lines
	/// and `junc` is the one used for junctions between columns and lines
	pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator {
		return LineSeparator{line: line, junc: junc, ljunc: ljunc, rjunc: rjunc};
	}

	/// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
	pub fn print<T: Write+?Sized>(&self, out: &mut T, col_width: &[usize], colsep: bool, lborder: bool, rborder: bool) -> Result<(), Error> {
		if lborder {
			try!(out.write_all(&[self.ljunc as u8]));
		}
		let mut iter = col_width.into_iter().peekable();
		while let Some(width) = iter.next() {
			try!(out.write_all(&vec![self.line as u8; width+2]));
			if colsep && iter.peek().is_some() {
				try!(out.write_all(&[self.junc as u8]));
			}
		}
		if rborder {
			try!(out.write_all(&[self.rjunc as u8]));
		}
		return out.write_all(NEWLINE);
	}
}

impl Default for LineSeparator {
	fn default() -> Self {
		return LineSeparator::new('-', '+', '+', '+');
	}
}

/// Contains the table formatting rules
#[derive(Clone, Debug)]
pub struct TableFormat {
	/// Optional column separator character
	csep: Option<char>,
	/// Optional left border character
	lborder: Option<char>,
	/// Optional right border character
	rborder: Option<char>,
	/// Optional internal line separator
	lsep: Option<LineSeparator>,
	/// Optional title line separator
	tsep: Option<LineSeparator>,
	/// Optional top line separator
	top_sep: Option<LineSeparator>,
	/// Optional bottom line separator
	bottom_sep: Option<LineSeparator>
}

impl Default for TableFormat {
	fn default() -> Self {
		return TableFormat::new(None, None, None);
	}
}

impl TableFormat {

	/// Create a new TableFormat.
	///
	/// `csep` is the character used for separating columns.
	/// `lsep` is an optional `LineSeparator` defining how to separate lines.
	/// `tsep` is an optional `LineSeparator` defining the format of the separator after the title line (if set).
	/// If `tsep` is set to `None`, then `lsep` will be used, f it's set.
	pub fn new(csep: Option<char>, lsep: Option<LineSeparator>, tsep: Option<LineSeparator>) -> TableFormat {
		 return TableFormat{
			 csep: csep,
			 lborder: None,
			 rborder: None,
			 lsep: lsep,
			 tsep: tsep,
			 top_sep: None,
			 bottom_sep: None
		 };
	}

	fn get_sep_for_line(&self, pos: LinePosition) -> &Option<LineSeparator> {
		return match pos {
			LinePosition::Intern => return &self.lsep,
			LinePosition::Top => return &self.top_sep,
			LinePosition::Bottom => return &self.bottom_sep,
			LinePosition::Title => match &self.tsep {
				s @ &Some(_) => s,
				&None => &self.lsep
			}
		};
	}

	/// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
	pub fn print_line_separator<T: Write+?Sized>(&self, out: &mut T, col_width: &[usize], pos: LinePosition) -> Result<(), Error> {
		return match *self.get_sep_for_line(pos) {
			Some(ref l) => l.print(out, col_width, self.csep.is_some(), self.lborder.is_some(), self.rborder.is_some()),
			None => Ok(())
		};
	}

	/// Print a column separator
	fn get_column_separator(&self, pos: ColumnPosition) -> Option<char> {
		return match pos {
			ColumnPosition::Left => self.lborder,
			ColumnPosition::Intern => self.csep,
			ColumnPosition::Right => self.rborder
		};
	}

	pub fn print_column_separator<T: Write+?Sized>(&self, out: &mut T, pos: ColumnPosition) -> Result<(), Error> {
		return match self.get_column_separator(pos) {
			Some(s) => out.write_all(&[s as u8]),
			None => Ok(())
		};
	}
}

/// A line separator mad of `-` and `+`
pub const MINUS_PLUS_SEP: LineSeparator = LineSeparator{line: '-', junc: '+', ljunc: '+', rjunc: '+'};
/// A line separator mad of `=` and `+`
pub const EQU_PLUS_SEP: LineSeparator = LineSeparator{line: '=', junc: '+', ljunc: '+', rjunc: '+'};

/// Default table format, printing a table like this :
///
/// ```text
/// +----+----+
/// | T1 | T2 |
/// +====+====+
/// |    |    |
/// +----+----+
/// |    |    |
/// +----+----+
/// ```
pub const FORMAT_DEFAULT: TableFormat = TableFormat{csep: Some('|'), lborder: Some('|'), rborder: Some('|'), lsep: Some(MINUS_PLUS_SEP), tsep: Some(EQU_PLUS_SEP), top_sep: Some(MINUS_PLUS_SEP), bottom_sep: Some(MINUS_PLUS_SEP)};

/// Similar to `FORMAT_DEFAULT` but without special separator after title line
pub const FORMAT_NO_TITLE: TableFormat = TableFormat{csep: Some('|'), lborder: Some('|'), rborder: Some('|'), lsep: Some(MINUS_PLUS_SEP), tsep: Some(MINUS_PLUS_SEP), top_sep: Some(MINUS_PLUS_SEP), bottom_sep: Some(MINUS_PLUS_SEP)};

/// With no line separator, but with title separator
pub const FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = TableFormat{csep: Some('|'), lborder: Some('|'), rborder: Some('|'), lsep: None, tsep: Some(MINUS_PLUS_SEP), top_sep: Some(MINUS_PLUS_SEP), bottom_sep: Some(MINUS_PLUS_SEP)};

/// With no line or title separator
pub const FORMAT_NO_LINESEP: TableFormat = TableFormat{csep: Some('|'), lborder: Some('|'), rborder: Some('|'), lsep: None, tsep: None, top_sep: None, bottom_sep: None};

/// No column seprarator
pub const FORMAT_NO_COLSEP: TableFormat = TableFormat{csep: None, lborder: None, rborder: None, lsep: Some(MINUS_PLUS_SEP), tsep: Some(EQU_PLUS_SEP), top_sep: Some(MINUS_PLUS_SEP), bottom_sep: Some(MINUS_PLUS_SEP)};

/// Format for printing a table without any separators (only alignment)
pub const FORMAT_NO_BORDER: TableFormat = TableFormat{csep: None, lborder: None, rborder: None, lsep: None, tsep: None, top_sep: None, bottom_sep: None};
<|endoftext|>


<commit_before>use std::cmp::{max, min};
use std::collections::HashMap;

use cassowary::{Constraint, Expression, Solver, Variable};
use cassowary::WeightedRelation::*;
use cassowary::strength::{REQUIRED, WEAK};

use terminal::Terminal;
use backend::Backend;

#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
    Horizontal,
    Vertical,
}

/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

impl Default for Rect {
    fn default() -> Rect {
        Rect {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        }
    }
}

impl Rect {
    pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
        Rect {
            x: x,
            y: y,
            width: width,
            height: height,
        }
    }

    pub fn area(&self) -> u16 {
        self.width * self.height
    }

    pub fn left(&self) -> u16 {
        self.x
    }

    pub fn right(&self) -> u16 {
        self.x + self.width
    }

    pub fn top(&self) -> u16 {
        self.y
    }

    pub fn bottom(&self) -> u16 {
        self.y + self.height
    }

    pub fn inner(&self, margin: u16) -> Rect {
        if self.width < 2 * margin || self.height < 2 * margin {
            Rect::default()
        } else {
            Rect {
                x: self.x + margin,
                y: self.y + margin,
                width: self.width - 2 * margin,
                height: self.height - 2 * margin,
            }
        }
    }

    pub fn union(&self, other: &Rect) -> Rect {
        let x1 = min(self.x, other.x);
        let y1 = min(self.y, other.y);
        let x2 = max(self.x + self.width, other.x + other.width);
        let y2 = max(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersection(&self, other: &Rect) -> Rect {
        let x1 = max(self.x, other.x);
        let y1 = max(self.y, other.y);
        let x2 = min(self.x + self.width, other.x + other.width);
        let y2 = min(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersects(&self, other: &Rect) -> bool {
        self.x < other.x + other.width && self.x + self.width > other.x
            && self.y < other.y + other.height && self.y + self.height > other.y
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Size {
    Fixed(u16),
    Percent(u16),
    Max(u16),
    Min(u16),
}

/// Wrapper function around the cassowary-rs solver to be able to split a given
/// area into smaller ones based on the preferred widths or heights and the direction.
///
/// # Examples
/// ```
/// # extern crate tui;
/// # use tui::layout::{Rect, Size, Direction, split};
///
/// # fn main() {
///     let chunks = split(&Rect{x: 2, y: 2, width: 10, height: 10},
///                        &Direction::Vertical,
///                        0,
///                        &[Size::Fixed(5), Size::Min(0)]);
///     assert_eq!(chunks, vec![Rect{x:2, y: 2, width: 10, height: 5},
///                             Rect{x: 2, y: 7, width: 10, height: 5}])
/// # }
///
/// ```
pub fn split(area: &Rect, dir: &Direction, margin: u16, sizes: &[Size]) -> Vec<Rect> {
    let mut solver = Solver::new();
    let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
    let elements = sizes
        .iter()
        .map(|_| Element::new())
        .collect::<Vec<Element>>();
    let mut results = sizes.iter().map(|_| Rect::default()).collect::<Vec<Rect>>();
    let dest_area = area.inner(margin);
    for (i, e) in elements.iter().enumerate() {
        vars.insert(e.x, (i, 0));
        vars.insert(e.y, (i, 1));
        vars.insert(e.width, (i, 2));
        vars.insert(e.height, (i, 3));
    }
    let mut constraints: Vec<Constraint> = Vec::with_capacity(elements.len() * 4 + sizes.len() * 6);
    for elt in &elements {
        constraints.push(elt.left() | GE(REQUIRED) | f64::from(dest_area.left()));
        constraints.push(elt.top() | GE(REQUIRED) | f64::from(dest_area.top()));
        constraints.push(elt.right() | LE(REQUIRED) | f64::from(dest_area.right()));
        constraints.push(elt.bottom() | LE(REQUIRED) | f64::from(dest_area.bottom()));
    }
    if let Some(first) = elements.first() {
        constraints.push(match *dir {
            Direction::Horizontal => first.left() | EQ(REQUIRED) | f64::from(dest_area.left()),
            Direction::Vertical => first.top() | EQ(REQUIRED) | f64::from(dest_area.top()),
        });
    }
    if let Some(last) = elements.last() {
        constraints.push(match *dir {
            Direction::Horizontal => last.right() | EQ(REQUIRED) | f64::from(dest_area.right()),
            Direction::Vertical => last.bottom() | EQ(REQUIRED) | f64::from(dest_area.bottom()),
        });
    }
    match *dir {
        Direction::Horizontal => {
            for pair in elements.windows(2) {
                constraints.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
            }
            for (i, size) in sizes.iter().enumerate() {
                constraints.push(elements[i].y | EQ(REQUIRED) | f64::from(dest_area.y));
                constraints.push(elements[i].height | EQ(REQUIRED) | f64::from(dest_area.height));
                constraints.push(match *size {
                    Size::Fixed(v) => elements[i].width | EQ(WEAK) | f64::from(v),
                    Size::Percent(v) => {
                        elements[i].width | EQ(WEAK) | (f64::from(v * dest_area.width) / 100.0)
                    }
                    Size::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
                    Size::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
                });
            }
        }
        Direction::Vertical => {
            for pair in elements.windows(2) {
                constraints.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
            }
            for (i, size) in sizes.iter().enumerate() {
                constraints.push(elements[i].x | EQ(REQUIRED) | f64::from(dest_area.x));
                constraints.push(elements[i].width | EQ(REQUIRED) | f64::from(dest_area.width));
                constraints.push(match *size {
                    Size::Fixed(v) => elements[i].height | EQ(WEAK) | f64::from(v),
                    Size::Percent(v) => {
                        elements[i].height | EQ(WEAK) | (f64::from(v * dest_area.height) / 100.0)
                    }
                    Size::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
                    Size::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
                });
            }
        }
    }
    solver.add_constraints(&constraints).unwrap();
    for &(var, value) in solver.fetch_changes() {
        let (index, attr) = vars[&var];
        let value = if value.is_sign_negative() {
            0
        } else {
            value as u16
        };
        match attr {
            0 => {
                results[index].x = value;
            }
            1 => {
                results[index].y = value;
            }
            2 => {
                results[index].width = value;
            }
            3 => {
                results[index].height = value;
            }
            _ => {}
        }
    }

    // Fix imprecision by extending the last item a bit if necessary
    if let Some(last) = results.last_mut() {
        match *dir {
            Direction::Vertical => {
                last.height = dest_area.bottom() - last.y;
            }
            Direction::Horizontal => {
                last.width = dest_area.right() - last.x;
            }
        }
    }
    results
}

/// A container used by the solver inside split
struct Element {
    x: Variable,
    y: Variable,
    width: Variable,
    height: Variable,
}

impl Element {
    fn new() -> Element {
        Element {
            x: Variable::new(),
            y: Variable::new(),
            width: Variable::new(),
            height: Variable::new(),
        }
    }

    fn left(&self) -> Variable {
        self.x
    }

    fn top(&self) -> Variable {
        self.y
    }

    fn right(&self) -> Expression {
        self.x + self.width
    }

    fn bottom(&self) -> Expression {
        self.y + self.height
    }
}

/// Describes a layout and may be used to group widgets in a specific area of the terminal
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// use tui::layout::{Group, Direction, Size};
/// # fn main() {
///     Group::default()
///         .direction(Direction::Vertical)
///         .margin(0)
///         .sizes(&[Size::Percent(50), Size::Percent(50)]);
/// # }
/// ```
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct Group {
    pub direction: Direction,
    pub margin: u16,
    pub sizes: Vec<Size>,
}

impl Default for Group {
    fn default() -> Group {
        Group {
            direction: Direction::Horizontal,
            margin: 0,
            sizes: Vec::new(),
        }
    }
}

impl Group {
    pub fn direction(&mut self, direction: Direction) -> &mut Group {
        self.direction = direction;
        self
    }

    pub fn margin(&mut self, margin: u16) -> &mut Group {
        self.margin = margin;
        self
    }

    pub fn sizes(&mut self, sizes: &[Size]) -> &mut Group {
        self.sizes = Vec::from(sizes);
        self
    }
    pub fn render<F, B>(&self, t: &mut Terminal<B>, area: &Rect, mut f: F)
    where
        B: Backend,
        F: FnMut(&mut Terminal<B>, &[Rect]),
    {
        let chunks = t.compute_layout(self, area);
        f(t, &chunks);
    }
}
<commit_msg>[layout] Replace FnMut with FnOnce in Group::render<commit_after>use std::cmp::{max, min};
use std::collections::HashMap;

use cassowary::{Constraint, Expression, Solver, Variable};
use cassowary::WeightedRelation::*;
use cassowary::strength::{REQUIRED, WEAK};

use terminal::Terminal;
use backend::Backend;

#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
    Horizontal,
    Vertical,
}

/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

impl Default for Rect {
    fn default() -> Rect {
        Rect {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        }
    }
}

impl Rect {
    pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
        Rect {
            x: x,
            y: y,
            width: width,
            height: height,
        }
    }

    pub fn area(&self) -> u16 {
        self.width * self.height
    }

    pub fn left(&self) -> u16 {
        self.x
    }

    pub fn right(&self) -> u16 {
        self.x + self.width
    }

    pub fn top(&self) -> u16 {
        self.y
    }

    pub fn bottom(&self) -> u16 {
        self.y + self.height
    }

    pub fn inner(&self, margin: u16) -> Rect {
        if self.width < 2 * margin || self.height < 2 * margin {
            Rect::default()
        } else {
            Rect {
                x: self.x + margin,
                y: self.y + margin,
                width: self.width - 2 * margin,
                height: self.height - 2 * margin,
            }
        }
    }

    pub fn union(&self, other: &Rect) -> Rect {
        let x1 = min(self.x, other.x);
        let y1 = min(self.y, other.y);
        let x2 = max(self.x + self.width, other.x + other.width);
        let y2 = max(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersection(&self, other: &Rect) -> Rect {
        let x1 = max(self.x, other.x);
        let y1 = max(self.y, other.y);
        let x2 = min(self.x + self.width, other.x + other.width);
        let y2 = min(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersects(&self, other: &Rect) -> bool {
        self.x < other.x + other.width && self.x + self.width > other.x
            && self.y < other.y + other.height && self.y + self.height > other.y
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Size {
    Fixed(u16),
    Percent(u16),
    Max(u16),
    Min(u16),
}

/// Wrapper function around the cassowary-rs solver to be able to split a given
/// area into smaller ones based on the preferred widths or heights and the direction.
///
/// # Examples
/// ```
/// # extern crate tui;
/// # use tui::layout::{Rect, Size, Direction, split};
///
/// # fn main() {
///     let chunks = split(&Rect{x: 2, y: 2, width: 10, height: 10},
///                        &Direction::Vertical,
///                        0,
///                        &[Size::Fixed(5), Size::Min(0)]);
///     assert_eq!(chunks, vec![Rect{x:2, y: 2, width: 10, height: 5},
///                             Rect{x: 2, y: 7, width: 10, height: 5}])
/// # }
///
/// ```
pub fn split(area: &Rect, dir: &Direction, margin: u16, sizes: &[Size]) -> Vec<Rect> {
    let mut solver = Solver::new();
    let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
    let elements = sizes
        .iter()
        .map(|_| Element::new())
        .collect::<Vec<Element>>();
    let mut results = sizes.iter().map(|_| Rect::default()).collect::<Vec<Rect>>();
    let dest_area = area.inner(margin);
    for (i, e) in elements.iter().enumerate() {
        vars.insert(e.x, (i, 0));
        vars.insert(e.y, (i, 1));
        vars.insert(e.width, (i, 2));
        vars.insert(e.height, (i, 3));
    }
    let mut constraints: Vec<Constraint> = Vec::with_capacity(elements.len() * 4 + sizes.len() * 6);
    for elt in &elements {
        constraints.push(elt.left() | GE(REQUIRED) | f64::from(dest_area.left()));
        constraints.push(elt.top() | GE(REQUIRED) | f64::from(dest_area.top()));
        constraints.push(elt.right() | LE(REQUIRED) | f64::from(dest_area.right()));
        constraints.push(elt.bottom() | LE(REQUIRED) | f64::from(dest_area.bottom()));
    }
    if let Some(first) = elements.first() {
        constraints.push(match *dir {
            Direction::Horizontal => first.left() | EQ(REQUIRED) | f64::from(dest_area.left()),
            Direction::Vertical => first.top() | EQ(REQUIRED) | f64::from(dest_area.top()),
        });
    }
    if let Some(last) = elements.last() {
        constraints.push(match *dir {
            Direction::Horizontal => last.right() | EQ(REQUIRED) | f64::from(dest_area.right()),
            Direction::Vertical => last.bottom() | EQ(REQUIRED) | f64::from(dest_area.bottom()),
        });
    }
    match *dir {
        Direction::Horizontal => {
            for pair in elements.windows(2) {
                constraints.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
            }
            for (i, size) in sizes.iter().enumerate() {
                constraints.push(elements[i].y | EQ(REQUIRED) | f64::from(dest_area.y));
                constraints.push(elements[i].height | EQ(REQUIRED) | f64::from(dest_area.height));
                constraints.push(match *size {
                    Size::Fixed(v) => elements[i].width | EQ(WEAK) | f64::from(v),
                    Size::Percent(v) => {
                        elements[i].width | EQ(WEAK) | (f64::from(v * dest_area.width) / 100.0)
                    }
                    Size::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
                    Size::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
                });
            }
        }
        Direction::Vertical => {
            for pair in elements.windows(2) {
                constraints.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
            }
            for (i, size) in sizes.iter().enumerate() {
                constraints.push(elements[i].x | EQ(REQUIRED) | f64::from(dest_area.x));
                constraints.push(elements[i].width | EQ(REQUIRED) | f64::from(dest_area.width));
                constraints.push(match *size {
                    Size::Fixed(v) => elements[i].height | EQ(WEAK) | f64::from(v),
                    Size::Percent(v) => {
                        elements[i].height | EQ(WEAK) | (f64::from(v * dest_area.height) / 100.0)
                    }
                    Size::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
                    Size::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
                });
            }
        }
    }
    solver.add_constraints(&constraints).unwrap();
    for &(var, value) in solver.fetch_changes() {
        let (index, attr) = vars[&var];
        let value = if value.is_sign_negative() {
            0
        } else {
            value as u16
        };
        match attr {
            0 => {
                results[index].x = value;
            }
            1 => {
                results[index].y = value;
            }
            2 => {
                results[index].width = value;
            }
            3 => {
                results[index].height = value;
            }
            _ => {}
        }
    }

    // Fix imprecision by extending the last item a bit if necessary
    if let Some(last) = results.last_mut() {
        match *dir {
            Direction::Vertical => {
                last.height = dest_area.bottom() - last.y;
            }
            Direction::Horizontal => {
                last.width = dest_area.right() - last.x;
            }
        }
    }
    results
}

/// A container used by the solver inside split
struct Element {
    x: Variable,
    y: Variable,
    width: Variable,
    height: Variable,
}

impl Element {
    fn new() -> Element {
        Element {
            x: Variable::new(),
            y: Variable::new(),
            width: Variable::new(),
            height: Variable::new(),
        }
    }

    fn left(&self) -> Variable {
        self.x
    }

    fn top(&self) -> Variable {
        self.y
    }

    fn right(&self) -> Expression {
        self.x + self.width
    }

    fn bottom(&self) -> Expression {
        self.y + self.height
    }
}

/// Describes a layout and may be used to group widgets in a specific area of the terminal
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// use tui::layout::{Group, Direction, Size};
/// # fn main() {
///     Group::default()
///         .direction(Direction::Vertical)
///         .margin(0)
///         .sizes(&[Size::Percent(50), Size::Percent(50)]);
/// # }
/// ```
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct Group {
    pub direction: Direction,
    pub margin: u16,
    pub sizes: Vec<Size>,
}

impl Default for Group {
    fn default() -> Group {
        Group {
            direction: Direction::Horizontal,
            margin: 0,
            sizes: Vec::new(),
        }
    }
}

impl Group {
    pub fn direction(&mut self, direction: Direction) -> &mut Group {
        self.direction = direction;
        self
    }

    pub fn margin(&mut self, margin: u16) -> &mut Group {
        self.margin = margin;
        self
    }

    pub fn sizes(&mut self, sizes: &[Size]) -> &mut Group {
        self.sizes = Vec::from(sizes);
        self
    }
    pub fn render<F, B>(&self, t: &mut Terminal<B>, area: &Rect, f: F)
    where
        B: Backend,
        F: FnOnce(&mut Terminal<B>, &[Rect]),
    {
        let chunks = t.compute_layout(self, area);
        f(t, &chunks);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate gbrs;

#[cfg(test)]
mod tests {

    use gbrs::gameboy::{Cartridge, Cpu, Interconnect};

    #[test]
    fn call() {
        let (mut cpu, mut interconnect) =
            create_cpu(gb_asm![0x00 0x00 0xCD 0x0C 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0C]);
        //                               ^^^^^^^^^ jump to the 'INC C' opcode here --------^^^^

        cpu.registers.c = 0x3C;
        cpu.step(&mut interconnect);
        cpu.step(&mut interconnect);
        cpu.step(&mut interconnect);
        assert_eq!(0x0C, cpu.registers.pc);
        assert_eq!(0x05, interconnect.read_u16(0xFFFD));
        cpu.step(&mut interconnect);
        assert_eq!(0x3D, cpu.registers.c); // C should be 0x3C + 1
    }

    #[test]
    fn cp_n() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xFE 0x3C]);

        cpu.registers.a = 0x3C;
        cpu.step(&mut interconnect);

        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(false, cpu.registers.flags.half_carry);
        assert_eq!(true, cpu.registers.flags.negative);
        assert_eq!(false, cpu.registers.flags.carry);
    }

    #[test]
    fn dec_b() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x05]);

        cpu.registers.b = 0x01;
        cpu.step(&mut interconnect);

        assert_eq!(0x00, cpu.registers.b);
        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(false, cpu.registers.flags.half_carry);
    }

    #[test]
    fn dec_b_2() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x05]);

        cpu.registers.b = 0x00;
        cpu.step(&mut interconnect);

        assert_eq!(0xFF, cpu.registers.b);
        assert_eq!(false, cpu.registers.flags.zero);
        assert_eq!(true, cpu.registers.flags.half_carry);
    }

    #[test]
    fn dec_c() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x0D]);

        cpu.registers.c = 0x01;
        cpu.step(&mut interconnect);

        assert_eq!(0x00, cpu.registers.c);
        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(false, cpu.registers.flags.half_carry);
    }

    #[test]
    fn dec_c_2() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x0D]);

        cpu.registers.c = 0x00;
        cpu.step(&mut interconnect);

        assert_eq!(0xFF, cpu.registers.c);
        assert_eq!(false, cpu.registers.flags.zero);
        assert_eq!(true, cpu.registers.flags.half_carry);
    }

    #[test]
    fn di() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xF3]);

        cpu.registers.flags.ime = true;
        cpu.step(&mut interconnect);

        assert_eq!(false, cpu.registers.flags.ime);
    }

    #[test]
    fn inc_c() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x0C]);

        cpu.registers.c = 0xFF;
        cpu.step(&mut interconnect);

        assert_eq!(0x00, cpu.registers.c);
        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(true, cpu.registers.flags.half_carry);
        assert_eq!(false, cpu.registers.flags.negative);
    }

    #[test]
    fn jr_nz_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xAF 0x20 0xFD]);

        cpu.step(&mut interconnect);
        cpu.registers.flags.zero = false;
        cpu.step(&mut interconnect);

        assert_eq!(0, cpu.registers.pc);

        cpu.registers.a = 0xFF;
        cpu.step(&mut interconnect);

        assert_eq!(0, cpu.registers.a);
    }

    #[test]
    fn ld_a_hli() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x2A]);

        cpu.registers.a = 0xE5;
        interconnect.write_u8(0xC000, 0xC3);
        cpu.registers.set_hl(0xC000);
        cpu.step(&mut interconnect);

        assert_eq!(0xC3, cpu.registers.a);
        assert_eq!(0xC001, cpu.registers.get_hl());
    }

    #[test]
    fn ld_a_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x3E 0xA3]);

        cpu.registers.a = 0xE5;
        cpu.step(&mut interconnect);

        assert_eq!(0xA3, cpu.registers.a);
    }

    #[test]
    fn ld_b_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x06 0xA5]);

        cpu.step(&mut interconnect);

        assert_eq!(0xA5, cpu.registers.b);
    }

    #[test]
    fn ld_c_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x0E 0xA4]);

        cpu.step(&mut interconnect);

        assert_eq!(0xA4, cpu.registers.c);
    }

    #[test]
    fn ld_a_ff00_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xF0 0x44]);

        interconnect.write_u8(0xFF44, 0xA9);
        cpu.step(&mut interconnect);

        assert_eq!(0xA9, cpu.registers.a);
    }

    #[test]
    fn ld_ff00_imm8_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xE0 0x44]);

        cpu.registers.a = 0xAF;
        cpu.step(&mut interconnect);

        assert_eq!(0xAF, interconnect.read_u8(0xFF44));
    }

    #[test]
    fn ld_ff00_c_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xE2]);

        cpu.registers.a = 0xAF;
        cpu.registers.c = 0x47;
        interconnect.write_u8(0xFF47, 0xC3);
        cpu.step(&mut interconnect);

        assert_eq!(0xAF, interconnect.read_u8(0xFF47));
    }

    #[test]
    fn ld_hl_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x36 0x5B]);

        cpu.registers.set_hl(0x014D);
        cpu.step(&mut interconnect);

        assert_eq!(0x5B, interconnect.read_u8(0x014D));
    }

    #[test]
    fn ld_hl_imm16() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x21 0x5B 0x3A]);

        cpu.step(&mut interconnect);

        assert_eq!(0x3A, cpu.registers.h);
        assert_eq!(0x5B, cpu.registers.l);
    }

    #[test]
    fn ld_hld_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x32]);

        cpu.registers.a = 0xE5;
        cpu.registers.set_hl(0xC000);
        interconnect.write_u8(0xC000, 0xE3);
        cpu.step(&mut interconnect);

        assert_eq!(0xE5, interconnect.read_u8(0xC000));
        assert_eq!(0xBFFF, cpu.registers.get_hl());
    }

    #[test]
    fn ld_imm16_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xEA 0xC5 0xAF]);

        cpu.registers.a = 0xCC;
        interconnect.write_u8(0xAFC5, 0xE3);
        cpu.step(&mut interconnect);

        assert_eq!(0xCC, interconnect.read_u8(0xAFC5));
    }

    #[test]
    fn ld_sp_imm16() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x31 0xCF 0xF7]);

        cpu.step(&mut interconnect);

        assert_eq!(0xF7CF, cpu.registers.sp);
    }

    #[test]
    fn xor_a_xors_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xAF]);

        cpu.registers.a = 0xFF;
        cpu.step(&mut interconnect);

        assert_eq!(0, cpu.registers.a);
    }

    fn create_cpu(rom: Vec<u8>) -> (Cpu, Interconnect) {
        let cart = Cartridge::with_rom(rom);
        (Cpu::new(false), Interconnect::with_cart(cart))
    }
}
<commit_msg>Add comments to CALL test<commit_after>#[macro_use]
extern crate gbrs;

#[cfg(test)]
mod tests {

    use gbrs::gameboy::{Cartridge, Cpu, Interconnect};

    #[test]
    fn call() {
        let (mut cpu, mut interconnect) =
            create_cpu(gb_asm![0x00 0x00 0xCD 0x0C 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0C]);
        //                               ^^^^^^^^^ jump to the 'INC C' opcode here --------^^^^

        cpu.registers.c = 0x3C;
        cpu.step(&mut interconnect); // step over NOP
        cpu.step(&mut interconnect); // step over NOP
        cpu.step(&mut interconnect); // step over CALL 0x000C (jump to byte 12)
        assert_eq!(0x0C, cpu.registers.pc); // program counter should be at byte 12
        assert_eq!(0x05, interconnect.read_u16(0xFFFD)); // return address on stack should be byte 5 (2 nops + 3 bytes for the call)
        cpu.step(&mut interconnect); // step over 'INC C'
        assert_eq!(0x3D, cpu.registers.c); // C should be 0x3C + 1
    }

    #[test]
    fn cp_n() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xFE 0x3C]);

        cpu.registers.a = 0x3C;
        cpu.step(&mut interconnect);

        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(false, cpu.registers.flags.half_carry);
        assert_eq!(true, cpu.registers.flags.negative);
        assert_eq!(false, cpu.registers.flags.carry);
    }

    #[test]
    fn dec_b() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x05]);

        cpu.registers.b = 0x01;
        cpu.step(&mut interconnect);

        assert_eq!(0x00, cpu.registers.b);
        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(false, cpu.registers.flags.half_carry);
    }

    #[test]
    fn dec_b_2() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x05]);

        cpu.registers.b = 0x00;
        cpu.step(&mut interconnect);

        assert_eq!(0xFF, cpu.registers.b);
        assert_eq!(false, cpu.registers.flags.zero);
        assert_eq!(true, cpu.registers.flags.half_carry);
    }

    #[test]
    fn dec_c() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x0D]);

        cpu.registers.c = 0x01;
        cpu.step(&mut interconnect);

        assert_eq!(0x00, cpu.registers.c);
        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(false, cpu.registers.flags.half_carry);
    }

    #[test]
    fn dec_c_2() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x0D]);

        cpu.registers.c = 0x00;
        cpu.step(&mut interconnect);

        assert_eq!(0xFF, cpu.registers.c);
        assert_eq!(false, cpu.registers.flags.zero);
        assert_eq!(true, cpu.registers.flags.half_carry);
    }

    #[test]
    fn di() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xF3]);

        cpu.registers.flags.ime = true;
        cpu.step(&mut interconnect);

        assert_eq!(false, cpu.registers.flags.ime);
    }

    #[test]
    fn inc_c() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x0C]);

        cpu.registers.c = 0xFF;
        cpu.step(&mut interconnect);

        assert_eq!(0x00, cpu.registers.c);
        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(true, cpu.registers.flags.half_carry);
        assert_eq!(false, cpu.registers.flags.negative);
    }

    #[test]
    fn jr_nz_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xAF 0x20 0xFD]);

        cpu.step(&mut interconnect);
        cpu.registers.flags.zero = false;
        cpu.step(&mut interconnect);

        assert_eq!(0, cpu.registers.pc);

        cpu.registers.a = 0xFF;
        cpu.step(&mut interconnect);

        assert_eq!(0, cpu.registers.a);
    }

    #[test]
    fn ld_a_hli() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x2A]);

        cpu.registers.a = 0xE5;
        interconnect.write_u8(0xC000, 0xC3);
        cpu.registers.set_hl(0xC000);
        cpu.step(&mut interconnect);

        assert_eq!(0xC3, cpu.registers.a);
        assert_eq!(0xC001, cpu.registers.get_hl());
    }

    #[test]
    fn ld_a_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x3E 0xA3]);

        cpu.registers.a = 0xE5;
        cpu.step(&mut interconnect);

        assert_eq!(0xA3, cpu.registers.a);
    }

    #[test]
    fn ld_b_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x06 0xA5]);

        cpu.step(&mut interconnect);

        assert_eq!(0xA5, cpu.registers.b);
    }

    #[test]
    fn ld_c_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x0E 0xA4]);

        cpu.step(&mut interconnect);

        assert_eq!(0xA4, cpu.registers.c);
    }

    #[test]
    fn ld_a_ff00_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xF0 0x44]);

        interconnect.write_u8(0xFF44, 0xA9);
        cpu.step(&mut interconnect);

        assert_eq!(0xA9, cpu.registers.a);
    }

    #[test]
    fn ld_ff00_imm8_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xE0 0x44]);

        cpu.registers.a = 0xAF;
        cpu.step(&mut interconnect);

        assert_eq!(0xAF, interconnect.read_u8(0xFF44));
    }

    #[test]
    fn ld_ff00_c_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xE2]);

        cpu.registers.a = 0xAF;
        cpu.registers.c = 0x47;
        interconnect.write_u8(0xFF47, 0xC3);
        cpu.step(&mut interconnect);

        assert_eq!(0xAF, interconnect.read_u8(0xFF47));
    }

    #[test]
    fn ld_hl_imm8() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x36 0x5B]);

        cpu.registers.set_hl(0x014D);
        cpu.step(&mut interconnect);

        assert_eq!(0x5B, interconnect.read_u8(0x014D));
    }

    #[test]
    fn ld_hl_imm16() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x21 0x5B 0x3A]);

        cpu.step(&mut interconnect);

        assert_eq!(0x3A, cpu.registers.h);
        assert_eq!(0x5B, cpu.registers.l);
    }

    #[test]
    fn ld_hld_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x32]);

        cpu.registers.a = 0xE5;
        cpu.registers.set_hl(0xC000);
        interconnect.write_u8(0xC000, 0xE3);
        cpu.step(&mut interconnect);

        assert_eq!(0xE5, interconnect.read_u8(0xC000));
        assert_eq!(0xBFFF, cpu.registers.get_hl());
    }

    #[test]
    fn ld_imm16_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xEA 0xC5 0xAF]);

        cpu.registers.a = 0xCC;
        interconnect.write_u8(0xAFC5, 0xE3);
        cpu.step(&mut interconnect);

        assert_eq!(0xCC, interconnect.read_u8(0xAFC5));
    }

    #[test]
    fn ld_sp_imm16() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0x31 0xCF 0xF7]);

        cpu.step(&mut interconnect);

        assert_eq!(0xF7CF, cpu.registers.sp);
    }

    #[test]
    fn xor_a_xors_a() {
        let (mut cpu, mut interconnect) = create_cpu(gb_asm![0xAF]);

        cpu.registers.a = 0xFF;
        cpu.step(&mut interconnect);

        assert_eq!(0, cpu.registers.a);
    }

    fn create_cpu(rom: Vec<u8>) -> (Cpu, Interconnect) {
        let cart = Cartridge::with_rom(rom);
        (Cpu::new(false), Interconnect::with_cart(cart))
    }
}
<|endoftext|>


<commit_before>extern crate rusty_trap;

#[test]
fn it_can_exec () {
    let inferior = rusty_trap::trap_inferior_exec("./inferiors/twelve", { 0 });
    assert_eq!(12, rusty_trap::trap_inferior_continue(inferior));
}
<commit_msg>Use legal syntax<commit_after>extern crate rusty_trap;

#[test]
fn it_can_exec () {
    let inferior = rusty_trap::trap_inferior_exec("./inferiors/twelve", &[]);
    assert_eq!(12, rusty_trap::trap_inferior_continue(inferior));
}
<|endoftext|>