<commit_before>use configuration::Configuration;
use std::slice::SliceExt;
use std::cmp::min;
use score::Score;
use sorted_result_set::SortedResultSet;
use std::ascii::AsciiExt;

#[derive(Debug)]
pub struct Search<'s> {
    pub config: &'s Configuration,
    current: usize,
    pub query: String,
    pub selection: Option<String>,
    pub result: Vec<String>,
    choice_stack: Vec<Vec<&'s String>>,
    done: bool,
}

impl<'s> Search<'s> {
    pub fn blank(config: &'s Configuration) -> Search<'s> {
        let query = config.initial_search.clone();
        let mut choice_stack: Vec<Vec<&'s String>> = Vec::new();
        let mut result = Vec::new();

        for choice in config.choices.iter().take(config.visible_limit) {
            result.push(choice.clone());
        }

        let mut first_stack_frame = Vec::new();
        for choice in config.choices.iter() {
            first_stack_frame.push(choice);
        }

        choice_stack.push(first_stack_frame);

        Search::new(config, query, choice_stack, result, 0, false)
    }

    pub fn is_done(&self) -> bool {
        self.done
    }

    pub fn done(self) -> Search<'s> {
        Search::new(self.config, self.query, self.choice_stack, self.result, self.current, true)
    }

    fn new(config: &'s Configuration, query: String, choice_stack: Vec<Vec<&'s String>>, result: Vec<String>, index: usize, done: bool) -> Search<'s> {
        let selection = Search::select(&result, index);

        Search { config: config,
                 current: index,
                 query: query,
                 selection: selection,
                 result: result,
                 choice_stack: choice_stack,
                 done: done}
    }

    fn new_for_index(self, index: usize) -> Search<'s> {
        Search::new(self.config, self.query, self.choice_stack, self.result, index, self.done)
    }

    fn new_for_query(mut self, new_query: String) -> Search<'s> {
        let mut results = SortedResultSet::new(self.config.visible_limit);

        let mut filtered_choices: Vec<&String> = Vec::new();

        Search::iter_matches(new_query.as_slice(), &self.config.choices,
                        |match_str, quality| { results.push(match_str, quality);
                                               filtered_choices.push(match_str)
                                             });

        self.choice_stack.push(filtered_choices);
        Search::new(self.config, new_query, self.choice_stack, results.as_sorted_vec(), 0, self.done)
    }

    pub fn iter_matches<F: FnMut(&'s String, f32)>(query: &str, choices: &'s Vec<String>, mut f: F) {
        let lower_query = query.to_ascii_lowercase();

        for choice in choices.iter() {
            let lower_choice = choice.to_ascii_lowercase();

            match Score::score(&lower_choice, &lower_query) {
                0.0     => continue,
                quality => f(choice, quality),
            };
        }
    }

    fn select(result: &Vec<String>, index: usize) -> Option<String> {
        match result.get(index) {
            Some(t) => Some(t.clone()),
            None => None,
        }
    }

    pub fn down(self) -> Search<'s> {
        let next_index = self.next_index();
        self.new_for_index(next_index)
    }

    pub fn up(self) -> Search<'s> {
        let next_index = self.prev_index();
        self.new_for_index(next_index)
    }

    pub fn append_to_search(self, input: &str) -> Search<'s> {
        let mut new_query = self.query.clone();
        new_query.push_str(input.as_slice());

        self.new_for_query(new_query)
    }

    pub fn backspace(self) -> Search<'s> {
        let mut new_query = self.query.clone();
        new_query.pop();

        self.new_for_query(new_query)
    }

    fn next_index(&self) -> usize {
        let next_index = self.current + 1;

        if next_index >= self.actual_limit() {
            0
        } else {
            next_index
        }
    }

    fn prev_index(&self) -> usize {
        if self.current == 0 {
            self.actual_limit() - 1
        } else {
            self.current - 1
        }
    }

    fn actual_limit(&self) -> usize {
        min(self.config.visible_limit, self.num_matches())
    }

    pub fn num_matches(&self) -> usize {
        self.choice_stack.last().unwrap().len()
    }
}

#[cfg(test)]
mod tests {
    extern crate test;
    use configuration::Configuration;
    use super::*;
    use self::test::Bencher;
    use sorted_result_set::SortedResultSet;

    fn one_two_three() -> Vec<String> {
        vec!["one".to_string(),
             "two".to_string(),
             "three".to_string()]
    }

    #[test]
    fn it_selects_the_first_choice_by_default() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.selection, Some("one".to_string()));
    }

    #[test]
    fn it_selets_the_second_when_down_is_called() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.down().selection, Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_end_of_list() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.down().down().down().down().selection, Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_top_of_list() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.up().up().selection, Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_visible_limit() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, Some(2));
        let search = Search::blank(&config);

        assert_eq!(search.down().down().down().selection, Some("two".to_string()));
    }

    #[test]
    fn it_moves_down_the_filtered_search_results() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("t").down().selection, Some("three".to_string()));
    }

    #[test]
    fn it_moves_down_the_filtered_search_results_twice() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("t").append_to_search("w").selection, Some("two".to_string()));
    }

    #[test]
    fn it_handles_not_matching_anything() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("x").selection, None);
    }

    #[test]
    fn up_match_nothing_after_filtering_all_out() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("x");

        assert_eq!(search.up().selection, None);
    }

    #[test]
    fn down_match_nothing_after_filtering_all_out() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("x");

        assert_eq!(search.down().selection, None);
    }

    #[test]
    fn backspaces_over_characters() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("e");

        assert_eq!(search.query, "e");
        assert_eq!(search.backspace().query, "");
    }

    #[test]
    fn resets_the_index_when_removing_char_from_search() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("e");

        assert_eq!(search.down().backspace().current, 0);
    }

    #[test]
    fn resets_the_index_when_adding_char_to_seach() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).down();

        assert_eq!(search.append_to_search("o").current, 0);
    }

    #[test]
    fn previous_results_appear_after_backspace() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("t");

        assert_eq!(search.backspace().num_matches(), 3);
    }

    #[test]
    fn initial_search_is_not_done() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert!(!search.is_done());
    }

    #[test]
    fn done_search_is_done() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).done();

        assert!(search.is_done());
    }

    #[test]
    fn done_search_has_selection() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).done();

        assert_eq!(search.selection, Some("one".to_string()));
    }

    #[test]
    fn loop_around_when_reaching_bottom_of_choices() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("n").down();

        assert_eq!(search.selection, Some("one".to_string()));
    }

    #[test]
    fn loop_around_when_reaching_top_of_choices() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("n").up();

        assert_eq!(search.selection, Some("one".to_string()));
    }

    #[test]
    fn search_is_case_insensitive() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("T");

        assert_eq!(search.num_matches(), 2);
    }

    #[test]
    fn uses_configs_visible_limit_as_result_size() {
        let input = input_times(30);
        let config = Configuration::from_inputs(input, None, Some(20));
        let search = Search::blank(&config).append_to_search("T");

        assert_eq!(search.num_matches(), 20);
    }

    fn input_times(n: usize) ->Vec<String> {
        let mut result: Vec<String> = Vec::new();
        for thing in one_two_three().iter().cycle().take(n) {
            result.push(thing.clone());
        }
        result
    }

    //84114 ns/iter (+/- 33515)
    #[bench]
    fn filter_speed(b: &mut Bencher) {
        let input = input_times(1000);
        let query = "t";

        b.iter(||{
            let mut results = SortedResultSet::new(20);
            Search::iter_matches(query, &input,
                                    |choice, quality| results.push(choice, quality));
            results
        });
    }
}
<commit_msg>WIP: Start looking into pushing/poping of the stack<commit_after>use configuration::Configuration;
use std::slice::SliceExt;
use std::cmp::min;
use score::Score;
use sorted_result_set::SortedResultSet;
use std::ascii::AsciiExt;

#[derive(Debug)]
pub struct Search<'s> {
    pub config: &'s Configuration,
    current: usize,
    pub query: String,
    pub selection: Option<String>,
    pub result: Vec<String>,
    choice_stack: Vec<Vec<&'s String>>,
    done: bool,
}

impl<'s> Search<'s> {
    pub fn blank(config: &'s Configuration) -> Search<'s> {
        let query = config.initial_search.clone();

        let mut choice_stack: Vec<Vec<&'s String>> = Vec::new();
        let mut result = Vec::new();
        let mut first_stack_frame = Vec::new();

        for choice in config.choices.iter().take(config.visible_limit) {
            result.push(choice.clone());
        }

        for choice in config.choices.iter() {
            first_stack_frame.push(choice);
        }

        choice_stack.push(first_stack_frame);

        Search::new(config, query, choice_stack, result, 0, false)
    }

    pub fn is_done(&self) -> bool {
        self.done
    }

    pub fn done(self) -> Search<'s> {
        Search::new(self.config, self.query, self.choice_stack, self.result, self.current, true)
    }

    fn new(config: &'s Configuration, query: String, choice_stack: Vec<Vec<&'s String>>, result: Vec<String>, index: usize, done: bool) -> Search<'s> {
        let selection = Search::select(&result, index);

