<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 = 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() {
    }
}
<commit_msg>[Remedy] Output message before get and set the parameter<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();

        print_with_tag(Tag::Info, format!("Get the parameter for \"{}\"", key.to_str()));

        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 key = key.borrow();

        print_with_tag(Tag::Info, format!("Set the parameter for \"{}\"", key.to_str()));

        let value = try!(Self::validate(key, 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>#![feature(old_path)]

extern crate piston;
extern crate ai_behavior;
extern crate sprite;
extern crate graphics;
extern crate sdl2_window;
extern crate opengl_graphics;

use std::cell::RefCell;
use std::rc::Rc;

use sprite::*;
use ai_behavior::{
    Action,
    Sequence,
    Wait,
    WaitForever,
    While,
};

use sdl2_window::Sdl2Window;
use opengl_graphics::{
    GlGraphics,
    OpenGL,
    Texture,
};

fn main() {
    let (width, height) = (300, 300);
    let opengl = OpenGL::_3_2;
    let window = Sdl2Window::new(
        opengl,
        piston::window::WindowSettings {
            title: "Sprite".to_string(),
            size: [width, height],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    let mut scene = Scene::new();
    let tex = Path::new("./rust-logo.png");
    let tex = Rc::new(Texture::from_path(&tex).unwrap());
    let mut sprite = Sprite::from_texture(tex.clone());
    sprite.set_position(width as f64 / 2.0, height as f64 / 2.0);

    let id = scene.add_child(sprite);

    // Run a sequence or animations.
    let seq = Sequence(vec![
        Action(Ease(EaseFunction::CubicOut, Box::new(ScaleTo(2.0, 0.5, 0.5)))),
        Action(Ease(EaseFunction::BounceOut, Box::new(MoveBy(1.0, 0.0, 100.0)))),
        Action(Ease(EaseFunction::ElasticOut, Box::new(MoveBy(2.0, 0.0, -100.0)))),
        Action(Ease(EaseFunction::BackInOut, Box::new(MoveBy(1.0, 0.0, -100.0)))),
        Wait(0.5),
        Action(Ease(EaseFunction::ExponentialInOut, Box::new(MoveBy(1.0, 0.0, 100.0)))),
        Action(Blink(1.0, 5)),
        While(Box::new(WaitForever), vec![
            Action(Ease(EaseFunction::QuadraticIn, Box::new(FadeOut(1.0)))),
            Action(Ease(EaseFunction::QuadraticOut, Box::new(FadeIn(1.0)))),
        ]),
    ]);
    scene.run(id.clone(), &seq);

    // This animation and the one above can run in parallel.
    let rotate = Action(Ease(EaseFunction::ExponentialInOut,
        Box::new(RotateTo(2.0, 360.0))));
    scene.run(id.clone(), &rotate);

    println!("Press any key to pause/resume the animation!");

    let ref mut gl = GlGraphics::new(opengl);
    let window = RefCell::new(window);
    for e in piston::events(&window) {
        use piston::event::{ PressEvent, RenderEvent };

        scene.event(&e);

        if let Some(args) = e.render_args() {
            use graphics::*;
            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
                graphics::clear([1.0, 1.0, 1.0, 1.0], gl);
                scene.draw(&c, gl);
            });
        }
        if let Some(_) = e.press_args() {
            scene.toggle(id.clone(), &seq);
            scene.toggle(id.clone(), &rotate);
        }
    }
}
<commit_msg>Upgrade "sprite" example to latest piston-graphics<commit_after>#![feature(old_path)]

extern crate piston;
extern crate ai_behavior;
extern crate sprite;
extern crate graphics;
extern crate sdl2_window;
extern crate opengl_graphics;

use std::cell::RefCell;
use std::rc::Rc;

use sprite::*;
use ai_behavior::{
    Action,
    Sequence,
    Wait,
    WaitForever,
    While,
};

use sdl2_window::Sdl2Window;
use opengl_graphics::{
    GlGraphics,
    OpenGL,
    Texture,
};

fn main() {
    let (width, height) = (300, 300);
    let opengl = OpenGL::_3_2;
    let window = Sdl2Window::new(
        opengl,
        piston::window::WindowSettings {
            title: "Sprite".to_string(),
            size: [width, height],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    let mut scene = Scene::new();
    let tex = Path::new("./rust-logo.png");
    let tex = Rc::new(Texture::from_path(&tex).unwrap());
    let mut sprite = Sprite::from_texture(tex.clone());
    sprite.set_position(width as f64 / 2.0, height as f64 / 2.0);

    let id = scene.add_child(sprite);

    // Run a sequence or animations.
    let seq = Sequence(vec![
        Action(Ease(EaseFunction::CubicOut, Box::new(ScaleTo(2.0, 0.5, 0.5)))),
        Action(Ease(EaseFunction::BounceOut, Box::new(MoveBy(1.0, 0.0, 100.0)))),
        Action(Ease(EaseFunction::ElasticOut, Box::new(MoveBy(2.0, 0.0, -100.0)))),
        Action(Ease(EaseFunction::BackInOut, Box::new(MoveBy(1.0, 0.0, -100.0)))),
        Wait(0.5),
        Action(Ease(EaseFunction::ExponentialInOut, Box::new(MoveBy(1.0, 0.0, 100.0)))),
        Action(Blink(1.0, 5)),
        While(Box::new(WaitForever), vec![
            Action(Ease(EaseFunction::QuadraticIn, Box::new(FadeOut(1.0)))),
            Action(Ease(EaseFunction::QuadraticOut, Box::new(FadeIn(1.0)))),
        ]),
    ]);
    scene.run(id.clone(), &seq);

    // This animation and the one above can run in parallel.
    let rotate = Action(Ease(EaseFunction::ExponentialInOut,
        Box::new(RotateTo(2.0, 360.0))));
    scene.run(id.clone(), &rotate);

    println!("Press any key to pause/resume the animation!");

    let ref mut gl = GlGraphics::new(opengl);
    let window = RefCell::new(window);
    for e in piston::events(&window) {
        use piston::event::{ PressEvent, RenderEvent };

        scene.event(&e);

        if let Some(args) = e.render_args() {
            use graphics::*;
            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
                graphics::clear([1.0, 1.0, 1.0, 1.0], gl);
                scene.draw(c.transform, gl);
            });
        }
        if let Some(_) = e.press_args() {
            scene.toggle(id.clone(), &seq);
            scene.toggle(id.clone(), &rotate);
        }
    }
}
<|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 the
//! first line of each crate was
//!
//! ```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 comparision 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),
//!     [`Some`](../result/enum.Result.html),
//!     [`None`](../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>doc: prelude nits<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 comparision 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),
//!     [`Some`](../result/enum.Result.html),
//!     [`None`](../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 2012 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.

/*!
 * A simple map based on a vector for small integer keys. Space requirements
 * are O(highest integer key).
 */

use core::container::{Container, Mutable, Map, Set};
use core::iter::BaseIter;
use core::option::{Some, None};
use core::prelude::*;

pub struct SmallIntMap<T> {
    priv v: ~[Option<T>],
}

impl<V> SmallIntMap<V>: BaseIter<(uint, &V)> {
    /// Visit all key-value pairs in order
    pure fn each(&self, it: fn(&(uint, &self/V)) -> bool) {
        for uint::range(0, self.v.len()) |i| {
            match self.v[i] {
              Some(ref elt) => if !it(&(i, elt)) { break },
              None => ()
            }
        }
    }

    pure fn size_hint(&self) -> Option<uint> { Some(self.len()) }
}

impl<V> SmallIntMap<V>: Container {
    /// Return the number of elements in the map
    pure fn len(&self) -> uint {
        let mut sz = 0;
        for self.v.each |item| {
            if item.is_some() {
                sz += 1;
            }
        }
        sz
    }

    /// Return true if the map contains no elements
    pure fn is_empty(&self) -> bool { self.len() == 0 }
}

impl<V> SmallIntMap<V>: Mutable {
    /// Clear the map, removing all key-value pairs.
    fn clear(&mut self) { self.v.clear() }
}

impl<V> SmallIntMap<V>: Map<uint, V> {
    /// Return true if the map contains a value for the specified key
    pure fn contains_key(&self, key: &uint) -> bool {
        self.find(key).is_some()
    }

    /// Visit all keys in order
    pure fn each_key(&self, blk: fn(key: &uint) -> bool) {
        self.each(|&(k, _)| blk(&k))
    }

    /// Visit all values in order
    pure fn each_value(&self, blk: fn(value: &V) -> bool) {
        self.each(|&(_, v)| blk(v))
    }

    /// Return the value corresponding to the key in the map
    pure fn find(&self, key: &uint) -> Option<&self/V> {
        if *key < self.v.len() {
            match self.v[*key] {
              Some(ref value) => Some(value),
              None => None
            }
        } else {
            None
        }
    }

    /// Insert a key-value pair into the map. An existing value for a
    /// key is replaced by the new value. Return true if the key did
    /// not already exist in the map.
    fn insert(&mut self, key: uint, value: V) -> bool {
        let exists = self.contains_key(&key);
        let len = self.v.len();
        if len <= key {
            vec::grow_fn(&mut self.v, key - len + 1, |_| None);
        }
        self.v[key] = Some(value);
        !exists
    }

    /// Remove a key-value pair from the map. Return true if the key
    /// was present in the map, otherwise false.
    fn remove(&mut self, key: &uint) -> bool {
        if *key >= self.v.len() {
            return false;
        }
        let removed = self.v[*key].is_some();
        self.v[*key] = None;
        removed
    }
}

pub impl<V> SmallIntMap<V> {
    /// Create an empty SmallIntMap
    static pure fn new() -> SmallIntMap<V> { SmallIntMap{v: ~[]} }

    pure fn get(&self, key: &uint) -> &self/V {
        self.find(key).expect("key not present")
    }
}

pub impl<V: Copy> SmallIntMap<V> {
    fn update_with_key(&mut self, key: uint, val: V,
                       ff: fn(uint, V, V) -> V) -> bool {
        match self.find(&key) {
          None => self.insert(key, val),
          Some(orig) => self.insert(key, ff(key, copy *orig, val)),
        }
    }

    fn update(&mut self, key: uint, newval: V, ff: fn(V, V) -> V) -> bool {
        self.update_with_key(key, newval, |_k, v, v1| ff(v,v1))
    }
}

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

    #[test]
    fn test_len() {
        let mut map = SmallIntMap::new();
        assert map.len() == 0;
        assert map.is_empty();
        assert map.insert(5, 20);
        assert map.len() == 1;
        assert !map.is_empty();
        assert map.insert(11, 12);
        assert map.len() == 2;
        assert !map.is_empty();
        assert map.insert(14, 22);
        assert map.len() == 3;
        assert !map.is_empty();
    }

    #[test]
    fn test_clear() {
        let mut map = SmallIntMap::new();
        assert map.insert(5, 20);
        assert map.insert(11, 12);
        assert map.insert(14, 22);
        map.clear();
        assert map.is_empty();
        assert map.find(&5).is_none();
        assert map.find(&11).is_none();
        assert map.find(&14).is_none();
    }

    #[test]
    fn test_insert_with_key() {
        let mut map = SmallIntMap::new();

        // given a new key, initialize it with this new count, given
        // given an existing key, add more to its count
        fn addMoreToCount(_k: uint, v0: uint, v1: uint) -> uint {
            v0 + v1
        }

        fn addMoreToCount_simple(v0: uint, v1: uint) -> uint {
            v0 + v1
        }

        // count integers
        map.update(3, 1, addMoreToCount_simple);
        map.update_with_key(9, 1, addMoreToCount);
        map.update(3, 7, addMoreToCount_simple);
        map.update_with_key(5, 3, addMoreToCount);
        map.update_with_key(3, 2, addMoreToCount);

        // check the total counts
        assert map.find(&3).get() == &10;
        assert map.find(&5).get() == &3;
        assert map.find(&9).get() == &1;

        // sadly, no sevens were counted
        assert map.find(&7).is_none();
    }
}
<commit_msg>implement ReverseIter for SmallIntMap<commit_after>// Copyright 2012 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.

/*!
 * A simple map based on a vector for small integer keys. Space requirements
 * are O(highest integer key).
 */

use core::container::{Container, Mutable, Map, Set};
use core::iter::{BaseIter, ReverseIter};
use core::option::{Some, None};
use core::prelude::*;

pub struct SmallIntMap<T> {
    priv v: ~[Option<T>],
}

impl<V> SmallIntMap<V>: BaseIter<(uint, &V)> {
    /// Visit all key-value pairs in order
    pure fn each(&self, it: fn(&(uint, &self/V)) -> bool) {
        for uint::range(0, self.v.len()) |i| {
            match self.v[i] {
              Some(ref elt) => if !it(&(i, elt)) { break },
              None => ()
            }
        }
    }

    pure fn size_hint(&self) -> Option<uint> { Some(self.len()) }
}

impl<V> SmallIntMap<V>: ReverseIter<(uint, &V)> {
    /// Visit all key-value pairs in reverse order
    pure fn each_reverse(&self, it: fn(&(uint, &self/V)) -> bool) {
        for uint::range_rev(self.v.len(), 0) |i| {
            match self.v[i - 1] {
              Some(ref elt) => if !it(&(i - 1, elt)) { break },
              None => ()
            }
        }
    }
}

impl<V> SmallIntMap<V>: Container {
    /// Return the number of elements in the map
    pure fn len(&self) -> uint {
        let mut sz = 0;
        for self.v.each |item| {
            if item.is_some() {
                sz += 1;
            }
        }
        sz
    }

    /// Return true if the map contains no elements
    pure fn is_empty(&self) -> bool { self.len() == 0 }
}

impl<V> SmallIntMap<V>: Mutable {
    /// Clear the map, removing all key-value pairs.
    fn clear(&mut self) { self.v.clear() }
}

impl<V> SmallIntMap<V>: Map<uint, V> {
    /// Return true if the map contains a value for the specified key
    pure fn contains_key(&self, key: &uint) -> bool {
        self.find(key).is_some()
    }

    /// Visit all keys in order
    pure fn each_key(&self, blk: fn(key: &uint) -> bool) {
        self.each(|&(k, _)| blk(&k))
    }

    /// Visit all values in order
    pure fn each_value(&self, blk: fn(value: &V) -> bool) {
        self.each(|&(_, v)| blk(v))
    }

    /// Return the value corresponding to the key in the map
    pure fn find(&self, key: &uint) -> Option<&self/V> {
        if *key < self.v.len() {
            match self.v[*key] {
              Some(ref value) => Some(value),
              None => None
            }
        } else {
            None
        }
    }

    /// Insert a key-value pair into the map. An existing value for a
    /// key is replaced by the new value. Return true if the key did
    /// not already exist in the map.
    fn insert(&mut self, key: uint, value: V) -> bool {
        let exists = self.contains_key(&key);
        let len = self.v.len();
        if len <= key {
            vec::grow_fn(&mut self.v, key - len + 1, |_| None);
        }
        self.v[key] = Some(value);
        !exists
    }

    /// Remove a key-value pair from the map. Return true if the key
    /// was present in the map, otherwise false.
    fn remove(&mut self, key: &uint) -> bool {
        if *key >= self.v.len() {
            return false;
        }
        let removed = self.v[*key].is_some();
        self.v[*key] = None;
        removed
    }
}

pub impl<V> SmallIntMap<V> {
    /// Create an empty SmallIntMap
    static pure fn new() -> SmallIntMap<V> { SmallIntMap{v: ~[]} }

    pure fn get(&self, key: &uint) -> &self/V {
        self.find(key).expect("key not present")
    }
}

pub impl<V: Copy> SmallIntMap<V> {
    fn update_with_key(&mut self, key: uint, val: V,
                       ff: fn(uint, V, V) -> V) -> bool {
        match self.find(&key) {
          None => self.insert(key, val),
          Some(orig) => self.insert(key, ff(key, copy *orig, val)),
        }
    }

    fn update(&mut self, key: uint, newval: V, ff: fn(V, V) -> V) -> bool {
        self.update_with_key(key, newval, |_k, v, v1| ff(v,v1))
    }
}

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

    #[test]
    fn test_len() {
        let mut map = SmallIntMap::new();
        assert map.len() == 0;
        assert map.is_empty();
        assert map.insert(5, 20);
        assert map.len() == 1;
        assert !map.is_empty();
        assert map.insert(11, 12);
        assert map.len() == 2;
        assert !map.is_empty();
        assert map.insert(14, 22);
        assert map.len() == 3;
        assert !map.is_empty();
    }

    #[test]
    fn test_clear() {
        let mut map = SmallIntMap::new();
        assert map.insert(5, 20);
        assert map.insert(11, 12);
        assert map.insert(14, 22);
        map.clear();
        assert map.is_empty();
        assert map.find(&5).is_none();
        assert map.find(&11).is_none();
        assert map.find(&14).is_none();
    }

    #[test]
    fn test_insert_with_key() {
        let mut map = SmallIntMap::new();

        // given a new key, initialize it with this new count, given
        // given an existing key, add more to its count
        fn addMoreToCount(_k: uint, v0: uint, v1: uint) -> uint {
            v0 + v1
        }

        fn addMoreToCount_simple(v0: uint, v1: uint) -> uint {
            v0 + v1
        }

        // count integers
        map.update(3, 1, addMoreToCount_simple);
        map.update_with_key(9, 1, addMoreToCount);
        map.update(3, 7, addMoreToCount_simple);
        map.update_with_key(5, 3, addMoreToCount);
        map.update_with_key(3, 2, addMoreToCount);

        // check the total counts
        assert map.find(&3).get() == &10;
        assert map.find(&5).get() == &3;
        assert map.find(&9).get() == &1;

        // sadly, no sevens were counted
        assert map.find(&7).is_none();
    }
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![crate_name="macro_platformtree"]
#![crate_type="dylib"]
#![warn(missing_doc)]

#![feature(plugin_registrar, quote, managed_boxes)]

extern crate platformtree;
extern crate rustc;
extern crate serialize;
extern crate syntax;

use std::clone::Clone;

use rustc::plugin::Registry;
use syntax::ast;
use syntax::codemap::DUMMY_SP;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, ItemModifier};
use syntax::ext::build::AstBuilder;
use syntax::owned_slice::OwnedSlice;
use syntax::print::pprust;
use syntax::util::small_vector::SmallVector;
use syntax::ptr::P;

