<commit_before>use std::error::Error as StdError;
use std::fmt;

/// Errors that can be returned from various operations
///
pub enum Error {
    /// Returned if menu Menu function isn't supported
    MenusNotSupported,
    /// Menu already exists
    MenuExists(String),
    /// Menu already exists
    WindowCreate(String),
    /// Unable to Update
    UpdateFailed(String),
}

impl fmt::Debug for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::MenusNotSupported => write!(formatter, "Menus not supported"),
            Error::MenuExists(_) => write!(formatter, "Menu already exists"),
            Error::WindowCreate(_) => write!(formatter, "Failed to create window"),
            Error::UpdateFailed(_) => write!(formatter, "Failed to Update"),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::MenusNotSupported => write!(fmt, "{:?}", self),
            Error::MenuExists(ref e) => write!(fmt, "{:?} {:?}", self, e),
            Error::WindowCreate(ref e) => write!(fmt, "{:?} {:?}", self, e),
            Error::UpdateFailed(ref e) => write!(fmt, "{:?} {:?}", self, e),
        }
    }
}

impl StdError for Error {}
<commit_msg>Made the debug text for Error include any sub-errors (#232)<commit_after>use std::error::Error as StdError;
use std::fmt;

/// Errors that can be returned from various operations
///
pub enum Error {
    /// Returned if menu Menu function isn't supported
    MenusNotSupported,
    /// Menu already exists
    MenuExists(String),
    /// Menu already exists
    WindowCreate(String),
    /// Unable to Update
    UpdateFailed(String),
}

impl fmt::Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::MenusNotSupported => write!(formatter, "Menus not supported"),
            Error::MenuExists(_) => write!(formatter, "Menu already exists"),
            Error::WindowCreate(_) => write!(formatter, "Failed to create window"),
            Error::UpdateFailed(_) => write!(formatter, "Failed to Update"),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::MenusNotSupported => write!(fmt, "{}", self),
            Error::MenuExists(ref e) => write!(fmt, "{}, {:?}", self, e),
            Error::WindowCreate(ref e) => write!(fmt, "{}, {:?}", self, e),
            Error::UpdateFailed(ref e) => write!(fmt, "{}, {:?}", self, e),
        }
    }
}

impl StdError for Error {}
<|endoftext|>


<commit_before>extern crate regex;
extern crate toml;

use self::regex::Error as RegexError;
use self::toml::ParserError as ParseTomlError;

use constant::{self, ME};
use std::num::ParseIntError;
use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::Error as IoError;

#[derive(Debug)]
pub enum CliError {
    Config(ConfigError),
    Cron(CronError),
    Essential(EssentialLack),
    Io(IoError),
    ParseInt(ParseIntError),
    ParseToml(ParseTomlError),
    Regex(RegexError),
    RunningPlace(RunningPlaceError),
    Usage(Usage),
}
impl Display for CliError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            CliError::Config(ref e)       => e.fmt(f),
            CliError::Cron(ref e)         => e.fmt(f),
            CliError::Essential(ref e)    => e.fmt(f),
            CliError::Io(ref e)           => e.fmt(f),
            CliError::ParseInt(ref e)     => e.fmt(f),
            CliError::ParseToml(ref e)    => e.fmt(f),
            CliError::Regex(ref e)        => e.fmt(f),
            CliError::RunningPlace(ref e) => e.fmt(f),
            CliError::Usage(ref u)        => u.fmt(f),
        }
    }
}
impl Error for CliError {
    fn cause(&self) -> Option<&Error> {
        match *self {
            CliError::Config(ref e)       => Some(e),
            CliError::Cron(ref e)         => Some(e),
            CliError::Essential(ref e)    => Some(e),
            CliError::Io(ref e)           => Some(e),
            CliError::ParseInt(ref e)     => Some(e),
            CliError::ParseToml(ref e)    => Some(e),
            CliError::Regex(ref e)        => Some(e),
            CliError::RunningPlace(ref e) => Some(e),
            CliError::Usage(ref u)        => Some(u),
        }
    }

    fn description(&self) -> &str {
        match *self {
            CliError::Config(ref e)       => e.description(),
            CliError::Cron(ref e)         => e.description(),
            CliError::Essential(ref e)    => e.description(),
            CliError::Io(ref e)           => e.description(),
            CliError::ParseInt(ref e)     => e.description(),
            CliError::ParseToml(ref e)    => e.description(),
            CliError::Regex(ref e)        => e.description(),
            CliError::RunningPlace(ref e) => e.description(),
            CliError::Usage(ref u)        => u.description(),
        }
    }
}
impl From<ConfigError> for CliError {
    fn from(e: ConfigError) -> CliError {
        CliError::Config(e)
    }
}
impl From<CronError> for CliError {
    fn from(e: CronError) -> CliError {
        CliError::Cron(e)
    }
}
impl From<EssentialLack> for CliError {
    fn from(e: EssentialLack) -> CliError {
        CliError::Essential(e)
    }
}
impl From<IoError> for CliError {
    fn from(e: IoError) -> CliError {
        CliError::Io(e)
    }
}
impl From<ParseIntError> for CliError {
    fn from(e: ParseIntError) -> CliError {
        CliError::ParseInt(e)
    }
}
impl From<ParseTomlError> for CliError {
    fn from(e: ParseTomlError) -> CliError {
        CliError::ParseToml(e)
    }
}
impl From<RegexError> for CliError {
    fn from(e: RegexError) -> CliError {
        CliError::Regex(e)
    }
}
impl From<RunningPlaceError> for CliError {
    fn from(e: RunningPlaceError) -> CliError {
        CliError::RunningPlace(e)
    }
}
impl From<Usage> for CliError {
    fn from(u: Usage) -> CliError {
        CliError::Usage(u)
    }
}

#[derive(Debug, PartialEq)]
pub enum ConfigErrorKind {
    Something,
    InvalidKey,
    NonStringValue,
    NotFoundBurnMoratorium,
    NotFoundSweepMoratorium,
    NotFoundSweepPeriod,
    NotFoundSweepTime,
    BurnMoratorium,
    SweepMoratorium,
    SweepPeriod,
    SweepTime,
}
#[derive(Debug, PartialEq)]
pub struct ConfigError {
    kind: ConfigErrorKind,
}
impl ConfigError {
    pub fn new(kind: ConfigErrorKind) -> ConfigError {
        ConfigError { kind: kind }
    }
}
impl Display for ConfigError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", match self.kind {
            ConfigErrorKind::Something               => r#"Something went to wrong"#,
            ConfigErrorKind::InvalidKey              => r#"Please set key in ["burn.moratorium", "sweep.moratorium", "sweep.period", "sweep.time"]"#,
            ConfigErrorKind::NonStringValue          => r#"Please set values as "String""#,
            ConfigErrorKind::NotFoundBurnMoratorium  => r#"Please set "burn.moratorium""#,
            ConfigErrorKind::NotFoundSweepMoratorium => r#"Please set "sweep.moratorium""#,
            ConfigErrorKind::NotFoundSweepPeriod     => r#"Please set "sweep.period""#,
            ConfigErrorKind::NotFoundSweepTime       => r#"Please set "sweep.time""#,
            ConfigErrorKind::BurnMoratorium          => r#"Please set value like "3days" or "1week" as "burn.moratorium""#,
            ConfigErrorKind::SweepMoratorium         => r#"Please set value like "10hours" or "1week" as "sweep.moratorium""#,
            ConfigErrorKind::SweepPeriod             => r#"Please set "daily" or "weekly" as "sweep.period""#,
            ConfigErrorKind::SweepTime               => r#"Please set value from "00:00" to "23:59" as "sweep.time""#,
        })
    }
}
impl Error for ConfigError {
    fn description(&self) -> &str { "invalid parameters" }
}

#[derive(Debug, PartialEq)]
pub enum CronErrorKind {
    BinExistsInInvalidDir,
    InvalidCharacterCode,
    InvalidPath,
    FailedToWrite,
}
#[derive(Debug, PartialEq)]
pub struct CronError {
    kind: CronErrorKind,
}
impl CronError {
    pub fn new(kind: CronErrorKind) -> Self {
        CronError { kind: kind }
    }
}
impl Display for CronError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", match self.kind {
            CronErrorKind::BinExistsInInvalidDir => "The \"bin\" exists in directory whose path include non-UTF-8 charecter code",
            CronErrorKind::InvalidCharacterCode  => "The \"cron\" file consists of non-UTF-8 character code",
            CronErrorKind::InvalidPath           => "The path to current directory include non-UTF-8 character code",
            CronErrorKind::FailedToWrite         => "Failed to write new \"cron\" file"
        })
    }
}
impl Error for CronError {
    fn description(&self) -> &str { "problems around cron" }
}

#[derive(Debug, PartialEq)]
pub enum EssentialKind {
    WorkingDir,
    StorageDir,
    ConfigFile,
    IgnoreFile,
}
#[derive(Debug, PartialEq)]
pub struct EssentialLack {
    what: EssentialKind,
}
impl EssentialLack {
    pub fn new(what: EssentialKind) -> EssentialLack {
        EssentialLack { what: what }
    }
}
impl Display for EssentialLack {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{} does not exist", match self.what {
            EssentialKind::WorkingDir => format!("\"{}\" directory", constant::WORKING_DIR_NAME),
            EssentialKind::StorageDir => format!("\"{}\" directory", constant::STORAGE_DIR_NAME),
            EssentialKind::ConfigFile => format!("\"{}\" file"     , constant::CONFIG_FILE_NAME),
            EssentialKind::IgnoreFile => format!("\"{}\" file"     , constant::IGNORE_FILE_NAME),
        })
    }
}
impl Error for EssentialLack {
    fn description(&self) -> &str { "essential file does not exist" }
}

#[derive(Debug, PartialEq)]
pub struct RunningPlaceError {
    dir: String,
}
impl RunningPlaceError {
    pub fn new(dir: String) -> RunningPlaceError {
        RunningPlaceError { dir: dir }
    }
}
impl Display for RunningPlaceError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "Cannot run in \"{}\" directory", self.dir)
    }
}
impl Error for RunningPlaceError {
    fn description(&self) -> &str { "cannot run in banned directories" }
}

#[derive(Debug, PartialEq)]
pub enum UsageKind {
    Nothing,
    Help,
    Version,
    Init,
    Config,
    Ignore,
    Sweep,
    Burn,
    Start,
    End,
    Destroy,
    Patrol,
}
impl UsageKind {
    fn command(&self) -> &str {
        match *self {
            UsageKind::Nothing => "<Command>",
            UsageKind::Help    => "help",
            UsageKind::Version => "version",
            UsageKind::Init    => "init",
            UsageKind::Config  => "config",
            UsageKind::Ignore  => "ignore",
            UsageKind::Sweep   => "sweep",
            UsageKind::Burn    => "burn",
            UsageKind::Start   => "start",
            UsageKind::End     => "end",
            UsageKind::Destroy => "destroy",
            UsageKind::Patrol  => "patrol",
        }
    }

    fn common_usage(&self) -> String {
        format!("{} {}", ME, self.command())
    }

    fn usages(&self) -> Vec<String> {
        match *self {
            UsageKind::Help   => vec![format!("{} <SubCommand>", self.common_usage())],
            UsageKind::Config => vec![
                format!("{} set <Key> <Value>", self.common_usage()),
                format!("{} init"             , self.common_usage()),
            ],
            UsageKind::Ignore => vec![
                format!("{} add <File> ..."   , self.common_usage()),
                format!("{} remove <File> ...", self.common_usage()),
                format!("{} current"          , self.common_usage()),
                format!("{} clear"            , self.common_usage()),
            ],
            UsageKind::Sweep => vec![format!("{} [all] [indeed]", self.common_usage())],
            UsageKind::Burn  => vec![format!("{} [indeed]", self.common_usage())],
            _ => vec![self.common_usage()],
        }
    }

