devloop 0.1.3

A tool to help with repetitive commands during development.
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

use error::DevloopError;

use yaml_rust::{Yaml, YamlLoader};

pub struct DevloopConfig {
    pub tasks: Vec<Task>,
    pub actions: HashMap<String, Task>,
    pub reminders: String,
}

#[derive(Debug)]
pub struct Task {
    pub name: String,
    pub command: String,
}

pub fn load<S: Into<String>>(filename: S) -> Result<DevloopConfig, DevloopError> {
    let mut contents = String::new();
    File::open(filename.into())?.read_to_string(&mut contents)?;

    let document = &YamlLoader::load_from_str(&contents)?[0];

    debug!("document: {:?}", document);

    let tasks = document["tasks"]
        .as_vec()
        .ok_or(DevloopError::InvalidConfig)?
        .into_iter()
        .filter_map(|task| Task::parse(task))
        .collect();

    debug!("tasks: {:?}", tasks);

    let actions = document["actions"]
        .as_hash()
        .ok_or(DevloopError::InvalidConfig)?
        .into_iter()
        .filter_map(|(key, value)| {
            Some((key.as_str()?.to_owned(), Task::parse(value)?))
        })
        .collect();

    debug!("actions: {:?}", actions);

    let reminders = document["reminders"].as_str().unwrap_or("").to_owned();

    Ok(DevloopConfig {
        tasks: tasks,
        actions: actions,
        reminders: reminders,
    })
}

impl Task {
    fn parse(task: &Yaml) -> Option<Self> {
        let parsed = Task::parse_no_warning(task);
        if parsed.is_none() {
            error!("Failed to parse {:?} as a task, ignoring.", task);
            ::std::io::stdin().read_line(&mut String::new()).unwrap();
        }
        parsed
    }

    fn parse_no_warning(task: &Yaml) -> Option<Self> {
        let task = task.as_vec()?;
        Some(Task {
            name: task.get(0)?.as_str()?.to_owned(),
            command: task.get(1)?.as_str()?.to_owned(),
        })
    }
}