use platformtree::parser::Parser;
use platformtree::builder::Builder;
use platformtree::builder::meta_args::ToTyHash;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
  reg.register_macro("platformtree", macro_platformtree);
  reg.register_macro("platformtree_verbose", macro_platformtree_verbose);
  reg.register_syntax_extension(syntax::parse::token::intern("zinc_task"),
      ItemModifier(box macro_zinc_task));
}

pub fn macro_platformtree(cx: &mut ExtCtxt, _: Span, tts: &[ast::TokenTree])
    -> Box<MacResult+'static> {
  let pt = Parser::new(cx, tts).parse_platformtree();
  let items = Builder::build(cx, pt.unwrap())
    .expect(format!("Unexpected failure on {}", line!()).as_slice())
    .emit_items(cx);
  MacItems::new(items)
}

pub fn macro_platformtree_verbose(cx: &mut ExtCtxt, sp: Span,
    tts: &[ast::TokenTree]) -> Box<MacResult+'static> {
  let result = macro_platformtree(cx, sp, tts);
  println!("Platform Tree dump:")
  for i in result.make_items().unwrap().as_slice().iter() {
    println!("{}", pprust::item_to_string(i.deref()));
  }

  macro_platformtree(cx, sp, tts)
}

fn macro_zinc_task(cx: &mut ExtCtxt, _: Span, _: &ast::MetaItem,
    it: P<ast::Item>) -> P<ast::Item> {
  match it.node {
    ast::ItemFn(ref decl, style, abi, _, ref block) => {
      let istr = syntax::parse::token::get_ident(it.ident);
      let fn_name = istr.get();
      let ty_params = platformtree::builder::meta_args::get_ty_params_for_task(cx, fn_name);

      let params = ty_params.iter().map(|ty| {
        cx.typaram(
            DUMMY_SP,
            cx.ident_of(ty.to_tyhash().as_slice()),
            OwnedSlice::from_vec(vec!(cx.typarambound(
                cx.path(DUMMY_SP, ty.as_slice().split_str("::").map(|t| cx.ident_of(t)).collect())))),
            None,
            None)
      }).collect();

      let new_arg = cx.arg(DUMMY_SP, cx.ident_of("args"), cx.ty_rptr(
          DUMMY_SP,
          cx.ty_path(
              cx.path_all(
                  DUMMY_SP,
                  false,
                  ["pt".to_string(), fn_name.to_string() + "_args"].iter().map(|t| cx.ident_of(t.as_slice())).collect(),
                  vec!(),
                  ty_params.iter().map(|ty| {
                    cx.ty_path(cx.path_ident(DUMMY_SP, cx.ident_of(ty.to_tyhash().as_slice())), None)
                  }).collect()),
              None),
          None,
          ast::MutImmutable));
      let new_decl = P(ast::FnDecl {
        inputs: vec!(new_arg),
        ..decl.deref().clone()
      });

      let new_generics = ast::Generics {
        lifetimes: vec!(),
        ty_params: OwnedSlice::from_vec(params),
        where_clause: ast::WhereClause {
          id: ast::DUMMY_NODE_ID,
          predicates: vec!(),
        }
      };
      let new_node = ast::ItemFn(new_decl, style, abi, new_generics, block.clone());

      P(ast::Item {node: new_node, ..it.deref().clone() })
    },
    _ => fail!(),
  }
}