        Search { config: config,
                 current: index,
                 query: query,
                 selection: selection,
                 result: result,
                 choice_stack: choice_stack,
                 done: done}
    }

    fn new_for_index(self, index: usize) -> Search<'s> {
        Search::new(self.config, self.query, self.choice_stack, self.result, index, self.done)
    }

    fn new_for_query(mut self, new_query: String) -> Search<'s> {
        let mut results = SortedResultSet::new(self.config.visible_limit);

        let mut filtered_choices: Vec<&String> = Vec::new();
        Search::iter_matches(new_query.as_slice(), &self.choice_stack.last().unwrap(),
                        |match_str, quality| { results.push(match_str, quality);
                                               filtered_choices.push(match_str)
                                             });

        self.choice_stack.push(filtered_choices);
        Search::new(self.config, new_query, self.choice_stack, results.as_sorted_vec(), 0, self.done)
    }

    pub fn iter_matches<F: FnMut(&'s String, f32)>(query: &str, choices: &Vec<&'s String>, mut f: F) {
        let lower_query = query.to_ascii_lowercase();

        for choice in choices.iter() {
            let lower_choice = choice.to_ascii_lowercase();

            match Score::score(&lower_choice, &lower_query) {
                0.0     => continue,
                quality => f(choice, quality),
            };
        }
    }

    fn select(result: &Vec<String>, index: usize) -> Option<String> {
        match result.get(index) {
            Some(t) => Some(t.clone()),
            None => None,
        }
    }

    pub fn down(self) -> Search<'s> {
        let next_index = self.next_index();
        self.new_for_index(next_index)
    }

    pub fn up(self) -> Search<'s> {
        let next_index = self.prev_index();
        self.new_for_index(next_index)
    }

    pub fn append_to_search(self, input: &str) -> Search<'s> {
        let mut new_query = self.query.clone();
        new_query.push_str(input.as_slice());

        self.new_for_query(new_query)
    }

    pub fn backspace(self) -> Search<'s> {
        let mut new_query = self.query.clone();
        new_query.pop();

        self.new_for_query(new_query)
    }

    fn next_index(&self) -> usize {
        let next_index = self.current + 1;

        if next_index >= self.actual_limit() {
            0
        } else {
            next_index
        }
    }

    fn prev_index(&self) -> usize {
        if self.current == 0 {
            self.actual_limit() - 1
        } else {
            self.current - 1
        }
    }

    fn actual_limit(&self) -> usize {
        min(self.config.visible_limit, self.num_matches())
    }

    pub fn num_matches(&self) -> usize {
        self.choice_stack.last().unwrap().len()
    }
}

#[cfg(test)]
mod tests {
    extern crate test;
    use configuration::Configuration;
    use super::*;
    use self::test::Bencher;
    use sorted_result_set::SortedResultSet;

    fn one_two_three() -> Vec<String> {
        vec!["one".to_string(),
             "two".to_string(),
             "three".to_string()]
    }

    // Move me later
    #[test]
    fn it_pushes_to_the_stack_when_appending() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("t");

        assert_eq!(search.choice_stack.len(), 2);
    }

    #[test]
    fn it_pop_from_the_stack_when_backspacing() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("t").backspace();

        assert_eq!(search.choice_stack.len(), 1);
    }

    #[test]
    fn it_selects_the_first_choice_by_default() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.selection, Some("one".to_string()));
    }

    #[test]
    fn it_selets_the_second_when_down_is_called() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.down().selection, Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_end_of_list() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.down().down().down().down().selection, Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_top_of_list() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.up().up().selection, Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_visible_limit() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, Some(2));
        let search = Search::blank(&config);

        assert_eq!(search.down().down().down().selection, Some("two".to_string()));
    }

    #[test]
    fn it_moves_down_the_filtered_search_results() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("t").down().selection, Some("three".to_string()));
    }

    #[test]
    fn it_moves_down_the_filtered_search_results_twice() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("t").append_to_search("w").selection, Some("two".to_string()));
    }

    #[test]
    fn it_handles_not_matching_anything() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("x").selection, None);
    }

    #[test]
    fn up_match_nothing_after_filtering_all_out() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("x");

        assert_eq!(search.up().selection, None);
    }

    #[test]
    fn down_match_nothing_after_filtering_all_out() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("x");

        assert_eq!(search.down().selection, None);
    }

    #[test]
    fn backspaces_over_characters() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("e");

        assert_eq!(search.query, "e");
        assert_eq!(search.backspace().query, "");
    }

    #[test]
    fn resets_the_index_when_removing_char_from_search() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("e");

        assert_eq!(search.down().backspace().current, 0);
    }

    #[test]
    fn resets_the_index_when_adding_char_to_seach() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).down();

        assert_eq!(search.append_to_search("o").current, 0);
    }

    #[test]
    fn previous_results_appear_after_backspace() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("t");

        assert_eq!(search.backspace().num_matches(), 3);
    }

    #[test]
    fn initial_search_is_not_done() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config);

        assert!(!search.is_done());
    }

    #[test]
    fn done_search_is_done() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).done();

        assert!(search.is_done());
    }

    #[test]
    fn done_search_has_selection() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).done();

        assert_eq!(search.selection, Some("one".to_string()));
    }

    #[test]
    fn loop_around_when_reaching_bottom_of_choices() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("n").down();

        assert_eq!(search.selection, Some("one".to_string()));
    }

    #[test]
    fn loop_around_when_reaching_top_of_choices() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("n").up();

        assert_eq!(search.selection, Some("one".to_string()));
    }

    #[test]
    fn search_is_case_insensitive() {
        let input = one_two_three();
        let config = Configuration::from_inputs(input, None, None);
        let search = Search::blank(&config).append_to_search("T");

        assert_eq!(search.num_matches(), 2);
    }

    #[test]
    fn uses_configs_visible_limit_as_result_size() {
        let input = input_times(30);

        let mut bar = Vec::new();
        for f in input.iter() {
            bar.push((*f).clone());
        }

        let config = Configuration::from_inputs(bar, None, Some(20));
        let search = Search::blank(&config).append_to_search("T");

        assert_eq!(search.num_matches(), 20);
    }

    fn input_times<'a>(n: usize) ->Vec<&'a String> {
        let mut result: Vec<&String> = Vec::new();
        for thing in one_two_three().iter().cycle().take(n) {
            result.push(thing);
        }
        result
    }

    //84114 ns/iter (+/- 33515)
    #[bench]
    fn filter_speed(b: &mut Bencher) {
        let input = input_times(1000);
        let query = "t";

        b.iter(||{
            let mut results = SortedResultSet::new(20);
            Search::iter_matches(query, &input,
                                    |choice, quality| results.push(choice, quality));
            results
        });
    }
}
<|endoftext|>


<commit_before>use color::{Color888, Colorf32};
use fastup::{self, Node, parse_for_first_node, parse_color};
use super::error_node;

#[derive(Debug, Copy, Clone, Default)]
pub struct Widget;

impl super::Widget for Widget {
    fn expand(&self, args: Vec<String>) -> Node {
        match args.len() {
            2 => node_from_args(args),
            _ => error_node("(ccdd44: cpu) takes 2 arguments: color-cold\\|color-hot"),
        }
    }
}

fn node_from_args(args: Vec<String>) -> Node {
    try_node_from_args(args).unwrap_or_else(|e| error_node(&e))
}

fn try_node_from_args(args: Vec<String>) -> Result<Node, String> {
    assert_eq!(args.len(), 2);

    let cold = parse_color(&args[0]).map_err(escape_fastup)?;
    let hot  = parse_color(&args[1]).map_err(escape_fastup)?;

    Ok(cpu_node(cold.into(), hot.into()))
}

fn cpu_node(cold: Colorf32, hot: Colorf32) -> Node {
    const BG_LIGHTNESS: f32 = 0.15;
    const FG_LIGHTNESS: f32 = 0.7;
    const IDLE_LIGHTNESS: f32 = 0.3;
    const IDLE_THRESHOLD: f32 = 0.05;
    const PROGRESS_INDICATOR: &'static str = "▁▂▃▄▅▆▇";

    let cpu_usages = cpu_usages();
    let cpu_total = {
        let sum: f32 = cpu_usages.iter().sum();
        let len = cpu_usages.len() as f32;
        sum / len
    };

    let bg_cold = cold.set_lightness(BG_LIGHTNESS);
    let bg_hot = hot.set_lightness(BG_LIGHTNESS);
    let bg = bg_cold.mix(bg_hot, cpu_total).clamp_to_888();

    let idle_cold = cold.set_lightness(IDLE_LIGHTNESS);
    let idle_hot = hot.set_lightness(IDLE_LIGHTNESS);
    let idle = idle_cold.mix(idle_hot, cpu_total).clamp_to_888();

    let fg_cold = cold.set_lightness(FG_LIGHTNESS);
    let fg_hot = hot.set_lightness(FG_LIGHTNESS);
    let make_fg_color = |usage: f32| {
        if usage <= IDLE_THRESHOLD {
            idle
        } else {
            let usage = (usage - IDLE_THRESHOLD) / (1.0 - IDLE_THRESHOLD);
            fg_cold.mix(fg_hot, usage).clamp_to_888()
        }
    };
    let make_cpu_indicator = |usage: f32| {
        let len = PROGRESS_INDICATOR.chars().count() as f32;
        let i = (len * usage).min(len - 1.0).max(0.0) as usize;
        PROGRESS_INDICATOR.chars().nth(i).unwrap()
    };

    let cpu_usages = cpu_usages
        .into_iter()
        .map(|usage| format!("({}:{})",
                make_fg_color(usage),
                make_cpu_indicator(usage)))
        .collect::<String>();
    let total_usage = {
        let color = make_fg_color(cpu_total);
        let percent = (100.0 * cpu_total) as i32;
        let percent = format!("{:>3}%", percent);
        let percent = escape_fastup(percent);
        format!("({}:{})", color, percent)
    };

    let node = format!("[{}: \\ {}{} ]", bg, cpu_usages, total_usage);
    parse_for_first_node(&node).unwrap()
}

fn cpu_usages() -> Vec<f32> {
    vec![0.0, 0.05, 0.1, 0.15, 0.3, 0.5, 0.7, 0.9, 1.0, 0.05]
}

fn escape_fastup<T>(input: T) -> String
    where T: ToString
{
    fastup::escape_for_text(&input.to_string())
}

<commit_msg>widget: cpu: use actual cpu data<commit_after>use color::{Color888, Colorf32};
use fastup::{self, Node, parse_for_first_node, parse_color};
use sysinfo::System;
use super::error_node;
use std::time::Duration;
use std::thread::sleep;

#[derive(Debug, Copy, Clone, Default)]
pub struct Widget;

impl super::Widget for Widget {
    fn expand(&self, args: Vec<String>) -> Node {
        match args.len() {
            2 => node_from_args(args),
            _ => error_node("(ccdd44: cpu) takes 2 arguments: color-cold\\|color-hot"),
        }
    }
}

fn node_from_args(args: Vec<String>) -> Node {
    try_node_from_args(args).unwrap_or_else(|e| error_node(&e))
}

fn try_node_from_args(args: Vec<String>) -> Result<Node, String> {
    assert_eq!(args.len(), 2);

    let cold = parse_color(&args[0]).map_err(escape_fastup)?;
    let hot = parse_color(&args[1]).map_err(escape_fastup)?;

    Ok(cpu_node(cold.into(), hot.into()))
}