    fn description(&self) -> &str {
        match *self {
            UsageKind::Nothing => "Keep your directories clean",
            UsageKind::Help    => "Display usage for each command",
            UsageKind::Version => "Display the version of this software",
            UsageKind::Init    => "Register current directory",
            UsageKind::Config  => "Change parameter",
            UsageKind::Ignore  => "Change \"non-dust\" file's list",
            UsageKind::Sweep   => "Sweep \"dust\" files and empty directories in current directory",
            UsageKind::Burn    => "Burn \"sweeped\" files",
            UsageKind::Start   => "Register with \"cron\" for autonomous operation",
            UsageKind::End     => "Unregister from \"cron\"",
            UsageKind::Destroy => "Unregister current directory",
            UsageKind::Patrol  => "Keep \"cron\" file clean",
        }
    }

    fn sub_commands(&self) -> Vec<String> {
        match *self {
            UsageKind::Nothing => vec![
                format!("{}{}", "help    # ", UsageKind::Help   .description()),
                format!("{}{}", "version # ", UsageKind::Version.description()),
                format!("{}{}", "init    # ", UsageKind::Init   .description()),
                format!("{}{}", "config  # ", UsageKind::Config .description()),
                format!("{}{}", "ignore  # ", UsageKind::Ignore .description()),
                format!("{}{}", "sweep   # ", UsageKind::Sweep  .description()),
                format!("{}{}", "burn    # ", UsageKind::Burn   .description()),
                format!("{}{}", "start   # ", UsageKind::Start  .description()),
                format!("{}{}", "end     # ", UsageKind::End    .description()),
                format!("{}{}", "destroy # ", UsageKind::Destroy.description()),
                format!("{}{}", "patrol  # ", UsageKind::Patrol .description()),
            ],
            UsageKind::Config => vec![
                "set  # Set parameter"       .to_string(),
                "init # Initialize configure".to_string(),
            ],
            UsageKind::Ignore => {
                let file_expression = "\"non-dust\" file's list";
                vec![
                    format!("add     # Add specified files to {}"        , file_expression),
                    format!("remove  # Remove specified files from {}"   , file_expression),
                    format!("current # Replace {} with current all files", file_expression),
                    format!("clear   # Clear {}"                         , file_expression),
                ]
            },
            UsageKind::Sweep => vec![
                "(none)     # Sweep fakely \"dust\"s"                                  .to_string(),
                "indeed     # Sweep indeed \"dust\"s"                                  .to_string(),
                "all        # Sweep fakely \"dust\"s including recently accessed files".to_string(),
                "all indeed # Sweep indeed \"dust\"s including recently accessed files".to_string(),
            ],
            UsageKind::Burn => vec![
                "(none) # Burn expired \"dust box\"s fakely".to_string(),
                "indeed # Burn expired \"dust box\"s indeed".to_string(),
            ],
            _ => Vec::new(),
        }
    }

    fn optional_items(&self) -> (&str, Vec<&str>) {
        match *self {
            UsageKind::Config => ("Keys", vec![
                r#"burn.moratorium  # Moratorium to delete directories in "warehouse""#,
                r#"sweep.moratorium # Moratorium to Move "dust"s to "warehouse""#,
                r#"sweep.period     # Period to Move "dust"s by autonomous "sweep""#,
                r#"sweep.time       # Time to Move "dust"s by autonomous "sweep""#,
            ]),
            _ => ("", Vec::new()),
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct Usage {
    kind: UsageKind,
}
impl Usage {
    pub fn new(kind: UsageKind) -> Usage {
        Usage { kind: kind }
    }

    fn message(&self) -> String {
        let usage = self.kind
            .usages()
            .iter()
            .fold(String::new(), |message, line| {
                message + "    " + line + "\n"
            });

        let main = format!("Usage:\n{}\nDescription:\n    {}\n", usage, self.kind.description());

        let sub_commands = Self::create_area("Command", self.kind.sub_commands());

        let (header, items) = self.kind.optional_items();
        let optional_area   = Self::create_area(header, items);

        main + &sub_commands + &optional_area
    }

    fn create_area<S: AsRef<str>>(header: &str, items: Vec<S>) -> String {
        if items.is_empty() {
            "".to_string()
        } else {
            format!("\n{}:\n{}", header, items
                .iter()
                .fold(String::new(), |body, item| body + "    " + item.as_ref() + "\n")
            )
        }
    }
}
impl Display for Usage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", self.message())
    }
}
impl Error for Usage {
    fn description(&self) -> &str { "show usage" }
}
<commit_msg>[Remedy] Revise help messages<commit_after>extern crate regex;
extern crate toml;

use self::regex::Error as RegexError;
use self::toml::ParserError as ParseTomlError;

use constant::{self, ME};
use std::num::ParseIntError;
use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::Error as IoError;

#[derive(Debug)]
pub enum CliError {
    Config(ConfigError),
    Cron(CronError),
    Essential(EssentialLack),
    Io(IoError),
    ParseInt(ParseIntError),
    ParseToml(ParseTomlError),
    Regex(RegexError),
    RunningPlace(RunningPlaceError),
    Usage(Usage),
}
impl Display for CliError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            CliError::Config(ref e)       => e.fmt(f),
            CliError::Cron(ref e)         => e.fmt(f),
            CliError::Essential(ref e)    => e.fmt(f),
            CliError::Io(ref e)           => e.fmt(f),
            CliError::ParseInt(ref e)     => e.fmt(f),
            CliError::ParseToml(ref e)    => e.fmt(f),
            CliError::Regex(ref e)        => e.fmt(f),
            CliError::RunningPlace(ref e) => e.fmt(f),
            CliError::Usage(ref u)        => u.fmt(f),
        }
    }
}
impl Error for CliError {
    fn cause(&self) -> Option<&Error> {
        match *self {
            CliError::Config(ref e)       => Some(e),
            CliError::Cron(ref e)         => Some(e),
            CliError::Essential(ref e)    => Some(e),
            CliError::Io(ref e)           => Some(e),
            CliError::ParseInt(ref e)     => Some(e),
            CliError::ParseToml(ref e)    => Some(e),
            CliError::Regex(ref e)        => Some(e),
            CliError::RunningPlace(ref e) => Some(e),
            CliError::Usage(ref u)        => Some(u),
        }
    }

    fn description(&self) -> &str {
        match *self {
            CliError::Config(ref e)       => e.description(),
            CliError::Cron(ref e)         => e.description(),
            CliError::Essential(ref e)    => e.description(),
            CliError::Io(ref e)           => e.description(),
            CliError::ParseInt(ref e)     => e.description(),
            CliError::ParseToml(ref e)    => e.description(),
            CliError::Regex(ref e)        => e.description(),
            CliError::RunningPlace(ref e) => e.description(),
            CliError::Usage(ref u)        => u.description(),
        }
    }
}
impl From<ConfigError> for CliError {
    fn from(e: ConfigError) -> CliError {
        CliError::Config(e)
    }
}
impl From<CronError> for CliError {
    fn from(e: CronError) -> CliError {
        CliError::Cron(e)
    }
}
impl From<EssentialLack> for CliError {
    fn from(e: EssentialLack) -> CliError {
        CliError::Essential(e)
    }
}
impl From<IoError> for CliError {
    fn from(e: IoError) -> CliError {
        CliError::Io(e)
    }
}
impl From<ParseIntError> for CliError {
    fn from(e: ParseIntError) -> CliError {
        CliError::ParseInt(e)
    }
}
impl From<ParseTomlError> for CliError {
    fn from(e: ParseTomlError) -> CliError {
        CliError::ParseToml(e)
    }
}
impl From<RegexError> for CliError {
    fn from(e: RegexError) -> CliError {
        CliError::Regex(e)
    }
}
impl From<RunningPlaceError> for CliError {
    fn from(e: RunningPlaceError) -> CliError {
        CliError::RunningPlace(e)
    }
}
impl From<Usage> for CliError {
    fn from(u: Usage) -> CliError {
        CliError::Usage(u)
    }
}

#[derive(Debug, PartialEq)]
pub enum ConfigErrorKind {
    Something,
    InvalidKey,
    NonStringValue,
    NotFoundBurnMoratorium,
    NotFoundSweepMoratorium,
    NotFoundSweepPeriod,
    NotFoundSweepTime,
    BurnMoratorium,
    SweepMoratorium,
    SweepPeriod,
    SweepTime,
}
#[derive(Debug, PartialEq)]
pub struct ConfigError {
    kind: ConfigErrorKind,
}
impl ConfigError {
    pub fn new(kind: ConfigErrorKind) -> ConfigError {
        ConfigError { kind: kind }
    }
}
impl Display for ConfigError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", match self.kind {
            ConfigErrorKind::Something               => r#"Something went to wrong"#,
            ConfigErrorKind::InvalidKey              => r#"Please set key in ["burn.moratorium", "sweep.moratorium", "sweep.period", "sweep.time"]"#,
            ConfigErrorKind::NonStringValue          => r#"Please set values as "String""#,
            ConfigErrorKind::NotFoundBurnMoratorium  => r#"Please set "burn.moratorium""#,
            ConfigErrorKind::NotFoundSweepMoratorium => r#"Please set "sweep.moratorium""#,
            ConfigErrorKind::NotFoundSweepPeriod     => r#"Please set "sweep.period""#,
            ConfigErrorKind::NotFoundSweepTime       => r#"Please set "sweep.time""#,
            ConfigErrorKind::BurnMoratorium          => r#"Please set value like "3days" or "1week" as "burn.moratorium""#,
            ConfigErrorKind::SweepMoratorium         => r#"Please set value like "10hours" or "1week" as "sweep.moratorium""#,
            ConfigErrorKind::SweepPeriod             => r#"Please set "daily" or "weekly" as "sweep.period""#,
            ConfigErrorKind::SweepTime               => r#"Please set value from "00:00" to "23:59" as "sweep.time""#,
        })
    }
}
impl Error for ConfigError {
    fn description(&self) -> &str { "invalid parameters" }
}

#[derive(Debug, PartialEq)]
pub enum CronErrorKind {
    BinExistsInInvalidDir,
    InvalidCharacterCode,
    InvalidPath,
    FailedToWrite,
}
#[derive(Debug, PartialEq)]
pub struct CronError {
    kind: CronErrorKind,
}
impl CronError {
    pub fn new(kind: CronErrorKind) -> Self {
        CronError { kind: kind }
    }
}
impl Display for CronError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", match self.kind {
            CronErrorKind::BinExistsInInvalidDir => "The \"bin\" exists in directory whose path include non-UTF-8 charecter code",
            CronErrorKind::InvalidCharacterCode  => "The \"cron\" file consists of non-UTF-8 character code",
            CronErrorKind::InvalidPath           => "The path to current directory include non-UTF-8 character code",
            CronErrorKind::FailedToWrite         => "Failed to write new \"cron\" file"
        })
    }
}
impl Error for CronError {
    fn description(&self) -> &str { "problems around cron" }
}

#[derive(Debug, PartialEq)]
pub enum EssentialKind {
    WorkingDir,
    StorageDir,
    ConfigFile,
    IgnoreFile,
}
#[derive(Debug, PartialEq)]
pub struct EssentialLack {
    what: EssentialKind,
}
impl EssentialLack {
    pub fn new(what: EssentialKind) -> EssentialLack {
        EssentialLack { what: what }
    }
}
impl Display for EssentialLack {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{} does not exist", match self.what {
            EssentialKind::WorkingDir => format!("\"{}\" directory", constant::WORKING_DIR_NAME),
            EssentialKind::StorageDir => format!("\"{}\" directory", constant::STORAGE_DIR_NAME),
            EssentialKind::ConfigFile => format!("\"{}\" file"     , constant::CONFIG_FILE_NAME),
            EssentialKind::IgnoreFile => format!("\"{}\" file"     , constant::IGNORE_FILE_NAME),
        })
    }
}
impl Error for EssentialLack {
    fn description(&self) -> &str { "essential file does not exist" }
}