pub struct MacItems {
  items: Vec<P<ast::Item>>
}

impl MacItems {
  pub fn new(items: Vec<P<ast::Item>>) -> Box<MacResult+'static> {
    box MacItems { items: items } as Box<MacResult>
  }
}
impl MacResult for MacItems {
  fn make_items(self: Box<MacItems>) -> Option<SmallVector<P<ast::Item>>> {
    Some(SmallVector::many(self.items.clone()))
  }
}
<commit_msg>platformtree: Fix nightly issues<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![crate_name="macro_platformtree"]
#![crate_type="dylib"]
#![warn(missing_doc)]

#![feature(plugin_registrar, quote)]

extern crate platformtree;
extern crate rustc;
extern crate serialize;
extern crate syntax;

use std::clone::Clone;

use rustc::plugin::Registry;
use syntax::ast;
use syntax::codemap::DUMMY_SP;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, ItemModifier, Modifier};
use syntax::ext::build::AstBuilder;
use syntax::owned_slice::OwnedSlice;
use syntax::print::pprust;
use syntax::util::small_vector::SmallVector;
use syntax::ptr::P;

use platformtree::parser::Parser;
use platformtree::builder::Builder;
use platformtree::builder::meta_args::ToTyHash;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
  reg.register_macro("platformtree", macro_platformtree);
  reg.register_macro("platformtree_verbose", macro_platformtree_verbose);
  reg.register_syntax_extension(syntax::parse::token::intern("zinc_task"),
      Modifier(box macro_zinc_task));
}