fn cpu_node(cold: Colorf32, hot: Colorf32) -> Node {
    const BG_LIGHTNESS: f32 = 0.15;
    const FG_LIGHTNESS: f32 = 0.7;
    const IDLE_LIGHTNESS: f32 = 0.3;
    const IDLE_THRESHOLD: f32 = 0.05;
    const PROGRESS_INDICATOR: &'static str = "▁▂▃▄▅▆▇";

    let (cpu_total, cpu_usages) = cpu_usages();

    let bg_cold = cold.set_lightness(BG_LIGHTNESS);
    let bg_hot = hot.set_lightness(BG_LIGHTNESS);
    let bg = bg_cold.mix(bg_hot, cpu_total).clamp_to_888();

    let idle_cold = cold.set_lightness(IDLE_LIGHTNESS);
    let idle_hot = hot.set_lightness(IDLE_LIGHTNESS);
    let idle = idle_cold.mix(idle_hot, cpu_total).clamp_to_888();

    let fg_cold = cold.set_lightness(FG_LIGHTNESS);
    let fg_hot = hot.set_lightness(FG_LIGHTNESS);
    let make_fg_color = |usage: f32| {
        if usage <= IDLE_THRESHOLD {
            idle
        } else {
            let usage = (usage - IDLE_THRESHOLD) / (1.0 - IDLE_THRESHOLD);
            fg_cold.mix(fg_hot, usage).clamp_to_888()
        }
    };
    let make_cpu_indicator = |usage: f32| {
        let len = PROGRESS_INDICATOR.chars().count() as f32;
        let i = (len * usage).min(len - 1.0).max(0.0) as usize;
        PROGRESS_INDICATOR.chars().nth(i).unwrap()
    };

    let cpu_usages = cpu_usages.into_iter()
        .map(|usage| format!("({}:{})", make_fg_color(usage), make_cpu_indicator(usage)))
        .collect::<String>();
    let total_usage = {
        let color = make_fg_color(cpu_total);
        let percent = (100.0 * cpu_total) as i32;
        let percent = format!("{:>3}%", percent);
        let percent = escape_fastup(percent);
        format!("({}:{})", color, percent)
    };

    let node = format!("[{}: \\ {}{} ]", bg, cpu_usages, total_usage);
    parse_for_first_node(&node).unwrap()
}

/// return (total, [usage])
fn cpu_usages() -> (f32, Vec<f32>) {
    let mut sys = System::new();
    // FIXME: Dirty hack by sleeping.
    // System refreshing should be done globally.
    sleep(Duration::from_millis(100));
    sys.refresh_system();
    let p = sys.get_processor_list();
    let total = p[0].get_cpu_usage();
    let usages = p.iter()
        .skip(1)
        .map(|x| x.get_cpu_usage())
        .collect::<Vec<_>>();
    (total, usages)
}

fn escape_fastup<T>(input: T) -> String
    where T: ToString
{
    fastup::escape_for_text(&input.to_string())
}

<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::mem;
use std::ptr;
use std::str;
use std::c_str::ToCStr;
use cgmath::{Matrix4};
use gl;
use gl::types::{GLuint, GLint, GLenum, GLchar};
use zgl::{Zgl};
use core_types::{ZInt};
use visualizer_types::{ZFloat, Color4, ColorId, MatId};

pub struct Shader {
    id: GLuint,
}

impl Shader {
    pub fn new(zgl: &Zgl, vs_src: &str, fs_src: &str) -> Shader {
        let vs = compile_shader(zgl, vs_src, gl::VERTEX_SHADER);
        let fs = compile_shader(zgl, fs_src, gl::FRAGMENT_SHADER);
        let program_id = link_program(zgl, vs, fs);
        Shader{id: program_id}
    }

    pub fn activate(&self, zgl: &Zgl) {
        unsafe {
            zgl.gl.UseProgram(self.id);
        }
        zgl.check();
    }

    pub fn set_uniform_mat4f(&self, zgl: &Zgl, mat_id: &MatId, mat: &Matrix4<ZFloat>) {
        unsafe {
            let data_ptr = mem::transmute(mat);
            // TODO: give name to magic parameters
            zgl.gl.UniformMatrix4fv(mat_id.id as ZInt, 1, gl::FALSE, data_ptr);
        }
        zgl.check();
    }

    pub fn set_uniform_color(&self, zgl: &Zgl, color_id: &ColorId, color: &Color4) {
        unsafe {
            let data_ptr = mem::transmute(color);
            zgl.gl.Uniform4fv(color_id.id as ZInt, 1, data_ptr);
        }
        zgl.check();
    }

    pub fn get_uniform_color(&self, zgl: &Zgl, name: &str) -> ColorId {
        let id = self.get_uniform(zgl, name);
        ColorId{id: id as GLuint}
    }

    pub fn get_uniform_mat(&self, zgl: &Zgl, name: &str) -> MatId {
        let id = self.get_uniform(zgl, name);
        MatId{id: id as GLuint}
    }

    fn get_uniform(&self, zgl: &Zgl, name: &str) -> GLuint {
        let id = name.with_c_str(|name| {
            unsafe {
                zgl.gl.GetUniformLocation(self.id, name) as GLuint
            }
        });
        assert!(id != -1);
        zgl.check();
        id
    }
}

fn compile_shader(zgl: &Zgl, src: &str, ty: GLenum) -> GLuint {
    let shader;
    unsafe {
        shader = zgl.gl.CreateShader(ty);
        zgl.check();
        src.with_c_str(|ptr| zgl.gl.ShaderSource(shader, 1, &ptr, ptr::null()));
        zgl.check();
        zgl.gl.CompileShader(shader);
        zgl.check();
        let mut status = gl::FALSE as GLint;
        zgl.gl.GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
        if status != gl::TRUE as GLint {
            let mut len = 0;
            zgl.gl.GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            // subtract 1 to skip the trailing null character
            let mut buf = Vec::with_capacity(len as uint - 1);
            zgl.gl.GetShaderInfoLog(
                shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice())
                .ok().expect("ShaderInfoLog not valid utf8"));
        }
    }
    shader
}

fn link_program(zgl: &Zgl, vs: GLuint, fs: GLuint) -> GLuint {
    unsafe {
        let program = zgl.gl.CreateProgram();
        zgl.check();
        zgl.gl.AttachShader(program, vs);
        zgl.check();
        zgl.gl.AttachShader(program, fs);
        zgl.check();
        zgl.gl.LinkProgram(program);
        zgl.check();
        zgl.gl.DeleteShader(vs);
        zgl.check();
        zgl.gl.DeleteShader(fs);
        zgl.check();
        zgl.gl.UseProgram(program);
        zgl.check();
        zgl.gl.DeleteProgram(program); // mark for deletion
        zgl.check();
        let mut status = gl::FALSE as GLint;
        zgl.gl.GetProgramiv(program, gl::LINK_STATUS, &mut status);
        if status != gl::TRUE as GLint {
            let mut len: GLint = 0;
            zgl.gl.GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            // subtract 1 to skip the trailing null character
            let mut buf = Vec::with_capacity(len as uint - 1);
            zgl.gl.GetProgramInfoLog(
                program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice())
                .ok().expect("ProgramInfoLog not valid utf8"));
        }
        program
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>src/shader.rs: Removed magic parameters<commit_after>// See LICENSE file for copyright and license details.

use std::mem;
use std::ptr;
use std::str;
use std::c_str::ToCStr;
use cgmath::{Matrix4};
use gl;
use gl::types::{GLuint, GLint, GLenum, GLchar};
use zgl::{Zgl};
use core_types::{ZInt};
use visualizer_types::{ZFloat, Color4, ColorId, MatId};

pub struct Shader {
    id: GLuint,
}

impl Shader {
    pub fn new(zgl: &Zgl, vs_src: &str, fs_src: &str) -> Shader {
        let vs = compile_shader(zgl, vs_src, gl::VERTEX_SHADER);
        let fs = compile_shader(zgl, fs_src, gl::FRAGMENT_SHADER);
        let program_id = link_program(zgl, vs, fs);
        Shader{id: program_id}
    }

    pub fn activate(&self, zgl: &Zgl) {
        unsafe {
            zgl.gl.UseProgram(self.id);
        }
        zgl.check();
    }

    pub fn set_uniform_mat4f(&self, zgl: &Zgl, mat_id: &MatId, mat: &Matrix4<ZFloat>) {
        let count = 1;
        let transpose = gl::FALSE;
        unsafe {
            let data_ptr = mem::transmute(mat);
            zgl.gl.UniformMatrix4fv(
                mat_id.id as ZInt, count, transpose, data_ptr);
        }
        zgl.check();
    }

    pub fn set_uniform_color(&self, zgl: &Zgl, color_id: &ColorId, color: &Color4) {
        unsafe {
            let data_ptr = mem::transmute(color);
            zgl.gl.Uniform4fv(color_id.id as ZInt, 1, data_ptr);
        }
        zgl.check();
    }

    pub fn get_uniform_color(&self, zgl: &Zgl, name: &str) -> ColorId {
        let id = self.get_uniform(zgl, name);
        ColorId{id: id as GLuint}
    }

    pub fn get_uniform_mat(&self, zgl: &Zgl, name: &str) -> MatId {
        let id = self.get_uniform(zgl, name);
        MatId{id: id as GLuint}
    }

    fn get_uniform(&self, zgl: &Zgl, name: &str) -> GLuint {
        let id = name.with_c_str(|name| {
            unsafe {
                zgl.gl.GetUniformLocation(self.id, name) as GLuint
            }
        });
        assert!(id != -1);
        zgl.check();
        id
    }
}

fn compile_shader(zgl: &Zgl, src: &str, ty: GLenum) -> GLuint {
    let shader;
    unsafe {
        shader = zgl.gl.CreateShader(ty);
        zgl.check();
        src.with_c_str(|ptr| zgl.gl.ShaderSource(shader, 1, &ptr, ptr::null()));
        zgl.check();
        zgl.gl.CompileShader(shader);
        zgl.check();
        let mut status = gl::FALSE as GLint;
        zgl.gl.GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
        if status != gl::TRUE as GLint {
            let mut len = 0;
            zgl.gl.GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            // subtract 1 to skip the trailing null character
            let mut buf = Vec::with_capacity(len as uint - 1);
            zgl.gl.GetShaderInfoLog(
                shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice())
                .ok().expect("ShaderInfoLog not valid utf8"));
        }
    }
    shader
}

