<commit_before>extern crate chrono;
extern crate regex;
extern crate toml;

use self::chrono::{Duration, NaiveTime};
use self::regex::Regex;
use self::toml::Value as Toml;

use constant::CONFIG_FILE_NAME;
use error::{CannotHappenError, CliError, ConfigError, ConfigErrorKind};
use lib::io::*;
use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{Error as IoError, Read};
use std::path::PathBuf;
use std::str::FromStr;

const CONFIG_KEY_BURN_AFTER  : &'static str = "burn.after";
const CONFIG_KEY_SWEEP_PERIOD: &'static str = "sweep.period";
const CONFIG_KEY_SWEEP_TIME  : &'static str = "sweep.time";

const CONFIG_DEFAULT_VALUE_BURN_AFTER  : &'static str = "2 weeks";
const CONFIG_DEFAULT_VALUE_SWEEP_PERIOD: &'static str = "daily";
const CONFIG_DEFAULT_VALUE_SWEEP_TIME  : &'static str = "00:00";

pub enum ConfigKey {
    BurnAfter,
    SweepPeriod,
    SweepTime,
}

impl ConfigKey {
    pub fn from<S: AsRef<str>>(key: S) -> Result<ConfigKey, ConfigError> {
        match key.as_ref().trim() {
            CONFIG_KEY_BURN_AFTER   => Ok(ConfigKey::BurnAfter),
            CONFIG_KEY_SWEEP_PERIOD => Ok(ConfigKey::SweepPeriod),
            CONFIG_KEY_SWEEP_TIME   => Ok(ConfigKey::SweepTime),
            _                       => Err(ConfigError::new(ConfigErrorKind::InvalidKey)),
        }
    }

    fn to_str(&self) -> &str {
        match *self {
            ConfigKey::BurnAfter   => CONFIG_KEY_BURN_AFTER,
            ConfigKey::SweepPeriod => CONFIG_KEY_SWEEP_PERIOD,
            ConfigKey::SweepTime   => CONFIG_KEY_SWEEP_TIME,
        }
    }

    fn to_pair(&self) -> (&str, &str) {
        let key = self
            .to_str()
            .split('.')
            .collect::<Vec<&str>>();
        (key[0], key[1]) // unsafe!
    }
}

type EditableTomlCore = BTreeMap<String, BTreeMap<String, String>>;

#[derive(Clone)]
struct EditableToml(EditableTomlCore);

impl EditableToml {
    fn from(toml: Toml) -> Result<Self, ConfigError> {
        toml::decode::<EditableTomlCore>(toml)
            .map(|c| EditableToml(c))
            .ok_or(ConfigError::new(ConfigErrorKind::Something))
    }

    fn overwrite<CK: Borrow<ConfigKey>>(&mut self, key: CK, value: String) {
        let &mut EditableToml(ref mut core) = self;

        let (first, second) = key.borrow().to_pair();
        let second          = second.to_string();

        if core.contains_key(first) {
            core.get_mut(first).map(|e| e.insert(second, value));
        } else {
            let mut entry = BTreeMap::new();
            entry.insert(second, value);

            core.insert(first.to_string(), entry);
        }
    }

    fn to_toml(self) -> Toml {
        let EditableToml(core) = self;
        toml::encode(&core)
    }
}

#[derive(Debug)]
pub struct Config {
    toml: Toml,
}

impl Config {
    fn path() -> PathBuf {
        path_buf![super::working_dir(), CONFIG_FILE_NAME]
    }

    pub fn exist() -> bool {
        Self::path().is_file()
    }

    fn to_string(&self) -> String {
        toml::encode_str(&self.toml)
    }

    pub fn create(&self) -> Result<(), IoError> {
        print_with_tag(Tag::Info, format!("Create \"{}\" file", CONFIG_FILE_NAME));

        super::create_setting_file(Self::path(), self.to_string())
    }

    fn _new(toml: Toml) -> Self {
        Config { toml: toml }
    }

    pub fn default() -> Self {
        let mut editable = EditableToml(BTreeMap::new());
        editable.overwrite(ConfigKey::BurnAfter  , CONFIG_DEFAULT_VALUE_BURN_AFTER  .to_string());
        editable.overwrite(ConfigKey::SweepPeriod, CONFIG_DEFAULT_VALUE_SWEEP_PERIOD.to_string());
        editable.overwrite(ConfigKey::SweepTime  , CONFIG_DEFAULT_VALUE_SWEEP_TIME  .to_string());

        Self::_new(editable.to_toml())
    }

    pub fn read() -> Result<Self, CliError> {
        print_with_tag(Tag::Info, format!("Read \"{}\" file", CONFIG_FILE_NAME));

        let mut f = try!(File::open(Self::path()));

        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));

        let result = contents
            .parse()
            .map_err(|e: Vec<_>| e.into_iter().next());
        let toml = match result {
            Ok(toml)    => toml,
            Err(option) => match option {
                Some(e) => return Err(From::from(e)),
                None    => return Err(From::from(CannotHappenError)),
            },
        };

        Ok(Self::_new(toml))
    }

    pub fn get<CK: Borrow<ConfigKey>>(&self, key: CK) -> Result<String, CliError> {
        let key = key.borrow();

        let result = self.toml
            .lookup(key.to_str())
            .ok_or(ConfigError::new(match *key {
                ConfigKey::BurnAfter   => ConfigErrorKind::NotFoundBurnAfter,
                ConfigKey::SweepPeriod => unimplemented!(),
                ConfigKey::SweepTime   => unimplemented!(),
            }));
        let value = try!(result);

        value
            .as_str()
            .ok_or(From::from(ConfigError::new(ConfigErrorKind::NonStringValue)))
            .and_then(|s| Self::validate(key, s))
    }

    pub fn to_duration(value: String) -> Result<Duration, CliError> {
        let mut value   = value.split(' ');
        let (num, unit) = match (value.next(), value.next()) {
            (Some(num), Some(unit)) => (num, unit),
            _                       => return Err(From::from(CannotHappenError)),
        };
        let num = try!(num.parse::<u32>()) as i64;

        let duration = match unit {
            "day"  | "days"  => Duration::days(num),
            "week" | "weeks" => Duration::weeks(num),
            _                => return Err(From::from(CannotHappenError)),
        };

        Ok(duration)
    }

    pub fn set<CK: Borrow<ConfigKey>, S: AsRef<str>>(mut self, key: CK, value: S) -> Result<Self, CliError> {
        let value = try!(Self::validate(key.borrow(), value));

        let mut editable = try!(EditableToml::from(self.toml));

        editable.overwrite(key, value);

        self.toml = editable.to_toml();

        Ok(self)
    }

    fn validate<CK: Borrow<ConfigKey>, S: AsRef<str>>(key: CK, value: S) -> Result<String, CliError> {
        let value = value.as_ref().trim();

        match *key.borrow() {
            ConfigKey::BurnAfter => {
                let pair = try!(Regex::new(r"^(?P<num>\d+)\s?(?P<unit>days?|weeks?)$"))
                    .captures(value)
                    .map_or((None, None), |caps| (caps.name("num"), caps.name("unit")));
                let (num, unit) = match pair {
                    (Some(num), Some(unit)) => (num, unit),
                    _                       => return Err(From::from(ConfigError::new(ConfigErrorKind::BurnAfter))),
                };
                Ok(format!("{} {}", num, unit))
            },
            ConfigKey::SweepPeriod => {
                match value {
                    "daily" | "weekly" => Ok(value.to_string()),
                    _                  => Err(From::from(ConfigError::new(ConfigErrorKind::SweepPeriod))),
                }
            },
            ConfigKey::SweepTime => {
                match NaiveTime::from_str(format!("{}:00", value).as_ref()) {
                    Ok(_)  => Ok(value.to_string()),
                    // should set Err(e) to Error::cause()
                    Err(_) => Err(From::from(ConfigError::new(ConfigErrorKind::SweepTime))),
                }
            },
        }
    }
}

#[test]
fn config_key_to_pair_should_return_pair() {
    let keys = [
        (ConfigKey::BurnAfter  , CONFIG_KEY_BURN_AFTER  ),
        (ConfigKey::SweepPeriod, CONFIG_KEY_SWEEP_PERIOD),
        (ConfigKey::SweepTime  , CONFIG_KEY_SWEEP_TIME  ),
    ];
    for &(ref key, ref correct) in &keys {
        let (first, second) = key.to_pair();
        assert_eq!(correct.to_string(), format!("{}.{}", first, second));
    }
}

#[test]
fn editable_toml_overwrite_should_add_value() {
    let b_tree_map: EditableTomlCore = BTreeMap::new();
    let mut correct                  = b_tree_map.clone();
    let mut editable                 = EditableToml(b_tree_map);

    let v1 = "value1".to_string();

    let mut entry         = BTreeMap::new();
    let key1              = ConfigKey::BurnAfter;
    let (first1, second1) = key1.to_pair();
    entry.insert(second1.to_string(), v1.clone());
    correct.insert(first1.to_string(), entry);

    editable.overwrite(ConfigKey::BurnAfter, v1);
    let EditableToml(calculated) = editable.clone();

    assert_eq!(&correct, &calculated);

    let v2 = "value2".to_string();
    let v3 = "value3".to_string();

    let mut entry         = BTreeMap::new();
    let key2              = ConfigKey::SweepPeriod;
    let (first2, second2) = key2.to_pair();
    let key3              = ConfigKey::SweepTime;
    let (_, second3)      = key3.to_pair();
    entry.insert(second2.to_string(), v2.clone());
    entry.insert(second3.to_string(), v3.clone());
    correct.insert(first2.to_string(), entry);

    editable.overwrite(ConfigKey::SweepPeriod, v2);
    editable.overwrite(ConfigKey::SweepTime  , v3);
    let &EditableToml(ref calculated) = &editable;

    assert_eq!(&correct, calculated);
}