#[derive(Debug, PartialEq)]
pub struct RunningPlaceError {
    dir: String,
}
impl RunningPlaceError {
    pub fn new(dir: String) -> RunningPlaceError {
        RunningPlaceError { dir: dir }
    }
}
impl Display for RunningPlaceError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "Cannot run in \"{}\" directory", self.dir)
    }
}
impl Error for RunningPlaceError {
    fn description(&self) -> &str { "cannot run in banned directories" }
}

#[derive(Debug, PartialEq)]
pub enum UsageKind {
    Nothing,
    Help,
    Version,
    Init,
    Config,
    Ignore,
    Sweep,
    Burn,
    Start,
    End,
    Destroy,
    Patrol,
}
impl UsageKind {
    fn command(&self) -> &str {
        match *self {
            UsageKind::Nothing => "<Command>",
            UsageKind::Help    => "help",
            UsageKind::Version => "version",
            UsageKind::Init    => "init",
            UsageKind::Config  => "config",
            UsageKind::Ignore  => "ignore",
            UsageKind::Sweep   => "sweep",
            UsageKind::Burn    => "burn",
            UsageKind::Start   => "start",
            UsageKind::End     => "end",
            UsageKind::Destroy => "destroy",
            UsageKind::Patrol  => "patrol",
        }
    }

    fn common_usage(&self) -> String {
        format!("{} {}", ME, self.command())
    }

    fn usages(&self) -> Vec<String> {
        match *self {
            UsageKind::Help   => vec![format!("{} <SubCommand>", self.common_usage())],
            UsageKind::Config => vec![
                format!("{} set <Key> <Value>", self.common_usage()),
                format!("{} init"             , self.common_usage()),
            ],
            UsageKind::Ignore => vec![
                format!("{} add <File> ..."   , self.common_usage()),
                format!("{} remove <File> ...", self.common_usage()),
                format!("{} current"          , self.common_usage()),
                format!("{} clear"            , self.common_usage()),
            ],
            UsageKind::Sweep => vec![format!("{} [all] [indeed]", self.common_usage())],
            UsageKind::Burn  => vec![format!("{} [indeed]", self.common_usage())],
            _ => vec![self.common_usage()],
        }
    }

    fn description(&self) -> &str {
        match *self {
            UsageKind::Nothing => "Keep your directories clean",
            UsageKind::Help    => "Display usage for each command",
            UsageKind::Version => "Display the version of this software",
            UsageKind::Init    => "Register current directory, i.e. create \".kic\" directory",
            UsageKind::Config  => "Change \"config.toml\" file's contents",
            UsageKind::Ignore  => "Change \"ignore\" file's contents",
            UsageKind::Sweep   => "Move dust files and empty directories into \"warehouse\" directory",
            UsageKind::Burn    => "Delete expired directories in \"warehouse\" directory",
            UsageKind::Start   => "Start autonomous \"sweep\" and \"burn\" (UNIX-like: cron, Windows: ?)",
            UsageKind::End     => "End autonomous \"sweep\" and \"burn\" (UNIX-like: cron, Windows: ?)",
            UsageKind::Destroy => "Unregister current directory, i.e. delete \".kic\" directory",
            UsageKind::Patrol  => "Keep your \"cron\" file clean (UNIX-like only)",
        }
    }

    fn sub_commands(&self) -> Vec<String> {
        match *self {
            UsageKind::Nothing => vec![
                format!("{}{}", "help    # ", UsageKind::Help   .description()),
                format!("{}{}", "version # ", UsageKind::Version.description()),
                format!("{}{}", "init    # ", UsageKind::Init   .description()),
                format!("{}{}", "config  # ", UsageKind::Config .description()),
                format!("{}{}", "ignore  # ", UsageKind::Ignore .description()),
                format!("{}{}", "sweep   # ", UsageKind::Sweep  .description()),
                format!("{}{}", "burn    # ", UsageKind::Burn   .description()),
                format!("{}{}", "start   # ", UsageKind::Start  .description()),
                format!("{}{}", "end     # ", UsageKind::End    .description()),
                format!("{}{}", "destroy # ", UsageKind::Destroy.description()),
                format!("{}{}", "patrol  # ", UsageKind::Patrol .description()),
            ],
            UsageKind::Config => vec![
                "set  # Set parameters related to \"sweep\" and \"burn\" commands".to_string(),
                "init # Initialize \"config.toml\" file"                          .to_string(),
            ],
            UsageKind::Ignore => vec![
                "add     # Add files which will be ignored to \"ignore\" file"                        .to_string(),
                "remove  # Remove files which have been ignored from \"ignore\" file"                 .to_string(),
                "current # Replace \"ignore\" file with one which register current all files"         .to_string(),
                "clear   # Clear \"ignore\" file, i.e. all files will be aimed from \"sweep\" command".to_string(),
            ],
            UsageKind::Sweep => vec![
                "(none)     # Move fakely dust files into \"warehouse\""                                  .to_string(),
                "indeed     # Move indeed dust files into \"warehouse\""                                  .to_string(),
                "all        # Move fakely dust files into \"warehouse\" including recently accessed files".to_string(),
                "all indeed # Move indeed dust files into \"warehouse\" including recently accessed files".to_string(),
            ],
            UsageKind::Burn => vec![
                "(none) # Delete expired directories in \"warehouse\" fakely".to_string(),
                "indeed # Delete expired directories in \"warehouse\" indeed".to_string(),
            ],
            _ => Vec::new(),
        }
    }

    fn optional_items(&self) -> (&str, Vec<&str>) {
        match *self {
            UsageKind::Config => ("Keys", vec![
                r#"burn.moratorium  # Moratorium to delete directories in "warehouse""#,
                r#"sweep.moratorium # Moratorium to Move "dust"s into "warehouse""#,
                r#"sweep.period     # Period to Move "dust"s by autonomous "sweep""#,
                r#"sweep.time       # Time to Move "dust"s by autonomous "sweep""#,
            ]),
            _ => ("", Vec::new()),
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct Usage {
    kind: UsageKind,
}
impl Usage {
    pub fn new(kind: UsageKind) -> Usage {
        Usage { kind: kind }
    }

    fn message(&self) -> String {
        let usage = self.kind
            .usages()
            .iter()
            .fold(String::new(), |message, line| {
                message + "    " + line + "\n"
            });

        let main = format!("Usage:\n{}\nDescription:\n    {}\n", usage, self.kind.description());

        let sub_commands = Self::create_area("Command", self.kind.sub_commands());

        let (header, items) = self.kind.optional_items();
        let optional_area   = Self::create_area(header, items);

        main + &sub_commands + &optional_area
    }

    fn create_area<S: AsRef<str>>(header: &str, items: Vec<S>) -> String {
        if items.is_empty() {
            "".to_string()
        } else {
            format!("\n{}:\n{}", header, items
                .iter()
                .fold(String::new(), |body, item| body + "    " + item.as_ref() + "\n")
            )
        }
    }
}
impl Display for Usage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", self.message())
    }
}
impl Error for Usage {
    fn description(&self) -> &str { "show usage" }
}
<|endoftext|>


<commit_before>use std::{error, fmt, io};

use crate::stream::Event;

/// This type represents all possible errors that can occur when working with plist data.
#[derive(Debug)]
pub struct Error {
    inner: Box<ErrorImpl>,
}

#[derive(Debug)]
pub(crate) struct ErrorImpl {
    kind: ErrorKind,
    file_position: Option<FilePosition>,
}

#[derive(Debug)]
pub(crate) enum ErrorKind {
    UnexpectedEof,
    UnexpectedEndOfEventStream,
    UnexpectedEventType {
        expected: EventKind,
        found: EventKind,
    },

    // Xml format-specific errors
    UnclosedXmlElement,
    UnpairedXmlClosingTag,
    UnexpectedXmlCharactersExpectedElement,
    UnexpectedXmlOpeningTag,
    UnknownXmlElement,
    InvalidXmlSyntax,
    InvalidXmlUtf8,
    InvalidDataString,
    InvalidDateString,
    InvalidIntegerString,
    InvalidRealString,
    UidNotSupportedInXmlPlist,

    // Binary format-specific errors
    ObjectTooLarge,
    InvalidMagic,
    InvalidTrailerObjectOffsetSize, // the size of byte offsets to objects in the object table
    InvalidTrailerObjectReferenceSize, // the size of indices into the object table
    InvalidObjectLength,
    ObjectReferenceTooLarge,
    ObjectOffsetTooLarge,
    RecursiveObject,
    NullObjectUnimplemented,
    FillObjectUnimplemented,
    IntegerOutOfRange,
    InfiniteOrNanDate,
    InvalidUtf8String,
    InvalidUtf16String,
    UnknownObjectType(u8),

    Io(io::Error),
    Serde(String),
}

#[derive(Debug)]
pub(crate) enum FilePosition {
    LineColumn(u64, u64),
    Offset(u64),
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) enum EventKind {
    StartArray,
    StartDictionary,
    EndCollection,
    Boolean,
    Data,
    Date,
    Integer,
    Real,
    String,
    Uid,

    ValueOrStartCollection,
    DictionaryKeyOrEndCollection,
}

impl Error {
    /// Returns true if this error was caused by a failure to read or write bytes on an IO stream.
    pub fn is_io(&self) -> bool {
        self.as_io().is_some()
    }

    /// Returns true if this error was caused by prematurely reaching the end of the input data.
    pub fn is_eof(&self) -> bool {
        if let ErrorKind::UnexpectedEof = self.inner.kind {
            true
        } else {
            false
        }
    }

    /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO stream.
    pub fn as_io(&self) -> Option<&io::Error> {
        if let ErrorKind::Io(err) = &self.inner.kind {
            Some(err)
        } else {
            None
        }
    }
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match &self.inner.kind {
            ErrorKind::Io(err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner.kind, f)
    }
}

impl ErrorKind {
    pub fn with_byte_offset(self, offset: u64) -> Error {
        self.with_position(FilePosition::Offset(offset))
    }

    pub fn with_position(self, pos: FilePosition) -> Error {
        Error {
            inner: Box::new(ErrorImpl {
                kind: self,
                file_position: Some(pos),
            }),
        }
    }

    pub fn without_position(self) -> Error {
        Error {
            inner: Box::new(ErrorImpl {
                kind: self,
                file_position: None,
            }),
        }
    }
}

impl EventKind {
    pub fn of_event(event: &Event) -> EventKind {
        match event {
            Event::StartArray(_) => EventKind::StartArray,
            Event::StartDictionary(_) => EventKind::StartDictionary,
            Event::EndCollection => EventKind::EndCollection,
            Event::Boolean(_) => EventKind::Boolean,
            Event::Data(_) => EventKind::Data,
            Event::Date(_) => EventKind::Date,
            Event::Integer(_) => EventKind::Integer,
            Event::Real(_) => EventKind::Real,
            Event::String(_) => EventKind::String,
            Event::Uid(_) => EventKind::Uid,
            Event::__Nonexhaustive => unreachable!(),
        }
    }
}

impl fmt::Display for EventKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            EventKind::StartArray => "StartArray",
            EventKind::StartDictionary => "StartDictionary",
            EventKind::EndCollection => "EndCollection",
            EventKind::Boolean => "Boolean",
            EventKind::Data => "Data",
            EventKind::Date => "Date",
            EventKind::Integer => "Integer",
            EventKind::Real => "Real",
            EventKind::String => "String",
            EventKind::Uid => "Uid",
            EventKind::ValueOrStartCollection => "value or start collection",
            EventKind::DictionaryKeyOrEndCollection => "dictionary key or end collection",
        }
        .fmt(f)
    }
}