fn link_program(zgl: &Zgl, vs: GLuint, fs: GLuint) -> GLuint {
    unsafe {
        let program = zgl.gl.CreateProgram();
        zgl.check();
        zgl.gl.AttachShader(program, vs);
        zgl.check();
        zgl.gl.AttachShader(program, fs);
        zgl.check();
        zgl.gl.LinkProgram(program);
        zgl.check();
        zgl.gl.DeleteShader(vs);
        zgl.check();
        zgl.gl.DeleteShader(fs);
        zgl.check();
        zgl.gl.UseProgram(program);
        zgl.check();
        zgl.gl.DeleteProgram(program); // mark for deletion
        zgl.check();
        let mut status = gl::FALSE as GLint;
        zgl.gl.GetProgramiv(program, gl::LINK_STATUS, &mut status);
        if status != gl::TRUE as GLint {
            let mut len: GLint = 0;
            zgl.gl.GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            // subtract 1 to skip the trailing null character
            let mut buf = Vec::with_capacity(len as uint - 1);
            zgl.gl.GetProgramInfoLog(
                program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice())
                .ok().expect("ProgramInfoLog not valid utf8"));
        }
        program
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>// Copyright 2015 Nicholas Bishop
//
// 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.

use vector;

/// Sphere represented by a center and radius
#[derive(Clone, Copy, PartialEq)]
pub struct Sphere3f {
    pub center: vector::Vec3f,
    pub radius: f32
}

impl Sphere3f {
    pub fn new(center: vector::Vec3f, radius: f32) -> Sphere3f {
        Sphere3f {
            center: center,
            radius: radius
        }
    }
}

#[test]
fn test_sphere3f_create() {
    Sphere3f::new(vector::vec3f(1, 2, 3), 4.0);
}
<commit_msg>Add constant for unit sphere<commit_after>// Copyright 2015 Nicholas Bishop
//
// 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.

use vector;

/// Sphere represented by a center and radius
#[derive(Clone, Copy, PartialEq)]
pub struct Sphere3f {
    pub center: vector::Vec3f,
    pub radius: f32
}

/// Sphere centered at (0.0, 0.0, 0.0) with a radius of 1.0
pub const UNIT: Sphere3f = Sphere3f { center: vector::ZERO_3F, radius: 1.0 };

impl Sphere3f {
    pub fn new(center: vector::Vec3f, radius: f32) -> Sphere3f {
        Sphere3f {
            center: center,
            radius: radius
        }
    }
}

#[test]
fn test_sphere3f_create() {
    Sphere3f::new(vector::vec3f(1, 2, 3), 4.0);
}
<|endoftext|>


<commit_before>use std::os::raw::{c_int, c_double, c_void};

use ffi;
use base::*;

extern "C" fn write_callback_wrapper<W>(raw_out: *mut ffi::SoundIoOutStream,
                                        min: c_int,
                                        max: c_int)
    where W: Fn(OutStream, i32, i32)
{
    let out = OutStream::new(raw_out);
    let callbacks_ptr = unsafe { (*out.stream).userdata as *const Box<OutStreamCallbacks> };
    let callbacks: &Box<OutStreamCallbacks> = unsafe { &*callbacks_ptr };
    callbacks.write.as_ref().map(|ref f| f(out, min as i32, max as i32));
}

extern "C" fn underflow_callback_wrapper<U>(raw_out: *mut ffi::SoundIoOutStream)
    where U: Fn(OutStream)
{
    let out = OutStream::new(raw_out);
    let callbacks_ptr = unsafe { (*out.stream).userdata as *const Box<OutStreamCallbacks> };
    let callbacks: &Box<OutStreamCallbacks> = unsafe { &*callbacks_ptr };
    callbacks.underflow.as_ref().map(|ref f| f(out));
}

extern "C" fn error_callback_wrapper<E>(raw_out: *mut ffi::SoundIoOutStream, error: ffi::SioError)
    where E: Fn(OutStream, ffi::SioError)
{
    let out = OutStream::new(raw_out);
    let callbacks_ptr = unsafe { (*out.stream).userdata as *const Box<OutStreamCallbacks> };
    let callbacks: &Box<OutStreamCallbacks> = unsafe { &*callbacks_ptr };
    callbacks.error.as_ref().map(|ref f| f(out, error));
}

struct OutStreamCallbacks<'a> {
    write: Option<Box<Fn(OutStream, i32, i32) + 'a>>,
    underflow: Option<Box<Fn(OutStream) + 'a>>,
    error: Option<Box<Fn(OutStream, ffi::SioError) + 'a>>,
}
impl<'a> Default for OutStreamCallbacks<'a> {
    fn default() -> Self {
        OutStreamCallbacks {
            write: None,
            underflow: None,
            error: None,
        }
    }
}
impl<'a> Drop for OutStreamCallbacks<'a> {
    fn drop(&mut self) {}
}

pub struct OutStream<'a> {
    stream: *mut ffi::SoundIoOutStream,
    callbacks: Box<OutStreamCallbacks<'a>>,
}
impl<'a> OutStream<'a> {
    pub fn new(raw_stream: *mut ffi::SoundIoOutStream) -> Self {
        let callbacks = Box::new(OutStreamCallbacks::default());
        OutStream {
            stream: raw_stream,
            callbacks: callbacks,
        }
    }