#[test]
fn default_should_return_config() {
    let correct: Toml = format!(
        r#"
            [burn]
            after = "{}"
            [sweep]
            period = "{}"
            time   = "{}"
        "#,
        CONFIG_DEFAULT_VALUE_BURN_AFTER,
        CONFIG_DEFAULT_VALUE_SWEEP_PERIOD,
        CONFIG_DEFAULT_VALUE_SWEEP_TIME
    )
        .parse()
        .unwrap();

    assert_eq!(correct, Config::default().toml);
}

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

    extern crate regex;

    use self::regex::Regex;

    use error::{CliError, ConfigError, ConfigErrorKind};
    use std::u32;

    #[test]
    fn get_should_return_ok() {
        let config = Config::default();

        assert!(config.get(ConfigKey::BurnAfter).is_ok());
    }
    #[test]
    #[should_panic(expected = "not yet implemented")]
    fn get_should_panic() {
        let config = Config::default();

        assert!(config.get(ConfigKey::SweepPeriod).is_ok());
        assert!(config.get(ConfigKey::SweepTime  ).is_ok());
    }

    #[test]
    fn set_should_return_ok() {
        let raw_values = [
            "1day"  .to_string(),
            "1days" .to_string(),
            "1 day" .to_string(),
            "1 days".to_string(),
            "1week"  .to_string(),
            "1weeks" .to_string(),
            "1 week" .to_string(),
            "1 weeks".to_string(),

            format!("{}day"  , u32::MAX),
            format!("{}days" , u32::MAX),
            format!("{} day" , u32::MAX),
            format!("{} days", u32::MAX),
            format!("{}week"  , u32::MAX),
            format!("{}weeks" , u32::MAX),
            format!("{} week" , u32::MAX),
            format!("{} weeks", u32::MAX),
        ];
        let corrects_and_inputs = raw_values
            .into_iter()
            .map(|i| {
                let re = Regex::new(r"(?P<num>\d+)(?P<unit>[^\d\s]+)").unwrap();
                (re.replace(i.as_ref(), "$num $unit"), i.clone())
            })
            .collect::<Vec<(String, String)>>();

        for &(ref correct, ref input) in &corrects_and_inputs {
            let config = Config::default()
                .set(ConfigKey::BurnAfter, input)
                .unwrap();
            assert_eq!(correct, &(config.get(ConfigKey::BurnAfter).unwrap()));
        }
    }
    #[test]
    #[should_panic(expected = "not yet implemented")]
    fn set_should_return_panic() {
        let raw_values = ["daily", "weekly"];
        for raw_value in &raw_values {
            let config = Config::default()
                .set(ConfigKey::SweepPeriod, &raw_value)
                .unwrap();
            assert_eq!(raw_value.to_string(), config.get(ConfigKey::SweepPeriod).unwrap())
        }

        let raw_values = ["00:00", "23:59"];
        for raw_value in &raw_values {
            let config = Config::default()
                .set(ConfigKey::SweepPeriod, &raw_value)
                .unwrap();
            assert_eq!(raw_value.to_string(), config.get(ConfigKey::SweepTime).unwrap())
        }
    }
    #[test]
    fn set_should_return_err() {
        let data_set = vec![
            (ConfigKey::BurnAfter, "1hour" , ConfigError::new(ConfigErrorKind::BurnAfter)),
            (ConfigKey::BurnAfter, "1month", ConfigError::new(ConfigErrorKind::BurnAfter)),
            (ConfigKey::BurnAfter, "1year" , ConfigError::new(ConfigErrorKind::BurnAfter)),
            (ConfigKey::BurnAfter, "-1day" , ConfigError::new(ConfigErrorKind::BurnAfter)),
            (ConfigKey::BurnAfter, "-1week", ConfigError::new(ConfigErrorKind::BurnAfter)),

            (ConfigKey::SweepPeriod, "day"    , ConfigError::new(ConfigErrorKind::SweepPeriod)),
            (ConfigKey::SweepPeriod, "week"   , ConfigError::new(ConfigErrorKind::SweepPeriod)),
            (ConfigKey::SweepPeriod, "hourly" , ConfigError::new(ConfigErrorKind::SweepPeriod)),
            (ConfigKey::SweepPeriod, "monthly", ConfigError::new(ConfigErrorKind::SweepPeriod)),
            (ConfigKey::SweepPeriod, "yearly" , ConfigError::new(ConfigErrorKind::SweepPeriod)),

            (ConfigKey::SweepTime, "-00:01"   , ConfigError::new(ConfigErrorKind::SweepTime)),
            (ConfigKey::SweepTime,  "24:00"   , ConfigError::new(ConfigErrorKind::SweepTime)),
            (ConfigKey::SweepTime,  "00"      , ConfigError::new(ConfigErrorKind::SweepTime)),
            (ConfigKey::SweepTime,  "00:00:00", ConfigError::new(ConfigErrorKind::SweepTime)),
        ];

        for (key, value, correct) in data_set.into_iter() {
            let cli_err = Config::default()
                .set(key, value)
                .unwrap_err();
            let err = match cli_err {
                CliError::Config(e) => e,
                _                   => panic!("unexpected error"),
            };
            assert_eq!(correct, err);
        }
    }

    #[test]
    fn to_duration_should_return_ok() {
    }
    #[test]
    fn to_duration_should_return_err() {
    }
}
<commit_msg>[Test] Use into_iter()<commit_after>extern crate chrono;
extern crate regex;
extern crate toml;

use self::chrono::{Duration, NaiveTime};
use self::regex::Regex;
use self::toml::Value as Toml;

use constant::CONFIG_FILE_NAME;
use error::{CannotHappenError, CliError, ConfigError, ConfigErrorKind};
use lib::io::*;
use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{Error as IoError, Read};
use std::path::PathBuf;
use std::str::FromStr;

const CONFIG_KEY_BURN_AFTER  : &'static str = "burn.after";
const CONFIG_KEY_SWEEP_PERIOD: &'static str = "sweep.period";
const CONFIG_KEY_SWEEP_TIME  : &'static str = "sweep.time";

const CONFIG_DEFAULT_VALUE_BURN_AFTER  : &'static str = "2 weeks";
const CONFIG_DEFAULT_VALUE_SWEEP_PERIOD: &'static str = "daily";
const CONFIG_DEFAULT_VALUE_SWEEP_TIME  : &'static str = "00:00";

pub enum ConfigKey {
    BurnAfter,
    SweepPeriod,
    SweepTime,
}

impl ConfigKey {
    pub fn from<S: AsRef<str>>(key: S) -> Result<ConfigKey, ConfigError> {
        match key.as_ref().trim() {
            CONFIG_KEY_BURN_AFTER   => Ok(ConfigKey::BurnAfter),
            CONFIG_KEY_SWEEP_PERIOD => Ok(ConfigKey::SweepPeriod),
            CONFIG_KEY_SWEEP_TIME   => Ok(ConfigKey::SweepTime),
            _                       => Err(ConfigError::new(ConfigErrorKind::InvalidKey)),
        }
    }

    fn to_str(&self) -> &str {
        match *self {
            ConfigKey::BurnAfter   => CONFIG_KEY_BURN_AFTER,
            ConfigKey::SweepPeriod => CONFIG_KEY_SWEEP_PERIOD,
            ConfigKey::SweepTime   => CONFIG_KEY_SWEEP_TIME,
        }
    }

    fn to_pair(&self) -> (&str, &str) {
        let key = self
            .to_str()
            .split('.')
            .collect::<Vec<&str>>();
        (key[0], key[1]) // unsafe!
    }
}

type EditableTomlCore = BTreeMap<String, BTreeMap<String, String>>;

#[derive(Clone)]
struct EditableToml(EditableTomlCore);

impl EditableToml {
    fn from(toml: Toml) -> Result<Self, ConfigError> {
        toml::decode::<EditableTomlCore>(toml)
            .map(|c| EditableToml(c))
            .ok_or(ConfigError::new(ConfigErrorKind::Something))
    }

    fn overwrite<CK: Borrow<ConfigKey>>(&mut self, key: CK, value: String) {
        let &mut EditableToml(ref mut core) = self;

        let (first, second) = key.borrow().to_pair();
        let second          = second.to_string();

        if core.contains_key(first) {
            core.get_mut(first).map(|e| e.insert(second, value));
        } else {
            let mut entry = BTreeMap::new();
            entry.insert(second, value);

            core.insert(first.to_string(), entry);
        }
    }

    fn to_toml(self) -> Toml {
        let EditableToml(core) = self;
        toml::encode(&core)
    }
}

#[derive(Debug)]
pub struct Config {
    toml: Toml,
}

impl Config {
    fn path() -> PathBuf {
        path_buf![super::working_dir(), CONFIG_FILE_NAME]
    }

    pub fn exist() -> bool {
        Self::path().is_file()
    }

    fn to_string(&self) -> String {
        toml::encode_str(&self.toml)
    }

    pub fn create(&self) -> Result<(), IoError> {
        print_with_tag(Tag::Info, format!("Create \"{}\" file", CONFIG_FILE_NAME));

        super::create_setting_file(Self::path(), self.to_string())
    }

    fn _new(toml: Toml) -> Self {
        Config { toml: toml }
    }

    pub fn default() -> Self {
        let mut editable = EditableToml(BTreeMap::new());
        editable.overwrite(ConfigKey::BurnAfter  , CONFIG_DEFAULT_VALUE_BURN_AFTER  .to_string());
        editable.overwrite(ConfigKey::SweepPeriod, CONFIG_DEFAULT_VALUE_SWEEP_PERIOD.to_string());
        editable.overwrite(ConfigKey::SweepTime  , CONFIG_DEFAULT_VALUE_SWEEP_TIME  .to_string());

        Self::_new(editable.to_toml())
    }