pub fn macro_platformtree(cx: &mut ExtCtxt, _: Span, tts: &[ast::TokenTree])
    -> Box<MacResult+'static> {
  let pt = Parser::new(cx, tts).parse_platformtree();
  let items = Builder::build(cx, pt.unwrap())
    .expect(format!("Unexpected failure on {}", line!()).as_slice())
    .emit_items(cx);
  MacItems::new(items)
}

pub fn macro_platformtree_verbose(cx: &mut ExtCtxt, sp: Span,
    tts: &[ast::TokenTree]) -> Box<MacResult+'static> {
  let result = macro_platformtree(cx, sp, tts);
  println!("Platform Tree dump:")
  for i in result.make_items().unwrap().as_slice().iter() {
    println!("{}", pprust::item_to_string(i.deref()));
  }

  macro_platformtree(cx, sp, tts)
}

fn macro_zinc_task(cx: &mut ExtCtxt, _: Span, _: &ast::MetaItem,
    it: P<ast::Item>) -> P<ast::Item> {
  match it.node {
    ast::ItemFn(ref decl, style, abi, _, ref block) => {
      let istr = syntax::parse::token::get_ident(it.ident);
      let fn_name = istr.get();
      let ty_params = platformtree::builder::meta_args::get_ty_params_for_task(cx, fn_name);

      let params = ty_params.iter().map(|ty| {
        cx.typaram(
            DUMMY_SP,
            cx.ident_of(ty.to_tyhash().as_slice()),
            OwnedSlice::from_vec(vec!(cx.typarambound(
                cx.path(DUMMY_SP, ty.as_slice().split_str("::").map(|t| cx.ident_of(t)).collect())))),
            None,
            None)
      }).collect();

      let new_arg = cx.arg(DUMMY_SP, cx.ident_of("args"), cx.ty_rptr(
          DUMMY_SP,
          cx.ty_path(
              cx.path_all(
                  DUMMY_SP,
                  false,
                  ["pt".to_string(), fn_name.to_string() + "_args"].iter().map(|t| cx.ident_of(t.as_slice())).collect(),
                  vec!(),
                  ty_params.iter().map(|ty| {
                    cx.ty_path(cx.path_ident(DUMMY_SP, cx.ident_of(ty.to_tyhash().as_slice())), None)
                  }).collect()),
              None),
          None,
          ast::MutImmutable));
      let new_decl = P(ast::FnDecl {
        inputs: vec!(new_arg),
        ..decl.deref().clone()
      });

      let new_generics = ast::Generics {
        lifetimes: vec!(),
        ty_params: OwnedSlice::from_vec(params),
        where_clause: ast::WhereClause {
          id: ast::DUMMY_NODE_ID,
          predicates: vec!(),
        }
      };
      let new_node = ast::ItemFn(new_decl, style, abi, new_generics, block.clone());

      P(ast::Item {node: new_node, ..it.deref().clone() })
    },
    _ => fail!(),
  }
}