    pub fn open(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_open(self.stream) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn start(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_start(self.stream) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn register_write_callback<W>(&mut self, callback: Box<W>)
        where W: Fn(OutStream, i32, i32) + 'a
    {
        // stored box reference to callback closure
        self.callbacks.write = Some(callback);
        unsafe {
            // register wrapper for write_callback
            (*self.stream).write_callback = Some(write_callback_wrapper::<W>);
            // store reference to callbacks struct in userdata pointer
            (*self.stream).userdata =
                &self.callbacks as *const Box<OutStreamCallbacks> as *mut c_void
        }
    }

    pub fn register_underflow_callback<U>(&mut self, callback: Box<U>)
        where U: Fn(OutStream) + 'a
    {
        self.callbacks.underflow = Some(callback);
        unsafe {
            // register wrapper for write_callback
            (*self.stream).underflow_callback = Some(underflow_callback_wrapper::<U>);
            // store reference to callbacks struct in userdata pointer
            (*self.stream).userdata =
                &self.callbacks as *const Box<OutStreamCallbacks> as *mut c_void
        }
    }

    pub fn register_error_callback<E>(&mut self, callback: Box<E>)
        where E: Fn(OutStream, ffi::SioError) + 'a
    {
        self.callbacks.error = Some(callback);
        unsafe {
            // register wrapper for write_callback
            (*self.stream).error_callback = Some(error_callback_wrapper::<E>);
            // store reference to callbacks struct in userdata pointer
            (*self.stream).userdata =
                &self.callbacks as *const Box<OutStreamCallbacks> as *mut c_void
        }
    }

    pub fn begin_write(&self,
                       areas: *mut *mut ffi::SoundIoChannelArea,
                       frame_count: *mut c_int)
                       -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_begin_write(self.stream, areas, frame_count) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn end_write(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_end_write(self.stream) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn clear_buffer(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_clear_buffer(self.stream) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn pause(&self, pause: bool) -> Option<ffi::SioError> {
        let pause_c_bool = match pause {
            true => 1u8,
            false => 0u8,
        };
        match unsafe { ffi::soundio_outstream_pause(self.stream, pause_c_bool) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn get_latency(&self) -> Result<f64, ffi::SioError> {
        let mut latency = 0.0f64;
        match unsafe {
            ffi::soundio_outstream_get_latency(self.stream, &mut latency as *mut c_double)
        } {
            ffi::SioError::None => Ok(latency),
            err @ _ => Err(err),
        }

    }

    pub fn current_format(&self) -> Result<ffi::SioFormat, ffi::SioError> {
        match unsafe { (*self.stream).format } {
            ffi::SioFormat::Invalid => Err(ffi::SioError::Invalid),
            fmt @ _ => Ok(fmt),
        }
    }

    pub fn get_layout(&self) -> ChannelLayout {
        ChannelLayout::new(unsafe { &(*self.stream).layout })
    }

    pub fn get_sample_rate(&self) -> i32 {
        unsafe { (*self.stream).sample_rate as i32 }
    }

    pub fn get_device(&self) -> Device {
        let dev = Device::new(unsafe { (*self.stream).device });
        dev.inc_ref();
        dev
    }

    pub fn destroy(&self) {
        unsafe { ffi::soundio_outstream_destroy(self.stream) }
    }
}
impl<'a> Drop for OutStream<'a> {
    fn drop(&mut self) {
        // TODO: call destroy manually.
        // OutStream will get dropped each time a new
        // struct is created from the same *mut pointer.
    }
}
<commit_msg>Shorten callback wrapper names<commit_after>use std::os::raw::{c_int, c_double, c_void};

use ffi;
use base::*;

extern "C" fn write_wrapper<W>(raw_out: *mut ffi::SoundIoOutStream, min: c_int, max: c_int)
    where W: Fn(OutStream, i32, i32)
{
    let out = OutStream::new(raw_out);
    let callbacks_ptr = unsafe { (*out.stream).userdata as *const Box<OutStreamCallbacks> };
    let callbacks: &Box<OutStreamCallbacks> = unsafe { &*callbacks_ptr };
    callbacks.write.as_ref().map(|ref f| f(out, min as i32, max as i32));
}

extern "C" fn underflow_wrapper<U>(raw_out: *mut ffi::SoundIoOutStream)
    where U: Fn(OutStream)
{
    let out = OutStream::new(raw_out);
    let callbacks_ptr = unsafe { (*out.stream).userdata as *const Box<OutStreamCallbacks> };
    let callbacks: &Box<OutStreamCallbacks> = unsafe { &*callbacks_ptr };
    callbacks.underflow.as_ref().map(|ref f| f(out));
}

extern "C" fn error_wrapper<E>(raw_out: *mut ffi::SoundIoOutStream, error: ffi::SioError)
    where E: Fn(OutStream, ffi::SioError)
{
    let out = OutStream::new(raw_out);
    let callbacks_ptr = unsafe { (*out.stream).userdata as *const Box<OutStreamCallbacks> };
    let callbacks: &Box<OutStreamCallbacks> = unsafe { &*callbacks_ptr };
    callbacks.error.as_ref().map(|ref f| f(out, error));
}

struct OutStreamCallbacks<'a> {
    write: Option<Box<Fn(OutStream, i32, i32) + 'a>>,
    underflow: Option<Box<Fn(OutStream) + 'a>>,
    error: Option<Box<Fn(OutStream, ffi::SioError) + 'a>>,
}
impl<'a> Default for OutStreamCallbacks<'a> {
    fn default() -> Self {
        OutStreamCallbacks {
            write: None,
            underflow: None,
            error: None,
        }
    }
}
impl<'a> Drop for OutStreamCallbacks<'a> {
    fn drop(&mut self) {}
}

pub struct OutStream<'a> {
    stream: *mut ffi::SoundIoOutStream,
    callbacks: Box<OutStreamCallbacks<'a>>,
}
impl<'a> OutStream<'a> {
    pub fn new(raw_stream: *mut ffi::SoundIoOutStream) -> Self {
        let callbacks = Box::new(OutStreamCallbacks::default());
        OutStream {
            stream: raw_stream,
            callbacks: callbacks,
        }
    }

    pub fn open(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_open(self.stream) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn start(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_start(self.stream) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn register_write_callback<W>(&mut self, callback: Box<W>)
        where W: Fn(OutStream, i32, i32) + 'a
    {
        // stored box reference to callback closure
        self.callbacks.write = Some(callback);
        unsafe {
            // register wrapper for write_callback
            (*self.stream).write_callback = Some(write_wrapper::<W>);
            // store reference to callbacks struct in userdata pointer
            (*self.stream).userdata =
                &self.callbacks as *const Box<OutStreamCallbacks> as *mut c_void
        }
    }

    pub fn register_underflow_callback<U>(&mut self, callback: Box<U>)
        where U: Fn(OutStream) + 'a
    {
        self.callbacks.underflow = Some(callback);
        unsafe {
            // register wrapper for write_callback
            (*self.stream).underflow_callback = Some(underflow_wrapper::<U>);
            // store reference to callbacks struct in userdata pointer
            (*self.stream).userdata =
                &self.callbacks as *const Box<OutStreamCallbacks> as *mut c_void
        }
    }

    pub fn register_error_callback<E>(&mut self, callback: Box<E>)
        where E: Fn(OutStream, ffi::SioError) + 'a
    {
        self.callbacks.error = Some(callback);
        unsafe {
            // register wrapper for write_callback
            (*self.stream).error_callback = Some(error_wrapper::<E>);
            // store reference to callbacks struct in userdata pointer
            (*self.stream).userdata =
                &self.callbacks as *const Box<OutStreamCallbacks> as *mut c_void
        }
    }

    pub fn begin_write(&self,
                       areas: *mut *mut ffi::SoundIoChannelArea,
                       frame_count: *mut c_int)
                       -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_begin_write(self.stream, areas, frame_count) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn end_write(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_end_write(self.stream) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn clear_buffer(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_outstream_clear_buffer(self.stream) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn pause(&self, pause: bool) -> Option<ffi::SioError> {
        let pause_c_bool = match pause {
            true => 1u8,
            false => 0u8,
        };
        match unsafe { ffi::soundio_outstream_pause(self.stream, pause_c_bool) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn get_latency(&self) -> Result<f64, ffi::SioError> {
        let mut latency = 0.0f64;
        match unsafe {
            ffi::soundio_outstream_get_latency(self.stream, &mut latency as *mut c_double)
        } {
            ffi::SioError::None => Ok(latency),
            err @ _ => Err(err),
        }

    }

    pub fn current_format(&self) -> Result<ffi::SioFormat, ffi::SioError> {
        match unsafe { (*self.stream).format } {
            ffi::SioFormat::Invalid => Err(ffi::SioError::Invalid),
            fmt @ _ => Ok(fmt),
        }
    }

    pub fn get_layout(&self) -> ChannelLayout {
        ChannelLayout::new(unsafe { &(*self.stream).layout })
    }

    pub fn get_sample_rate(&self) -> i32 {
        unsafe { (*self.stream).sample_rate as i32 }
    }

    pub fn get_device(&self) -> Device {
        let dev = Device::new(unsafe { (*self.stream).device });
        dev.inc_ref();
        dev
    }

    pub fn destroy(&self) {
        unsafe { ffi::soundio_outstream_destroy(self.stream) }
    }
}
impl<'a> Drop for OutStream<'a> {
    fn drop(&mut self) {
        // TODO: call destroy manually.
        // OutStream will get dropped each time a new
        // struct is created from the same *mut pointer.
    }
}
<|endoftext|>


<commit_before>use std::collections::{VecDeque};

fn hex_to_digit(c: u8) -> Result<u8, &'static str> {
    Ok(match c {
        b'0' ... b'9' => c - b'0',
        b'a' ... b'f' => c - b'a' + 10,
        b'A' ... b'F' => c - b'A' + 10,
        _ => return Err("Invalid hex value"),
    })
}

fn oct_to_digit(c: u8) -> Result<u8, &'static str> {
    Ok(match c {
        b'0' ... b'7' => c - b'0',
        _ => return Err("Invalid oct value"),
    })
}

fn unescape(s: &[u8]) -> Result<Vec<u8>, &'static str> {
    let mut buf = Vec::with_capacity(s.len());
    let mut oct_buf = VecDeque::with_capacity(3);

    let mut i = 0;

    macro_rules! read {
        () => ({
            match s.get(i) {
                None => return Err("Invalid escape"),
                Some(c) => {
                    i += 1;
                    *c
                }
            }
        })
    }

    macro_rules! peek {
        () => ({
            s.get(i).map(|c| *c)
        })
    }

    loop {
        if i >= s.len() {
            return Ok(buf)
        }

        let c = read!();

        if c != b'\\' {
            buf.push(c);
            continue
        }

        let marker = read!();

        match marker {
            b'\n' => (),
            b'\\' => buf.push(b'\\'),
            b'\'' => buf.push(b'\''),
            b'"' => buf.push(b'"'),
            b'a' => buf.push(b'\x07'),
            b'b' => buf.push(b'\x08'),
            b'f' => buf.push(b'\x0c'),
            b'n' => buf.push(b'\n'),
            b'r' => buf.push(b'\r'),
            b't' => buf.push(b'\t'),
            b'v' => buf.push(b'\x0b'),
            b'x' => {
                let first = try!(hex_to_digit(read!()));
                let second = try!(hex_to_digit(read!()));
                buf.push(first * 15 + second)
            }
            b'0' ... b'7' => {
                oct_buf.push_front(try!(oct_to_digit(marker)));
                peek!().and_then(|c| oct_to_digit(c).ok()).map(|s| {
                    oct_buf.push_front(s);
                    i += 1;

                    peek!().and_then(|c| oct_to_digit(c).ok()).map(|s| {
                        oct_buf.push_front(s);
                        i += 1;
                    });
                });
                let value = oct_buf.iter().enumerate().fold(0u16, |acc, (i, &v)| acc + v as u16 * (8u16.pow(i as u32)));
                oct_buf.clear();
                buf.push(if value > 255 {
                    255
                } else {
                    value as u8
                });
                continue
            },
            _ => {
                buf.push(b'\\');
                buf.push(marker);
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use super::{unescape};

    #[test]
    fn test_unescape() {
        assert_eq!(unescape(b"foo").unwrap(), b"foo");
        assert_eq!(unescape(b"f\\noo").unwrap(), b"f\noo");
        assert_eq!(unescape(b"f\\x01oo").unwrap(), b"f\x01oo");
        assert_eq!(unescape(b"f\\375oo").unwrap(), b"f\xfdoo");
        assert_eq!(unescape(b"f\\75oo").unwrap(), b"f\x3doo");
        assert_eq!(unescape(b"f\\5oo").unwrap(), b"f\x05oo");
        assert_eq!(unescape(b"f\\oo").unwrap(), b"f\\oo");
        assert_eq!(unescape(b"f\\coo").unwrap(), b"f\\coo");
    }
}<commit_msg>Add error type for unescape errors<commit_after>use std::collections::{VecDeque};

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        InvalidHexValue(c: u8)
        InvalidOctValue(c: u8)
        UnexpectedEnd
    }
}

fn hex_to_digit(c: u8) -> Result<u8, Error> {
    Ok(match c {
        b'0' ... b'9' => c - b'0',
        b'a' ... b'f' => c - b'a' + 10,
        b'A' ... b'F' => c - b'A' + 10,
        _ => return Err(Error::InvalidHexValue(c)),
    })
}

fn oct_to_digit(c: u8) -> Result<u8, Error> {
    Ok(match c {
        b'0' ... b'7' => c - b'0',
        _ => return Err(Error::InvalidOctValue(c)),
    })
}

fn unescape(s: &[u8]) -> Result<Vec<u8>, Error> {
    let mut buf = Vec::with_capacity(s.len());
    let mut oct_buf = VecDeque::with_capacity(3);

    let mut i = 0;

    macro_rules! read {
        () => ({
            match s.get(i) {
                None => return Err(Error::UnexpectedEnd),
                Some(c) => {
                    i += 1;
                    *c
                }
            }
        })
    }

    macro_rules! peek {
        () => ({
            s.get(i).map(|c| *c)
        })
    }

    loop {
        if i >= s.len() {
            return Ok(buf)
        }

        let c = read!();

        if c != b'\\' {
            buf.push(c);
            continue
        }

        let marker = read!();

        match marker {
            b'\n' => (),
            b'\\' => buf.push(b'\\'),
            b'\'' => buf.push(b'\''),
            b'"' => buf.push(b'"'),
            b'a' => buf.push(b'\x07'),
            b'b' => buf.push(b'\x08'),
            b'f' => buf.push(b'\x0c'),
            b'n' => buf.push(b'\n'),
            b'r' => buf.push(b'\r'),
            b't' => buf.push(b'\t'),
            b'v' => buf.push(b'\x0b'),
            b'x' => {
                let first = try!(hex_to_digit(read!()));
                let second = try!(hex_to_digit(read!()));
                buf.push(first * 15 + second)
            }
            b'0' ... b'7' => {
                oct_buf.push_front(try!(oct_to_digit(marker)));
                peek!().and_then(|c| oct_to_digit(c).ok()).map(|s| {
                    oct_buf.push_front(s);
                    i += 1;

                    peek!().and_then(|c| oct_to_digit(c).ok()).map(|s| {
                        oct_buf.push_front(s);
                        i += 1;
                    });
                });
                let value = oct_buf.iter().enumerate().fold(0u16, |acc, (i, &v)| acc + v as u16 * (8u16.pow(i as u32)));
                oct_buf.clear();
                buf.push(if value > 255 {
                    255
                } else {
                    value as u8
                });
                continue
            },
            _ => {
                buf.push(b'\\');
                buf.push(marker);
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use super::{unescape};

    #[test]
    fn test_unescape() {
        assert_eq!(unescape(b"foo").unwrap(), b"foo");
        assert_eq!(unescape(b"f\\noo").unwrap(), b"f\noo");
        assert_eq!(unescape(b"f\\x01oo").unwrap(), b"f\x01oo");
        assert_eq!(unescape(b"f\\375oo").unwrap(), b"f\xfdoo");
        assert_eq!(unescape(b"f\\75oo").unwrap(), b"f\x3doo");
        assert_eq!(unescape(b"f\\5oo").unwrap(), b"f\x05oo");
        assert_eq!(unescape(b"f\\oo").unwrap(), b"f\\oo");
        assert_eq!(unescape(b"f\\coo").unwrap(), b"f\\coo");
    }
}<|endoftext|>


<commit_before>// Filesystem scanning.

use ::Result;
use escape::*;
use suretree::{SureFile, SureTree};

use std::collections::BTreeMap;
use std::fs::{self, symlink_metadata, Metadata};
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};
use libc;

pub fn scan_fs(root: &str) -> Result<SureTree> {
    let root = PathBuf::from(root);

    walk_root(&root)
}

fn walk_root(path: &Path) -> Result<SureTree> {
    let meta = try!(symlink_metadata(path));

    if !meta.is_dir() {
        return Err(From::from("Root must be a directory"));
    }

    walk("__root__".to_string(), path, encode_atts(path, &meta), &meta)
}

fn walk(my_name: String, path: &Path, my_atts: BTreeMap<String, String>, my_meta: &Metadata)
    -> Result<SureTree>
{
    let mut entries = vec![];

    // TODO: Instead of failing everything because of read failure, just
    // fail some things.

    for entry in try!(fs::read_dir(path)) {
        let entry = try!(entry);
        entries.push(entry);
    }

    // Sort by inode before sorting.  This helps performance on some
    // filesystems (such as ext4).
    entries.sort_by(|a, b| a.ino().cmp(&b.ino()));

    let mut files: Vec<_> = entries.iter().filter_map(|e| {
        match e.metadata() {
            Ok(m) => {
                let path = e.path();
                let atts = encode_atts(&path, &m);

                // Check for crossing mountpoints.
                if m.is_dir() && m.dev() != my_meta.dev() {
                    return None;
                }

                Some(OneFile {
                    path: path,
                    meta: m,
                    atts: atts,
                })
            },
            Err(err) => {
                error!("Unable to stat file: {:?} ({})", e.path(), err);
                None
            },
        }
    }).collect();

    // Sort them back by name.
    files.sort_by(|a, b| a.path.file_name().cmp(&b.path.file_name()));

    // Build the SureTree data.
    let mut node = SureTree {
        name: my_name,
        atts: my_atts,
        children: Vec::new(),
        files: Vec::new(),
    };

    // Process all of the nodes.
    for ch in files {
        if ch.meta.is_dir() {
            let child_name = ch.path.file_name().unwrap().as_bytes().escaped();
            let child = try!(walk(child_name, &ch.path, ch.atts, &ch.meta));
            node.children.push(child);
        } else {
            let child_name = ch.path.file_name().unwrap().as_bytes().escaped();
            let child = SureFile {
                name: child_name,
                atts: ch.atts,
            };
            node.files.push(child);
        }
    }

    Ok(node)
}

// Encode the attributes for the given node.  Note that this returns, even
// when there is an error (resolving a symlink).  It logs an error, and
// returns a placeholder.
fn encode_atts(name: &Path, meta: &Metadata) -> BTreeMap<String, String> {
    // let fname = name.file_name().unwrap().as_bytes().escaped();
    let mode = meta.mode() & libc::S_IFMT;

    let mut base = BTreeMap::new();

    // These attributes apply to every node.
    base.insert("uid".to_string(), meta.uid().to_string());
    base.insert("gid".to_string(), meta.gid().to_string());
    base.insert("perm".to_string(), (meta.mode() & !libc::S_IFMT).to_string());

    // Other permissions are based on the type of the node.
    match mode {
        libc::S_IFDIR => {
            base.insert("kind".to_string(), "dir".to_string());
        },
        libc::S_IFREG => {
            base.insert("kind".to_string(), "reg".to_string());
            base.insert("ino".to_string(), meta.ino().to_string());
            time_info(&mut base, meta);
            // Note that the 'sha1' attribute is computed later.
        },
        libc::S_IFLNK => {
            base.insert("kind".to_string(), "lnk".to_string());
            let link = match fs::read_link(name) {
                Ok(l) => l,
                Err(err) => {
                    error!("Unable to read link: {:?} ({})", name, err);
                    // TODO: Generate a unique placeholder so this will
                    // always show up.
                    From::from("???")
                },
            };
            base.insert("lnk".to_string(), link.as_os_str().as_bytes().escaped());
        },
        libc::S_IFIFO => {
            base.insert("kind".to_string(), "fifo".to_string());
        },
        /* libc::S_IFSOCK */ 0o140000 => {
            base.insert("kind".to_string(), "sock".to_string());
        },
        libc::S_IFCHR => {
            base.insert("kind".to_string(), "chr".to_string());
            add_dev(&mut base, meta);
        },
        libc::S_IFBLK => {
            base.insert("kind".to_string(), "blk".to_string());
            add_dev(&mut base, meta);
        },
        _ => panic!("Unknown file type: 0o{:o}", mode),
    }

    // println!("{:?}: atts: {:?}", fname, base);
    base
}

fn add_dev(base: &mut BTreeMap<String, String>, meta: &Metadata) {
    let rdev = meta.rdev();
    // This is defined in a macro, and hasn't made it into libc.  Given how
    // it is defined in the header, it is unlikely to change, at least on
    // Linux.
    base.insert("devmaj".to_string(), ((rdev >> 8) & 0xfff).to_string());
    base.insert("devmin".to_string(), (rdev & 0xff).to_string());
}

fn time_info(base: &mut BTreeMap<String, String>, meta: &Metadata) {
    // TODO: Handle the nsec part of the time.
    base.insert("mtime".to_string(), meta.mtime().to_string());
    base.insert("ctime".to_string(), meta.ctime().to_string());
}

// Temp struct to hold information on intermediate files.
struct OneFile {
    path: PathBuf,
    meta: Metadata,
    atts: BTreeMap<String, String>,
}
<commit_msg>surefs: Fixup data stored<commit_after>// Filesystem scanning.

use ::Result;
use escape::*;
use suretree::{SureFile, SureTree};

use std::collections::BTreeMap;
use std::fs::{self, symlink_metadata, Metadata};
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};
use libc;

pub fn scan_fs(root: &str) -> Result<SureTree> {
    let root = PathBuf::from(root);

    walk_root(&root)
}

fn walk_root(path: &Path) -> Result<SureTree> {
    let meta = try!(symlink_metadata(path));

    if !meta.is_dir() {
        return Err(From::from("Root must be a directory"));
    }

    walk("__root__".to_string(), path, encode_atts(path, &meta), &meta)
}

fn walk(my_name: String, path: &Path, my_atts: BTreeMap<String, String>, my_meta: &Metadata)
    -> Result<SureTree>
{
    let mut entries = vec![];

    // TODO: Instead of failing everything because of read failure, just
    // fail some things.

    for entry in try!(fs::read_dir(path)) {
        let entry = try!(entry);
        entries.push(entry);
    }

    // Sort by inode before sorting.  This helps performance on some
    // filesystems (such as ext4).
    entries.sort_by(|a, b| a.ino().cmp(&b.ino()));

    let mut files: Vec<_> = entries.iter().filter_map(|e| {
        match e.metadata() {
            Ok(m) => {
                let path = e.path();
                let atts = encode_atts(&path, &m);

                // Check for crossing mountpoints.
                if m.is_dir() && m.dev() != my_meta.dev() {
                    return None;
                }

                Some(OneFile {
                    path: path,
                    meta: m,
                    atts: atts,
                })
            },
            Err(err) => {
                error!("Unable to stat file: {:?} ({})", e.path(), err);
                None
            },
        }
    }).collect();

    // Sort them back by name.
    files.sort_by(|a, b| a.path.file_name().cmp(&b.path.file_name()));

    // Build the SureTree data.
    let mut node = SureTree {
        name: my_name,
        atts: my_atts,
        children: Vec::new(),
        files: Vec::new(),
    };

    // Process all of the nodes.
    for ch in files {
        if ch.meta.is_dir() {
            let child_name = ch.path.file_name().unwrap().as_bytes().escaped();
            let child = try!(walk(child_name, &ch.path, ch.atts, &ch.meta));
            node.children.push(child);
        } else {
            let child_name = ch.path.file_name().unwrap().as_bytes().escaped();
            let child = SureFile {
                name: child_name,
                atts: ch.atts,
            };
            node.files.push(child);
        }
    }

    Ok(node)
}

// Encode the attributes for the given node.  Note that this returns, even
// when there is an error (resolving a symlink).  It logs an error, and
// returns a placeholder.
fn encode_atts(name: &Path, meta: &Metadata) -> BTreeMap<String, String> {
    // let fname = name.file_name().unwrap().as_bytes().escaped();
    let mode = meta.mode() & libc::S_IFMT;

    let mut base = BTreeMap::new();

    // These attributes apply to every node.
    base.insert("uid".to_string(), meta.uid().to_string());
    base.insert("gid".to_string(), meta.gid().to_string());
    base.insert("perm".to_string(), (meta.mode() & !libc::S_IFMT).to_string());

    // Other permissions are based on the type of the node.
    match mode {
        libc::S_IFDIR => {
            base.insert("kind".to_string(), "dir".to_string());
        },
        libc::S_IFREG => {
            base.insert("kind".to_string(), "file".to_string());
            base.insert("ino".to_string(), meta.ino().to_string());
            base.insert("size".to_string(), meta.size().to_string());
            time_info(&mut base, meta);
            // Note that the 'sha1' attribute is computed later.
        },
        libc::S_IFLNK => {
            base.insert("kind".to_string(), "lnk".to_string());
            let link = match fs::read_link(name) {
                Ok(l) => l,
                Err(err) => {
                    error!("Unable to read link: {:?} ({})", name, err);
                    // TODO: Generate a unique placeholder so this will
                    // always show up.
                    From::from("???")
                },
            };
            base.insert("lnk".to_string(), link.as_os_str().as_bytes().escaped());
        },
        libc::S_IFIFO => {
            base.insert("kind".to_string(), "fifo".to_string());
        },
        /* libc::S_IFSOCK */ 0o140000 => {
            base.insert("kind".to_string(), "sock".to_string());
        },
        libc::S_IFCHR => {
            base.insert("kind".to_string(), "chr".to_string());
            add_dev(&mut base, meta);
        },
        libc::S_IFBLK => {
            base.insert("kind".to_string(), "blk".to_string());
            add_dev(&mut base, meta);
        },
        _ => panic!("Unknown file type: 0o{:o}", mode),
    }

    // println!("{:?}: atts: {:?}", fname, base);
    base
}

fn add_dev(base: &mut BTreeMap<String, String>, meta: &Metadata) {
    let rdev = meta.rdev();
    // This is defined in a macro, and hasn't made it into libc.  Given how
    // it is defined in the header, it is unlikely to change, at least on
    // Linux.
    base.insert("devmaj".to_string(), ((rdev >> 8) & 0xfff).to_string());
    base.insert("devmin".to_string(), (rdev & 0xff).to_string());
}

fn time_info(base: &mut BTreeMap<String, String>, meta: &Metadata) {
    // TODO: Handle the nsec part of the time.
    base.insert("mtime".to_string(), meta.mtime().to_string());
    base.insert("ctime".to_string(), meta.ctime().to_string());
}

// Temp struct to hold information on intermediate files.
struct OneFile {
    path: PathBuf,
    meta: Metadata,
    atts: BTreeMap<String, String>,
}
<|endoftext|>


<commit_before>use super::{Upsert, Number, Integer};
use super::{Bson, Array, Object};
use std::borrow::Cow;

/* Add easy API for dot notation */

#[derive(Clone)]
pub struct Update<'a>(Object<'a>);

impl<'a> Update<'a> {
    pub fn new() -> Self {
        Update(Object::new())
    }

    pub fn field(&'a mut self, field: &'a str) -> UpdateField {
        UpdateField {
            root:   &mut self.0,
            field:  field,
        }
    }

    pub fn array(&'a mut self, array: &'a str) -> UpdateArray {
        UpdateArray {
            root:   &mut self.0,
            array:  array,
        }
    }

    pub fn isolate(mut self) -> Self {
        self.0.insert(ISOLATED, 1.to_bson_int());
        self
    }

    pub fn no_isolate(mut self) -> Self {
        self.0.remove(ISOLATED);
        self
    }
}

pub struct UpdateField<'a> {
    root:    &'a mut Object<'a>,
    field:   &'a str,
}

impl<'a> UpdateField<'a> {
    pub fn increment<N: Number<'a>>(self, amount: N) -> Self {
        self.add_modifier(INCREMENT, amount.to_bson_num())
    }

    #[inline]
    pub fn inc<N: Number<'a>>(self, amount: N) -> Self {
        self.increment(amount)
    }

    pub fn multiply<N: Number<'a>>(self, amount: N) -> Self {
        self.add_modifier(MULTIPLY, amount.to_bson_num())
    }

    #[inline]
    pub fn mul<N: Number<'a>>(self, amount: N) -> Self {
        self.multiply(amount)
    }

    pub fn min(self, value: Bson<'a>) -> Self {
        self.add_modifier(MIN, value)
    }

    pub fn max(self, value: Bson<'a>) -> Self {
        self.add_modifier(MAX, value)
    }

    pub fn and<I: Integer<'a>>(self, bits: I) -> Self {
        self.bit(AND, bits.to_bson_int())
    }

    pub fn or<I: Integer<'a>>(self, bits: I) -> Self {
        self.bit(OR, bits.to_bson_int())
    }

    pub fn xor<I: Integer<'a>>(self, bits: I) -> Self {
        self.bit(XOR, bits.to_bson_int())
    }