    pub fn read() -> Result<Self, CliError> {
        print_with_tag(Tag::Info, format!("Read \"{}\" file", CONFIG_FILE_NAME));

        let mut f = try!(File::open(Self::path()));

        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));

        let result = contents
            .parse()
            .map_err(|e: Vec<_>| e.into_iter().next());
        let toml = match result {
            Ok(toml)    => toml,
            Err(option) => match option {
                Some(e) => return Err(From::from(e)),
                None    => return Err(From::from(CannotHappenError)),
            },
        };

        Ok(Self::_new(toml))
    }

    pub fn get<CK: Borrow<ConfigKey>>(&self, key: CK) -> Result<String, CliError> {
        let key = key.borrow();

        let result = self.toml
            .lookup(key.to_str())
            .ok_or(ConfigError::new(match *key {
                ConfigKey::BurnAfter   => ConfigErrorKind::NotFoundBurnAfter,
                ConfigKey::SweepPeriod => unimplemented!(),
                ConfigKey::SweepTime   => unimplemented!(),
            }));
        let value = try!(result);

        value
            .as_str()
            .ok_or(From::from(ConfigError::new(ConfigErrorKind::NonStringValue)))
            .and_then(|s| Self::validate(key, s))
    }

    pub fn to_duration(value: String) -> Result<Duration, CliError> {
        let mut value   = value.split(' ');
        let (num, unit) = match (value.next(), value.next()) {
            (Some(num), Some(unit)) => (num, unit),
            _                       => return Err(From::from(CannotHappenError)),
        };
        let num = try!(num.parse::<u32>()) as i64;

        let duration = match unit {
            "day"  | "days"  => Duration::days(num),
            "week" | "weeks" => Duration::weeks(num),
            _                => return Err(From::from(CannotHappenError)),
        };

        Ok(duration)
    }

    pub fn set<CK: Borrow<ConfigKey>, S: AsRef<str>>(mut self, key: CK, value: S) -> Result<Self, CliError> {
        let value = try!(Self::validate(key.borrow(), value));

        let mut editable = try!(EditableToml::from(self.toml));

        editable.overwrite(key, value);

        self.toml = editable.to_toml();

        Ok(self)
    }

    fn validate<CK: Borrow<ConfigKey>, S: AsRef<str>>(key: CK, value: S) -> Result<String, CliError> {
        let value = value.as_ref().trim();

        match *key.borrow() {
            ConfigKey::BurnAfter => {
                let pair = try!(Regex::new(r"^(?P<num>\d+)\s?(?P<unit>days?|weeks?)$"))
                    .captures(value)
                    .map_or((None, None), |caps| (caps.name("num"), caps.name("unit")));
                let (num, unit) = match pair {
                    (Some(num), Some(unit)) => (num, unit),
                    _                       => return Err(From::from(ConfigError::new(ConfigErrorKind::BurnAfter))),
                };
                Ok(format!("{} {}", num, unit))
            },
            ConfigKey::SweepPeriod => {
                match value {
                    "daily" | "weekly" => Ok(value.to_string()),
                    _                  => Err(From::from(ConfigError::new(ConfigErrorKind::SweepPeriod))),
                }
            },
            ConfigKey::SweepTime => {
                match NaiveTime::from_str(format!("{}:00", value).as_ref()) {
                    Ok(_)  => Ok(value.to_string()),
                    // should set Err(e) to Error::cause()
                    Err(_) => Err(From::from(ConfigError::new(ConfigErrorKind::SweepTime))),
                }
            },
        }
    }
}

#[test]
fn config_key_to_pair_should_return_pair() {
    let keys = [
        (ConfigKey::BurnAfter  , CONFIG_KEY_BURN_AFTER  ),
        (ConfigKey::SweepPeriod, CONFIG_KEY_SWEEP_PERIOD),
        (ConfigKey::SweepTime  , CONFIG_KEY_SWEEP_TIME  ),
    ];
    for &(ref key, ref correct) in &keys {
        let (first, second) = key.to_pair();
        assert_eq!(correct.to_string(), format!("{}.{}", first, second));
    }
}

#[test]
fn editable_toml_overwrite_should_add_value() {
    let b_tree_map: EditableTomlCore = BTreeMap::new();
    let mut correct                  = b_tree_map.clone();
    let mut editable                 = EditableToml(b_tree_map);

    let v1 = "value1".to_string();

    let mut entry         = BTreeMap::new();
    let key1              = ConfigKey::BurnAfter;
    let (first1, second1) = key1.to_pair();
    entry.insert(second1.to_string(), v1.clone());
    correct.insert(first1.to_string(), entry);

    editable.overwrite(ConfigKey::BurnAfter, v1);
    let EditableToml(calculated) = editable.clone();

    assert_eq!(&correct, &calculated);

    let v2 = "value2".to_string();
    let v3 = "value3".to_string();

    let mut entry         = BTreeMap::new();
    let key2              = ConfigKey::SweepPeriod;
    let (first2, second2) = key2.to_pair();
    let key3              = ConfigKey::SweepTime;
    let (_, second3)      = key3.to_pair();
    entry.insert(second2.to_string(), v2.clone());
    entry.insert(second3.to_string(), v3.clone());
    correct.insert(first2.to_string(), entry);

    editable.overwrite(ConfigKey::SweepPeriod, v2);
    editable.overwrite(ConfigKey::SweepTime  , v3);
    let &EditableToml(ref calculated) = &editable;

    assert_eq!(&correct, calculated);
}

#[test]
fn default_should_return_config() {
    let correct: Toml = format!(
        r#"
            [burn]
            after = "{}"
            [sweep]
            period = "{}"
            time   = "{}"
        "#,
        CONFIG_DEFAULT_VALUE_BURN_AFTER,
        CONFIG_DEFAULT_VALUE_SWEEP_PERIOD,
        CONFIG_DEFAULT_VALUE_SWEEP_TIME
    )
        .parse()
        .unwrap();

    assert_eq!(correct, Config::default().toml);
}

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

    extern crate regex;

    use self::regex::Regex;

    use error::{CliError, ConfigError, ConfigErrorKind};
    use std::u32;

    #[test]
    fn get_should_return_ok() {
        let config = Config::default();

        assert!(config.get(ConfigKey::BurnAfter).is_ok());
    }
    #[test]
    #[should_panic(expected = "not yet implemented")]
    fn get_should_panic() {
        let config = Config::default();

        assert!(config.get(ConfigKey::SweepPeriod).is_ok());
        assert!(config.get(ConfigKey::SweepTime  ).is_ok());
    }

    #[test]
    fn set_should_return_ok() {
        let raw_values = vec![
            "1day"  .to_string(),
            "1days" .to_string(),
            "1 day" .to_string(),
            "1 days".to_string(),
            "1week"  .to_string(),
            "1weeks" .to_string(),
            "1 week" .to_string(),
            "1 weeks".to_string(),

            format!("{}day"  , u32::MAX),
            format!("{}days" , u32::MAX),
            format!("{} day" , u32::MAX),
            format!("{} days", u32::MAX),
            format!("{}week"  , u32::MAX),
            format!("{}weeks" , u32::MAX),
            format!("{} week" , u32::MAX),
            format!("{} weeks", u32::MAX),
        ];
        let corrects_and_inputs = raw_values
            .into_iter()
            .map(|i| {
                let re = Regex::new(r"(?P<num>\d+)(?P<unit>[^\d\s]+)").unwrap();
                (re.replace(i.as_ref(), "$num $unit"), i)
            })
            .collect::<Vec<(String, String)>>();

        for (correct, input) in corrects_and_inputs.into_iter() {
            let config = Config::default()
                .set(ConfigKey::BurnAfter, input)
                .unwrap();
            assert_eq!(correct, config.get(ConfigKey::BurnAfter).unwrap());
        }
    }
    #[test]
    #[should_panic(expected = "not yet implemented")]
    fn set_should_return_panic() {
        let raw_values = ["daily", "weekly"];
        for raw_value in &raw_values {
            let config = Config::default()
                .set(ConfigKey::SweepPeriod, &raw_value)
                .unwrap();
            assert_eq!(raw_value.to_string(), config.get(ConfigKey::SweepPeriod).unwrap())
        }

        let raw_values = ["00:00", "23:59"];
        for raw_value in &raw_values {
            let config = Config::default()
                .set(ConfigKey::SweepPeriod, &raw_value)
                .unwrap();
            assert_eq!(raw_value.to_string(), config.get(ConfigKey::SweepTime).unwrap())
        }
    }
    #[test]
    fn set_should_return_err() {
        let data_set = vec![
            (ConfigKey::BurnAfter, "1hour" , ConfigError::new(ConfigErrorKind::BurnAfter)),
            (ConfigKey::BurnAfter, "1month", ConfigError::new(ConfigErrorKind::BurnAfter)),
            (ConfigKey::BurnAfter, "1year" , ConfigError::new(ConfigErrorKind::BurnAfter)),
            (ConfigKey::BurnAfter, "-1day" , ConfigError::new(ConfigErrorKind::BurnAfter)),
            (ConfigKey::BurnAfter, "-1week", ConfigError::new(ConfigErrorKind::BurnAfter)),

            (ConfigKey::SweepPeriod, "day"    , ConfigError::new(ConfigErrorKind::SweepPeriod)),
            (ConfigKey::SweepPeriod, "week"   , ConfigError::new(ConfigErrorKind::SweepPeriod)),
            (ConfigKey::SweepPeriod, "hourly" , ConfigError::new(ConfigErrorKind::SweepPeriod)),
            (ConfigKey::SweepPeriod, "monthly", ConfigError::new(ConfigErrorKind::SweepPeriod)),
            (ConfigKey::SweepPeriod, "yearly" , ConfigError::new(ConfigErrorKind::SweepPeriod)),

            (ConfigKey::SweepTime, "-00:01"   , ConfigError::new(ConfigErrorKind::SweepTime)),
            (ConfigKey::SweepTime,  "24:00"   , ConfigError::new(ConfigErrorKind::SweepTime)),
            (ConfigKey::SweepTime,  "00"      , ConfigError::new(ConfigErrorKind::SweepTime)),
            (ConfigKey::SweepTime,  "00:00:00", ConfigError::new(ConfigErrorKind::SweepTime)),
        ];

        for (key, value, correct) in data_set.into_iter() {
            let cli_err = Config::default()
                .set(key, value)
                .unwrap_err();
            let err = match cli_err {
                CliError::Config(e) => e,
                _                   => panic!("unexpected error"),
            };
            assert_eq!(correct, err);
        }
    }

    #[test]
    fn to_duration_should_return_ok() {
    }
    #[test]
    fn to_duration_should_return_err() {
    }
}
<|endoftext|>