pub struct MacItems {
  items: Vec<P<ast::Item>>
}

impl MacItems {
  pub fn new(items: Vec<P<ast::Item>>) -> Box<MacResult+'static> {
    box MacItems { items: items } as Box<MacResult>
  }
}
impl MacResult for MacItems {
  fn make_items(self: Box<MacItems>) -> Option<SmallVector<P<ast::Item>>> {
    Some(SmallVector::many(self.items.clone()))
  }
}
<|endoftext|>


<commit_before>use rustbox;
use yoga;
use yoga_wrapper;

use yoga::Backend as B;
use yoga::Renders as R;

use measurer;
use renderer;

pub struct Backend<'rbox> {
    renderer: renderer::Renderer<'rbox>,
    measurer: measurer::Measurer,
}

impl<'rbox> Backend<'rbox> {
    pub fn new(rustbox: &rustbox::RustBox) -> Backend {
        Backend {
            renderer: renderer::Renderer::new(rustbox),
            measurer: measurer::Measurer {},
        }
    }
}

impl<'rbox, 'meas> yoga::Backend<'meas> for Backend<'rbox> {
    type Color = i32;
    type Renderer = renderer::Renderer<'rbox>;
    type Measurer = measurer::Measurer;

    fn render(&self, node: &yoga_wrapper::Node) {
        let renderer = self.get_renderer();
        renderer.render(node);
        renderer.rustbox.present();
    }

    fn get_renderer(&self) -> &renderer::Renderer<'rbox> {
        &self.renderer
    }

    fn create_context<'text>(&'meas self, text: &'text str) -> yoga_wrapper::Context<'text, 'meas> {
        yoga_wrapper::Context::new(text, &self.measurer)
    }
}
<commit_msg>chore(backend): cleanup use<commit_after>use rustbox;
use yoga;
use yoga_wrapper;

use yoga::Renders;

use measurer;
use renderer;

pub struct Backend<'rbox> {
    renderer: renderer::Renderer<'rbox>,
    measurer: measurer::Measurer,
}

impl<'rbox> Backend<'rbox> {
    pub fn new(rustbox: &rustbox::RustBox) -> Backend {
        Backend {
            renderer: renderer::Renderer::new(rustbox),
            measurer: measurer::Measurer {},
        }
    }
}

impl<'rbox, 'meas> yoga::Backend<'meas> for Backend<'rbox> {
    type Color = i32;
    type Renderer = renderer::Renderer<'rbox>;
    type Measurer = measurer::Measurer;

    fn render(&self, node: &yoga_wrapper::Node) {
        let renderer = self.get_renderer();
        renderer.render(node);
        renderer.rustbox.present();
    }

    fn get_renderer(&self) -> &renderer::Renderer<'rbox> {
        &self.renderer
    }

    fn create_context<'text>(&'meas self, text: &'text str) -> yoga_wrapper::Context<'text, 'meas> {
        yoga_wrapper::Context::new(text, &self.measurer)
    }
}
<|endoftext|>


<commit_before>extern crate rubigo_yes;

fn main() {
    match rubigo_yes::parse_args(std::env::args().skip(1).collect()) {
        Ok(ref req) => req.do_it(),
        Err(ref err) => err.show()
    }
}
<commit_msg>adds comments to main function<commit_after>extern crate rubigo_yes;

fn main() {
    // parse the args and run
    match rubigo_yes::parse_args(std::env::args().skip(1).collect()) {
        Ok(ref req) => req.do_it(),
        Err(ref err) => err.show()
    }
}
<|endoftext|>


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

use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use rusqlite::Connection;
use rusqlite;

use collections::Collection;
use folders::{Folder, Folders, RootFolder};
use fromdb::FromDb;
use images::Image;
use keywords::Keyword;
use keywordtree::KeywordTree;
use libraryfiles::LibraryFile;
use lrobject::{LrId, LrObject};

const LR3_VERSION: i32 = 3;
const LR4_VERSION: i32 = 4;
const LR6_VERSION: i32 = 6;

/// Catalog version.
#[derive(Debug, PartialEq)]
pub enum CatalogVersion {
    /// Unknown version
    Unknown,
    /// Lightroom 3.x catalog. Unhandled.
    Lr3,
    /// Lightroom 4.x catalog.
    Lr4,
    /// Lightroom 6.x catalog.
    Lr6,
}

/// A Lightroom Catalog.
pub struct Catalog {
    /// catalog path
    path: PathBuf,
    /// The version string
    pub version: String,
    /// The catalog version
    pub catalog_version: CatalogVersion,
    /// Id for the root (top level) keyword
    pub root_keyword_id: LrId,

    /// The keywords, mapped in the local `LrId`
    keywords: BTreeMap<LrId, Keyword>,
    /// The `Folders` container.
    folders: Folders,
    /// The `Image` container
    images: Vec<Image>,
    /// The `LibraryFile` container
    libfiles: Vec<LibraryFile>,
    /// The `Collection` container
    collections: Vec<Collection>,

    /// The sqlite connectio to the catalog
    dbconn: Option<Connection>,
}

impl Catalog {
    /// Create a new catalog.
    pub fn new(path: &Path) -> Catalog {
        Catalog {
            path: PathBuf::from(path),
            version: String::from(""),
            catalog_version: CatalogVersion::Unknown,
            root_keyword_id: 0,
            keywords: BTreeMap::new(),
            folders: Folders::new(),
            images: vec![],
            libfiles: vec![],
            collections: vec![],
            dbconn: None,
        }
    }

    /// Open catalog. Return false in failure.
    /// This doesn't check if the content is valid beyond the backing sqlite3.
    pub fn open(&mut self) -> bool {
        let conn_attempt = Connection::open(&self.path);
        if let Ok(conn) = conn_attempt {
            self.dbconn = Some(conn);

            return true;
        }

        false
    }

    /// Get a variable from the table.
    fn get_variable<T>(&self, name: &str) -> Option<T>
    where
        T: rusqlite::types::FromSql,
    {
        let conn = try_opt!(self.dbconn.as_ref());
        if let Ok(mut stmt) = conn.prepare("SELECT value FROM Adobe_variablesTable WHERE name=?1") {
            let mut rows = stmt.query(&[&name]).unwrap();
            if let Some(Ok(row)) = rows.next() {
                return Some(row.get(0));
            }
        }
        None
    }

    /// Pare the version string from the database.
    fn parse_version(mut v: String) -> i32 {
        v.truncate(2);
        if let Ok(version) = v.parse::<i32>() {
            version
        } else {
            0
        }
    }