pub(crate) fn from_io_without_position(err: io::Error) -> Error {
    ErrorKind::Io(err).without_position()
}

pub(crate) fn unexpected_event_type(expected: EventKind, found: &Event) -> Error {
    let found = EventKind::of_event(&found);
    ErrorKind::UnexpectedEventType { expected, found }.without_position()
}
<commit_msg>Add `Error::into_io` to support extracting the underlying `std::io::Error` from a `plist::Error`.<commit_after>use std::{error, fmt, io};

use crate::stream::Event;

/// This type represents all possible errors that can occur when working with plist data.
#[derive(Debug)]
pub struct Error {
    inner: Box<ErrorImpl>,
}

#[derive(Debug)]
pub(crate) struct ErrorImpl {
    kind: ErrorKind,
    file_position: Option<FilePosition>,
}

#[derive(Debug)]
pub(crate) enum ErrorKind {
    UnexpectedEof,
    UnexpectedEndOfEventStream,
    UnexpectedEventType {
        expected: EventKind,
        found: EventKind,
    },

    // Xml format-specific errors
    UnclosedXmlElement,
    UnpairedXmlClosingTag,
    UnexpectedXmlCharactersExpectedElement,
    UnexpectedXmlOpeningTag,
    UnknownXmlElement,
    InvalidXmlSyntax,
    InvalidXmlUtf8,
    InvalidDataString,
    InvalidDateString,
    InvalidIntegerString,
    InvalidRealString,
    UidNotSupportedInXmlPlist,

    // Binary format-specific errors
    ObjectTooLarge,
    InvalidMagic,
    InvalidTrailerObjectOffsetSize, // the size of byte offsets to objects in the object table
    InvalidTrailerObjectReferenceSize, // the size of indices into the object table
    InvalidObjectLength,
    ObjectReferenceTooLarge,
    ObjectOffsetTooLarge,
    RecursiveObject,
    NullObjectUnimplemented,
    FillObjectUnimplemented,
    IntegerOutOfRange,
    InfiniteOrNanDate,
    InvalidUtf8String,
    InvalidUtf16String,
    UnknownObjectType(u8),

    Io(io::Error),
    Serde(String),
}

#[derive(Debug)]
pub(crate) enum FilePosition {
    LineColumn(u64, u64),
    Offset(u64),
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) enum EventKind {
    StartArray,
    StartDictionary,
    EndCollection,
    Boolean,
    Data,
    Date,
    Integer,
    Real,
    String,
    Uid,

    ValueOrStartCollection,
    DictionaryKeyOrEndCollection,
}

impl Error {
    /// Returns true if this error was caused by a failure to read or write bytes on an IO stream.
    pub fn is_io(&self) -> bool {
        self.as_io().is_some()
    }

    /// Returns true if this error was caused by prematurely reaching the end of the input data.
    pub fn is_eof(&self) -> bool {
        if let ErrorKind::UnexpectedEof = self.inner.kind {
            true
        } else {
            false
        }
    }

    /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO
    /// stream.
    pub fn as_io(&self) -> Option<&io::Error> {
        if let ErrorKind::Io(err) = &self.inner.kind {
            Some(err)
        } else {
            None
        }
    }

    /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO
    /// stream or `self` if it was not.
    pub fn into_io(self) -> Result<io::Error, Self> {
        if let ErrorKind::Io(err) = self.inner.kind {
            Ok(err)
        } else {
            Err(self)
        }
    }
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match &self.inner.kind {
            ErrorKind::Io(err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner.kind, f)
    }
}

impl ErrorKind {
    pub fn with_byte_offset(self, offset: u64) -> Error {
        self.with_position(FilePosition::Offset(offset))
    }

    pub fn with_position(self, pos: FilePosition) -> Error {
        Error {
            inner: Box::new(ErrorImpl {
                kind: self,
                file_position: Some(pos),
            }),
        }
    }

    pub fn without_position(self) -> Error {
        Error {
            inner: Box::new(ErrorImpl {
                kind: self,
                file_position: None,
            }),
        }
    }
}

impl EventKind {
    pub fn of_event(event: &Event) -> EventKind {
        match event {
            Event::StartArray(_) => EventKind::StartArray,
            Event::StartDictionary(_) => EventKind::StartDictionary,
            Event::EndCollection => EventKind::EndCollection,
            Event::Boolean(_) => EventKind::Boolean,
            Event::Data(_) => EventKind::Data,
            Event::Date(_) => EventKind::Date,
            Event::Integer(_) => EventKind::Integer,
            Event::Real(_) => EventKind::Real,
            Event::String(_) => EventKind::String,
            Event::Uid(_) => EventKind::Uid,
            Event::__Nonexhaustive => unreachable!(),
        }
    }
}

impl fmt::Display for EventKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            EventKind::StartArray => "StartArray",
            EventKind::StartDictionary => "StartDictionary",
            EventKind::EndCollection => "EndCollection",
            EventKind::Boolean => "Boolean",
            EventKind::Data => "Data",
            EventKind::Date => "Date",
            EventKind::Integer => "Integer",
            EventKind::Real => "Real",
            EventKind::String => "String",
            EventKind::Uid => "Uid",
            EventKind::ValueOrStartCollection => "value or start collection",
            EventKind::DictionaryKeyOrEndCollection => "dictionary key or end collection",
        }
        .fmt(f)
    }
}

pub(crate) fn from_io_without_position(err: io::Error) -> Error {
    ErrorKind::Io(err).without_position()
}

pub(crate) fn unexpected_event_type(expected: EventKind, found: &Event) -> Error {
    let found = EventKind::of_event(&found);
    ErrorKind::UnexpectedEventType { expected, found }.without_position()
}
<|endoftext|>


<commit_before>/* automatically generated by rust-bindgen */

use bindgen::{glutCreateWindow, glutDestroyWindow, glutDisplayFunc, glutGet, glutGetWindow};
use bindgen::{glutInit, glutInitDisplayMode, glutPostRedisplay, glutReshapeFunc};
use bindgen::{glutReshapeWindow, glutSetWindow, glutSwapBuffers, glutTimerFunc};
use libc::*;
use dvec::DVec;
use ptr::{null, to_unsafe_ptr};
use str::to_bytes;
use task::local_data::{local_data_get, local_data_set};
use cast::reinterpret_cast;
use vec::raw::to_ptr;

/* FIXME: global variable glutStrokeRoman */

/* FIXME: global variable glutStrokeMonoRoman */

/* FIXME: global variable glutBitmap9By15 */

/* FIXME: global variable glutBitmap8By13 */

/* FIXME: global variable glutBitmapTimesRoman10 */

/* FIXME: global variable glutBitmapTimesRoman24 */

/* FIXME: global variable glutBitmapHelvetica10 */

/* FIXME: global variable glutBitmapHelvetica12 */

/* FIXME: global variable glutBitmapHelvetica18 */

pub type GLenum = i32;
pub type GLint = i32;
pub type GLfloat = f32;
pub type GLdouble = f64;

pub enum Window = c_int;

pub const DOUBLE: c_uint = 2 as c_uint;

const WINDOW_WIDTH: GLenum = 102;
const WINDOW_HEIGHT: GLenum = 103;

pub enum State {
    WindowWidth,
    WindowHeight
}

pub fn destroy<T>(_value: ~[T]) {
    // let it drop
}

pub fn init() unsafe {
    let argc = 0 as c_int;
    let command = to_bytes(~"glut");
    let argv: (*u8, *u8) = (to_ptr(command), null());
    let argv_p = reinterpret_cast(&to_unsafe_ptr(&argv));

    glutInit(to_unsafe_ptr(&argc), argv_p);

    destroy(move command);
}

pub fn create_window(name: ~str) -> Window unsafe {
    let bytes = to_bytes(name);
    return Window(glutCreateWindow(to_ptr(bytes) as *c_char));
}

pub fn destroy_window(window: Window) unsafe {
    glutDestroyWindow(*window);
}

pub fn reshape_window(window: Window, width: c_int, height: c_int) unsafe {
    let current_window = glutGetWindow();
    glutSetWindow(*window);
    glutReshapeWindow(width, height);
    glutSetWindow(current_window);
}

pub fn display_callback_tls_key(_callback: @fn@()) {
    // Empty.
}

pub extern fn display_callback() unsafe {
    let callback = local_data_get(display_callback_tls_key).get();
    (*callback)();
}

pub fn display_func(callback: fn@()) unsafe {
    local_data_set(display_callback_tls_key, @callback);
    glutDisplayFunc(display_callback);
}

pub fn timer_callback_tls_key(_callback: @DVec<fn@()>) {
    // Empty.
}

pub extern fn timer_callback(index: int) unsafe {
    let callbacks = local_data_get(timer_callback_tls_key).get();
    ((*callbacks)[index as uint])();
}

pub fn timer_func(msecs: u32, callback: fn@()) unsafe {
    let callbacks;
    match local_data_get(timer_callback_tls_key) {
        None => {
            callbacks = @DVec();
            local_data_set(timer_callback_tls_key, copy callbacks);
        }
        Some(existing_callbacks) => {
            callbacks = existing_callbacks;
        }
    }

    callbacks.push(callback);
    let index = (callbacks.len() - 1) as c_int;
    glutTimerFunc(msecs, timer_callback, index);
}

pub fn reshape_callback_tls_key(_callback: @fn@(x: c_int, y: c_int)) {
    // Empty.
}

pub extern fn reshape_callback(++width: c_int, ++height: c_int) unsafe {
    let callback = local_data_get(reshape_callback_tls_key).get();
    (*callback)(width, height);
}

pub fn reshape_func(_window: Window, callback: fn@(x: c_int, y: c_int)) unsafe {
    local_data_set(reshape_callback_tls_key, @callback);
    glutReshapeFunc(reshape_callback);
}

#[cfg(target_os="macos")]
pub fn check_loop() unsafe {
    ext::glutCheckLoop();
}

#[cfg(target_os="linux")]
pub fn check_loop() unsafe {
    ext::glutMainLoopEvent();
}

pub fn init_display_mode(mode: c_uint) unsafe {
    glutInitDisplayMode(mode);
}

pub fn swap_buffers() unsafe {
    glutSwapBuffers();
}

pub fn post_redisplay() unsafe {
    glutPostRedisplay();
}

pub fn get(state: State) -> c_int unsafe {
    let glut_state;
    match state {
        WindowWidth => glut_state = WINDOW_WIDTH,
        WindowHeight => glut_state = WINDOW_HEIGHT
    }
    glutGet(glut_state)
}

#[cfg(target_os="macos")]
#[nolink]
#[link_args="-framework GLUT"]
pub extern mod dummy {
}

#[cfg(target_os="linux")]
#[link_name="glut"]
pub extern mod dummy {
}

#[cfg(target_os="macos")]
#[nolink]
pub extern mod ext {
    // Mac GLUT extension.
    fn glutCheckLoop();
}

#[cfg(target_os="linux")]
#[nolink]
pub extern mod ext {
    // freeglut extension.
    fn glutMainLoopEvent();
}