    pub fn set(self, value: Bson<'a>) -> Self {
        self.add_modifier(SET, value)
    }

    pub fn unset(self) -> Self {
        self.add_modifier(UNSET, Bson::Str(""))
    }

    pub fn rename(self, name: &'a str) -> Self {
        self.add_modifier(RENAME, Bson::Str(name))
    }

    pub fn set_on_insert(self, value: Bson<'a>) -> Self {
        self.add_modifier(SET_ON_INSERT, value)
    }

    pub fn set_date_now(self) -> Self {
        self.add_modifier(CURRENT_DATE, Bson::Str(DATE_TYPE))
    }

    pub fn set_timestamp_now(self) -> Self {
        self.add_modifier(CURRENT_DATE, Bson::Str(TIMESTAMP_TYPE))
    }

    #[inline]
    fn add_modifier(mut self, category: &'static str, value: Bson<'a>) -> Self {
        {
            let group = self.root.object(category);
            group.insert(self.field, value);
        }
        self
    }

    #[inline]
    fn bit(mut self, op: &'static str, bits: Bson<'a>) -> Self {
        {
	        let bits_update = self.root.object(BIT);
	        let field_ops = bits_update.object(self.field);
	        field_ops.insert(op, bits);
        }
        self
    }
}

pub struct UpdateArray<'a> {
    root:    &'a mut Object<'a>,
    array:   &'a str,
}