    /// Load version info for the catalog.
    pub fn load_version(&mut self) {
        if let Some(version) = self.get_variable::<String>("Adobe_DBVersion") {
            self.version = version;
            let v = Catalog::parse_version(self.version.clone());
            self.catalog_version = match v {
                LR6_VERSION => CatalogVersion::Lr6,
                LR4_VERSION => CatalogVersion::Lr4,
                LR3_VERSION => CatalogVersion::Lr3,
                _ => CatalogVersion::Unknown,
            };
        }

        if let Some(root_keyword_id) = self.get_variable::<f64>("AgLibraryKeyword_rootTagID") {
            self.root_keyword_id = root_keyword_id.round() as LrId;
        }
    }

    /// Generic object loader leveraging the FromDb protocol
    fn load_objects<T: FromDb>(conn: &Connection) -> Vec<T> {
        let mut result: Vec<T> = vec![];
        let query = format!(
            "SELECT {} FROM {}",
            T::read_db_columns(),
            T::read_db_tables()
        );
        if let Ok(mut stmt) = conn.prepare(&query) {
            let mut rows = stmt.query(&[]).unwrap();
            while let Some(Ok(row)) = rows.next() {
                if let Some(object) = T::read_from(&row) {
                    result.push(object);
                }
            }
        }
        result
    }

    /// Load a keyword tree
    pub fn load_keywords_tree(&mut self) -> KeywordTree {
        let keywords = self.load_keywords();

        let mut tree = KeywordTree::new();
        tree.add_children(keywords);

        tree
    }

    /// Load keywords.
    pub fn load_keywords(&mut self) -> &BTreeMap<LrId, Keyword> {
        if self.keywords.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let result = Catalog::load_objects::<Keyword>(&conn);
                for keyword in result {
                    self.keywords.insert(keyword.id(), keyword);
                }
            }
        }
        &self.keywords
    }

    /// Load folders.
    pub fn load_folders(&mut self) -> &Folders {
        if self.folders.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let folders = Catalog::load_objects::<RootFolder>(&conn);
                self.folders.append_root_folders(folders);
                let mut folders = Catalog::load_objects::<Folder>(&conn);
                for ref mut folder in &mut folders {
                    folder.content = Some(folder.read_content(conn));
                }
                self.folders.append_folders(folders);
            }
        }
        &self.folders
    }

    /// Load library files (that back images)
    pub fn load_library_files(&mut self) -> &Vec<LibraryFile> {
        if self.libfiles.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut result = Catalog::load_objects::<LibraryFile>(&conn);
                self.libfiles.append(&mut result);
            }
        }
        &self.libfiles
    }

    /// Load images.
    pub fn load_images(&mut self) -> &Vec<Image> {
        if self.images.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut result = Catalog::load_objects::<Image>(&conn);
                self.images.append(&mut result);
            }
        }
        &self.images
    }

    /// Load collectons.
    pub fn load_collections(&mut self) -> &Vec<Collection> {
        if self.collections.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut collections = Catalog::load_objects::<Collection>(&conn);
                for ref mut collection in &mut collections {
                    collection.content = Some(collection.read_content(conn));
                }
                self.collections.append(&mut collections);
            }
        }
        &self.collections
    }
}
<commit_msg>catalog: new takes a AsRef<Path> now<commit_after>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use rusqlite::Connection;
use rusqlite;

use collections::Collection;
use folders::{Folder, Folders, RootFolder};
use fromdb::FromDb;
use images::Image;
use keywords::Keyword;
use keywordtree::KeywordTree;
use libraryfiles::LibraryFile;
use lrobject::{LrId, LrObject};

const LR3_VERSION: i32 = 3;
const LR4_VERSION: i32 = 4;
const LR6_VERSION: i32 = 6;

/// Catalog version.
#[derive(Debug, PartialEq)]
pub enum CatalogVersion {
    /// Unknown version
    Unknown,
    /// Lightroom 3.x catalog. Unhandled.
    Lr3,
    /// Lightroom 4.x catalog.
    Lr4,
    /// Lightroom 6.x catalog.
    Lr6,
}

/// A Lightroom Catalog.
pub struct Catalog {
    /// catalog path
    path: PathBuf,
    /// The version string
    pub version: String,
    /// The catalog version
    pub catalog_version: CatalogVersion,
    /// Id for the root (top level) keyword
    pub root_keyword_id: LrId,

    /// The keywords, mapped in the local `LrId`
    keywords: BTreeMap<LrId, Keyword>,
    /// The `Folders` container.
    folders: Folders,
    /// The `Image` container
    images: Vec<Image>,
    /// The `LibraryFile` container
    libfiles: Vec<LibraryFile>,
    /// The `Collection` container
    collections: Vec<Collection>,

    /// The sqlite connectio to the catalog
    dbconn: Option<Connection>,
}

impl Catalog {
    /// Create a new catalog.
    pub fn new<P>(path: P) -> Catalog
        where P: AsRef<Path>
    {
        Catalog {
            path: path.as_ref().to_path_buf(),
            version: "".to_string(),
            catalog_version: CatalogVersion::Unknown,
            root_keyword_id: 0,
            keywords: BTreeMap::new(),
            folders: Folders::new(),
            images: vec![],
            libfiles: vec![],
            collections: vec![],
            dbconn: None,
        }
    }

    /// Open catalog. Return false in failure.
    /// This doesn't check if the content is valid beyond the backing sqlite3.
    pub fn open(&mut self) -> bool {
        let conn_attempt = Connection::open(&self.path);
        if let Ok(conn) = conn_attempt {
            self.dbconn = Some(conn);

            return true;
        }

        false
    }

    /// Get a variable from the table.
    fn get_variable<T>(&self, name: &str) -> Option<T>
    where
        T: rusqlite::types::FromSql,
    {
        let conn = try_opt!(self.dbconn.as_ref());
        if let Ok(mut stmt) = conn.prepare("SELECT value FROM Adobe_variablesTable WHERE name=?1") {
            let mut rows = stmt.query(&[&name]).unwrap();
            if let Some(Ok(row)) = rows.next() {
                return Some(row.get(0));
            }
        }
        None
    }

    /// Pare the version string from the database.
    fn parse_version(mut v: String) -> i32 {
        v.truncate(2);
        if let Ok(version) = v.parse::<i32>() {
            version
        } else {
            0
        }
    }