<commit_before>//! Implementation of Rust panics via process aborts
//!
//! When compared to the implementation via unwinding, this crate is *much*
//! simpler! That being said, it's not quite as versatile, but here goes!

#![no_std]
#![unstable(feature = "panic_abort", issue = "32837")]
#![doc(
    html_root_url = "https://doc.rust-lang.org/nightly/",
    issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/"
)]
#![panic_runtime]
#![allow(unused_features)]
#![feature(core_intrinsics)]
#![feature(libc)]
#![feature(nll)]
#![feature(panic_runtime)]
#![feature(staged_api)]
#![feature(rustc_attrs)]

use core::any::Any;

#[rustc_std_internal_symbol]
#[cfg_attr(not(bootstrap), allow(improper_ctypes_definitions))]
pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) {
    unreachable!()
}

// "Leak" the payload and shim to the relevant abort on the platform in
// question.
//
// For Unix we just use `abort` from libc as it'll trigger debuggers, core
// dumps, etc, as one might expect. On Windows, however, the best option we have
// is the `__fastfail` intrinsics, but that's unfortunately not defined in LLVM,
// and the `RaiseFailFastException` function isn't available until Windows 7
// which would break compat with XP. For now just use `intrinsics::abort` which
// will kill us with an illegal instruction, which will do a good enough job for
// now hopefully.
#[rustc_std_internal_symbol]
pub unsafe extern "C" fn __rust_start_panic(_payload: usize) -> u32 {
    abort();

    cfg_if::cfg_if! {
        if #[cfg(any(unix, target_os = "cloudabi"))] {
            unsafe fn abort() -> ! {
                libc::abort();
            }
        } else if #[cfg(any(target_os = "hermit",
                        all(target_vendor = "fortanix", target_env = "sgx")))] {
            unsafe fn abort() -> ! {
                // call std::sys::abort_internal
                extern "C" {
                    pub fn __rust_abort() -> !;
                }
                __rust_abort();
            }
        } else {
            unsafe fn abort() -> ! {
                core::intrinsics::abort();
            }
        }
    }
}

// This... is a bit of an oddity. The tl;dr; is that this is required to link
// correctly, the longer explanation is below.
//
// Right now the binaries of libcore/libstd that we ship are all compiled with
// `-C panic=unwind`. This is done to ensure that the binaries are maximally
// compatible with as many situations as possible. The compiler, however,
// requires a "personality function" for all functions compiled with `-C
// panic=unwind`. This personality function is hardcoded to the symbol
// `rust_eh_personality` and is defined by the `eh_personality` lang item.
//
// So... why not just define that lang item here? Good question! The way that
// panic runtimes are linked in is actually a little subtle in that they're
// "sort of" in the compiler's crate store, but only actually linked if another
// isn't actually linked. This ends up meaning that both this crate and the
// panic_unwind crate can appear in the compiler's crate store, and if both
// define the `eh_personality` lang item then that'll hit an error.
//
// To handle this the compiler only requires the `eh_personality` is defined if
// the panic runtime being linked in is the unwinding runtime, and otherwise
// it's not required to be defined (rightfully so). In this case, however, this
// library just defines this symbol so there's at least some personality
// somewhere.
//
// Essentially this symbol is just defined to get wired up to libcore/libstd
// binaries, but it should never be called as we don't link in an unwinding
// runtime at all.
pub mod personalities {
    #[rustc_std_internal_symbol]
    #[cfg(not(any(
        all(target_arch = "wasm32", not(target_os = "emscripten"),),
        all(target_os = "windows", target_env = "gnu", target_arch = "x86_64",),
    )))]
    pub extern "C" fn rust_eh_personality() {}

    // On x86_64-pc-windows-gnu we use our own personality function that needs
    // to return `ExceptionContinueSearch` as we're passing on all our frames.
    #[rustc_std_internal_symbol]
    #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86_64"))]
    pub extern "C" fn rust_eh_personality(
        _record: usize,
        _frame: usize,
        _context: usize,
        _dispatcher: usize,
    ) -> u32 {
        1 // `ExceptionContinueSearch`
    }

    // These two are called by our startup objects on i686-pc-windows-gnu, but
    // they don't need to do anything so the bodies are nops.
    #[rustc_std_internal_symbol]
    #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
    pub extern "C" fn rust_eh_register_frames() {}
    #[rustc_std_internal_symbol]
    #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
    pub extern "C" fn rust_eh_unregister_frames() {}
}
<commit_msg>Tweak formatting.<commit_after>//! Implementation of Rust panics via process aborts
//!
//! When compared to the implementation via unwinding, this crate is *much*
//! simpler! That being said, it's not quite as versatile, but here goes!

#![no_std]
#![unstable(feature = "panic_abort", issue = "32837")]
#![doc(
    html_root_url = "https://doc.rust-lang.org/nightly/",
    issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/"
)]
#![panic_runtime]
#![allow(unused_features)]
#![feature(core_intrinsics)]
#![feature(libc)]
#![feature(nll)]
#![feature(panic_runtime)]
#![feature(staged_api)]
#![feature(rustc_attrs)]

use core::any::Any;

#[rustc_std_internal_symbol]
#[cfg_attr(not(bootstrap), allow(improper_ctypes_definitions))]
pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) {
    unreachable!()
}

// "Leak" the payload and shim to the relevant abort on the platform in
// question.
//
// For Unix we just use `abort` from libc as it'll trigger debuggers, core
// dumps, etc, as one might expect. On Windows, however, the best option we have
// is the `__fastfail` intrinsics, but that's unfortunately not defined in LLVM,
// and the `RaiseFailFastException` function isn't available until Windows 7
// which would break compat with XP. For now just use `intrinsics::abort` which
// will kill us with an illegal instruction, which will do a good enough job for
// now hopefully.
#[rustc_std_internal_symbol]
pub unsafe extern "C" fn __rust_start_panic(_payload: usize) -> u32 {
    abort();

    cfg_if::cfg_if! {
        if #[cfg(any(unix, target_os = "cloudabi"))] {
            unsafe fn abort() -> ! {
                libc::abort();
            }
        } else if #[cfg(any(target_os = "hermit",
                            all(target_vendor = "fortanix", target_env = "sgx")
        ))] {
            unsafe fn abort() -> ! {
                // call std::sys::abort_internal
                extern "C" {
                    pub fn __rust_abort() -> !;
                }
                __rust_abort();
            }
        } else {
            unsafe fn abort() -> ! {
                core::intrinsics::abort();
            }
        }
    }
}

// This... is a bit of an oddity. The tl;dr; is that this is required to link
// correctly, the longer explanation is below.
//
// Right now the binaries of libcore/libstd that we ship are all compiled with
// `-C panic=unwind`. This is done to ensure that the binaries are maximally
// compatible with as many situations as possible. The compiler, however,
// requires a "personality function" for all functions compiled with `-C
// panic=unwind`. This personality function is hardcoded to the symbol
// `rust_eh_personality` and is defined by the `eh_personality` lang item.
//
// So... why not just define that lang item here? Good question! The way that
// panic runtimes are linked in is actually a little subtle in that they're
// "sort of" in the compiler's crate store, but only actually linked if another
// isn't actually linked. This ends up meaning that both this crate and the
// panic_unwind crate can appear in the compiler's crate store, and if both
// define the `eh_personality` lang item then that'll hit an error.
//
// To handle this the compiler only requires the `eh_personality` is defined if
// the panic runtime being linked in is the unwinding runtime, and otherwise
// it's not required to be defined (rightfully so). In this case, however, this
// library just defines this symbol so there's at least some personality
// somewhere.
//
// Essentially this symbol is just defined to get wired up to libcore/libstd
// binaries, but it should never be called as we don't link in an unwinding
// runtime at all.
pub mod personalities {
    #[rustc_std_internal_symbol]
    #[cfg(not(any(
        all(target_arch = "wasm32", not(target_os = "emscripten"),),
        all(target_os = "windows", target_env = "gnu", target_arch = "x86_64",),
    )))]
    pub extern "C" fn rust_eh_personality() {}

    // On x86_64-pc-windows-gnu we use our own personality function that needs
    // to return `ExceptionContinueSearch` as we're passing on all our frames.
    #[rustc_std_internal_symbol]
    #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86_64"))]
    pub extern "C" fn rust_eh_personality(
        _record: usize,
        _frame: usize,
        _context: usize,
        _dispatcher: usize,
    ) -> u32 {
        1 // `ExceptionContinueSearch`
    }

    // These two are called by our startup objects on i686-pc-windows-gnu, but
    // they don't need to do anything so the bodies are nops.
    #[rustc_std_internal_symbol]
    #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
    pub extern "C" fn rust_eh_register_frames() {}
    #[rustc_std_internal_symbol]
    #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
    pub extern "C" fn rust_eh_unregister_frames() {}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Rust Prelude