#[nolink]
pub extern mod bindgen {

pub fn glutInit(++argcp: *c_int, ++argv: **c_char);

pub fn glutInitDisplayMode(++mode: c_uint);

pub fn glutInitDisplayString(++string: *c_char);

pub fn glutInitWindowPosition(++x: c_int, ++y: c_int);

pub fn glutInitWindowSize(++width: c_int, ++height: c_int);

pub fn glutMainLoop();

pub fn glutCreateWindow(++title: *c_char) -> c_int;

pub fn glutCreateSubWindow(++win: c_int, ++x: c_int, ++y: c_int, ++width: c_int, ++height: c_int) -> c_int;

pub fn glutDestroyWindow(++win: c_int);

pub fn glutPostRedisplay();

pub fn glutPostWindowRedisplay(++win: c_int);

pub fn glutSwapBuffers();

pub fn glutGetWindow() -> c_int;

pub fn glutSetWindow(++win: c_int);

pub fn glutSetWindowTitle(++title: *c_char);

pub fn glutSetIconTitle(++title: *c_char);

pub fn glutPositionWindow(++x: c_int, ++y: c_int);

pub fn glutReshapeWindow(++width: c_int, ++height: c_int);

pub fn glutPopWindow();

pub fn glutPushWindow();

pub fn glutIconifyWindow();

pub fn glutShowWindow();

pub fn glutHideWindow();

pub fn glutFullScreen();

pub fn glutSetCursor(++cursor: c_int);

pub fn glutWarpPointer(++x: c_int, ++y: c_int);

pub fn glutEstablishOverlay();

pub fn glutRemoveOverlay();

pub fn glutUseLayer(++layer: GLenum);

pub fn glutPostOverlayRedisplay();

pub fn glutPostWindowOverlayRedisplay(++win: c_int);

pub fn glutShowOverlay();

pub fn glutHideOverlay();

pub fn glutCreateMenu(++arg1: *u8) -> c_int;

pub fn glutDestroyMenu(++menu: c_int);

pub fn glutGetMenu() -> c_int;

pub fn glutSetMenu(++menu: c_int);

pub fn glutAddMenuEntry(++label: *c_char, ++value: c_int);

pub fn glutAddSubMenu(++label: *c_char, ++submenu: c_int);

pub fn glutChangeToMenuEntry(++item: c_int, ++label: *c_char, ++value: c_int);

pub fn glutChangeToSubMenu(++item: c_int, ++label: *c_char, ++submenu: c_int);

pub fn glutRemoveMenuItem(++item: c_int);

pub fn glutAttachMenu(++button: c_int);

pub fn glutDetachMenu(++button: c_int);

pub fn glutDisplayFunc(++func: *u8);

pub fn glutReshapeFunc(++func: *u8);

pub fn glutKeyboardFunc(++func: *u8);

pub fn glutMouseFunc(++func: *u8);

pub fn glutMotionFunc(++func: *u8);

pub fn glutPassiveMotionFunc(++func: *u8);

pub fn glutEntryFunc(++func: *u8);

pub fn glutVisibilityFunc(++func: *u8);

pub fn glutIdleFunc(++func: *u8);

pub fn glutTimerFunc(++millis: c_uint, ++func: *u8, ++value: c_int);

pub fn glutMenuStateFunc(++func: *u8);

pub fn glutSpecialFunc(++func: *u8);

pub fn glutSpaceballMotionFunc(++func: *u8);

pub fn glutSpaceballRotateFunc(++func: *u8);

pub fn glutSpaceballButtonFunc(++func: *u8);

pub fn glutButtonBoxFunc(++func: *u8);

pub fn glutDialsFunc(++func: *u8);

pub fn glutTabletMotionFunc(++func: *u8);

pub fn glutTabletButtonFunc(++func: *u8);

pub fn glutMenuStatusFunc(++func: *u8);

pub fn glutOverlayDisplayFunc(++func: *u8);

pub fn glutWindowStatusFunc(++func: *u8);

pub fn glutKeyboardUpFunc(++func: *u8);

pub fn glutSpecialUpFunc(++func: *u8);

pub fn glutJoystickFunc(++func: *u8, ++pollInterval: c_int);

pub fn glutSetColor(++arg1: c_int, ++red: GLfloat, ++green: GLfloat, ++blue: GLfloat);

pub fn glutGetColor(++ndx: c_int, ++component: c_int) -> GLfloat;

pub fn glutCopyColormap(++win: c_int);

pub fn glutGet(++_type: GLenum) -> c_int;

pub fn glutDeviceGet(++_type: GLenum) -> c_int;

pub fn glutExtensionSupported(++name: *c_char) -> c_int;

pub fn glutGetModifiers() -> c_int;

pub fn glutLayerGet(++_type: GLenum) -> c_int;

pub fn glutGetProcAddress(++procName: *c_char) -> *c_void;

pub fn glutBitmapCharacter(++font: *c_void, ++character: c_int);

pub fn glutBitmapWidth(++font: *c_void, ++character: c_int) -> c_int;

pub fn glutStrokeCharacter(++font: *c_void, ++character: c_int);

pub fn glutStrokeWidth(++font: *c_void, ++character: c_int) -> c_int;

pub fn glutBitmapLength(++font: *c_void, ++string: *c_uchar) -> c_int;

pub fn glutStrokeLength(++font: *c_void, ++string: *c_uchar) -> c_int;

pub fn glutWireSphere(++radius: GLdouble, ++slices: GLint, ++stacks: GLint);

pub fn glutSolidSphere(++radius: GLdouble, ++slices: GLint, ++stacks: GLint);

pub fn glutWireCone(++base: GLdouble, ++height: GLdouble, ++slices: GLint, ++stacks: GLint);

pub fn glutSolidCone(++base: GLdouble, ++height: GLdouble, ++slices: GLint, ++stacks: GLint);

pub fn glutWireCube(++size: GLdouble);

pub fn glutSolidCube(++size: GLdouble);

pub fn glutWireTorus(++innerRadius: GLdouble, ++outerRadius: GLdouble, ++sides: GLint, ++rings: GLint);

pub fn glutSolidTorus(++innerRadius: GLdouble, ++outerRadius: GLdouble, ++sides: GLint, ++rings: GLint);

pub fn glutWireDodecahedron();

pub fn glutSolidDodecahedron();

pub fn glutWireTeapot(++size: GLdouble);

pub fn glutSolidTeapot(++size: GLdouble);

pub fn glutWireOctahedron();

pub fn glutSolidOctahedron();

pub fn glutWireTetrahedron();

pub fn glutSolidTetrahedron();

pub fn glutWireIcosahedron();

pub fn glutSolidIcosahedron();

pub fn glutVideoResizeGet(++param: GLenum) -> c_int;

pub fn glutSetupVideoResizing();

pub fn glutStopVideoResizing();

pub fn glutVideoResize(++x: c_int, ++y: c_int, ++width: c_int, ++height: c_int);

pub fn glutVideoPan(++x: c_int, ++y: c_int, ++width: c_int, ++height: c_int);

pub fn glutReportErrors();

pub fn glutIgnoreKeyRepeat(++ignore: c_int);

pub fn glutSetKeyRepeat(++repeatMode: c_int);

pub fn glutForceJoystickFunc();

pub fn glutGameModeString(++string: *c_char);

pub fn glutEnterGameMode() -> c_int;

pub fn glutLeaveGameMode();

pub fn glutGameModeGet(++mode: GLenum) -> c_int;

}
<commit_msg>Update for changed function sigil syntax.<commit_after>/* automatically generated by rust-bindgen */

use bindgen::{glutCreateWindow, glutDestroyWindow, glutDisplayFunc, glutGet, glutGetWindow};
use bindgen::{glutInit, glutInitDisplayMode, glutPostRedisplay, glutReshapeFunc};
use bindgen::{glutReshapeWindow, glutSetWindow, glutSwapBuffers, glutTimerFunc};
use libc::*;
use dvec::DVec;
use ptr::{null, to_unsafe_ptr};
use str::to_bytes;
use task::local_data::{local_data_get, local_data_set};
use cast::reinterpret_cast;
use vec::raw::to_ptr;

/* FIXME: global variable glutStrokeRoman */

/* FIXME: global variable glutStrokeMonoRoman */

/* FIXME: global variable glutBitmap9By15 */

/* FIXME: global variable glutBitmap8By13 */

/* FIXME: global variable glutBitmapTimesRoman10 */

/* FIXME: global variable glutBitmapTimesRoman24 */

/* FIXME: global variable glutBitmapHelvetica10 */

/* FIXME: global variable glutBitmapHelvetica12 */

/* FIXME: global variable glutBitmapHelvetica18 */

pub type GLenum = i32;
pub type GLint = i32;
pub type GLfloat = f32;
pub type GLdouble = f64;

pub enum Window = c_int;

pub const DOUBLE: c_uint = 2 as c_uint;

const WINDOW_WIDTH: GLenum = 102;
const WINDOW_HEIGHT: GLenum = 103;

pub enum State {
    WindowWidth,
    WindowHeight
}

pub fn destroy<T>(_value: ~[T]) {
    // let it drop
}

pub fn init() unsafe {
    let argc = 0 as c_int;
    let command = to_bytes(~"glut");
    let argv: (*u8, *u8) = (to_ptr(command), null());
    let argv_p = reinterpret_cast(&to_unsafe_ptr(&argv));

    glutInit(to_unsafe_ptr(&argc), argv_p);

    destroy(move command);
}

pub fn create_window(name: ~str) -> Window unsafe {
    let bytes = to_bytes(name);
    return Window(glutCreateWindow(to_ptr(bytes) as *c_char));
}

pub fn destroy_window(window: Window) unsafe {
    glutDestroyWindow(*window);
}

pub fn reshape_window(window: Window, width: c_int, height: c_int) unsafe {
    let current_window = glutGetWindow();
    glutSetWindow(*window);
    glutReshapeWindow(width, height);
    glutSetWindow(current_window);
}

pub fn display_callback_tls_key(_callback: @@fn()) {
    // Empty.
}

pub extern fn display_callback() unsafe {
    let callback = local_data_get(display_callback_tls_key).get();
    (*callback)();
}

pub fn display_func(callback: @fn()) unsafe {
    local_data_set(display_callback_tls_key, @callback);
    glutDisplayFunc(display_callback);
}

pub fn timer_callback_tls_key(_callback: @DVec<@fn()>) {
    // Empty.
}

pub extern fn timer_callback(index: int) unsafe {
    let callbacks = local_data_get(timer_callback_tls_key).get();
    ((*callbacks)[index as uint])();
}

pub fn timer_func(msecs: u32, callback: @fn()) unsafe {
    let callbacks;
    match local_data_get(timer_callback_tls_key) {
        None => {
            callbacks = @DVec();
            local_data_set(timer_callback_tls_key, copy callbacks);
        }
        Some(existing_callbacks) => {
            callbacks = existing_callbacks;
        }
    }

    callbacks.push(callback);
    let index = (callbacks.len() - 1) as c_int;
    glutTimerFunc(msecs, timer_callback, index);
}

pub fn reshape_callback_tls_key(_callback: @@fn(x: c_int, y: c_int)) {
    // Empty.
}

pub extern fn reshape_callback(++width: c_int, ++height: c_int) unsafe {
    let callback = local_data_get(reshape_callback_tls_key).get();
    (*callback)(width, height);
}

pub fn reshape_func(_window: Window, callback: @fn(x: c_int, y: c_int)) unsafe {
    local_data_set(reshape_callback_tls_key, @callback);
    glutReshapeFunc(reshape_callback);
}

#[cfg(target_os="macos")]
pub fn check_loop() unsafe {
    ext::glutCheckLoop();
}

#[cfg(target_os="linux")]
pub fn check_loop() unsafe {
    ext::glutMainLoopEvent();
}

pub fn init_display_mode(mode: c_uint) unsafe {
    glutInitDisplayMode(mode);
}

pub fn swap_buffers() unsafe {
    glutSwapBuffers();
}

pub fn post_redisplay() unsafe {
    glutPostRedisplay();
}

pub fn get(state: State) -> c_int unsafe {
    let glut_state;
    match state {
        WindowWidth => glut_state = WINDOW_WIDTH,
        WindowHeight => glut_state = WINDOW_HEIGHT
    }
    glutGet(glut_state)
}

#[cfg(target_os="macos")]
#[nolink]
#[link_args="-framework GLUT"]
pub extern mod dummy {
}

#[cfg(target_os="linux")]
#[link_name="glut"]
pub extern mod dummy {
}