impl<'a> UpdateArray<'a> {
    /* TODO: Implement first-matching ( $ symbol )
     * and dot notation for arrays (including index)
     * .first_matching()
     * .index() | These can lead to either a field or another
     * .dot()   | array?
     */
    /* TODO: Refactor for less code bloat */

    pub fn push(self, value: Bson<'a>) -> Self {
        self.add_modifier(PUSH, value)
    }

    #[inline]
    pub fn push_at(self, value: Bson<'a>, position: u32) -> Self {
        self.push_pos_cow(Cow::Owned(vec![value]), position)
    }

    pub fn push_all(self, values: &'a Array<'a>) -> Self {
        {
	        let array = self.root.deep_object(PUSH, self.array);
	        array.insert(EACH, Bson::Array(Cow::Borrowed(values)));
        }
        self
    }

    #[inline]
    pub fn push_all_at(self, values: &'a Array<'a>, position: u32) -> Self {
        self.push_pos_cow(Cow::Borrowed(values), position)
    }

    pub fn slice(self, max: u32) -> Self {
        {
            let array = self.root.deep_object(PUSH, self.array);
            array.entry(EACH).or_insert_with(|| Bson::Array(Cow::Owned(Vec::new())));
            array.insert(SLICE, max.to_bson_int());
        }
        self
    }

    pub fn sort(self) -> Self {
        self.sort_array(1, None)
    }

    pub fn rev_sort(self) -> Self {
        self.sort_array(-1, None)
    }

    pub fn sort_by(self, field: &'a str) -> Self {
        self.sort_array(1, Some(field))
    }

    pub fn rev_sort_by(self, field: &'a str) -> Self {
        self.sort_array(-1, Some(field))
    }

    fn sort_array(mut self, direction: i32, spec: Option<&'a str>) -> Self {
        {
	        let array = self.root.deep_object(PUSH, self.array);
            array.entry(EACH).or_insert_with(|| Bson::Array(Cow::Owned(Vec::new())));
            if let Some(field) = spec {
                let sort = array.object(SORT);
                sort.insert(field, direction.to_bson_int());
            } else {
                array.insert(SORT, direction.to_bson_int());
            }
	    }
        self
    }

    pub fn pull(self, value: Bson<'a>) -> Self {
        self.add_modifier(PULL, value)
    }

    pub fn pull_all(self, values: &'a Array<'a>) -> Self {
        self.add_modifier(PULL_ALL, Bson::Array(Cow::Borrowed(values)))
    }

    /* TODO: Requires Query ops pub fn pull_if(self, condition: ) */

    pub fn pop_front(self) -> Self {
        // TODO: Check if we should be using FloatingPoint()
        // to avoid NumberInt("-1");
        self.add_modifier(POP, (-1).to_bson_int())
    }

    pub fn pop_back(self) -> Self {
        // TODO: Same as above
        self.add_modifier(POP, 1.to_bson_int())
    }

    pub fn add_to_set(self, value: Bson<'a>) -> Self {
        self.add_modifier(ADD_TO_SET, value)
    }

    pub fn add_all_to_set(self, values: &'a Array<'a>) -> Self {
        {
            let array = self.root.deep_object(ADD_TO_SET, self.array);
            array.insert(EACH, Bson::Array(Cow::Borrowed(values)));
        }
        self
    }

    #[inline]
    fn add_modifier(mut self, category: &'static str, value: Bson<'a>) -> Self {
        {
            let group = self.root.object(category);
            group.insert(self.array, value);
        }
        self
    }

    fn push_pos_cow(self, values: Cow<'a, Array<'a>>, position: u32) -> Self {
        {
            let array = self.root.deep_object(PUSH, self.array);
            array.insert(EACH, Bson::Array(values));
            array.insert(POSITION, position.to_bson_int());
        }
        self
    }
}

/*****************
 * Update Fields *
 *****************/