//!
//! Because `std` is required by most serious Rust software, it is
//! imported at the topmost level of every crate by default, as if
//! each crate contains the following:
//!
//! ```ignore
//! extern crate std;
//! ```
//!
//! This means that the contents of std can be accessed from any context
//! with the `std::` path prefix, as in `use std::vec`, `use std::thread::spawn`,
//! etc.
//!
//! Additionally, `std` contains a versioned *prelude* that reexports many of the
//! most common traits, types, and functions. *The contents of the prelude are
//! imported into every module by default*.  Implicitly, all modules behave as if
//! they contained the following [`use` statement][book-use]:
//!
//! [book-use]: ../../book/crates-and-modules.html#importing-modules-with-use
//!
//! ```ignore
//! use std::prelude::v1::*;
//! ```
//!
//! The prelude is primarily concerned with exporting *traits* that
//! are so pervasive that they would be onerous to import for every use,
//! particularly those that are commonly mentioned in [generic type
//! bounds][book-traits].
//!
//! The current version of the prelude (version 1) lives in
//! [`std::prelude::v1`](v1/index.html), and reexports the following.
//!
//! * `std::marker::`{
//!     [`Copy`](../marker/trait.Copy.html),
//!     [`Send`](../marker/trait.Send.html),
//!     [`Sized`](../marker/trait.Sized.html),
//!     [`Sync`](../marker/trait.Sync.html)
//!   }.
//!   The marker traits indicate fundamental properties of types.
//! * `std::ops::`{
//!     [`Drop`](../ops/trait.Drop.html),
//!     [`Fn`](../ops/trait.Fn.html),
//!     [`FnMut`](../ops/trait.FnMut.html),
//!     [`FnOnce`](../ops/trait.FnOnce.html)
//!   }.
//!   The [destructor][book-dtor] trait and the
//!   [closure][book-closures] traits, reexported from the same
//!   [module that also defines overloaded
//!   operators](../ops/index.html).
//! * `std::mem::`[`drop`](../mem/fn.drop.html).
//!   A convenience function for explicitly dropping a value.
//! * `std::boxed::`[`Box`](../boxed/struct.Box.html).
//!   The owned heap pointer.
//! * `std::borrow::`[`ToOwned`](../borrow/trait.ToOwned.html).
//!   The conversion trait that defines `to_owned`, the generic method
//!   for creating an owned type from a borrowed type.
//! * `std::clone::`[`Clone`](../clone/trait.Clone.html).
//!   The ubiquitous trait that defines `clone`, the method for
//!   producing copies of values that are consider expensive to copy.
//! * `std::cmp::`{
//!     [`PartialEq`](../cmp/trait.PartialEq.html),
//!     [`PartialOrd`](../cmp/trait.PartialOrd.html),
//!     [`Eq`](../cmp/trait.Eq.html),
//!     [`Ord`](../cmp/trait.Ord.html)
//!   }.
//!   The comparison traits, which implement the comparison operators
//!   and are often seen in trait bounds.
//! * `std::convert::`{
//!     [`AsRef`](../convert/trait.AsRef.html),
//!     [`AsMut`](../convert/trait.AsMut.html),
//!     [`Into`](../convert/trait.Into.html),
//!     [`From`](../convert/trait.From.html)
//!   }.
//!   Generic conversions, used by savvy API authors to create
//!   overloaded methods.
//! * `std::default::`[`Default`](../default/trait.Default).
//!   Types that have default values.
//! * `std::iter::`{
//!     [`Iterator`](../iter/trait.Iterator.html),
//!     [`Extend`](../iter/trait.Extend.html),
//!     [`IntoIterator`](../iter/trait.IntoIterator.html),
//!     [`DoubleEndedIterator`](../iter/trait.DoubleEndedIterator.html),
//!     [`ExactSizeIterator`](../iter/trait.ExactSizeIterator.html)
//!   }.
//!   [Iterators][book-iter].
//! * `std::option::Option::`{
//!     [`self`](../option/enum.Option.html),
//!     [`Some`](../option/enum.Option.html),
//!     [`None`](../option/enum.Option.html)
//!   }.
//!   The ubiquitous `Option` type and its two [variants][book-enums],
//!   `Some` and `None`.
//! * `std::result::Result::`{
//!     [`self`](../result/enum.Result.html),
//!     [`Ok`](../result/enum.Result.html),
//!     [`Err`](../result/enum.Result.html)
//!   }.
//!   The ubiquitous `Result` type and its two [variants][book-enums],
//!   `Ok` and `Err`.
//! * `std::slice::`[`SliceConcatExt`](../slice/trait.SliceConcatExt.html).
//!   An unstable extension to slices that shouldn't have to exist.
//! * `std::string::`{
//!     [`String`](../string/struct.String.html),
//!     [`ToString`](../string/trait.ToString.html)
//!   }.
//!   Heap allocated strings.
//! * `std::vec::`[`Vec`](../vec/struct.Vec.html).
//!   Heap allocated vectors.
//!
//! [book-traits]: ../../book/traits.html
//! [book-closures]: ../../book/closures.html
//! [book-dtor]: ../../book/drop.html
//! [book-iter]: ../../book/iterators.html
//! [book-enums]: ../../book/enums.html

#![stable(feature = "rust1", since = "1.0.0")]

pub mod v1;
<commit_msg>Fix the link to `Default` trait in the prelude docs<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Rust Prelude
//!
//! Because `std` is required by most serious Rust software, it is
//! imported at the topmost level of every crate by default, as if
//! each crate contains the following:
//!
//! ```ignore
//! extern crate std;
//! ```
//!
//! This means that the contents of std can be accessed from any context
//! with the `std::` path prefix, as in `use std::vec`, `use std::thread::spawn`,
//! etc.
//!
//! Additionally, `std` contains a versioned *prelude* that reexports many of the
//! most common traits, types, and functions. *The contents of the prelude are
//! imported into every module by default*.  Implicitly, all modules behave as if
//! they contained the following [`use` statement][book-use]:
//!
//! [book-use]: ../../book/crates-and-modules.html#importing-modules-with-use
//!
//! ```ignore
//! use std::prelude::v1::*;
//! ```
//!
//! The prelude is primarily concerned with exporting *traits* that
//! are so pervasive that they would be onerous to import for every use,
//! particularly those that are commonly mentioned in [generic type
//! bounds][book-traits].
//!
//! The current version of the prelude (version 1) lives in
//! [`std::prelude::v1`](v1/index.html), and reexports the following.
//!
//! * `std::marker::`{
//!     [`Copy`](../marker/trait.Copy.html),
//!     [`Send`](../marker/trait.Send.html),
//!     [`Sized`](../marker/trait.Sized.html),
//!     [`Sync`](../marker/trait.Sync.html)
//!   }.
//!   The marker traits indicate fundamental properties of types.
//! * `std::ops::`{
//!     [`Drop`](../ops/trait.Drop.html),
//!     [`Fn`](../ops/trait.Fn.html),
//!     [`FnMut`](../ops/trait.FnMut.html),
//!     [`FnOnce`](../ops/trait.FnOnce.html)
//!   }.
//!   The [destructor][book-dtor] trait and the
//!   [closure][book-closures] traits, reexported from the same
//!   [module that also defines overloaded
//!   operators](../ops/index.html).
//! * `std::mem::`[`drop`](../mem/fn.drop.html).
//!   A convenience function for explicitly dropping a value.
//! * `std::boxed::`[`Box`](../boxed/struct.Box.html).
//!   The owned heap pointer.
//! * `std::borrow::`[`ToOwned`](../borrow/trait.ToOwned.html).
//!   The conversion trait that defines `to_owned`, the generic method
//!   for creating an owned type from a borrowed type.
//! * `std::clone::`[`Clone`](../clone/trait.Clone.html).
//!   The ubiquitous trait that defines `clone`, the method for
//!   producing copies of values that are consider expensive to copy.
//! * `std::cmp::`{
//!     [`PartialEq`](../cmp/trait.PartialEq.html),
//!     [`PartialOrd`](../cmp/trait.PartialOrd.html),
//!     [`Eq`](../cmp/trait.Eq.html),
//!     [`Ord`](../cmp/trait.Ord.html)
//!   }.
//!   The comparison traits, which implement the comparison operators
//!   and are often seen in trait bounds.
//! * `std::convert::`{
//!     [`AsRef`](../convert/trait.AsRef.html),
//!     [`AsMut`](../convert/trait.AsMut.html),
//!     [`Into`](../convert/trait.Into.html),
//!     [`From`](../convert/trait.From.html)
//!   }.
//!   Generic conversions, used by savvy API authors to create
//!   overloaded methods.
//! * `std::default::`[`Default`](../default/trait.Default.html).
//!   Types that have default values.
//! * `std::iter::`{
//!     [`Iterator`](../iter/trait.Iterator.html),
//!     [`Extend`](../iter/trait.Extend.html),
//!     [`IntoIterator`](../iter/trait.IntoIterator.html),
//!     [`DoubleEndedIterator`](../iter/trait.DoubleEndedIterator.html),
//!     [`ExactSizeIterator`](../iter/trait.ExactSizeIterator.html)
//!   }.
//!   [Iterators][book-iter].
//! * `std::option::Option::`{
//!     [`self`](../option/enum.Option.html),
//!     [`Some`](../option/enum.Option.html),
//!     [`None`](../option/enum.Option.html)
//!   }.
//!   The ubiquitous `Option` type and its two [variants][book-enums],
//!   `Some` and `None`.
//! * `std::result::Result::`{
//!     [`self`](../result/enum.Result.html),
//!     [`Ok`](../result/enum.Result.html),
//!     [`Err`](../result/enum.Result.html)
//!   }.
//!   The ubiquitous `Result` type and its two [variants][book-enums],
//!   `Ok` and `Err`.
//! * `std::slice::`[`SliceConcatExt`](../slice/trait.SliceConcatExt.html).
//!   An unstable extension to slices that shouldn't have to exist.
//! * `std::string::`{
//!     [`String`](../string/struct.String.html),
//!     [`ToString`](../string/trait.ToString.html)
//!   }.
//!   Heap allocated strings.
//! * `std::vec::`[`Vec`](../vec/struct.Vec.html).
//!   Heap allocated vectors.
//!
//! [book-traits]: ../../book/traits.html
//! [book-closures]: ../../book/closures.html
//! [book-dtor]: ../../book/drop.html
//! [book-iter]: ../../book/iterators.html
//! [book-enums]: ../../book/enums.html

#![stable(feature = "rust1", since = "1.0.0")]