    /// Load version info for the catalog.
    pub fn load_version(&mut self) {
        if let Some(version) = self.get_variable::<String>("Adobe_DBVersion") {
            self.version = version;
            let v = Catalog::parse_version(self.version.clone());
            self.catalog_version = match v {
                LR6_VERSION => CatalogVersion::Lr6,
                LR4_VERSION => CatalogVersion::Lr4,
                LR3_VERSION => CatalogVersion::Lr3,
                _ => CatalogVersion::Unknown,
            };
        }

        if let Some(root_keyword_id) = self.get_variable::<f64>("AgLibraryKeyword_rootTagID") {
            self.root_keyword_id = root_keyword_id.round() as LrId;
        }
    }

    /// Generic object loader leveraging the FromDb protocol
    fn load_objects<T: FromDb>(conn: &Connection) -> Vec<T> {
        let mut result: Vec<T> = vec![];
        let query = format!(
            "SELECT {} FROM {}",
            T::read_db_columns(),
            T::read_db_tables()
        );
        if let Ok(mut stmt) = conn.prepare(&query) {
            let mut rows = stmt.query(&[]).unwrap();
            while let Some(Ok(row)) = rows.next() {
                if let Some(object) = T::read_from(&row) {
                    result.push(object);
                }
            }
        }
        result
    }

    /// Load a keyword tree
    pub fn load_keywords_tree(&mut self) -> KeywordTree {
        let keywords = self.load_keywords();

        let mut tree = KeywordTree::new();
        tree.add_children(keywords);

        tree
    }

    /// Load keywords.
    pub fn load_keywords(&mut self) -> &BTreeMap<LrId, Keyword> {
        if self.keywords.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let result = Catalog::load_objects::<Keyword>(&conn);
                for keyword in result {
                    self.keywords.insert(keyword.id(), keyword);
                }
            }
        }
        &self.keywords
    }

    /// Load folders.
    pub fn load_folders(&mut self) -> &Folders {
        if self.folders.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let folders = Catalog::load_objects::<RootFolder>(&conn);
                self.folders.append_root_folders(folders);
                let mut folders = Catalog::load_objects::<Folder>(&conn);
                for ref mut folder in &mut folders {
                    folder.content = Some(folder.read_content(conn));
                }
                self.folders.append_folders(folders);
            }
        }
        &self.folders
    }

    /// Load library files (that back images)
    pub fn load_library_files(&mut self) -> &Vec<LibraryFile> {
        if self.libfiles.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut result = Catalog::load_objects::<LibraryFile>(&conn);
                self.libfiles.append(&mut result);
            }
        }
        &self.libfiles
    }

    /// Load images.
    pub fn load_images(&mut self) -> &Vec<Image> {
        if self.images.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut result = Catalog::load_objects::<Image>(&conn);
                self.images.append(&mut result);
            }
        }
        &self.images
    }

    /// Load collectons.
    pub fn load_collections(&mut self) -> &Vec<Collection> {
        if self.collections.is_empty() {
            if let Some(ref conn) = self.dbconn {
                let mut collections = Catalog::load_objects::<Collection>(&conn);
                for ref mut collection in &mut collections {
                    collection.content = Some(collection.read_content(conn));
                }
                self.collections.append(&mut collections);
            }
        }
        &self.collections
    }
}
<|endoftext|>


<commit_before>// mod channel

use rustc_serialize::json;
use client::{Client, ClientList};
use std::fmt;
use std::cmp;
use std::ops::Deref;
use std::str::FromStr;
use map::*;
use escaping::*;
use error;

/// Channel contains a TeamSpeak 3 channel.
/// # Example
/// ```
/// use sqlib::channel;
///
/// let channel = sqlib::channel::Channel::new(0, "test".to_string());
///
/// assert!(channel.is_empty());
/// assert_eq!("test".to_string(), format!("{}", channel));
/// ```
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Channel {
    pub cid: i64,
    pub channel_name: String,
    pub clients: Vec<Client>,
}

impl Default for Channel {
    fn default() -> Channel {
        Channel {
            cid: 0,
            channel_name: String::new(),
            clients: Vec::new(),
        }
    }
}

impl Channel {
    /// Create an empty Channel from a channel id and a name.
    pub fn new(channel_id: i64, name: String) -> Channel {
        let mut channel = Channel::default();
        channel.cid = channel_id;
        channel.channel_name = name;
        channel.unescape();
        channel
    }

    fn unescape(&mut self) {
        self.channel_name = unescape(&self.channel_name);
    }

    /// Create a new Channel from a given map.
    pub fn from_map(map: &StringMap) -> Channel {
        let mut channel = Channel::default();
        channel.mut_from_map(map);
        channel
    }

    /// Create a new Channel from a given Channel and a map.
    pub fn update_from_map(channel: Channel, map: &StringMap) -> Channel {
        let mut channel = channel.clone();
        channel.mut_from_map(map);
        channel
    }

    /// Mutate self from a given map.
    pub fn mut_from_map(&mut self, map: &StringMap) {
        update_from_map(map, "cid", &mut self.cid);
        update_from_map(map, "channel_name", &mut self.channel_name);
        self.unescape();
    }

    pub fn is_empty(&self) -> bool {
        self.clients.is_empty()
    }

    pub fn clients_len(&self) -> usize {
        self.clients.len()
    }

    pub fn add_client(&mut self, client: Client) {
        self.clients.push(client);
    }

    pub fn sort_clients(&mut self) {
        self.clients.sort()
    }

    /// Remove all Server Query Clients from the Channel.
    pub fn remove_sq_clients(&mut self) {
        let new_clients = self.clients
            .iter()
            .filter(|c| c.is_client())
            .map(|c| c.clone())
            .collect();
        self.clients = new_clients;
    }

    /// Creates a JSON String from self.
    pub fn as_json(&self) -> String {
        json::encode(self).unwrap_or(String::new())
    }
}

impl FromStr for Channel {
    type Err = error::Error;
    fn from_str(s: &str) -> error::Result<Self> {
        let map = to_map(s);
        Ok(Channel::from_map(&map))
    }
}

impl fmt::Display for Channel {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.channel_name));
        if !self.is_empty() {
            for client in self.clients.iter() {
                try!(write!(f, "\n  {}", client));
            }
        };
        Ok(())
    }
}

impl PartialEq for Channel {
    fn eq(&self, other: &Self) -> bool {
        self.cid.eq(&other.cid)
    }
}

impl Eq for Channel {}

impl PartialOrd for Channel {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.cid.partial_cmp(&other.cid)
    }
}

impl Ord for Channel {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.cid.cmp(&other.cid)
    }
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, RustcDecodable, RustcEncodable)]
pub struct ChannelList(Vec<Channel>);

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

impl ChannelList {
    pub fn vec(&self) -> &Vec<Channel> {
        let &ChannelList(ref v) = self;
        v
    }