#[cfg(target_os="macos")]
#[nolink]
pub extern mod ext {
    // Mac GLUT extension.
    fn glutCheckLoop();
}

#[cfg(target_os="linux")]
#[nolink]
pub extern mod ext {
    // freeglut extension.
    fn glutMainLoopEvent();
}

#[nolink]
pub extern mod bindgen {

pub fn glutInit(++argcp: *c_int, ++argv: **c_char);

pub fn glutInitDisplayMode(++mode: c_uint);

pub fn glutInitDisplayString(++string: *c_char);

pub fn glutInitWindowPosition(++x: c_int, ++y: c_int);

pub fn glutInitWindowSize(++width: c_int, ++height: c_int);

pub fn glutMainLoop();

pub fn glutCreateWindow(++title: *c_char) -> c_int;

pub fn glutCreateSubWindow(++win: c_int, ++x: c_int, ++y: c_int, ++width: c_int, ++height: c_int) -> c_int;

pub fn glutDestroyWindow(++win: c_int);

pub fn glutPostRedisplay();

pub fn glutPostWindowRedisplay(++win: c_int);

pub fn glutSwapBuffers();

pub fn glutGetWindow() -> c_int;

pub fn glutSetWindow(++win: c_int);

pub fn glutSetWindowTitle(++title: *c_char);

pub fn glutSetIconTitle(++title: *c_char);

pub fn glutPositionWindow(++x: c_int, ++y: c_int);

pub fn glutReshapeWindow(++width: c_int, ++height: c_int);

pub fn glutPopWindow();

pub fn glutPushWindow();

pub fn glutIconifyWindow();

pub fn glutShowWindow();

pub fn glutHideWindow();

pub fn glutFullScreen();

pub fn glutSetCursor(++cursor: c_int);

pub fn glutWarpPointer(++x: c_int, ++y: c_int);

pub fn glutEstablishOverlay();

pub fn glutRemoveOverlay();

pub fn glutUseLayer(++layer: GLenum);

pub fn glutPostOverlayRedisplay();

pub fn glutPostWindowOverlayRedisplay(++win: c_int);

pub fn glutShowOverlay();

pub fn glutHideOverlay();

pub fn glutCreateMenu(++arg1: *u8) -> c_int;

pub fn glutDestroyMenu(++menu: c_int);

pub fn glutGetMenu() -> c_int;

pub fn glutSetMenu(++menu: c_int);

pub fn glutAddMenuEntry(++label: *c_char, ++value: c_int);

pub fn glutAddSubMenu(++label: *c_char, ++submenu: c_int);

pub fn glutChangeToMenuEntry(++item: c_int, ++label: *c_char, ++value: c_int);

pub fn glutChangeToSubMenu(++item: c_int, ++label: *c_char, ++submenu: c_int);

pub fn glutRemoveMenuItem(++item: c_int);

pub fn glutAttachMenu(++button: c_int);

pub fn glutDetachMenu(++button: c_int);

pub fn glutDisplayFunc(++func: *u8);

pub fn glutReshapeFunc(++func: *u8);

pub fn glutKeyboardFunc(++func: *u8);

pub fn glutMouseFunc(++func: *u8);

pub fn glutMotionFunc(++func: *u8);

pub fn glutPassiveMotionFunc(++func: *u8);

pub fn glutEntryFunc(++func: *u8);

pub fn glutVisibilityFunc(++func: *u8);

pub fn glutIdleFunc(++func: *u8);

pub fn glutTimerFunc(++millis: c_uint, ++func: *u8, ++value: c_int);

pub fn glutMenuStateFunc(++func: *u8);

pub fn glutSpecialFunc(++func: *u8);

pub fn glutSpaceballMotionFunc(++func: *u8);

pub fn glutSpaceballRotateFunc(++func: *u8);

pub fn glutSpaceballButtonFunc(++func: *u8);

pub fn glutButtonBoxFunc(++func: *u8);

pub fn glutDialsFunc(++func: *u8);

pub fn glutTabletMotionFunc(++func: *u8);

pub fn glutTabletButtonFunc(++func: *u8);

pub fn glutMenuStatusFunc(++func: *u8);

pub fn glutOverlayDisplayFunc(++func: *u8);

pub fn glutWindowStatusFunc(++func: *u8);

pub fn glutKeyboardUpFunc(++func: *u8);

pub fn glutSpecialUpFunc(++func: *u8);

pub fn glutJoystickFunc(++func: *u8, ++pollInterval: c_int);

pub fn glutSetColor(++arg1: c_int, ++red: GLfloat, ++green: GLfloat, ++blue: GLfloat);

pub fn glutGetColor(++ndx: c_int, ++component: c_int) -> GLfloat;

pub fn glutCopyColormap(++win: c_int);

pub fn glutGet(++_type: GLenum) -> c_int;

pub fn glutDeviceGet(++_type: GLenum) -> c_int;

pub fn glutExtensionSupported(++name: *c_char) -> c_int;

pub fn glutGetModifiers() -> c_int;

pub fn glutLayerGet(++_type: GLenum) -> c_int;

pub fn glutGetProcAddress(++procName: *c_char) -> *c_void;

pub fn glutBitmapCharacter(++font: *c_void, ++character: c_int);

pub fn glutBitmapWidth(++font: *c_void, ++character: c_int) -> c_int;

pub fn glutStrokeCharacter(++font: *c_void, ++character: c_int);

pub fn glutStrokeWidth(++font: *c_void, ++character: c_int) -> c_int;

pub fn glutBitmapLength(++font: *c_void, ++string: *c_uchar) -> c_int;

pub fn glutStrokeLength(++font: *c_void, ++string: *c_uchar) -> c_int;

pub fn glutWireSphere(++radius: GLdouble, ++slices: GLint, ++stacks: GLint);

pub fn glutSolidSphere(++radius: GLdouble, ++slices: GLint, ++stacks: GLint);

pub fn glutWireCone(++base: GLdouble, ++height: GLdouble, ++slices: GLint, ++stacks: GLint);

pub fn glutSolidCone(++base: GLdouble, ++height: GLdouble, ++slices: GLint, ++stacks: GLint);

pub fn glutWireCube(++size: GLdouble);

pub fn glutSolidCube(++size: GLdouble);

pub fn glutWireTorus(++innerRadius: GLdouble, ++outerRadius: GLdouble, ++sides: GLint, ++rings: GLint);

pub fn glutSolidTorus(++innerRadius: GLdouble, ++outerRadius: GLdouble, ++sides: GLint, ++rings: GLint);

pub fn glutWireDodecahedron();

pub fn glutSolidDodecahedron();

pub fn glutWireTeapot(++size: GLdouble);

pub fn glutSolidTeapot(++size: GLdouble);

pub fn glutWireOctahedron();

pub fn glutSolidOctahedron();

pub fn glutWireTetrahedron();

pub fn glutSolidTetrahedron();

pub fn glutWireIcosahedron();

pub fn glutSolidIcosahedron();

pub fn glutVideoResizeGet(++param: GLenum) -> c_int;

pub fn glutSetupVideoResizing();

pub fn glutStopVideoResizing();

pub fn glutVideoResize(++x: c_int, ++y: c_int, ++width: c_int, ++height: c_int);

pub fn glutVideoPan(++x: c_int, ++y: c_int, ++width: c_int, ++height: c_int);

pub fn glutReportErrors();

pub fn glutIgnoreKeyRepeat(++ignore: c_int);

pub fn glutSetKeyRepeat(++repeatMode: c_int);

pub fn glutForceJoystickFunc();

pub fn glutGameModeString(++string: *c_char);

pub fn glutEnterGameMode() -> c_int;

pub fn glutLeaveGameMode();

pub fn glutGameModeGet(++mode: GLenum) -> c_int;

}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use itertools::Itertools;

use libimagstore::store::Entry;

use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

use error::TagErrorKind;
use error::TagError as TE;
use error::ResultExt;
use error::Result;
use tag::{Tag, TagSlice};
use tag::is_tag_str;

use toml::Value;

pub trait Tagable {

    fn get_tags(&self) -> Result<Vec<Tag>>;
    fn set_tags(&mut self, ts: &[Tag]) -> Result<()>;

    fn add_tag(&mut self, t: Tag) -> Result<()>;
    fn remove_tag(&mut self, t: Tag) -> Result<()>;

    fn has_tag(&self, t: TagSlice) -> Result<bool>;
    fn has_tags(&self, ts: &[Tag]) -> Result<bool>;

}

impl Tagable for Value {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        let tags = self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError)?;

        match tags {
            Some(&Value::Array(ref tags)) => {
                if !tags.iter().all(|t| is_match!(*t, Value::String(_))) {
                    return Err(TagErrorKind::TagTypeError.into());
                }
                if tags.iter().any(|t| match *t {
                    Value::String(ref s) => !is_tag_str(s).is_ok(),
                    _ => unreachable!()})
                {
                    return Err(TagErrorKind::NotATag.into());
                }

                Ok(tags.iter()
                    .cloned()
                    .map(|t| {
                        match t {
                           Value::String(s) => s,
                           _ => unreachable!(),
                        }
                    })
                    .collect())
            },
            None => Ok(vec![]),
            _ => Err(TagErrorKind::TagTypeError.into()),
        }
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        if ts.iter().any(|tag| !is_tag_str(tag).is_ok()) {
            debug!("Not a tag: '{}'", ts.iter().filter(|t| !is_tag_str(t).is_ok()).next().unwrap());
            return Err(TagErrorKind::NotATag.into());
        }

        let a = ts.iter().unique().map(|t| Value::String(t.clone())).collect();
        debug!("Setting tags = {:?}", a);
        self.insert("tag.values", Value::Array(a))
            .map(|_| ())
            .chain_err(|| TagErrorKind::HeaderWriteError)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        if !is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))? {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                debug!("Pushing tag = {:?} to list = {:?}", t, tags);
                tags.push(t);
                self.set_tags(&tags.into_iter().unique().collect::<Vec<_>>()[..])
            })
            .map(|_| ())
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        if !is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))? {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                tags.retain(|tag| tag.clone() != t);
                self.set_tags(&tags[..])
            })
            .map(|_| ())
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        let tags = self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError)?;

        if !tags.iter().all(|t| is_match!(*t, &Value::String(_))) {
            return Err(TagErrorKind::TagTypeError.into());
        }

        Ok(tags
           .iter()
           .any(|tag| {
               match *tag {
                   &Value::String(ref s) => { s == t },
                   _ => unreachable!()
               }
           }))
    }

    fn has_tags(&self, tags: &[Tag]) -> Result<bool> {
        let mut result = true;
        for tag in tags {
            result = result && self.has_tag(tag)?;
        }

        Ok(result)
    }

}

impl Tagable for Entry {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        self.get_header().get_tags()
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        self.get_header_mut().set_tags(ts)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().add_tag(t)
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().remove_tag(t)
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        self.get_header().has_tag(t)
    }

    fn has_tags(&self, ts: &[Tag]) -> Result<bool> {
        self.get_header().has_tags(ts)
    }

}

<commit_msg>Refactoring: Use function chaining rather than matching<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use itertools::Itertools;

use libimagstore::store::Entry;

use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

use error::TagErrorKind;
use error::TagError as TE;
use error::ResultExt;
use error::Result;
use tag::{Tag, TagSlice};
use tag::is_tag_str;

use toml::Value;

pub trait Tagable {

    fn get_tags(&self) -> Result<Vec<Tag>>;
    fn set_tags(&mut self, ts: &[Tag]) -> Result<()>;

    fn add_tag(&mut self, t: Tag) -> Result<()>;
    fn remove_tag(&mut self, t: Tag) -> Result<()>;

    fn has_tag(&self, t: TagSlice) -> Result<bool>;
    fn has_tags(&self, ts: &[Tag]) -> Result<bool>;

}