pub mod v1;
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GdkPixbufLoader — Application-driven progressive image loading.

use std;
use ffi;
use glib::{to_bool, GlibContainer, Error};
use glib::translate::ToGlibPtr;
use libc::{c_int, c_uint};

#[repr(C)]
pub struct PixbufLoader {
    pointer: *mut ffi::C_GdkPixbufLoader
}

impl PixbufLoader {
    /// Creates a new pixbuf loader object.
    ///
    /// # Failures
    /// Returns `None` if the pixbuf loader cannot be created.
    pub fn new() -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new() };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    /// Creates a new pixbuf loader object that always attempts to parse image
    /// data as if it were an image of type `image_type`, instead of
    /// identifying the type automatically. Useful if you want an error if the
    /// image isn't the expected type, for loading image formats that can't be
    /// reliably identified by looking at the data, or if the user manually
    /// forces a specific type.
    ///
    /// The list of supported image formats depends on what image loaders are
    /// installed, but typically "png", "jpeg", "gif", "tiff" and "xpm" are
    /// among the supported formats.
    ///
    /// # Failures
    /// Returns an `Error` if the pixbuf loader cannot be created. Query the
    /// error for more detailed information.
    ///
    /// # Panics
    /// Fails if the pixbuf loader cannot be retrieved.
    pub fn new_with_type(image_type: &str) -> Result<PixbufLoader, Error> {
        let mut error = std::ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_type(image_type.borrow_to_glib().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            Ok(PixbufLoader::wrap_pointer(tmp))
        } else {
            Err(Error::wrap(error))
        }
    }

    /// Creates a new pixbuf loader object that always attempts to parse image
    /// data as if it were an image of mime type `mime_type`, instead of
    /// identifying the type automatically. Useful if you want an error if the
    /// image isn't the expected mime type, for loading image formats that can't
    /// be reliably identified by looking at the data, or if the user manually
    /// forces a specific mime type.
    ///
    /// The list of supported mime types depends on what image loaders are
    /// installed, but typically "image/png", "image/jpeg", "image/gif",
    /// "image/tiff" and "image/x-xpixmap" are among the supported mime types.
    ///
    /// # Failures
    /// Returns an `Error` if the pixbuf loader cannot be created. Query the
    /// error for more detailed information.
    ///
    /// # Panics
    /// Fails if the pixbuf loader cannot be retrieved.
    pub fn new_with_mime_type(mime_type: &str) -> Result<PixbufLoader, Error> {
        let mut error = std::ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_mime_type(mime_type.borrow_to_glib().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            Ok(PixbufLoader::wrap_pointer(tmp))
        } else {
            Err(Error::wrap(error))
        }
    }

    /// Obtains the available information about the format of the currently
    /// loading image file.  Returns `None` if not enough data has been written
    /// to determine the format.
    pub fn get_format(&self) -> Option<::PixbufFormat> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_format(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::PixbufFormat::wrap_pointer(tmp))
        }
    }

    /// This will cause a pixbuf loader to parse the  bytes of an image stored
    /// in `buf`. It will return `Ok` if the data was loaded successfully, and
    /// `Err` if an error occurred. In the latter case, the loader will be
    /// closed, and will not accept further writes.
    ///
    /// # Failures
    /// Returns an `Error` if `buf` cannot be written to the loader.  Query the
    /// `Error` for more detailed information.
    pub fn loader_write(&self, buf: &[u8]) -> Result<(), Error> {
        unsafe {
            let mut error = std::ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_write(self.unwrap_pointer(), buf.as_ptr(), buf.len() as c_uint, &mut error)) {
                true => Ok(()),
                false => Err(Error::wrap(error))
            }
        }
    }

    /*pub fn loader_write_bytes(&self, buffer: &glib::Bytes, error: &mut Error) -> bool {
      gdk_pixbuf_loader_write_bytes
      }*/

    /// Causes the image to be scaled while it is loaded. The desired image
    /// size can be determined relative to the original size of the image by
    /// calling `set_size()` from a signal handler for the `size-prepared`
    /// signal.
    ///
    /// Attempts to set the desired image size are ignored after the emission
    /// of the `size-prepared` signal.
    pub fn set_size(&self, width: isize, height: isize) {
        unsafe { ffi::gdk_pixbuf_loader_set_size(self.unwrap_pointer(), width as c_int, height as c_int) }
    }

    /// Queries the `Pixbuf` that a pixbuf loader is currently creating. In
    /// general it only makes sense to call this function after the
    /// `area-prepared` signal has been emitted by the loader; this means that
    /// enough data has been read to know the size of the image that will be
    /// allocated. If the loader has not received enough data via
    /// `loader_write()`, then this function returns `None`. If the loader is
    /// an animation, it will return the "static image" of the animation.
    pub fn get_pixbuf(&self) -> Option<::Pixbuf> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_pixbuf(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::Pixbuf::wrap_pointer(tmp))
        }
    }

    /*pub fn get_animation(&self) -> Option<::PixbufAnimation> {
      let tmp = unsafe { ffi::gdk_pixbuf_loader_get_animation(self.unwrap_pointer()) };

      if tmp.is_null() {
      None
      } else {
      Some(::PixbufAnimation::wrap_pointer(tmp))
      }
      }*/

    /// Informs a pixbuf loader that no further writes with `loader_write()`
    /// will occur, so that it can free its internal loading structures. Also,
    /// tries to parse any data that hasn't yet been parsed.
    ///
    /// # Failures
    /// If the remaining data is partial or corrupt, an error will be returned.
    /// Query the `Error` for more detailed information.
    pub fn close(&self) -> Result<(), Error> {
        unsafe {
            let mut error = std::ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_close(self.unwrap_pointer(), &mut error)) {
                true => Ok(()),
                false => Err(Error::wrap(error))
            }
        }
    }
}

impl_GObjectFunctions!(PixbufLoader, C_GdkPixbufLoader);
<commit_msg>Minor namespace style changes<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GdkPixbufLoader — Application-driven progressive image loading.

use std::ptr;
use ffi;
use glib::{to_bool, GlibContainer, Error};
use glib::translate::ToGlibPtr;
use libc::{c_int, c_uint};

#[repr(C)]
pub struct PixbufLoader {
    pointer: *mut ffi::C_GdkPixbufLoader
}

impl PixbufLoader {
    /// Creates a new pixbuf loader object.
    ///
    /// # Failures
    /// Returns `None` if the pixbuf loader cannot be created.
    pub fn new() -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new() };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    /// Creates a new pixbuf loader object that always attempts to parse image
    /// data as if it were an image of type `image_type`, instead of
    /// identifying the type automatically. Useful if you want an error if the
    /// image isn't the expected type, for loading image formats that can't be
    /// reliably identified by looking at the data, or if the user manually
    /// forces a specific type.
    ///
    /// The list of supported image formats depends on what image loaders are
    /// installed, but typically "png", "jpeg", "gif", "tiff" and "xpm" are
    /// among the supported formats.
    ///
    /// # Failures
    /// Returns an `Error` if the pixbuf loader cannot be created. Query the
    /// error for more detailed information.
    ///
    /// # Panics
    /// Fails if the pixbuf loader cannot be retrieved.
    pub fn new_with_type(image_type: &str) -> Result<PixbufLoader, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_type(image_type.borrow_to_glib().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            Ok(PixbufLoader::wrap_pointer(tmp))
        } else {
            Err(Error::wrap(error))
        }
    }

    /// Creates a new pixbuf loader object that always attempts to parse image
    /// data as if it were an image of mime type `mime_type`, instead of
    /// identifying the type automatically. Useful if you want an error if the
    /// image isn't the expected mime type, for loading image formats that can't
    /// be reliably identified by looking at the data, or if the user manually
    /// forces a specific mime type.
    ///
    /// The list of supported mime types depends on what image loaders are
    /// installed, but typically "image/png", "image/jpeg", "image/gif",
    /// "image/tiff" and "image/x-xpixmap" are among the supported mime types.
    ///
    /// # Failures
    /// Returns an `Error` if the pixbuf loader cannot be created. Query the
    /// error for more detailed information.
    ///
    /// # Panics
    /// Fails if the pixbuf loader cannot be retrieved.
    pub fn new_with_mime_type(mime_type: &str) -> Result<PixbufLoader, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_mime_type(mime_type.borrow_to_glib().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            Ok(PixbufLoader::wrap_pointer(tmp))
        } else {
            Err(Error::wrap(error))
        }
    }

    /// Obtains the available information about the format of the currently
    /// loading image file.  Returns `None` if not enough data has been written
    /// to determine the format.
    pub fn get_format(&self) -> Option<::PixbufFormat> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_format(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::PixbufFormat::wrap_pointer(tmp))
        }
    }

    /// This will cause a pixbuf loader to parse the  bytes of an image stored
    /// in `buf`. It will return `Ok` if the data was loaded successfully, and
    /// `Err` if an error occurred. In the latter case, the loader will be
    /// closed, and will not accept further writes.
    ///
    /// # Failures
    /// Returns an `Error` if `buf` cannot be written to the loader.  Query the
    /// `Error` for more detailed information.
    pub fn loader_write(&self, buf: &[u8]) -> Result<(), Error> {
        unsafe {
            let mut error = ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_write(self.unwrap_pointer(), buf.as_ptr(), buf.len() as c_uint, &mut error)) {
                true => Ok(()),
                false => Err(Error::wrap(error))
            }
        }
    }

    /*pub fn loader_write_bytes(&self, buffer: &glib::Bytes, error: &mut Error) -> bool {
      gdk_pixbuf_loader_write_bytes
      }*/

    /// Causes the image to be scaled while it is loaded. The desired image
    /// size can be determined relative to the original size of the image by
    /// calling `set_size()` from a signal handler for the `size-prepared`
    /// signal.
    ///
    /// Attempts to set the desired image size are ignored after the emission
    /// of the `size-prepared` signal.
    pub fn set_size(&self, width: isize, height: isize) {
        unsafe { ffi::gdk_pixbuf_loader_set_size(self.unwrap_pointer(), width as c_int, height as c_int) }
    }

    /// Queries the `Pixbuf` that a pixbuf loader is currently creating. In
    /// general it only makes sense to call this function after the
    /// `area-prepared` signal has been emitted by the loader; this means that
    /// enough data has been read to know the size of the image that will be
    /// allocated. If the loader has not received enough data via
    /// `loader_write()`, then this function returns `None`. If the loader is
    /// an animation, it will return the "static image" of the animation.
    pub fn get_pixbuf(&self) -> Option<::Pixbuf> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_pixbuf(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::Pixbuf::wrap_pointer(tmp))
        }
    }

    /*pub fn get_animation(&self) -> Option<::PixbufAnimation> {
      let tmp = unsafe { ffi::gdk_pixbuf_loader_get_animation(self.unwrap_pointer()) };

      if tmp.is_null() {
      None
      } else {
      Some(::PixbufAnimation::wrap_pointer(tmp))
      }
      }*/

    /// Informs a pixbuf loader that no further writes with `loader_write()`
    /// will occur, so that it can free its internal loading structures. Also,
    /// tries to parse any data that hasn't yet been parsed.
    ///
    /// # Failures
    /// If the remaining data is partial or corrupt, an error will be returned.
    /// Query the `Error` for more detailed information.
    pub fn close(&self) -> Result<(), Error> {
        unsafe {
            let mut error = ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_close(self.unwrap_pointer(), &mut error)) {
                true => Ok(()),
                false => Err(Error::wrap(error))
            }
        }
    }
}