    pub fn get_mut(&mut self) -> &mut Vec<Channel> {
        let &mut ChannelList(ref mut v) = self;
        v
    }

    pub fn get_vec(self) -> Vec<Channel> {
        let ChannelList(v) = self;
        v
    }

    pub fn from_maps(maps: &Vec<StringMap>) -> ChannelList {
        let mut vec = Vec::new();
        for map in maps.iter() {
            let channel = Channel::from_map(map);
            vec.push(channel);
        }
        ChannelList(vec)
    }

    pub fn insert_client(&mut self, client: &Client) {
        let cid = client.cid;
        for channel in self.get_mut().iter_mut() {
            if channel.cid == cid {
                channel.add_client(client.clone());
            }
        }
    }

    pub fn merge_clients(&mut self, clients: &ClientList) {
        for client in clients.iter() {
            self.insert_client(client);
        }
    }

    pub fn as_json(&self) -> String {
        json::encode(self.vec()).unwrap_or(String::new())
    }
}

impl Deref for ChannelList {
    type Target = Vec<Channel>;
    fn deref(&self) -> &Vec<Channel> {
        self.vec()
    }
}

impl FromStr for ChannelList {
    type Err = error::Error;
    fn from_str(s: &str) -> error::Result<Self> {
        let maps = s.split('|').map(to_map).collect();
        Ok(ChannelList::from_maps(&maps))
    }
}
<commit_msg>implementing AsRef and AsMut for sqlib::channel::ChannelList<commit_after>// mod channel

use rustc_serialize::json;
use client::{Client, ClientList};
use std::fmt;
use std::cmp;
use std::ops::Deref;
use std::str::FromStr;
use map::*;
use escaping::*;
use error;

/// Channel contains a TeamSpeak 3 channel.
/// # Example
/// ```
/// use sqlib::channel;
///
/// let channel = sqlib::channel::Channel::new(0, "test".to_string());
///
/// assert!(channel.is_empty());
/// assert_eq!("test".to_string(), format!("{}", channel));
/// ```
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Channel {
    pub cid: i64,
    pub channel_name: String,
    pub clients: Vec<Client>,
}

impl Default for Channel {
    fn default() -> Channel {
        Channel {
            cid: 0,
            channel_name: String::new(),
            clients: Vec::new(),
        }
    }
}

impl Channel {
    /// Create an empty Channel from a channel id and a name.
    pub fn new(channel_id: i64, name: String) -> Channel {
        let mut channel = Channel::default();
        channel.cid = channel_id;
        channel.channel_name = name;
        channel.unescape();
        channel
    }

    fn unescape(&mut self) {
        self.channel_name = unescape(&self.channel_name);
    }

    /// Create a new Channel from a given map.
    pub fn from_map(map: &StringMap) -> Channel {
        let mut channel = Channel::default();
        channel.mut_from_map(map);
        channel
    }

    /// Create a new Channel from a given Channel and a map.
    pub fn update_from_map(channel: Channel, map: &StringMap) -> Channel {
        let mut channel = channel.clone();
        channel.mut_from_map(map);
        channel
    }

    /// Mutate self from a given map.
    pub fn mut_from_map(&mut self, map: &StringMap) {
        update_from_map(map, "cid", &mut self.cid);
        update_from_map(map, "channel_name", &mut self.channel_name);
        self.unescape();
    }

    pub fn is_empty(&self) -> bool {
        self.clients.is_empty()
    }

    pub fn clients_len(&self) -> usize {
        self.clients.len()
    }

    pub fn add_client(&mut self, client: Client) {
        self.clients.push(client);
    }

    pub fn sort_clients(&mut self) {
        self.clients.sort()
    }

    /// Remove all Server Query Clients from the Channel.
    pub fn remove_sq_clients(&mut self) {
        let new_clients = self.clients
            .iter()
            .filter(|c| c.is_client())
            .map(|c| c.clone())
            .collect();
        self.clients = new_clients;
    }

    /// Creates a JSON String from self.
    pub fn as_json(&self) -> String {
        json::encode(self).unwrap_or(String::new())
    }
}

impl FromStr for Channel {
    type Err = error::Error;
    fn from_str(s: &str) -> error::Result<Self> {
        let map = to_map(s);
        Ok(Channel::from_map(&map))
    }
}

impl fmt::Display for Channel {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.channel_name));
        if !self.is_empty() {
            for client in self.clients.iter() {
                try!(write!(f, "\n  {}", client));
            }
        };
        Ok(())
    }
}

impl PartialEq for Channel {
    fn eq(&self, other: &Self) -> bool {
        self.cid.eq(&other.cid)
    }
}

impl Eq for Channel {}

impl PartialOrd for Channel {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.cid.partial_cmp(&other.cid)
    }
}

impl Ord for Channel {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.cid.cmp(&other.cid)
    }
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, RustcDecodable, RustcEncodable)]
pub struct ChannelList(Vec<Channel>);

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

impl ChannelList {
    pub fn into_inner(self) -> Vec<Channel> {
        let ChannelList(channel) = self;
        channel
    }

    pub fn from_maps(maps: &Vec<StringMap>) -> ChannelList {
        let mut vec = Vec::new();
        for map in maps.iter() {
            let channel = Channel::from_map(map);
            vec.push(channel);
        }
        ChannelList(vec)
    }

    pub fn insert_client(&mut self, client: &Client) {
        let cid = client.cid;
        for channel in self.as_mut().iter_mut() {
            if channel.cid == cid {
                channel.add_client(client.clone());
            }
        }
    }

    pub fn merge_clients(&mut self, clients: &ClientList) {
        for client in clients.iter() {
            self.insert_client(client);
        }
    }

    pub fn as_json(&self) -> String {
        json::encode(self.as_ref()).unwrap_or(String::new())
    }
}

impl Deref for ChannelList {
    type Target = Vec<Channel>;
    fn deref(&self) -> &Vec<Channel> {
        self.as_ref()
    }
}

impl AsRef<Vec<Channel>> for ChannelList {
    fn as_ref(&self) -> &Vec<Channel> {
        let &ChannelList(ref channel) = self;
        channel
    }
}

impl AsMut<Vec<Channel>> for ChannelList {
    fn as_mut(&mut self) -> &mut Vec<Channel> {
        let &mut ChannelList(ref mut channel) = self;
        channel
    }
}

impl FromStr for ChannelList {
    type Err = error::Error;
    fn from_str(s: &str) -> error::Result<Self> {
        let maps = s.split('|').map(to_map).collect();
        Ok(ChannelList::from_maps(&maps))
    }
}
<|endoftext|>