impl Tagable for Value {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        self.read("tag.values")
            .chain_err(|| TagErrorKind::HeaderReadError)?
            .map(|val| {
                debug!("Got Value of tags...");
                val.as_array()
                    .map(|tags| {
                        debug!("Got Array<T> of tags...");
                        if !tags.iter().all(|t| is_match!(*t, Value::String(_))) {
                            debug!("Got Array<T>, T != String of tags: {:?}", tags);
                            return Err(TagErrorKind::TagTypeError.into());
                        }
                        debug!("Got Array<String> of tags...");
                        if tags.iter().any(|t| match *t {
                            Value::String(ref s) => !is_tag_str(s).is_ok(),
                            _ => unreachable!()})
                        {
                            debug!("At least one tag is not a valid tag string");
                            return Err(TagErrorKind::NotATag.into());
                        }

                        Ok(tags.iter()
                            .cloned()
                            .map(|t| {
                                match t {
                                   Value::String(s) => s,
                                   _ => unreachable!(),
                                }
                            })
                            .collect())
                    })
                    .unwrap_or(Ok(vec![]))
            })
            .unwrap_or(Ok(vec![]))
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        if ts.iter().any(|tag| !is_tag_str(tag).is_ok()) {
            debug!("Not a tag: '{}'", ts.iter().filter(|t| !is_tag_str(t).is_ok()).next().unwrap());
            return Err(TagErrorKind::NotATag.into());
        }

        let a = ts.iter().unique().map(|t| Value::String(t.clone())).collect();
        debug!("Setting tags = {:?}", a);
        self.insert("tag.values", Value::Array(a))
            .map(|_| ())
            .chain_err(|| TagErrorKind::HeaderWriteError)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        if !is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))? {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                debug!("Pushing tag = {:?} to list = {:?}", t, tags);
                tags.push(t);
                self.set_tags(&tags.into_iter().unique().collect::<Vec<_>>()[..])
            })
            .map(|_| ())
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        if !is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))? {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                tags.retain(|tag| tag.clone() != t);
                self.set_tags(&tags[..])
            })
            .map(|_| ())
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        let tags = self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError)?;

        if !tags.iter().all(|t| is_match!(*t, &Value::String(_))) {
            return Err(TagErrorKind::TagTypeError.into());
        }

        Ok(tags
           .iter()
           .any(|tag| {
               match *tag {
                   &Value::String(ref s) => { s == t },
                   _ => unreachable!()
               }
           }))
    }

    fn has_tags(&self, tags: &[Tag]) -> Result<bool> {
        let mut result = true;
        for tag in tags {
            result = result && self.has_tag(tag)?;
        }

        Ok(result)
    }

}

impl Tagable for Entry {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        self.get_header().get_tags()
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        self.get_header_mut().set_tags(ts)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().add_tag(t)
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().remove_tag(t)
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        self.get_header().has_tag(t)
    }

    fn has_tags(&self, ts: &[Tag]) -> Result<bool> {
        self.get_header().has_tags(ts)
    }

}

<|endoftext|>


<commit_before>use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType};
use geometry::CGPoint;
use event_source::{CGEventSource,CGEventSourceRef};

use libc;
use std::mem;
use std::ptr;

pub type CGKeyCode = libc::uint16_t;

/// Flags for events
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventFlags {
  // Device-independent modifier key bits.
  AlphaShift = 0x00010000,
  Shift = 0x00020000,
  Control = 0x00040000,
  Alternate = 0x00080000,
  Command = 0x00100000,

  // Special key identifiers.
  Help = 0x00400000,
  SecondaryFn = 0x00800000,

  // Identifies key events from numeric keypad area on extended keyboards.
  NumericPad = 0x00200000,

  // Indicates if mouse/pen movement events are not being coalesced
  NonCoalesced = 0x00000100,
}

/// Constants that specify the different types of input events.
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventType {
    Null = 1 << 0,

    // Mouse events.
    LeftMouseDown = 1 << 1,
    LeftMouseUp = 1 << 2,
    RightMouseDown = 1 << 3,
    RightMouseUp = 1 << 4,
    MouseMoved = 1 << 5,
    LeftMouseDragged = 1 << 6,
    RightMouseDragged = 1 << 7,

    // Keyboard events.
    KeyDown = 1 << 10,
    KeyUp = 1 << 11,
    FlagsChanged = 1 << 12,

    // Specialized control devices.
    ScrollWheel = 1 << 22,
    TabletPointer = 1 << 23,
    TabletProximity = 1 << 24,
    OtherMouseDown = 1 << 25,
    OtherMouseUp = 1 << 26,
    OtherMouseDragged = 1 << 27,

    // Out of band event types. These are delivered to the event tap callback
    // to notify it of unusual conditions that disable the event tap.
    TapDisabledByTimeout = 0xFFFFFFFE,
    TapDisabledByUserInput = 0xFFFFFFFF,
}

// Constants that specify buttons on a one, two, or three-button mouse.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGMouseButton {
    Left,
    Right,
    Center,
}

/// Possible tapping points for events.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapLocation {
    HID,
    Session,
    AnnotatedSession,
}

#[repr(C)]
pub struct __CGEvent;

pub type CGEventRef = *const __CGEvent;

pub struct CGEvent {
    obj: CGEventRef,
}

impl Clone for CGEvent {
    #[inline]
    fn clone(&self) -> CGEvent {
        unsafe {
            TCFType::wrap_under_get_rule(self.obj)
        }
    }
}

impl Drop for CGEvent {
    fn drop(&mut self) {
        unsafe {
            let ptr = self.as_CFTypeRef();
            assert!(ptr != ptr::null());
            CFRelease(ptr);
        }
    }
}

impl TCFType<CGEventRef> for CGEvent {
    #[inline]
    fn as_concrete_TypeRef(&self) -> CGEventRef {
        self.obj
    }

    #[inline]
    unsafe fn wrap_under_get_rule(reference: CGEventRef) -> CGEvent {
        let reference: CGEventRef = mem::transmute(CFRetain(mem::transmute(reference)));
        TCFType::wrap_under_create_rule(reference)
    }

    #[inline]
    fn as_CFTypeRef(&self) -> CFTypeRef {
        unsafe {
            mem::transmute(self.as_concrete_TypeRef())
        }
    }

    #[inline]
    unsafe fn wrap_under_create_rule(obj: CGEventRef) -> CGEvent {
        CGEvent {
            obj: obj,
        }
    }

    #[inline]
    fn type_id() -> CFTypeID {
        unsafe {
            CGEventGetTypeID()
        }
    }
}

impl CGEvent {
    pub fn new_keyboard_event(
        source: CGEventSource,
        keycode: CGKeyCode,
        keydown: bool
    ) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreateKeyboardEvent(source.as_concrete_TypeRef(), keycode, keydown);
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn new_mouse_event(
        source: CGEventSource,
        mouse_type: CGEventType,
        mouse_cursor_position: CGPoint,
        mouse_button: CGMouseButton
    ) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreateMouseEvent(source.as_concrete_TypeRef(), mouse_type,
                mouse_cursor_position, mouse_button);
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn post(&self, tap_location: CGEventTapLocation) {
        unsafe {
            CGEventPost(tap_location, self.as_concrete_TypeRef());
        }
    }

    pub fn post_to_pid(&self, pid: libc::pid_t) {
        unsafe {
            CGEventPostToPid(pid, self.as_concrete_TypeRef());
        }
    }

    pub fn set_flags(&self, flags: CGEventFlags) {
        unsafe {
            CGEventSetFlags(self.as_concrete_TypeRef(), flags);
        }
    }

    pub fn get_flags(&self) -> CGEventFlags {
        unsafe {
            CGEventGetFlags(self.as_concrete_TypeRef())
        }
    }
}

#[link(name = "ApplicationServices", kind = "framework")]
extern {
    /// Return the type identifier for the opaque type `CGEventRef'.
    fn CGEventGetTypeID() -> CFTypeID;

    /// Return a new keyboard event.
    ///
    /// The event source may be taken from another event, or may be NULL. Based
    /// on the virtual key code values entered, the appropriate key down, key up,
    /// or flags changed events are generated.
    ///
    /// All keystrokes needed to generate a character must be entered, including
    /// SHIFT, CONTROL, OPTION, and COMMAND keys. For example, to produce a 'Z',
    /// the SHIFT key must be down, the 'z' key must go down, and then the SHIFT
    /// and 'z' key must be released:
    fn CGEventCreateKeyboardEvent(source: CGEventSourceRef, keycode: CGKeyCode,
        keydown: bool) -> CGEventRef;

    /// Return a new mouse event.
    ///
    /// The event source may be taken from another event, or may be NULL.
    /// `mouseType' should be one of the mouse event types. `mouseCursorPosition'
    /// should be the position of the mouse cursor in global coordinates.
    /// `mouseButton' should be the button that's changing state; `mouseButton'
    /// is ignored unless `mouseType' is one of `kCGEventOtherMouseDown',
    /// `kCGEventOtherMouseDragged', or `kCGEventOtherMouseUp'.
    ///
    /// The current implemementation of the event system supports a maximum of
    /// thirty-two buttons. Mouse button 0 is the primary button on the mouse.
    /// Mouse button 1 is the secondary mouse button (right). Mouse button 2 is
    /// the center button, and the remaining buttons are in USB device order.
    fn CGEventCreateMouseEvent(source: CGEventSourceRef, mouseType: CGEventType,
        mouseCursorPosition: CGPoint, mouseButton: CGMouseButton) -> CGEventRef;

    /// Post an event into the event stream at a specified location.
    ///
    /// This function posts the specified event immediately before any event taps
    /// instantiated for that location, and the event passes through any such
    /// taps.
    fn CGEventPost(tapLocation: CGEventTapLocation, event: CGEventRef);

    /// Post an event to a specified process ID
    fn CGEventPostToPid(pid: libc::pid_t, event: CGEventRef);

    /// Set the event flags of an event.
    fn CGEventSetFlags(event: CGEventRef, flags: CGEventFlags);

    /// Return the event flags of an event.
    fn CGEventGetFlags(event: CGEventRef) -> CGEventFlags;
}
<commit_msg>Add support for CGEvent::new & CGEventGetLocation<commit_after>use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType};
use geometry::CGPoint;
use event_source::{CGEventSource,CGEventSourceRef};

use libc;
use std::mem;
use std::ptr;

pub type CGKeyCode = libc::uint16_t;

/// Flags for events
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventFlags {
  // Device-independent modifier key bits.
  AlphaShift = 0x00010000,
  Shift = 0x00020000,
  Control = 0x00040000,
  Alternate = 0x00080000,
  Command = 0x00100000,

  // Special key identifiers.
  Help = 0x00400000,
  SecondaryFn = 0x00800000,

  // Identifies key events from numeric keypad area on extended keyboards.
  NumericPad = 0x00200000,

  // Indicates if mouse/pen movement events are not being coalesced
  NonCoalesced = 0x00000100,
}

/// Constants that specify the different types of input events.
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventType {
    Null = 1 << 0,

    // Mouse events.
    LeftMouseDown = 1 << 1,
    LeftMouseUp = 1 << 2,
    RightMouseDown = 1 << 3,
    RightMouseUp = 1 << 4,
    MouseMoved = 1 << 5,
    LeftMouseDragged = 1 << 6,
    RightMouseDragged = 1 << 7,

    // Keyboard events.
    KeyDown = 1 << 10,
    KeyUp = 1 << 11,
    FlagsChanged = 1 << 12,

    // Specialized control devices.
    ScrollWheel = 1 << 22,
    TabletPointer = 1 << 23,
    TabletProximity = 1 << 24,
    OtherMouseDown = 1 << 25,
    OtherMouseUp = 1 << 26,
    OtherMouseDragged = 1 << 27,

    // Out of band event types. These are delivered to the event tap callback
    // to notify it of unusual conditions that disable the event tap.
    TapDisabledByTimeout = 0xFFFFFFFE,
    TapDisabledByUserInput = 0xFFFFFFFF,
}