impl_GObjectFunctions!(PixbufLoader, C_GdkPixbufLoader);
<|endoftext|>


<commit_before>use std::io::Result;
use std::result;
use std::collections::HashMap;
use std::vec::Vec;
use std::cmp::{Eq, PartialEq};
use std::hash::{Hash, Hasher};

type Pixels = usize;
type FileName = String;
type FilePath = String;

#[derive(Clone, Eq, PartialEq, Hash)]
enum ResolutionUnits {
    PixelsPerInch,
    PixelsPerCentimeter,
}

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

#[derive(Clone, Eq, PartialEq, Hash)]
enum ImageFileFormat {
    TIFF,
    PNG,
    JPEG,
    UNKNOWN,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageDimensions {
    xPixels: Pixels,
    yPixels: Pixels,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageResolution {
    amount: Pixels,
    units: ResolutionUnits,
}

impl ImageResolution {
    fn new(amount: usize, units: ResolutionUnits) -> ImageResolution {
        ImageResolution {
            amount: amount,
            units: units,
        }
    }
}

#[derive(Clone)]
enum PageOps {
    Identify(FilePath),
    Rescale(Pixels, Direction),
    ExpandLeftEdge(Pixels),
    ExpandRightEdge(Pixels),
    ExpandTopEdge(Pixels),
    ExpandBottomEdge(Pixels),
    TrimLeftEdge(Pixels),
    TrimRightEdge(Pixels),
    TrimTopEdge(Pixels),
    TrimBottomEdge(Pixels),
    SetResolution(ImageResolution),
}

trait ElementaryPageOperations {
    fn identify(path: FilePath)                -> Result<String>;
    fn rescale(amount: Pixels, dir: Direction) -> Result<String>;
    fn expand_left_edge(amount: Pixels)        -> Result<String>;
    fn expand_right_edge(amount: Pixels)       -> Result<String>;
    fn expand_top_edge(amount: Pixels)         -> Result<String>;
    fn expand_bottom_edge(amount: Pixels)      -> Result<String>;
    fn trim_left_edge(amount: Pixels)          -> Result<String>;
    fn trim_right_edge(amount: Pixels)         -> Result<String>;
    fn trim_top_edge(amount: Pixels)           -> Result<String>;
    fn trim_bottom_edge(amount: Pixels)        -> Result<String>;
    fn set_resolution(res: ImageResolution)    -> Result<String>;
}

impl ElementaryPageOperations {
    fn run_operation<Op>(page_op: PageOps) -> Result<String>
        where Op: ElementaryPageOperations {

        match page_op {
            PageOps::Identify(path)           => Op::identify(path),
            PageOps::Rescale(amount, dir)     => Op::rescale(amount, dir),
            PageOps::ExpandLeftEdge(amount)   => Op::expand_left_edge(amount),
            PageOps::ExpandRightEdge(amount)  => Op::expand_right_edge(amount),
            PageOps::ExpandTopEdge(amount)    => Op::expand_top_edge(amount),
            PageOps::ExpandBottomEdge(amount) => Op::expand_bottom_edge(amount),
            PageOps::TrimLeftEdge(amount)     => Op::trim_left_edge(amount),
            PageOps::TrimRightEdge(amount)    => Op::trim_right_edge(amount),
            PageOps::TrimTopEdge(amount)      => Op::trim_top_edge(amount),
            PageOps::TrimBottomEdge(amount)   => Op::trim_bottom_edge(amount),
            PageOps::SetResolution(res)       => Op::set_resolution(res),
        }
    }
}

#[derive(Clone)]
struct CompoundPageOperation {
    page_name: FileName,
    page_path: FilePath,
    ops: Vec<PageOps>,
}

impl CompoundPageOperation {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOperation {
        let mut vec = Vec::new();
        for op in ops.iter() {
            vec.push(op.clone());
        }

        CompoundPageOperation {
            page_name: page_name,
            page_path: page_path,
            ops: vec
        }
    }

    fn run_operation<Op>(&self) -> Result<String>
        where Op: ElementaryPageOperations {

        unimplemented!();
/*
        for op in self.ops {
            Op::run_operation(op)
        }
*/
    }
}

#[derive(Clone, Eq)]
struct Page {
    file_name: FileName,
    file_extension: ImageFileFormat,
    file_path: FilePath,
    dimensions: ImageDimensions,
    resolution: ImageResolution,
}

impl Page {
    fn new  (
                file_name: FileName,
                file_extension: ImageFileFormat,
                file_path: FilePath,
                dimensions: ImageDimensions,
                resolution: ImageResolution
            ) -> Page
    {
        Page {
            file_name: file_name,
            file_extension: file_extension,
            file_path: file_path,
            dimensions: dimensions,
            resolution: resolution,
        }
    }
}

impl PartialEq for Page {
    fn eq(&self, other: &Page) -> bool {
            self.file_name      == other.file_name
        &&  self.file_extension == other.file_extension
        &&  self.file_path      == other.file_path
        &&  self.dimensions     == other.dimensions
        &&  self.resolution     == other.resolution
    }
}

impl Hash for Page {
    fn hash<H>(&self, state: &mut H)
        where H: Hasher {

        self.file_name.hash(state);
        self.file_extension.hash(state);
        self.file_path.hash(state);
        self.dimensions.hash(state);
        self.resolution.hash(state);
    }
}

struct OperationSchedule {
    schedule: HashMap<Page, CompoundPageOperation>,
}

struct OperationResult {
    results: Vec<Result<String>>,
}

impl OperationSchedule {
    fn new() -> Self {
        OperationSchedule {
            schedule: HashMap::new(),
        }
    }

    fn add_action(&mut self, page: Page, op: CompoundPageOperation) {
        self.schedule.insert(page, op);
    }

    fn build_schedule(pages: &[Page], ops: &[CompoundPageOperation]) -> Result<Self, ()> {
        if pages.len() == ops.len() {
            for page_number in 0..pages.len() {
                self.add_action(pages[page_number].clone(), ops[page_number].clone());
            }
        } else {
            ()
        }

    }

    fn run_schedule(schedule: OperationSchedule) -> OperationResult {
        unimplemented!();
    }

}

<commit_msg>Fix compiler warnings.<commit_after>use std::io::Result as IoResult;
use std::result::Result;
use std::collections::HashMap;
use std::vec::Vec;
use std::cmp::{Eq, PartialEq};
use std::hash::{Hash, Hasher};

type Pixels = usize;
type FileName = String;
type FilePath = String;

#[derive(Clone, Eq, PartialEq, Hash)]
enum ResolutionUnits {
    PixelsPerInch,
    PixelsPerCentimeter,
}

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

#[derive(Clone, Eq, PartialEq, Hash)]
enum ImageFileFormat {
    TIFF,
    PNG,
    JPEG,
    UNKNOWN,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageDimensions {
    xPixels: Pixels,
    yPixels: Pixels,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageResolution {
    amount: Pixels,
    units: ResolutionUnits,
}

impl ImageResolution {
    fn new(amount: usize, units: ResolutionUnits) -> ImageResolution {
        ImageResolution {
            amount: amount,
            units: units,
        }
    }
}

#[derive(Clone)]
enum PageOps {
    Identify(FilePath),
    Rescale(Pixels, Direction),
    ExpandLeftEdge(Pixels),
    ExpandRightEdge(Pixels),
    ExpandTopEdge(Pixels),
    ExpandBottomEdge(Pixels),
    TrimLeftEdge(Pixels),
    TrimRightEdge(Pixels),
    TrimTopEdge(Pixels),
    TrimBottomEdge(Pixels),
    SetResolution(ImageResolution),
}

trait ElementaryPageOperations {
    fn identify(path: FilePath)                -> IoResult<String>;
    fn rescale(amount: Pixels, dir: Direction) -> IoResult<String>;
    fn expand_left_edge(amount: Pixels)        -> IoResult<String>;
    fn expand_right_edge(amount: Pixels)       -> IoResult<String>;
    fn expand_top_edge(amount: Pixels)         -> IoResult<String>;
    fn expand_bottom_edge(amount: Pixels)      -> IoResult<String>;
    fn trim_left_edge(amount: Pixels)          -> IoResult<String>;
    fn trim_right_edge(amount: Pixels)         -> IoResult<String>;
    fn trim_top_edge(amount: Pixels)           -> IoResult<String>;
    fn trim_bottom_edge(amount: Pixels)        -> IoResult<String>;
    fn set_resolution(res: ImageResolution)    -> IoResult<String>;
}

fn run_operation<Op>(page_op: PageOps) -> IoResult<String>
        where Op: ElementaryPageOperations {

        match page_op {
            PageOps::Identify(path)           => Op::identify(path),
            PageOps::Rescale(amount, dir)     => Op::rescale(amount, dir),
            PageOps::ExpandLeftEdge(amount)   => Op::expand_left_edge(amount),
            PageOps::ExpandRightEdge(amount)  => Op::expand_right_edge(amount),
            PageOps::ExpandTopEdge(amount)    => Op::expand_top_edge(amount),
            PageOps::ExpandBottomEdge(amount) => Op::expand_bottom_edge(amount),
            PageOps::TrimLeftEdge(amount)     => Op::trim_left_edge(amount),
            PageOps::TrimRightEdge(amount)    => Op::trim_right_edge(amount),
            PageOps::TrimTopEdge(amount)      => Op::trim_top_edge(amount),
            PageOps::TrimBottomEdge(amount)   => Op::trim_bottom_edge(amount),
            PageOps::SetResolution(res)       => Op::set_resolution(res),
        }
    }

#[derive(Clone)]
struct CompoundPageOperation {
    page_name: FileName,
    page_path: FilePath,
    ops: Vec<PageOps>,
}

impl CompoundPageOperation {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOperation {
        let mut vec = Vec::new();
        for op in ops.iter() {
            vec.push(op.clone());
        }

        CompoundPageOperation {
            page_name: page_name,
            page_path: page_path,
            ops: vec
        }
    }

    fn run_operation<Op>(&self) -> IoResult<String>
        where Op: ElementaryPageOperations {

        unimplemented!();
/*
        for op in self.ops {
            Op::run_operation(op)
        }
*/
    }
}

#[derive(Clone, Eq)]
struct Page {
    file_name: FileName,
    file_extension: ImageFileFormat,
    file_path: FilePath,
    dimensions: ImageDimensions,
    resolution: ImageResolution,
}

impl Page {
    fn new  (
                file_name: FileName,
                file_extension: ImageFileFormat,
                file_path: FilePath,
                dimensions: ImageDimensions,
                resolution: ImageResolution
            ) -> Page
    {
        Page {
            file_name: file_name,
            file_extension: file_extension,
            file_path: file_path,
            dimensions: dimensions,
            resolution: resolution,
        }
    }
}

impl PartialEq for Page {
    fn eq(&self, other: &Page) -> bool {
            self.file_name      == other.file_name
        &&  self.file_extension == other.file_extension
        &&  self.file_path      == other.file_path
        &&  self.dimensions     == other.dimensions
        &&  self.resolution     == other.resolution
    }
}

impl Hash for Page {
    fn hash<H>(&self, state: &mut H)
        where H: Hasher {

        self.file_name.hash(state);
        self.file_extension.hash(state);
        self.file_path.hash(state);
        self.dimensions.hash(state);
        self.resolution.hash(state);
    }
}

struct OperationSchedule {
    schedule: HashMap<Page, CompoundPageOperation>,
}

struct OperationResult {
    results: Vec<IoResult<String>>,
}

enum OperationScheduleError {
    LengthMismatch,
}

impl OperationSchedule {
    fn new() -> Self {
        OperationSchedule {
            schedule: HashMap::new(),
        }
    }

    fn add_action(&mut self, page: Page, op: CompoundPageOperation) {
        self.schedule.insert(page, op);
    }

    fn build_schedule(pages: &[Page], ops: &[CompoundPageOperation]) -> Result<Self, OperationScheduleError> {
        if pages.len() == ops.len() {

            let mut schedule = OperationSchedule::new();

            for page_number in 0..pages.len() {
                schedule.add_action(pages[page_number].clone(), ops[page_number].clone());
            }

            Ok(schedule)

        } else {
            Err(OperationScheduleError::LengthMismatch)
        }

    }

    fn run_schedule(schedule: &OperationSchedule) -> OperationResult {
        unimplemented!();
    }

}

<|endoftext|>


<commit_before>use runtime::Runtime;
use storage::backend::StorageBackend;

use module::Module;
use module::CommandResult;
use module::CommandEnv;

use module::bm::header::build_header;
use storage::json::parser::JsonHeaderParser;
use storage::parser::{Parser, FileHeaderParser};
use storage::file::File;

use clap::ArgMatches;
use regex::Regex;

pub fn add_command(module: &Module, env: CommandEnv) -> CommandResult {
    let url = env.matches.value_of("url").unwrap();
    let tags = get_tags(env.rt, env.matches);
    info!("Adding url '{}' with tags '{:?}'", url, tags);

    let header  = build_header(&String::from(url), &tags);
    let file    = File::new_with_header(module, header);
    let parser  = Parser::new(JsonHeaderParser::new(None));
    let putres  = env.bk.put_file(file, &parser);

    Ok(())
}

pub fn list_command(module: &Module, env: CommandEnv) -> CommandResult {
    let tags    = get_tags(env.rt, env.matches);
    let matcher = get_matcher(env.rt, env.matches);

    match matcher {
        Some(reg) => {
            info!("Listing urls with matcher '{}' and with tags {:?}",
                     reg.as_str(),
                     tags);
        }
        None => {
            info!("Listing urls with tags {:?}", tags);
        }
    }

    Ok(())
}

pub fn remove_command(module: &Module, env: CommandEnv) -> CommandResult {
    let tags    = get_tags(env.rt, env.matches);
    let matcher = get_matcher(env.rt, env.matches);
    let id      = get_id(env.rt, env.matches);

    match id {
        Some(idstr) => {
            info!("Removing urls with id '{}'", idstr);
        }
        None => {
            match matcher {
                Some(reg) => {
                    info!("Removing urls with matcher '{}' and with tags {:?}",
                             reg.as_str(), tags);
                }
                None => {
                    info!("Listing urls with tags {:?}", tags);
                }
            }
        }
    }

    Ok(())
}

/*
 *
 * Private helpers
 *
 */

fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Vec<String> {
    debug!("Fetching tags from commandline");
    sub.value_of("tags").and_then(|tags|
                                  Some(tags.split(",")
                                       .into_iter()
                                       .map(|s| s.to_string())
                                       .filter(|e|
                                            if e.contains(" ") {
                                                warn!("Tag contains spaces: '{}'", e);
                                                false
                                            } else {
                                                true
                                            }).collect()
                                      )
                                 ).or(Some(vec![])).unwrap()

}

fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
    debug!("Fetching matcher from commandline");
    if let Some(s) = sub.value_of("match") {
        if let Ok(r) = Regex::new(s) {
            return Some(r)
        } else {
            error!("Regex error, continuing without regex");
        }
    }
    None

}

fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
    debug!("Fetching id from commandline");
    sub.value_of("id").and_then(|s| Some(String::from(s)))
}

<commit_msg>Build ModuleError out of StorageBackendError if any<commit_after>use runtime::Runtime;
use storage::backend::StorageBackend;

use module::Module;
use module::ModuleError;
use module::CommandResult;
use module::CommandEnv;

use module::bm::header::build_header;
use storage::json::parser::JsonHeaderParser;
use storage::parser::{Parser, FileHeaderParser};
use storage::file::File;

use clap::ArgMatches;
use regex::Regex;

pub fn add_command(module: &Module, env: CommandEnv) -> CommandResult {
    let url = env.matches.value_of("url").unwrap();
    let tags = get_tags(env.rt, env.matches);
    info!("Adding url '{}' with tags '{:?}'", url, tags);

    let header  = build_header(&String::from(url), &tags);
    let file    = File::new_with_header(module, header);
    let parser  = Parser::new(JsonHeaderParser::new(None));
    let putres  = env.bk.put_file(file, &parser);

    putres.map_err(|sberr| {
        let mut err = ModuleError::new("Storage Backend Error");
        err.caused_by = Some(Box::new(sberr));
        err
    })
}

pub fn list_command(module: &Module, env: CommandEnv) -> CommandResult {
    let tags    = get_tags(env.rt, env.matches);
    let matcher = get_matcher(env.rt, env.matches);

    match matcher {
        Some(reg) => {
            info!("Listing urls with matcher '{}' and with tags {:?}",
                     reg.as_str(),
                     tags);
        }
        None => {
            info!("Listing urls with tags {:?}", tags);
        }
    }

    Ok(())
}

pub fn remove_command(module: &Module, env: CommandEnv) -> CommandResult {
    let tags    = get_tags(env.rt, env.matches);
    let matcher = get_matcher(env.rt, env.matches);
    let id      = get_id(env.rt, env.matches);

    match id {
        Some(idstr) => {
            info!("Removing urls with id '{}'", idstr);
        }
        None => {
            match matcher {
                Some(reg) => {
                    info!("Removing urls with matcher '{}' and with tags {:?}",
                             reg.as_str(), tags);
                }
                None => {
                    info!("Listing urls with tags {:?}", tags);
                }
            }
        }
    }

    Ok(())
}

/*
 *
 * Private helpers
 *
 */

fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Vec<String> {
    debug!("Fetching tags from commandline");
    sub.value_of("tags").and_then(|tags|
                                  Some(tags.split(",")
                                       .into_iter()
                                       .map(|s| s.to_string())
                                       .filter(|e|
                                            if e.contains(" ") {
                                                warn!("Tag contains spaces: '{}'", e);
                                                false
                                            } else {
                                                true
                                            }).collect()
                                      )
                                 ).or(Some(vec![])).unwrap()

}

fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
    debug!("Fetching matcher from commandline");
    if let Some(s) = sub.value_of("match") {
        if let Ok(r) = Regex::new(s) {
            return Some(r)
        } else {
            error!("Regex error, continuing without regex");
        }
    }
    None

}

fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
    debug!("Fetching id from commandline");
    sub.value_of("id").and_then(|s| Some(String::from(s)))
}

<|endoftext|>