const INCREMENT:      &'static str = "$inc";
const MULTIPLY:       &'static str = "$mul";
const RENAME:         &'static str = "$rename";
const SET_ON_INSERT:  &'static str = "$setOnInsert";
const SET:            &'static str = "$set";
const UNSET:          &'static str = "$unset";
const MIN:            &'static str = "$min";
const MAX:            &'static str = "$max";
const CURRENT_DATE:   &'static str = "$currentDate";
const DATE_TYPE:      &'static str = "{$type:\"date\"}";
const TIMESTAMP_TYPE: &'static str = "{$type:\"timestamp\"}";

/*****************
 * Update Arrays *
 *****************/
/* Operators */
const FIRST:      &'static str = "$";
const ADD_TO_SET: &'static str = "$addToSet";
const POP:        &'static str = "$pop";
const PULL_ALL:   &'static str = "$pullAll";
const PULL:       &'static str = "$pull";
const PUSH:       &'static str = "$push";
/* Modifiers */
const EACH:       &'static str = "$each";
const SLICE:      &'static str = "$slice";
const SORT:       &'static str = "$sort";
const POSITION:   &'static str = "$position";

/*********************
 * Bitwise Operation *
 *********************/
const BIT: &'static str = "$bit";
const AND: &'static str = "and";
const XOR: &'static str = "xor";
const OR:  &'static str = "or";

/*************
 * Isolation *
 *************/
const ISOLATED: &'static str = "$isolated";
<commit_msg>Added notes<commit_after>use super::{Upsert, Number, Integer};
use super::{Bson, Array, Object};
use std::borrow::Cow;

/* TODO: Add easy API for dot notation */
/* TODO: Make every large argument be a CoWs (take Vec or &Vec) */

#[derive(Clone)]
pub struct Update<'a>(Object<'a>);

impl<'a> Update<'a> {
    pub fn new() -> Self {
        Update(Object::new())
    }

    pub fn field(&'a mut self, field: &'a str) -> UpdateField {
        UpdateField {
            root:   &mut self.0,
            field:  field,
        }
    }

    pub fn array(&'a mut self, array: &'a str) -> UpdateArray {
        UpdateArray {
            root:   &mut self.0,
            array:  array,
        }
    }

    pub fn isolate(mut self) -> Self {
        self.0.insert(ISOLATED, 1.to_bson_int());
        self
    }

    pub fn no_isolate(mut self) -> Self {
        self.0.remove(ISOLATED);
        self
    }
}

pub struct UpdateField<'a> {
    root:    &'a mut Object<'a>,
    field:   &'a str,
}

impl<'a> UpdateField<'a> {
    pub fn increment<N: Number<'a>>(self, amount: N) -> Self {
        self.add_modifier(INCREMENT, amount.to_bson_num())
    }

    #[inline]
    pub fn inc<N: Number<'a>>(self, amount: N) -> Self {
        self.increment(amount)
    }

    pub fn multiply<N: Number<'a>>(self, amount: N) -> Self {
        self.add_modifier(MULTIPLY, amount.to_bson_num())
    }

    #[inline]
    pub fn mul<N: Number<'a>>(self, amount: N) -> Self {
        self.multiply(amount)
    }

    pub fn min(self, value: Bson<'a>) -> Self {
        self.add_modifier(MIN, value)
    }

    pub fn max(self, value: Bson<'a>) -> Self {
        self.add_modifier(MAX, value)
    }

    pub fn and<I: Integer<'a>>(self, bits: I) -> Self {
        self.bit(AND, bits.to_bson_int())
    }

    pub fn or<I: Integer<'a>>(self, bits: I) -> Self {
        self.bit(OR, bits.to_bson_int())
    }

    pub fn xor<I: Integer<'a>>(self, bits: I) -> Self {
        self.bit(XOR, bits.to_bson_int())
    }

    pub fn set(self, value: Bson<'a>) -> Self {
        self.add_modifier(SET, value)
    }

    pub fn unset(self) -> Self {
        self.add_modifier(UNSET, Bson::Str(""))
    }

    pub fn rename(self, name: &'a str) -> Self {
        self.add_modifier(RENAME, Bson::Str(name))
    }

    pub fn set_on_insert(self, value: Bson<'a>) -> Self {
        self.add_modifier(SET_ON_INSERT, value)
    }

    pub fn set_date_now(self) -> Self {
        self.add_modifier(CURRENT_DATE, Bson::Str(DATE_TYPE))
    }

    pub fn set_timestamp_now(self) -> Self {
        self.add_modifier(CURRENT_DATE, Bson::Str(TIMESTAMP_TYPE))
    }

    #[inline]
    fn add_modifier(mut self, category: &'static str, value: Bson<'a>) -> Self {
        {
            let group = self.root.object(category);
            group.insert(self.field, value);
        }
        self
    }

    #[inline]
    fn bit(mut self, op: &'static str, bits: Bson<'a>) -> Self {
        {
	        let bits_update = self.root.object(BIT);
	        let field_ops = bits_update.object(self.field);
	        field_ops.insert(op, bits);
        }
        self
    }
}

pub struct UpdateArray<'a> {
    root:    &'a mut Object<'a>,
    array:   &'a str,
}

impl<'a> UpdateArray<'a> {
    /* TODO: Implement first-matching ( $ symbol )
     * and dot notation for arrays (including index)
     * .first_matching()
     * .index() | These can lead to either a field or another
     * .dot()   | array?
     */
    /* TODO: Refactor for less code bloat */

    pub fn push(self, value: Bson<'a>) -> Self {
        self.add_modifier(PUSH, value)
    }

    #[inline]
    pub fn push_at(self, value: Bson<'a>, position: u32) -> Self {
        self.push_pos_cow(Cow::Owned(vec![value]), position)
    }

    pub fn push_all(self, values: &'a Array<'a>) -> Self {
        {
	        let array = self.root.deep_object(PUSH, self.array);
	        array.insert(EACH, Bson::Array(Cow::Borrowed(values)));
        }
        self
    }

    #[inline]
    pub fn push_all_at(self, values: &'a Array<'a>, position: u32) -> Self {
        self.push_pos_cow(Cow::Borrowed(values), position)
    }

    pub fn slice(self, max: u32) -> Self {
        {
            let array = self.root.deep_object(PUSH, self.array);
            array.entry(EACH).or_insert_with(|| Bson::Array(Cow::Owned(Vec::new())));
            array.insert(SLICE, max.to_bson_int());
        }
        self
    }

    pub fn sort(self) -> Self {
        self.sort_array(1, None)
    }

    pub fn rev_sort(self) -> Self {
        self.sort_array(-1, None)
    }

    pub fn sort_by(self, field: &'a str) -> Self {
        self.sort_array(1, Some(field))
    }

    pub fn rev_sort_by(self, field: &'a str) -> Self {
        self.sort_array(-1, Some(field))
    }

    fn sort_array(mut self, direction: i32, spec: Option<&'a str>) -> Self {
        {
	        let array = self.root.deep_object(PUSH, self.array);
            array.entry(EACH).or_insert_with(|| Bson::Array(Cow::Owned(Vec::new())));
            if let Some(field) = spec {
                let sort = array.object(SORT);
                sort.insert(field, direction.to_bson_int());
            } else {
                array.insert(SORT, direction.to_bson_int());
            }
	    }
        self
    }

    pub fn pull(self, value: Bson<'a>) -> Self {
        self.add_modifier(PULL, value)
    }

    pub fn pull_all(self, values: &'a Array<'a>) -> Self {
        self.add_modifier(PULL_ALL, Bson::Array(Cow::Borrowed(values)))
    }

    /* TODO: Requires Query ops pub fn pull_if(self, condition: ) */

    pub fn pop_front(self) -> Self {
        // TODO: Check if we should be using FloatingPoint()
        // to avoid NumberInt("-1");
        self.add_modifier(POP, (-1).to_bson_int())
    }

    pub fn pop_back(self) -> Self {
        // TODO: Same as above
        self.add_modifier(POP, 1.to_bson_int())
    }

    pub fn add_to_set(self, value: Bson<'a>) -> Self {
        self.add_modifier(ADD_TO_SET, value)
    }

    pub fn add_all_to_set(self, values: &'a Array<'a>) -> Self {
        {
            let array = self.root.deep_object(ADD_TO_SET, self.array);
            array.insert(EACH, Bson::Array(Cow::Borrowed(values)));
        }
        self
    }

    #[inline]
    fn add_modifier(mut self, category: &'static str, value: Bson<'a>) -> Self {
        {
            let group = self.root.object(category);
            group.insert(self.array, value);
        }
        self
    }

    fn push_pos_cow(self, values: Cow<'a, Array<'a>>, position: u32) -> Self {
        {
            let array = self.root.deep_object(PUSH, self.array);
            array.insert(EACH, Bson::Array(values));
            array.insert(POSITION, position.to_bson_int());
        }
        self
    }
}

/*****************
 * Update Fields *
 *****************/
const INCREMENT:      &'static str = "$inc";
const MULTIPLY:       &'static str = "$mul";
const RENAME:         &'static str = "$rename";
const SET_ON_INSERT:  &'static str = "$setOnInsert";
const SET:            &'static str = "$set";
const UNSET:          &'static str = "$unset";
const MIN:            &'static str = "$min";
const MAX:            &'static str = "$max";
const CURRENT_DATE:   &'static str = "$currentDate";
const DATE_TYPE:      &'static str = "{$type:\"date\"}";
const TIMESTAMP_TYPE: &'static str = "{$type:\"timestamp\"}";

/*****************
 * Update Arrays *
 *****************/
/* Operators */
const FIRST:      &'static str = "$";
const ADD_TO_SET: &'static str = "$addToSet";
const POP:        &'static str = "$pop";
const PULL_ALL:   &'static str = "$pullAll";
const PULL:       &'static str = "$pull";
const PUSH:       &'static str = "$push";
/* Modifiers */
const EACH:       &'static str = "$each";
const SLICE:      &'static str = "$slice";
const SORT:       &'static str = "$sort";
const POSITION:   &'static str = "$position";

/*********************
 * Bitwise Operation *
 *********************/
const BIT: &'static str = "$bit";
const AND: &'static str = "and";
const XOR: &'static str = "xor";
const OR:  &'static str = "or";

/*************
 * Isolation *
 *************/
const ISOLATED: &'static str = "$isolated";
<|endoftext|>