// Constants that specify buttons on a one, two, or three-button mouse.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGMouseButton {
    Left,
    Right,
    Center,
}

/// Possible tapping points for events.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapLocation {
    HID,
    Session,
    AnnotatedSession,
}

#[repr(C)]
pub struct __CGEvent;

pub type CGEventRef = *const __CGEvent;

pub struct CGEvent {
    obj: CGEventRef,
}

impl Clone for CGEvent {
    #[inline]
    fn clone(&self) -> CGEvent {
        unsafe {
            TCFType::wrap_under_get_rule(self.obj)
        }
    }
}

impl Drop for CGEvent {
    fn drop(&mut self) {
        unsafe {
            let ptr = self.as_CFTypeRef();
            assert!(ptr != ptr::null());
            CFRelease(ptr);
        }
    }
}

impl TCFType<CGEventRef> for CGEvent {
    #[inline]
    fn as_concrete_TypeRef(&self) -> CGEventRef {
        self.obj
    }

    #[inline]
    unsafe fn wrap_under_get_rule(reference: CGEventRef) -> CGEvent {
        let reference: CGEventRef = mem::transmute(CFRetain(mem::transmute(reference)));
        TCFType::wrap_under_create_rule(reference)
    }

    #[inline]
    fn as_CFTypeRef(&self) -> CFTypeRef {
        unsafe {
            mem::transmute(self.as_concrete_TypeRef())
        }
    }

    #[inline]
    unsafe fn wrap_under_create_rule(obj: CGEventRef) -> CGEvent {
        CGEvent {
            obj: obj,
        }
    }

    #[inline]
    fn type_id() -> CFTypeID {
        unsafe {
            CGEventGetTypeID()
        }
    }
}

impl CGEvent {
    pub fn new(source: CGEventSource) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreate(source.as_concrete_TypeRef());
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn new_keyboard_event(
        source: CGEventSource,
        keycode: CGKeyCode,
        keydown: bool
    ) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreateKeyboardEvent(source.as_concrete_TypeRef(), keycode, keydown);
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn new_mouse_event(
        source: CGEventSource,
        mouse_type: CGEventType,
        mouse_cursor_position: CGPoint,
        mouse_button: CGMouseButton
    ) -> Result<CGEvent, ()> {
        unsafe {
            let event_ref = CGEventCreateMouseEvent(source.as_concrete_TypeRef(), mouse_type,
                mouse_cursor_position, mouse_button);
            if event_ref != ptr::null() {
                Ok(TCFType::wrap_under_create_rule(event_ref))
            } else {
                Err(())
            }
        }
    }

    pub fn post(&self, tap_location: CGEventTapLocation) {
        unsafe {
            CGEventPost(tap_location, self.as_concrete_TypeRef());
        }
    }

    pub fn location(&self) -> CGPoint {
        unsafe {
            CGEventGetLocation(self.as_concrete_TypeRef())
        }
    }

    pub fn post_to_pid(&self, pid: libc::pid_t) {
        unsafe {
            CGEventPostToPid(pid, self.as_concrete_TypeRef());
        }
    }

    pub fn set_flags(&self, flags: CGEventFlags) {
        unsafe {
            CGEventSetFlags(self.as_concrete_TypeRef(), flags);
        }
    }

    pub fn get_flags(&self) -> CGEventFlags {
        unsafe {
            CGEventGetFlags(self.as_concrete_TypeRef())
        }
    }
}

#[link(name = "ApplicationServices", kind = "framework")]
extern {
    /// Return the type identifier for the opaque type `CGEventRef'.
    fn CGEventGetTypeID() -> CFTypeID;

    /// Return a new event using the event source `source'. If `source' is NULL,
    /// the default source is used.
    fn CGEventCreate(source: CGEventSourceRef) -> CGEventRef;

    /// Return a new keyboard event.
    ///
    /// The event source may be taken from another event, or may be NULL. Based
    /// on the virtual key code values entered, the appropriate key down, key up,
    /// or flags changed events are generated.
    ///
    /// All keystrokes needed to generate a character must be entered, including
    /// SHIFT, CONTROL, OPTION, and COMMAND keys. For example, to produce a 'Z',
    /// the SHIFT key must be down, the 'z' key must go down, and then the SHIFT
    /// and 'z' key must be released:
    fn CGEventCreateKeyboardEvent(source: CGEventSourceRef, keycode: CGKeyCode,
        keydown: bool) -> CGEventRef;

    /// Return a new mouse event.
    ///
    /// The event source may be taken from another event, or may be NULL.
    /// `mouseType' should be one of the mouse event types. `mouseCursorPosition'
    /// should be the position of the mouse cursor in global coordinates.
    /// `mouseButton' should be the button that's changing state; `mouseButton'
    /// is ignored unless `mouseType' is one of `kCGEventOtherMouseDown',
    /// `kCGEventOtherMouseDragged', or `kCGEventOtherMouseUp'.
    ///
    /// The current implemementation of the event system supports a maximum of
    /// thirty-two buttons. Mouse button 0 is the primary button on the mouse.
    /// Mouse button 1 is the secondary mouse button (right). Mouse button 2 is
    /// the center button, and the remaining buttons are in USB device order.
    fn CGEventCreateMouseEvent(source: CGEventSourceRef, mouseType: CGEventType,
        mouseCursorPosition: CGPoint, mouseButton: CGMouseButton) -> CGEventRef;

    /// Post an event into the event stream at a specified location.
    ///
    /// This function posts the specified event immediately before any event taps
    /// instantiated for that location, and the event passes through any such
    /// taps.
    fn CGEventPost(tapLocation: CGEventTapLocation, event: CGEventRef);

    /// Post an event to a specified process ID
    fn CGEventPostToPid(pid: libc::pid_t, event: CGEventRef);

    /// Set the event flags of an event.
    fn CGEventSetFlags(event: CGEventRef, flags: CGEventFlags);

    /// Return the event flags of an event.
    fn CGEventGetFlags(event: CGEventRef) -> CGEventFlags;

    /// Return the location of an event in global display coordinates.
    /// CGPointZero is returned if event is not a valid CGEventRef.
    fn CGEventGetLocation(event: CGEventRef) -> CGPoint;
}
<|endoftext|>


<commit_before>extern crate image;
extern crate time;

use std::path::Path;

use vector::*;
use objects::*;
mod lighting;

pub type Image = image::RgbImage;

pub fn write_image(image: Image, filename: &str) {
    let _ = image.save(&Path::new(filename));
}

pub fn raytrace(scene: &Scene, width: u32, height: u32, h_fov: f64) {
    let f_width = width as f64;
    let f_height = height as f64;
    let v_fov = h_fov * (f_height / f_width);

    let vw_per_pixel = 2.0 * (h_fov / 2.0).tan() / f_width;
    let vh_per_pixel = 2.0 * (v_fov / 2.0).tan() / f_height;

    let mut image = image::RgbImage::new(width, height);

    let norm_up = scene.camera.up.normalized();
    let norm_right = scene.camera.direction.cross(scene.camera.up).normalized();

    let mut rays: u64 = 0;

    let start_time = time::precise_time_ns();

    for (x, y, pixel) in image.enumerate_pixels_mut() {
        let cast_ray = |dx: f64, dy: f64, rays: &mut u64| {
            *rays += 1;
            let fi = (x as f64) + dx;
            let fj = (y as f64) + dy;

            let u_rel = vw_per_pixel * (fi - (f_width / 2.0));
            let v_rel = vh_per_pixel * (fj - (f_height / 2.0));

            let pixel_loc = scene.camera.position + scene.camera.direction +
                (norm_right * u_rel) + (norm_up * v_rel);

            let ray = Ray {
                origin: scene.camera.position,
                direction: pixel_loc - scene.camera.position
            };

            let (color, new_rays) = lighting::get_color(&scene, &ray, 3);
            *rays += new_rays;
            color
        };
        // This makes the compiler happy. I'm not really sure what's going
        // on though.
        let c1 = cast_ray(0.25, 0.25, &mut rays);
        let c2 = cast_ray(0.75, 0.25, &mut rays);
        let c3 = cast_ray(0.50, 0.50, &mut rays);
        let c4 = cast_ray(0.75, 0.75, &mut rays);
        let c5 = cast_ray(0.25, 0.75, &mut rays);

        *pixel = ((c1 + c2 + c3 + c4 + c5) / 5.0).to_rgb();
    }

    write_image(image, "test.png");

    let elapsed_time = ((time::precise_time_ns() - start_time) as f64) /
        1000000.0;
    let rays_per_s = 1000.0 * (rays as f64) / elapsed_time;
    println!("Traced {} rays in {} ms ({} rays per second).",
             rays, elapsed_time, rays_per_s);
}
<commit_msg>Refactor ray tracing loop in preparation for multithreading<commit_after>extern crate image;
extern crate time;

use std::iter;
use std::path::Path;

use vector::*;
use objects::*;
mod lighting;

pub type Image = image::RgbImage;

pub fn write_image(image: Image, filename: &str) {
    let _ = image.save(&Path::new(filename));
}

pub fn raytrace(scene: &Scene, width: u32, height: u32, h_fov: f64) {
    let f_width = width as f64;
    let f_height = height as f64;
    let v_fov = h_fov * (f_height / f_width);

    let vw_per_pixel = 2.0 * (h_fov / 2.0).tan() / f_width;
    let vh_per_pixel = 2.0 * (v_fov / 2.0).tan() / f_height;

    let norm_up = scene.camera.up.normalized();
    let norm_right = scene.camera.direction.cross(scene.camera.up).normalized();

    let mut image = image::RgbImage::new(width, height);
    let start_time = time::precise_time_ns();

    let mut rays: u64 = 0;

    // This is ugly, but splitting it out into a let binding makes the
    // compiler sad.
    for ((x, y), pixel) in (0..width).flat_map(|y| {
        iter::repeat(y).zip(0..height)
    }).map(|coord| {
        let (x, y) = coord;
        let cast_ray = |dx: f64, dy: f64, rays: &mut u64| {
            *rays += 1;
            let fi = (x as f64) + dx;
            let fj = (y as f64) + dy;

            let u_rel = vw_per_pixel * (fi - (f_width / 2.0));
            let v_rel = vh_per_pixel * (fj - (f_height / 2.0));

            let pixel_loc = scene.camera.position + scene.camera.direction +
                (norm_right * u_rel) + (norm_up * v_rel);

            let ray = Ray {
                origin: scene.camera.position,
                direction: pixel_loc - scene.camera.position
            };

            let (color, new_rays) = lighting::get_color(&scene, &ray, 3);
            *rays += new_rays;
            color
        };
        // This makes the compiler happy. I'm not really sure what's going
        // on though.
        let c1 = cast_ray(0.25, 0.25, &mut rays);
        let c2 = cast_ray(0.75, 0.25, &mut rays);
        let c3 = cast_ray(0.50, 0.50, &mut rays);
        let c4 = cast_ray(0.75, 0.75, &mut rays);
        let c5 = cast_ray(0.25, 0.75, &mut rays);

        (coord, (c1 + c2 + c3 + c4 + c5) / 5.0)
    }) {
        image.put_pixel(x as u32, y as u32, pixel.to_rgb());
    }

    write_image(image, "test.png");

    let elapsed_time = ((time::precise_time_ns() - start_time) as f64) /
        1000000.0;
    let rays_per_s = 1000.0 * (rays as f64) / elapsed_time;
    println!("Traced {} rays in {} ms ({} rays per second).",
             rays, elapsed_time, rays_per_s);
}
<|endoftext|>