<commit_before>use super::config::{Config, Capture};
use std::collections::HashMap;
use std::io::{BufRead, Write};

#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    LittleEndian,
    BigEndian,
    HexList,
}

struct Replacement {
    // Line for purposes of printing errors.
    line: usize,
    // Left shift amount
    shift: i32,
    // Segment to add the value to.
    add_segment: usize,
    // The index in the add_segment to add the value.
    index: usize,
    // The tag to retrieve the position to add.
    tag: String,
    // The segment for which we are retrieving the position from the tag.
    pos_segment: usize,
    // The offset we add after retrieving the value from the tag.
    pos_offset: isize,
}

pub struct Parser<'a> {
    config: &'a Config,
    segments: Vec<Vec<u64>>,
    tags: HashMap<String, Vec<usize>>,
    plus_tags: Vec<(usize, Vec<usize>)>,
    minus_tags: Vec<(usize, Vec<usize>)>,
    replacements: Vec<Replacement>,
}

fn shift_left_or_right(a: u64, shift: i32) -> u64 {
    if shift < 0 {
        a >> (-shift)
    } else {
        a << shift
    }
}

impl<'a> Parser<'a> {
    pub fn new(config: &'a Config) -> Self {
        Parser {
            config: config,
            segments: {
                let mut v = Vec::new();
                for _ in 0..config.segment_widths.len() {
                    v.push(Vec::new());
                }
                v
            },
            tags: HashMap::new(),
            plus_tags: Vec::new(),
            minus_tags: Vec::new(),
            replacements: Vec::new(),
        }
    }

    pub fn link(&mut self) {
        // Iterate through every replacement.
        'outer: for r in &self.replacements {
            if r.tag.chars().all(|c| c == '+') {
                for e in &self.plus_tags {
                    // Ensure they have the same amount of pluses and that the relevant segment is at least as high.
                    if e.0 == r.tag.len() && e.1[r.add_segment] >= r.index {
                        self.segments[r.add_segment][r.index] +=
                            shift_left_or_right((e.1[r.pos_segment] as isize + r.pos_offset) as u64, r.shift);

                        break 'outer;
                    }
                }
                panic!("Error: Forward + tag used on line {} was never defined.",
                       r.line);
            } else if r.tag.chars().all(|c| c == '-') {
                for e in self.minus_tags.iter().rev() {
                    // Ensure they have the same amount of minuses and that the relevant segment is at least as low.
                    if e.0 == r.tag.len() && e.1[r.add_segment] < r.index {
                        self.segments[r.add_segment][r.index] +=
                            shift_left_or_right((e.1[r.pos_segment] as isize + r.pos_offset) as u64, r.shift);

                        break 'outer;
                    }
                }
                panic!("Error: Forward + tag used on line {} was never defined.",
                       r.line);
            } else {
                // Get the tag offset vector corresponding to the replacement.
                let tag = self.tags.get(&r.tag).unwrap_or_else(|| {
                    panic!("Error: Tag \"{}\" used on line {} never defined.",
                           r.tag,
                           r.line);
                });
                self.segments[r.add_segment][r.index] +=
                    shift_left_or_right((tag[r.pos_segment] as isize + r.pos_offset) as u64,
                                        r.shift);
            }
        }
    }

    pub fn parse<B>(&mut self, bufread: B)
        where B: BufRead
    {
        for (index, line) in bufread.lines().enumerate() {
            let line = line.unwrap_or_else(|e| panic!("Error: Failed to read from buffer: {}", e));

            // Remove everything after the first #, which denotes a comment.
            let line = line.splitn(2, '#').next().unwrap();

            if self.config.split_whitespace {
                for word in line.split_whitespace() {
                    self.parse_segment(word, index + 1);
                }
            } else {
                self.parse_segment(line, index + 1);
            }
        }
    }

    pub fn output<W>(&self, format: OutputFormat, segment: usize, w: &mut W)
        where W: Write
    {
        use byteorder::{ByteOrder, LittleEndian, BigEndian};
        match format {
            OutputFormat::LittleEndian => {
                // Allocate enough bytes to store a u64
                let mut bytes = [0; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    LittleEndian::write_u64(&mut bytes, *val);
                    w.write_all(&bytes[0..width]).unwrap_or_else(|e| {
                        panic!("Error: Writing to output file for segment {} failed: {}",
                               segment,
                               e);
                    });
                }
            }
            OutputFormat::BigEndian => {
                // Allocate enough bytes to store a u64
                let mut bytes = [0; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    BigEndian::write_u64(&mut bytes, *val);
                    w.write_all(&bytes[0..width]).unwrap_or_else(|e| {
                        panic!("Error: Writing to output file for segment {} failed: {}",
                               segment,
                               e);
                    });
                }
            }
            OutputFormat::HexList => {
                use std::iter::FromIterator;
                // Allocate enough bytes to store a u64
                let mut bytes = [0u8; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    BigEndian::write_u64(&mut bytes, *val);
                    w.write_all((String::from_iter(bytes[(8 - width)..8]
                                .iter()
                                .map(|&b| format!("{:02X}", b))) +
                                    "\n")
                            .as_bytes())
                        .unwrap_or_else(|e| {
                            panic!("Error: Writing to output file for segment {} failed: {}",
                                   segment,
                                   e);
                        });
                }
            }
        }
    }

    pub fn parse_segment(&mut self, segment: &str, line: usize) {
        if segment.is_empty() {
            return;
        }
        if self.attempt_tag_create(segment, line) {
            return;
        }
        if self.attempt_rules(segment, line) {
            return;
        }
        panic!("Error: Unrecognized symbol \"{}\" on line {}.",
               segment,
               line);
    }

    fn attempt_tag_create(&mut self, segment: &str, line: usize) -> bool {
        for tc in &self.config.tag_creates {
            if let Some(caps) = tc.regex.as_ref().unwrap().captures(segment) {
                let s = caps.at(1).unwrap();
                if s.chars().all(|c| c == '+') {
                    self.plus_tags.push((s.len(),
                                         self.segments
                        .iter()
                        .map(|v| v.len())
                        .collect()));
                } else if s.chars().all(|c| c == '-') {
                    self.minus_tags.push((s.len(),
                                          self.segments
                        .iter()
                        .map(|v| v.len())
                        .collect()));
                } else {
                    use std::collections::hash_map::Entry;
                    match self.tags.entry(s.to_string()) {
                        Entry::Occupied(_) => {
                            panic!("Attempted to create duplicate tag \"{}\" on line {}",
                                   s,
                                   line)
                        }
                        Entry::Vacant(v) => {
                            v.insert(self.segments
                                .iter()
                                .map(|v| v.len())
                                .collect());
                        }
                    }
                }
                return true;
            }
        }
        false
    }

    fn attempt_rules(&mut self, segment: &str, line: usize) -> bool {
        let config = &self.config;
        for rule in &config.rules {
            if let Some(caps) = rule.regex.as_ref().unwrap().captures(segment) {
                let mut segvals = rule.segment_values.clone();
                for self_reference in &rule.self_references {
                    segvals[self_reference.add_segment][self_reference.add_index] +=
                        if self_reference.shift < 0 {
                            (self.segments[self_reference.from_segment].len() as u64) >>
                            ((-self_reference.shift) as u32)
                        } else {
                            (self.segments[self_reference.from_segment].len() as u64) <<
                            (self_reference.shift as u32)
                        };
                }
                for (index, capture) in rule.captures.iter().enumerate() {
                    use std::mem::transmute;
                    let cap_string = caps.at(index + 1).unwrap();
                    match *capture {
                        Capture::Tag { ref feedbacks } => {
                            for feedback in feedbacks {
                                self.replacements.push(Replacement {
                                    line: line,
                                    shift: feedback.shift,
                                    add_segment: feedback.add_segment,
                                    index: self.segments[feedback.add_segment].len() +
                                           feedback.add_index,
                                    tag: String::from(cap_string),
                                    pos_segment: feedback.from_segment,
                                    pos_offset: if feedback.relative {
                                        feedback.offset -
                                        self.segments[feedback.from_segment].len() as isize
                                    } else {
                                        feedback.offset
                                    },
                                });
                            }
                        }
                        Capture::Str { add_segment } => {
                            for c in cap_string.chars() {
                                self.segments[add_segment].push(c as u64);
                            }
                        }
                        Capture::Num { ref feedbacks, ref base } => {
                            let pval = i64::from_str_radix(cap_string, *base).unwrap_or_else(|e| {
                                panic!("Error: Failed to parse captured string \"{}\" from \
                                        \"{}\" on line {}: {}",
                                       cap_string,
                                       segment,
                                       line,
                                       e);
                            });
                            let val: u64 = unsafe { transmute(pval) };

                            for feedback in feedbacks {
                                let mut shiftval = shift_left_or_right(val, feedback.shift);
                                if feedback.negate {
                                    shiftval = !shiftval + 1;
                                }
                                if feedback.fill {
                                    let baseval = segvals[feedback.segment][feedback.index];
                                    let fill_amount = shiftval as isize + feedback.fill_offset;
                                    if fill_amount.is_negative() {
                                        panic!("Error: Got a negative fill amount!");
                                    } else if feedback.align {
                                        while self.segments[feedback.segment].len() <
                                              fill_amount as usize {
                                            self.segments[feedback.segment].push(baseval);
                                        }
                                    } else {
                                        for _ in 0..fill_amount {
                                            self.segments[feedback.segment].push(baseval);
                                        }
                                    }
                                    segvals[feedback.segment].pop();
                                } else {
                                    segvals[feedback.segment][feedback.index] += shiftval;
                                }
                            }
                        }
                    }
                }
                for (segvec, segment) in segvals.iter_mut().zip(self.segments.iter_mut()) {
                    segment.append(segvec);
                }
                return true;
            }
        }
        false
    }
}
<commit_msg>fix issue where all tags after a + or - tag are ignored due to a break vs continue<commit_after>use super::config::{Config, Capture};
use std::collections::HashMap;
use std::io::{BufRead, Write};

#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    LittleEndian,
    BigEndian,
    HexList,
}

struct Replacement {
    // Line for purposes of printing errors.
    line: usize,
    // Left shift amount
    shift: i32,
    // Segment to add the value to.
    add_segment: usize,
    // The index in the add_segment to add the value.
    index: usize,
    // The tag to retrieve the position to add.
    tag: String,
    // The segment for which we are retrieving the position from the tag.
    pos_segment: usize,
    // The offset we add after retrieving the value from the tag.
    pos_offset: isize,
}

pub struct Parser<'a> {
    config: &'a Config,
    segments: Vec<Vec<u64>>,
    tags: HashMap<String, Vec<usize>>,
    plus_tags: Vec<(usize, Vec<usize>)>,
    minus_tags: Vec<(usize, Vec<usize>)>,
    replacements: Vec<Replacement>,
}

fn shift_left_or_right(a: u64, shift: i32) -> u64 {
    if shift < 0 {
        a >> (-shift)
    } else {
        a << shift
    }
}

impl<'a> Parser<'a> {
    pub fn new(config: &'a Config) -> Self {
        Parser {
            config: config,
            segments: {
                let mut v = Vec::new();
                for _ in 0..config.segment_widths.len() {
                    v.push(Vec::new());
                }
                v
            },
            tags: HashMap::new(),
            plus_tags: Vec::new(),
            minus_tags: Vec::new(),
            replacements: Vec::new(),
        }
    }

    pub fn link(&mut self) {
        // Iterate through every replacement.
        'outer: for r in &self.replacements {
            if r.tag.chars().all(|c| c == '+') {
                for e in &self.plus_tags {
                    // Ensure they have the same amount of pluses and that the relevant segment is at least as high.
                    if e.0 == r.tag.len() && e.1[r.add_segment] >= r.index {
                        self.segments[r.add_segment][r.index] +=
                            shift_left_or_right((e.1[r.pos_segment] as isize + r.pos_offset) as u64, r.shift);

                        continue 'outer;
                    }
                }
                panic!("Error: Forward + tag used on line {} was never defined.",
                       r.line);
            } else if r.tag.chars().all(|c| c == '-') {
                for e in self.minus_tags.iter().rev() {
                    // Ensure they have the same amount of minuses and that the relevant segment is lower.
                    if e.0 == r.tag.len() && e.1[r.add_segment] < r.index {
                        self.segments[r.add_segment][r.index] +=
                            shift_left_or_right((e.1[r.pos_segment] as isize + r.pos_offset) as u64, r.shift);

                        continue 'outer;
                    }
                }
                panic!("Error: Backward - tag used on line {} was never defined.",
                       r.line);
            } else {
                // Get the tag offset vector corresponding to the replacement.
                let tag = self.tags.get(&r.tag).unwrap_or_else(|| {
                    panic!("Error: Tag \"{}\" used on line {} never defined.",
                           r.tag,
                           r.line);
                });
                self.segments[r.add_segment][r.index] +=
                    shift_left_or_right((tag[r.pos_segment] as isize + r.pos_offset) as u64,
                                        r.shift);
            }
        }
    }

    pub fn parse<B>(&mut self, bufread: B)
        where B: BufRead
    {
        for (index, line) in bufread.lines().enumerate() {
            let line = line.unwrap_or_else(|e| panic!("Error: Failed to read from buffer: {}", e));

            // Remove everything after the first #, which denotes a comment.
            let line = line.splitn(2, '#').next().unwrap();

            if self.config.split_whitespace {
                for word in line.split_whitespace() {
                    self.parse_segment(word, index + 1);
                }
            } else {
                self.parse_segment(line, index + 1);
            }
        }
    }

    pub fn output<W>(&self, format: OutputFormat, segment: usize, w: &mut W)
        where W: Write
    {
        use byteorder::{ByteOrder, LittleEndian, BigEndian};
        match format {
            OutputFormat::LittleEndian => {
                // Allocate enough bytes to store a u64
                let mut bytes = [0; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    LittleEndian::write_u64(&mut bytes, *val);
                    w.write_all(&bytes[0..width]).unwrap_or_else(|e| {
                        panic!("Error: Writing to output file for segment {} failed: {}",
                               segment,
                               e);
                    });
                }
            }
            OutputFormat::BigEndian => {
                // Allocate enough bytes to store a u64
                let mut bytes = [0; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    BigEndian::write_u64(&mut bytes, *val);
                    w.write_all(&bytes[0..width]).unwrap_or_else(|e| {
                        panic!("Error: Writing to output file for segment {} failed: {}",
                               segment,
                               e);
                    });
                }
            }
            OutputFormat::HexList => {
                use std::iter::FromIterator;
                // Allocate enough bytes to store a u64
                let mut bytes = [0u8; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    BigEndian::write_u64(&mut bytes, *val);
                    w.write_all((String::from_iter(bytes[(8 - width)..8]
                                .iter()
                                .map(|&b| format!("{:02X}", b))) +
                                    "\n")
                            .as_bytes())
                        .unwrap_or_else(|e| {
                            panic!("Error: Writing to output file for segment {} failed: {}",
                                   segment,
                                   e);
                        });
                }
            }
        }
    }

    pub fn parse_segment(&mut self, segment: &str, line: usize) {
        if segment.is_empty() {
            return;
        }
        if self.attempt_tag_create(segment, line) {
            return;
        }
        if self.attempt_rules(segment, line) {
            return;
        }
        panic!("Error: Unrecognized symbol \"{}\" on line {}.",
               segment,
               line);
    }

    fn attempt_tag_create(&mut self, segment: &str, line: usize) -> bool {
        for tc in &self.config.tag_creates {
            if let Some(caps) = tc.regex.as_ref().unwrap().captures(segment) {
                let s = caps.at(1).unwrap();
                if s.chars().all(|c| c == '+') {
                    self.plus_tags.push((s.len(),
                                         self.segments
                        .iter()
                        .map(|v| v.len())
                        .collect()));
                } else if s.chars().all(|c| c == '-') {
                    self.minus_tags.push((s.len(),
                                          self.segments
                        .iter()
                        .map(|v| v.len())
                        .collect()));
                } else {
                    use std::collections::hash_map::Entry;
                    match self.tags.entry(s.to_string()) {
                        Entry::Occupied(_) => {
                            panic!("Attempted to create duplicate tag \"{}\" on line {}",
                                   s,
                                   line)
                        }
                        Entry::Vacant(v) => {
                            v.insert(self.segments
                                .iter()
                                .map(|v| v.len())
                                .collect());
                        }
                    }
                }
                return true;
            }
        }
        false
    }

    fn attempt_rules(&mut self, segment: &str, line: usize) -> bool {
        let config = &self.config;
        for rule in &config.rules {
            if let Some(caps) = rule.regex.as_ref().unwrap().captures(segment) {
                let mut segvals = rule.segment_values.clone();
                for self_reference in &rule.self_references {
                    segvals[self_reference.add_segment][self_reference.add_index] +=
                        if self_reference.shift < 0 {
                            (self.segments[self_reference.from_segment].len() as u64) >>
                            ((-self_reference.shift) as u32)
                        } else {
                            (self.segments[self_reference.from_segment].len() as u64) <<
                            (self_reference.shift as u32)
                        };
                }
                for (index, capture) in rule.captures.iter().enumerate() {
                    use std::mem::transmute;
                    let cap_string = caps.at(index + 1).unwrap();
                    match *capture {
                        Capture::Tag { ref feedbacks } => {
                            for feedback in feedbacks {
                                self.replacements.push(Replacement {
                                    line: line,
                                    shift: feedback.shift,
                                    add_segment: feedback.add_segment,
                                    index: self.segments[feedback.add_segment].len() +
                                           feedback.add_index,
                                    tag: String::from(cap_string),
                                    pos_segment: feedback.from_segment,
                                    pos_offset: if feedback.relative {
                                        feedback.offset -
                                        self.segments[feedback.from_segment].len() as isize
                                    } else {
                                        feedback.offset
                                    },
                                });
                            }
                        }
                        Capture::Str { add_segment } => {
                            for c in cap_string.chars() {
                                self.segments[add_segment].push(c as u64);
                            }
                        }
                        Capture::Num { ref feedbacks, ref base } => {
                            let pval = i64::from_str_radix(cap_string, *base).unwrap_or_else(|e| {
                                panic!("Error: Failed to parse captured string \"{}\" from \
                                        \"{}\" on line {}: {}",
                                       cap_string,
                                       segment,
                                       line,
                                       e);
                            });
                            let val: u64 = unsafe { transmute(pval) };

                            for feedback in feedbacks {
                                let mut shiftval = shift_left_or_right(val, feedback.shift);
                                if feedback.negate {
                                    shiftval = !shiftval + 1;
                                }
                                if feedback.fill {
                                    let baseval = segvals[feedback.segment][feedback.index];
                                    let fill_amount = shiftval as isize + feedback.fill_offset;
                                    if fill_amount.is_negative() {
                                        panic!("Error: Got a negative fill amount!");
                                    } else if feedback.align {
                                        while self.segments[feedback.segment].len() <
                                              fill_amount as usize {
                                            self.segments[feedback.segment].push(baseval);
                                        }
                                    } else {
                                        for _ in 0..fill_amount {
                                            self.segments[feedback.segment].push(baseval);
                                        }
                                    }
                                    segvals[feedback.segment].pop();
                                } else {
                                    segvals[feedback.segment][feedback.index] += shiftval;
                                }
                            }
                        }
                    }
                }
                for (segvec, segment) in segvals.iter_mut().zip(self.segments.iter_mut()) {
                    segment.append(segvec);
                }
                return true;
            }
        }
        false
    }
}
<|endoftext|>


<commit_before>use glib::object::Cast;
use gtk::{self, Type, Widget};
use gtk::{BoxExt, CellLayoutExt, ContainerExt, TreeModelExt, WidgetExt};

use sysinfo::*;

use notebook::NoteBook;

use std::cell::Cell;
use std::collections::HashMap;
use std::rc::Rc;

#[allow(dead_code)]
pub struct Procs {
    pub left_tree: gtk::TreeView,
    pub scroll: gtk::ScrolledWindow,
    pub current_pid: Rc<Cell<Option<i32>>>,
    pub kill_button: gtk::Button,
    pub info_button: gtk::Button,
    pub vertical_layout: gtk::Box,
    pub list_store: gtk::ListStore,
    pub columns: Vec<gtk::TreeViewColumn>,
}

impl Procs {
    pub fn new(proc_list: &HashMap<i32, Process>, note: &mut NoteBook) -> Procs {
        let left_tree = gtk::TreeView::new();
        let scroll = gtk::ScrolledWindow::new(None, None);
        let current_pid = Rc::new(Cell::new(None));
        let kill_button = gtk::Button::new_with_label("End task");
        let info_button = gtk::Button::new_with_label("More information");
        let current_pid1 = current_pid.clone();
        let kill_button1 = kill_button.clone();
        let info_button1 = info_button.clone();

        let mut columns: Vec<gtk::TreeViewColumn> = Vec::new();

        let list_store = gtk::ListStore::new(&[
            // The first four columns of the model are going to be visible in the view.
            Type::I32,       // pid
            Type::String,    // name
            Type::String,    // CPU
            Type::U32,       // mem
            // These two will serve as keys when sorting by process name and CPU usage.
            Type::String,    // name_lowercase
            Type::F32,       // CPU_f32
        ]);

        append_column("pid", &mut columns, &left_tree);
        append_column("process name", &mut columns, &left_tree);
        append_column("cpu usage", &mut columns, &left_tree);
        append_column("memory usage (in kB)", &mut columns, &left_tree);

        // When we click the "name" column the order is defined by the
        // "name_lowercase" effectively making the built-in comparator ignore case.
        columns[1].set_sort_column_id(4);
        // Likewise clicking the "CPU" column sorts by the "CPU_f32" one because
        // we want the order to be numerical not lexicographical.
        columns[2].set_sort_column_id(5);

        for (_, pro) in proc_list {
            create_and_fill_model(&list_store, pro.pid, &format!("{:?}", &pro.cmd), &pro.name,
                                  pro.cpu_usage, pro.memory);
        }

        left_tree.set_model(Some(&list_store));
        left_tree.set_headers_visible(true);
        //let filter = gtk::TreeModelFilter::new(&list_store, None);
        scroll.add(&left_tree);
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let horizontal_layout = gtk::Grid::new();
        /*let list_store1 = list_store.clone();

        filter.set_modify_func(|_, iter, value| {
            list_store1.get_value(&iter, 4).get::<String>().unwrap()
                       .contains(value.get::<String>().unwrap().to_lowercase())
        });*/

        left_tree.connect_cursor_changed(move |tree_view| {
            let selection = tree_view.get_selection();
            let (pid, ret) = if let Some((model, iter)) = selection.get_selected() {
                if let Some(x) = model.get_value(&iter, 0).get() {
                    (Some(x), true)
                } else {
                    (None, false)
                }
            } else {
                (None, false)
            };
            current_pid1.set(pid);
            kill_button1.set_sensitive(ret);
            info_button1.set_sensitive(ret);
        });
        kill_button.set_sensitive(false);
        info_button.set_sensitive(false);

        vertical_layout.pack_start(&scroll, true, true, 0);
        horizontal_layout.attach(&info_button, 0, 0, 2, 1);
        horizontal_layout.attach_next_to(&kill_button, Some(&info_button),
                                         gtk::PositionType::Right, 2, 1);
        horizontal_layout.set_column_homogeneous(true);
        vertical_layout.pack_start(&horizontal_layout, false, true, 0);

        let vertical_layout : Widget = vertical_layout.upcast();
        note.create_tab("Process list", &vertical_layout);

        Procs {
            left_tree: left_tree,
            scroll: scroll,
            current_pid: current_pid,
            kill_button: kill_button,
            info_button: info_button,
            vertical_layout: vertical_layout.downcast::<gtk::Box>().unwrap(),
            list_store: list_store,
            columns: columns,
        }
    }
}

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>, left_tree: &gtk::TreeView) {
    let id = v.len() as i32;
    let renderer = gtk::CellRendererText::new();

    let column = gtk::TreeViewColumn::new();
    column.set_title(title);
    column.set_resizable(true);
    column.pack_start(&renderer, true);
    column.add_attribute(&renderer, "text", id);
    column.set_clickable(true);
    column.set_sort_column_id(id);
    left_tree.append_column(&column);
    v.push(column);
}

pub fn create_and_fill_model(list_store: &gtk::ListStore, pid: i32, cmdline: &str, name: &str,
                             cpu: f32, memory: u64) {
    if cmdline.len() < 1 {
        return;
    }
    list_store.insert_with_values(None,
                                  &[0, 1, 2, 3, 4, 5],
                                  &[&pid,
                                    &name,
                                    &format!("{:.1}", cpu),
                                    &memory,
                                    &name.to_lowercase(),
                                    &cpu
                                   ]);
}
<commit_msg>Improvement on the procs display<commit_after>use glib::object::Cast;
use gtk::{self, Type, Widget};
use gtk::{BoxExt, CellLayoutExt, ContainerExt, TreeModelExt, WidgetExt};

use sysinfo::*;

use notebook::NoteBook;

use std::cell::Cell;
use std::collections::HashMap;
use std::rc::Rc;

#[allow(dead_code)]
pub struct Procs {
    pub left_tree: gtk::TreeView,
    pub scroll: gtk::ScrolledWindow,
    pub current_pid: Rc<Cell<Option<i32>>>,
    pub kill_button: gtk::Button,
    pub info_button: gtk::Button,
    pub vertical_layout: gtk::Box,
    pub list_store: gtk::ListStore,
    pub columns: Vec<gtk::TreeViewColumn>,
}

impl Procs {
    pub fn new(proc_list: &HashMap<i32, Process>, note: &mut NoteBook) -> Procs {
        let left_tree = gtk::TreeView::new();
        let scroll = gtk::ScrolledWindow::new(None, None);
        let current_pid = Rc::new(Cell::new(None));
        let kill_button = gtk::Button::new_with_label("End task");
        let info_button = gtk::Button::new_with_label("More information");
        let current_pid1 = current_pid.clone();
        let kill_button1 = kill_button.clone();
        let info_button1 = info_button.clone();

        let mut columns: Vec<gtk::TreeViewColumn> = Vec::new();

        let list_store = gtk::ListStore::new(&[
            // The first four columns of the model are going to be visible in the view.
            Type::I32,       // pid
            Type::String,    // name
            Type::String,    // CPU
            Type::U32,       // mem
            // These two will serve as keys when sorting by process name and CPU usage.
            Type::String,    // name_lowercase
            Type::F32,       // CPU_f32
        ]);

        append_column("pid", &mut columns, &left_tree, None);
        append_column("process name", &mut columns, &left_tree, Some(200));
        append_column("cpu usage", &mut columns, &left_tree, None);
        append_column("memory usage (in kB)", &mut columns, &left_tree, None);

        // When we click the "name" column the order is defined by the
        // "name_lowercase" effectively making the built-in comparator ignore case.
        columns[1].set_sort_column_id(4);
        // Likewise clicking the "CPU" column sorts by the "CPU_f32" one because
        // we want the order to be numerical not lexicographical.
        columns[2].set_sort_column_id(5);

        for (_, pro) in proc_list {
            create_and_fill_model(&list_store, pro.pid, &format!("{:?}", &pro.cmd), &pro.name,
                                  pro.cpu_usage, pro.memory);
        }

        left_tree.set_model(Some(&list_store));
        left_tree.set_headers_visible(true);
        //let filter = gtk::TreeModelFilter::new(&list_store, None);
        scroll.add(&left_tree);
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let horizontal_layout = gtk::Grid::new();
        /*let list_store1 = list_store.clone();

        filter.set_modify_func(|_, iter, value| {
            list_store1.get_value(&iter, 4).get::<String>().unwrap()
                       .contains(value.get::<String>().unwrap().to_lowercase())
        });*/

        left_tree.connect_cursor_changed(move |tree_view| {
            let selection = tree_view.get_selection();
            let (pid, ret) = if let Some((model, iter)) = selection.get_selected() {
                if let Some(x) = model.get_value(&iter, 0).get() {
                    (Some(x), true)
                } else {
                    (None, false)
                }
            } else {
                (None, false)
            };
            current_pid1.set(pid);
            kill_button1.set_sensitive(ret);
            info_button1.set_sensitive(ret);
        });
        kill_button.set_sensitive(false);
        info_button.set_sensitive(false);

        vertical_layout.pack_start(&scroll, true, true, 0);
        horizontal_layout.attach(&info_button, 0, 0, 2, 1);
        horizontal_layout.attach_next_to(&kill_button, Some(&info_button),
                                         gtk::PositionType::Right, 2, 1);
        horizontal_layout.set_column_homogeneous(true);
        vertical_layout.pack_start(&horizontal_layout, false, true, 0);

        let vertical_layout : Widget = vertical_layout.upcast();
        note.create_tab("Process list", &vertical_layout);

        Procs {
            left_tree: left_tree,
            scroll: scroll,
            current_pid: current_pid,
            kill_button: kill_button,
            info_button: info_button,
            vertical_layout: vertical_layout.downcast::<gtk::Box>().unwrap(),
            list_store: list_store,
            columns: columns,
        }
    }
}

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>, left_tree: &gtk::TreeView,
                 max_width: Option<i32>) {
    let id = v.len() as i32;
    let renderer = gtk::CellRendererText::new();

    let column = gtk::TreeViewColumn::new();
    column.set_title(title);
    column.set_resizable(true);
    if let Some(max_width) = max_width {
        column.set_max_width(max_width);
        column.set_expand(true);
    }
    column.pack_start(&renderer, true);
    column.add_attribute(&renderer, "text", id);
    column.set_clickable(true);
    column.set_sort_column_id(id);
    left_tree.append_column(&column);
    v.push(column);
}

pub fn create_and_fill_model(list_store: &gtk::ListStore, pid: i32, cmdline: &str, name: &str,
                             cpu: f32, memory: u64) {
    if cmdline.len() < 1 {
        return;
    }
    list_store.insert_with_values(None,
                                  &[0, 1, 2, 3, 4, 5],
                                  &[&pid,
                                    &name,
                                    &format!("{:.1}", cpu),
                                    &memory,
                                    &name.to_lowercase(),
                                    &cpu
                                   ]);
}
<|endoftext|>


<commit_before>use unicode_width::UnicodeWidthStr;

use key::Key;
use pattern::{self, Pattern};

pub struct Query {
    patterns: Vec<Pattern>,
    string: String,
}

impl Query {
    fn new(string: String) -> Self {
        Query {
            patterns: pattern::patterns_from_str(string.as_ref()),
            string: string,
        }
    }

    pub fn test(&self, haystack: &[u8]) -> bool {
        self.patterns.iter().all(|p| p.test(haystack))
    }
}

impl AsRef<str> for Query {
    fn as_ref(&self) -> &str {
        self.string.as_ref()
    }
}

pub struct QueryEditor {
    cursor_position: usize,
    string: String,
}

impl QueryEditor {
    pub fn new<S: Into<String>>(string: S) -> QueryEditor {
        let string = string.into();
        QueryEditor {
            cursor_position: UnicodeWidthStr::width(AsRef::<str>::as_ref(&string)),
            string: string.into(),
        }
    }

    pub fn put_key(&mut self, key: Key) {
        use key::Key::*;
        match key {
            CtrlA => {
                self.cursor_position = 0;
            }
            CtrlB => {
                if self.cursor_position > 0 {
                    self.cursor_position -= 1;
                }
            }
            CtrlD => {
                if self.cursor_position < self.string.len() {
                    let cursor = self.cursor_position;
                    self.string.remove(cursor);
                }
            }
            CtrlE => {
                self.cursor_position = self.string.len();
            }
            CtrlF => {
                if self.cursor_position < self.string.len() {
                    self.cursor_position += 1;
                }
            }
            CtrlH | Del => {
                if self.cursor_position > 0 {
                    let cursor = self.cursor_position;
                    self.string.remove(cursor - 1);
                    self.cursor_position -= 1;
                }
            }
            CtrlK => {
                let cursor = self.cursor_position;
                self.string.truncate(cursor);
            }
            CtrlW => {
                let cursor = self.cursor_position;
                let word_end = self.string[0..cursor].rfind(|ch| ch != ' ').unwrap_or(0);
                let word_start = self.string[0..word_end].rfind(' ').and_then(|i| Some(i + 1)).unwrap_or(0);
                self.string.truncate(word_start);
                self.cursor_position = word_start;
            }
            Char(ch) => {
                let cursor = self.cursor_position;
                self.string.insert(cursor, ch);
                self.cursor_position += 1;
            }
            _  => { }
        }
    }

    pub fn cursor_position(&self) -> usize {
        self.cursor_position as usize
    }

    pub fn query(&self) -> Query {
        Query::new(self.string.clone())
    }
}

impl AsRef<str> for QueryEditor {
    fn as_ref(&self) -> &str {
        self.string.as_ref()
    }
}
<commit_msg>Remove unnecessary into()<commit_after>use unicode_width::UnicodeWidthStr;

use key::Key;
use pattern::{self, Pattern};

pub struct Query {
    patterns: Vec<Pattern>,
    string: String,
}

impl Query {
    fn new(string: String) -> Self {
        Query {
            patterns: pattern::patterns_from_str(string.as_ref()),
            string: string,
        }
    }

    pub fn test(&self, haystack: &[u8]) -> bool {
        self.patterns.iter().all(|p| p.test(haystack))
    }
}

impl AsRef<str> for Query {
    fn as_ref(&self) -> &str {
        self.string.as_ref()
    }
}

pub struct QueryEditor {
    cursor_position: usize,
    string: String,
}

impl QueryEditor {
    pub fn new<S: Into<String>>(string: S) -> QueryEditor {
        let string = string.into();
        QueryEditor {
            cursor_position: UnicodeWidthStr::width(AsRef::<str>::as_ref(&string)),
            string: string,
        }
    }

    pub fn put_key(&mut self, key: Key) {
        use key::Key::*;
        match key {
            CtrlA => {
                self.cursor_position = 0;
            }
            CtrlB => {
                if self.cursor_position > 0 {
                    self.cursor_position -= 1;
                }
            }
            CtrlD => {
                if self.cursor_position < self.string.len() {
                    let cursor = self.cursor_position;
                    self.string.remove(cursor);
                }
            }
            CtrlE => {
                self.cursor_position = self.string.len();
            }
            CtrlF => {
                if self.cursor_position < self.string.len() {
                    self.cursor_position += 1;
                }
            }
            CtrlH | Del => {
                if self.cursor_position > 0 {
                    let cursor = self.cursor_position;
                    self.string.remove(cursor - 1);
                    self.cursor_position -= 1;
                }
            }
            CtrlK => {
                let cursor = self.cursor_position;
                self.string.truncate(cursor);
            }
            CtrlW => {
                let cursor = self.cursor_position;
                let word_end = self.string[0..cursor].rfind(|ch| ch != ' ').unwrap_or(0);
                let word_start = self.string[0..word_end].rfind(' ').and_then(|i| Some(i + 1)).unwrap_or(0);
                self.string.truncate(word_start);
                self.cursor_position = word_start;
            }
            Char(ch) => {
                let cursor = self.cursor_position;
                self.string.insert(cursor, ch);
                self.cursor_position += 1;
            }
            _  => { }
        }
    }

    pub fn cursor_position(&self) -> usize {
        self.cursor_position as usize
    }

    pub fn query(&self) -> Query {
        Query::new(self.string.clone())
    }
}

impl AsRef<str> for QueryEditor {
    fn as_ref(&self) -> &str {
        self.string.as_ref()
    }
}
<|endoftext|>


<commit_before>//! Defines complex queries over Ardite driver data structures.

use std::convert::From;
use linear_map::LinearMap;
use value::{Key, Pointer, Value};

/// Specifies a positive integer range in a traditional SQL format.
pub struct Range {
  /// How many items should be included in this range.
  pub limit: Option<u64>,
  /// How many items should be skipped from the full set in this range.
  pub skip: Option<u64>
}

impl Default for Range {
  fn default() -> Range {
    Range {
      limit: None,
      skip: None
    }
  }
}

/// A condition which will resolve to a boolean value after comparing a certain
/// value with a set rule.
pub enum Condition {
  /// The condition always passes.
  True,
  /// The condition always fails.
  False,
  /// Inverts a condition.
  Not(Box<Condition>),
  /// Composes many conditions. They all must be true for the condition to be
  /// true.
  And(Vec<Condition>),
  /// Composes many conditions. Only one must be true for the condition to be
  /// true.
  Or(Vec<Condition>),
  /// Partial conditions on some keys of an object.
  Keys(LinearMap<Key, Condition>),
  /// If the compared value is exactly equal to this one, the condition passes.
  Equal(Value)
}

impl Default for Condition {
  fn default() -> Condition {
    Condition::True
  }
}

/// Specifies a complex driver query. The query is structured like a tree
/// except each node is unaware of its name (or if it even has a name). It
/// cannot be expected that a `Query` tree will map 1 to 1 with a `Value` tree.
#[derive(PartialEq, Debug)]
pub enum Query {
  /// Queries a single value.
  All,
  /// Queries some partial properties of an object.
  Keys(LinearMap<Key, Query>)
}

impl Default for Query {
  fn default() -> Query {
    Query::All
  }
}

impl From<Pointer> for Query {
  fn from(pointer: Pointer) -> Self {
    // Reverse loop through the pointer to construct the query.
    pointer.iter().rev().fold(Query::All, |acc, key| {
      let mut map = LinearMap::new();
      map.insert(key.to_owned(), acc);
      Query::Keys(map)
    })
  }
}

#[cfg(test)]
mod tests {
  use query::Query;

  #[test]
  fn test_from_pointer() {
    assert_eq!(Query::from(point!["hello", "good", "world"]), Query::Keys(linear_map! {
      str!("hello") => Query::Keys(linear_map! {
        str!("good") => Query::Keys(linear_map! {
          str!("world") => Query::All
        })
      })
    }));
    assert_eq!(Query::from(point!["good"]), Query::Keys(linear_map! {
      str!("good") => Query::All
    }));
    assert_eq!(Query::from(point![]), Query::All);
  }
}
<commit_msg>refactor: reorder range<commit_after>//! Defines complex queries over Ardite driver data structures.

use std::convert::From;
use linear_map::LinearMap;
use value::{Key, Pointer, Value};

/// A condition which will resolve to a boolean value after comparing a certain
/// value with a set rule.
pub enum Condition {
  /// The condition always passes.
  True,
  /// The condition always fails.
  False,
  /// Inverts a condition.
  Not(Box<Condition>),
  /// Composes many conditions. They all must be true for the condition to be
  /// true.
  And(Vec<Condition>),
  /// Composes many conditions. Only one must be true for the condition to be
  /// true.
  Or(Vec<Condition>),
  /// Partial conditions on some keys of an object.
  Keys(LinearMap<Key, Condition>),
  /// If the compared value is exactly equal to this one, the condition passes.
  Equal(Value)
}

impl Default for Condition {
  fn default() -> Condition {
    Condition::True
  }
}

/// Specifies a positive integer range in a traditional SQL format.
pub struct Range {
  /// How many items should be included in this range.
  pub limit: Option<u64>,
  /// How many items should be skipped from the full set in this range.
  pub skip: Option<u64>
}

impl Default for Range {
  fn default() -> Range {
    Range {
      limit: None,
      skip: None
    }
  }
}

/// Specifies a complex driver query. The query is structured like a tree
/// except each node is unaware of its name (or if it even has a name). It
/// cannot be expected that a `Query` tree will map 1 to 1 with a `Value` tree.
#[derive(PartialEq, Debug)]
pub enum Query {
  /// Queries a single value.
  All,
  /// Queries some partial properties of an object.
  Keys(LinearMap<Key, Query>)
}

impl Default for Query {
  fn default() -> Query {
    Query::All
  }
}

impl From<Pointer> for Query {
  fn from(pointer: Pointer) -> Self {
    // Reverse loop through the pointer to construct the query.
    pointer.iter().rev().fold(Query::All, |acc, key| {
      let mut map = LinearMap::new();
      map.insert(key.to_owned(), acc);
      Query::Keys(map)
    })
  }
}

#[cfg(test)]
mod tests {
  use query::Query;

  #[test]
  fn test_from_pointer() {
    assert_eq!(Query::from(point!["hello", "good", "world"]), Query::Keys(linear_map! {
      str!("hello") => Query::Keys(linear_map! {
        str!("good") => Query::Keys(linear_map! {
          str!("world") => Query::All
        })
      })
    }));
    assert_eq!(Query::from(point!["good"]), Query::Keys(linear_map! {
      str!("good") => Query::All
    }));
    assert_eq!(Query::from(point![]), Query::All);
  }
}
<|endoftext|>


<commit_before>use std::io::BufReader;
use std::fs::File;

use serde_json;

use cgmath::*;
use shape::*;
use vec3d::*;
use light::*;

use color;
use color::Color;

use std;

#[derive(Serialize, Deserialize)]
pub struct Scene {
    shapes: Vec<Shape>,
    light: Light,
    camera_pos: Vec3d,
    camera_dir: Vec3d,
    camera_up: Vec3d,
    max_reflection: i32,
}

impl Scene {
    pub fn new(light: Light, camera_pos: Vec3d, camera_dir: Vec3d, camera_up: Vec3d) -> Scene {
        Scene {
            shapes: Vec::new(),
            light: light,
            camera_pos: camera_pos,
            camera_dir: camera_dir,
            camera_up: camera_up,
            max_reflection: 4,
        }
    }

    pub fn from_file(filename: &str) -> Result<Scene, String> {
        File::open(filename)
            .map_err(|e| format!("Can't open input file: {}", e))
            .and_then(|f| serde_json::from_reader(BufReader::new(f)).map_err(|e| format!("JSON Parse error: {}", e)))
    }

    pub fn add_shape(&mut self, s: Shape) {
        self.shapes.push(s);
    }

    pub fn step(&mut self) {
        // self.light.translate(&[1.0, 0.0, 0.0]);
    }

    pub fn mv_camera_fwd(&mut self) {
        self.camera_pos = self.camera_pos + self.camera_dir;
    }

    pub fn mv_camera_back(&mut self) {
        self.camera_pos = self.camera_pos - self.camera_dir;
    }

    pub fn rot_camera(&mut self, x_rot: f64, y_rot: f64) {
        self.camera_dir = vec3_roty(self.camera_dir, x_rot);

        // project camera_dir to X plane
        let mut x_proj = self.camera_dir;
        x_proj.y = 0.0;
        x_proj = x_proj.normalize();

        let angle = if x_proj.x < 0.0 {
            -Vec3d::new(0.0, 0.0, 1.0).dot(x_proj).acos()
        } else {
            Vec3d::new(0.0, 0.0, 1.0).dot(x_proj).acos()
        };

        self.camera_dir = vec3_roty(self.camera_dir, -angle);
        self.camera_dir = vec3_rotx(self.camera_dir, y_rot);
        self.camera_dir = vec3_roty(self.camera_dir, angle);

        self.camera_dir = self.camera_dir.normalize();
    }

    /// Returns an iterator that scans a full frame at given resolution
    pub fn draw_iter(&self, h: usize, v: usize) -> DrawIter {
        DrawIter::new(self, h, v)
    }

    /// draw_iter converted to (u8, u8, u8)
    pub fn draw_iter_u8(&self,
                        h: usize,
                        v: usize)
                        -> std::iter::Map<DrawIter, fn(Color) -> (u8, u8, u8)> {
        self.draw_iter(h, v).map(Color::col_to_u8)
    }

    /// Returns an iterator for a line at given resolution
    pub fn line_iter(&self, h: usize, v: usize, l: usize) -> LineIter {
        LineIter::new(self, h, v, l)
    }

    /// line_iter converted to (u8, u8, u8)
    pub fn line_iter_u8(&self,
                        h: usize,
                        v: usize,
                        l: usize)
                        -> std::iter::Map<LineIter, fn(Color) -> (u8, u8, u8)> {
        self.line_iter(h, v, l).map(Color::col_to_u8)
    }

    // Recursively trace lines
    fn trace(&self, point: &Vec3d, dir: &Vec3d, depth: i32) -> Color {
        if depth >= self.max_reflection {
            return color::BLACK;
        }

        if let Some(intersect) = self.closest_q(point, dir) {
            let feeler_d = self.light.get_pos() - intersect.point;
            let dist_light = feeler_d.magnitude();
            let feeler_d_unit = feeler_d.normalize();
            let direct_light = self.is_direct_light(&intersect.point, &feeler_d_unit, dist_light);

            let local = phong(point, &intersect, &self.light, direct_light);

            let tmp = (intersect.point - point).normalize();
            let reflection_dir = tmp - (intersect.normal * 2.0 * tmp.dot(intersect.normal));

            let reflected = self.trace(&intersect.point, &reflection_dir, depth + 1);

            Color::add(&local,
                       &(Color::scale(&reflected, intersect.material.reflectivity)))
        } else {
            color::BLACK
        }
    }

    // Is there anything on the path to the light
    fn is_direct_light(&self, point: &Vec3d, dir: &Vec3d, dl: f64) -> bool {
        !self.shapes
             .iter()
             .map(|&ref x| x.intersect_dist(point, dir))
             .any(|dist_opt| dist_opt.map_or(false, |i| i < dl))
    }

    // Checks against all objects and returns closest intersection
    fn closest_q(&self, point: &Vec3d, dir: &Vec3d) -> Option<Intersection> {
        let find_min_opt = |min, (idx, op_val)| {
            match (min, op_val) {
                (Some((_, min_val)), Some(val)) if val < min_val => Some((idx, val)),
                (None, Some(val)) => Some((idx, val)),
                _ => min,
            }
        };

        self.shapes
            .iter()
            .map(|x| x.intersect_dist(point, dir))
            .enumerate()
            .fold(None, find_min_opt)
            .and_then(|(idx, _)| self.shapes[idx].intersect(point, dir))
    }
}

/// Iterator that iterates over a full frame
pub struct DrawIter<'a> {
    h_res: usize,
    v_res: usize,
    x: usize,
    y: usize,
    left: Vec3d,
    right_step: Vec3d,
    down_step: Vec3d,
    point: Vec3d,
    scene: &'a Scene,
}

impl<'a> DrawIter<'a> {
    fn new(scene: &Scene, h: usize, v: usize) -> DrawIter {
        let left = scene.camera_dir.cross(scene.camera_up).normalize();
        let up = left.cross(scene.camera_dir).normalize();
        let point = left + up + scene.camera_dir;
        let right_step = left * (-2.0 / h as f64);
        let down_step = up * (-2.0 / v as f64);
        DrawIter {
            h_res: h,
            v_res: v,
            x: 0,
            y: 0,
            left: left,
            point: point,
            right_step: right_step,
            down_step: down_step,
            scene: scene,
        }
    }
}

impl<'a> Iterator for DrawIter<'a> {
    type Item = Color;

    fn next(&mut self) -> Option<Self::Item> {
        if self.y >= self.v_res {
            return None;
        }

        self.point = self.point + self.right_step;

        let c = self.scene
            .trace(&self.scene.camera_pos, &self.point.normalize(), 0);

        self.x += 1;
        if self.x >= self.h_res {
            self.x = 0;
            self.y += 1;
            self.point = self.point + self.left;
            self.point = self.point + self.left;
            self.point = self.point + self.down_step;
        }

        Some(c)
    }
}

/// Iterator that iterates over a single line
pub struct LineIter<'a> {
    h_res: usize,
    x: usize,
    right_step: Vec3d,
    point: Vec3d,
    scene: &'a Scene,
}

impl<'a> LineIter<'a> {
    fn new(scene: &Scene, h: usize, v: usize, l: usize) -> LineIter {
        let left = scene.camera_dir.cross(scene.camera_up).normalize();
        let up = left.cross(scene.camera_dir).normalize();
        let mut point = left + up + scene.camera_dir;
        let right_step = left * (-2.0 / h as f64);
        let down_step = up * (-2.0 / v as f64);

        point = point + down_step * (l as f64);
        LineIter {
            h_res: h,
            x: 0,
            point: point,
            right_step: right_step,
            scene: scene,
        }
    }
}

impl<'a> Iterator for LineIter<'a> {
    type Item = Color;

    fn next(&mut self) -> Option<Self::Item> {
        if self.x >= self.h_res {
            return None;
        }
        self.x += 1;

        self.point = self.point + self.right_step;

        let c = self.scene
            .trace(&self.scene.camera_pos, &self.point.normalize(), 0);

        Some(c)
    }
}

// Calculate color with Phong model
fn phong(view_point: &Vec3d,
         intersection: &Intersection,
         light: &Light,
         direct_light: bool)
         -> Color {

    let point_material = intersection.material;
    let light_pos = light.get_pos();
    let light_color = light.get_color();
    let mut d = (light_pos - intersection.point)
        .normalize()
        .dot(intersection.normal);

    if d < 0.0 {
        d = 0.0;
    }

    let r_diffuse = if direct_light {
        light_color.0 * point_material.diffuse_color.0 * d
    } else {
        0.0
    };
    let g_diffuse = if direct_light {
        light_color.1 * point_material.diffuse_color.1 * d
    } else {
        0.0
    };
    let b_diffuse = if direct_light {
        light_color.2 * point_material.diffuse_color.2 * d
    } else {
        0.0
    };

    let r_ambient = point_material.ambient_color.0;
    let g_ambient = point_material.ambient_color.1;
    let b_ambient = point_material.ambient_color.2;

    let v = (view_point - intersection.point).normalize();
    let lt = (light_pos - intersection.point).normalize();
    let r = intersection.normal * (2.0 * intersection.normal.dot(lt)) - lt;

    let mut s = r.dot(v).powf(point_material.shininess);
    if s < 0.0 {
        s = 0.0;
    }

    let r_specular = if direct_light {
        light_color.0 * point_material.specular_color.0 * s
    } else {
        0.0
    };
    let g_specular = if direct_light {
        light_color.1 * point_material.specular_color.1 * s
    } else {
        0.0
    };
    let b_specular = if direct_light {
        light_color.2 * point_material.specular_color.2 * s
    } else {
        0.0
    };

    let mut r_sum = r_ambient + r_diffuse + r_specular;
    let mut g_sum = g_ambient + g_diffuse + g_specular;
    let mut b_sum = b_ambient + b_diffuse + b_specular;

    if r_sum > 1.0 {
        r_sum = 1.0;
    }

    if g_sum > 1.0 {
        g_sum = 1.0;
    }

    if b_sum > 1.0 {
        b_sum = 1.0;
    }

    Color(r_sum, g_sum, b_sum)
}
<commit_msg>Add error type<commit_after>use std::io;
use std::iter::Map;
use std::io::BufReader;
use std::fs::File;

use serde_json;

use cgmath::*;
use shape::*;
use vec3d::*;
use light::*;

use color;
use color::Color;

#[derive(Serialize, Deserialize)]
pub struct Scene {
    shapes: Vec<Shape>,
    light: Light,
    camera_pos: Vec3d,
    camera_dir: Vec3d,
    camera_up: Vec3d,
    max_reflection: i32,
}

#[derive(Debug)]
pub enum CraycrayError {
    Io(io::Error),
    Serde(serde_json::Error),
}

impl Scene {
    pub fn new(light: Light, camera_pos: Vec3d, camera_dir: Vec3d, camera_up: Vec3d) -> Scene {
        Scene {
            shapes: Vec::new(),
            light: light,
            camera_pos: camera_pos,
            camera_dir: camera_dir,
            camera_up: camera_up,
            max_reflection: 4,
        }
    }

    pub fn from_file(filename: &str) -> Result<Scene, CraycrayError> {
        File::open(filename)
            .map_err(CraycrayError::Io)
            .and_then(|f| serde_json::from_reader(BufReader::new(f)).map_err(CraycrayError::Serde))
    }

    pub fn add_shape(&mut self, s: Shape) {
        self.shapes.push(s);
    }

    pub fn step(&mut self) {
        // self.light.translate(&[1.0, 0.0, 0.0]);
    }

    pub fn mv_camera_fwd(&mut self) {
        self.camera_pos = self.camera_pos + self.camera_dir;
    }

    pub fn mv_camera_back(&mut self) {
        self.camera_pos = self.camera_pos - self.camera_dir;
    }

    pub fn rot_camera(&mut self, x_rot: f64, y_rot: f64) {
        self.camera_dir = vec3_roty(self.camera_dir, x_rot);

        // project camera_dir to X plane
        let mut x_proj = self.camera_dir;
        x_proj.y = 0.0;
        x_proj = x_proj.normalize();

        let angle = if x_proj.x < 0.0 {
            -Vec3d::new(0.0, 0.0, 1.0).dot(x_proj).acos()
        } else {
            Vec3d::new(0.0, 0.0, 1.0).dot(x_proj).acos()
        };

        self.camera_dir = vec3_roty(self.camera_dir, -angle);
        self.camera_dir = vec3_rotx(self.camera_dir, y_rot);
        self.camera_dir = vec3_roty(self.camera_dir, angle);

        self.camera_dir = self.camera_dir.normalize();
    }

    /// Returns an iterator that scans a full frame at given resolution
    pub fn draw_iter(&self, h: usize, v: usize) -> DrawIter {
        DrawIter::new(self, h, v)
    }

    /// draw_iter converted to (u8, u8, u8)
    pub fn draw_iter_u8(&self,
                        h: usize,
                        v: usize)
                        -> Map<DrawIter, fn(Color) -> (u8, u8, u8)> {
        self.draw_iter(h, v).map(Color::col_to_u8)
    }

    /// Returns an iterator for a line at given resolution
    pub fn line_iter(&self, h: usize, v: usize, l: usize) -> LineIter {
        LineIter::new(self, h, v, l)
    }

    /// line_iter converted to (u8, u8, u8)
    pub fn line_iter_u8(&self,
                        h: usize,
                        v: usize,
                        l: usize)
                        -> Map<LineIter, fn(Color) -> (u8, u8, u8)> {
        self.line_iter(h, v, l).map(Color::col_to_u8)
    }

    // Recursively trace lines
    fn trace(&self, point: &Vec3d, dir: &Vec3d, depth: i32) -> Color {
        if depth >= self.max_reflection {
            return color::BLACK;
        }

        if let Some(intersect) = self.closest_q(point, dir) {
            let feeler_d = self.light.get_pos() - intersect.point;
            let dist_light = feeler_d.magnitude();
            let feeler_d_unit = feeler_d.normalize();
            let direct_light = self.is_direct_light(&intersect.point, &feeler_d_unit, dist_light);

            let local = phong(point, &intersect, &self.light, direct_light);

            let tmp = (intersect.point - point).normalize();
            let reflection_dir = tmp - (intersect.normal * 2.0 * tmp.dot(intersect.normal));

            let reflected = self.trace(&intersect.point, &reflection_dir, depth + 1);

            Color::add(&local,
                       &(Color::scale(&reflected, intersect.material.reflectivity)))
        } else {
            color::BLACK
        }
    }

    // Is there anything on the path to the light
    fn is_direct_light(&self, point: &Vec3d, dir: &Vec3d, dl: f64) -> bool {
        !self.shapes
             .iter()
             .map(|&ref x| x.intersect_dist(point, dir))
             .any(|dist_opt| dist_opt.map_or(false, |i| i < dl))
    }

    // Checks against all objects and returns closest intersection
    fn closest_q(&self, point: &Vec3d, dir: &Vec3d) -> Option<Intersection> {
        let find_min_opt = |min, (idx, op_val)| {
            match (min, op_val) {
                (Some((_, min_val)), Some(val)) if val < min_val => Some((idx, val)),
                (None, Some(val)) => Some((idx, val)),
                _ => min,
            }
        };

        self.shapes
            .iter()
            .map(|x| x.intersect_dist(point, dir))
            .enumerate()
            .fold(None, find_min_opt)
            .and_then(|(idx, _)| self.shapes[idx].intersect(point, dir))
    }
}

/// Iterator that iterates over a full frame
pub struct DrawIter<'a> {
    h_res: usize,
    v_res: usize,
    x: usize,
    y: usize,
    left: Vec3d,
    right_step: Vec3d,
    down_step: Vec3d,
    point: Vec3d,
    scene: &'a Scene,
}

impl<'a> DrawIter<'a> {
    fn new(scene: &Scene, h: usize, v: usize) -> DrawIter {
        let left = scene.camera_dir.cross(scene.camera_up).normalize();
        let up = left.cross(scene.camera_dir).normalize();
        let point = left + up + scene.camera_dir;
        let right_step = left * (-2.0 / h as f64);
        let down_step = up * (-2.0 / v as f64);
        DrawIter {
            h_res: h,
            v_res: v,
            x: 0,
            y: 0,
            left: left,
            point: point,
            right_step: right_step,
            down_step: down_step,
            scene: scene,
        }
    }
}

impl<'a> Iterator for DrawIter<'a> {
    type Item = Color;

    fn next(&mut self) -> Option<Self::Item> {
        if self.y >= self.v_res {
            return None;
        }

        self.point = self.point + self.right_step;

        let c = self.scene
            .trace(&self.scene.camera_pos, &self.point.normalize(), 0);

        self.x += 1;
        if self.x >= self.h_res {
            self.x = 0;
            self.y += 1;
            self.point = self.point + self.left;
            self.point = self.point + self.left;
            self.point = self.point + self.down_step;
        }

        Some(c)
    }
}

/// Iterator that iterates over a single line
pub struct LineIter<'a> {
    h_res: usize,
    x: usize,
    right_step: Vec3d,
    point: Vec3d,
    scene: &'a Scene,
}

impl<'a> LineIter<'a> {
    fn new(scene: &Scene, h: usize, v: usize, l: usize) -> LineIter {
        let left = scene.camera_dir.cross(scene.camera_up).normalize();
        let up = left.cross(scene.camera_dir).normalize();
        let mut point = left + up + scene.camera_dir;
        let right_step = left * (-2.0 / h as f64);
        let down_step = up * (-2.0 / v as f64);

        point = point + down_step * (l as f64);
        LineIter {
            h_res: h,
            x: 0,
            point: point,
            right_step: right_step,
            scene: scene,
        }
    }
}

impl<'a> Iterator for LineIter<'a> {
    type Item = Color;

    fn next(&mut self) -> Option<Self::Item> {
        if self.x >= self.h_res {
            return None;
        }
        self.x += 1;

        self.point = self.point + self.right_step;

        let c = self.scene
            .trace(&self.scene.camera_pos, &self.point.normalize(), 0);

        Some(c)
    }
}

// Calculate color with Phong model
fn phong(view_point: &Vec3d,
         intersection: &Intersection,
         light: &Light,
         direct_light: bool)
         -> Color {

    let point_material = intersection.material;
    let light_pos = light.get_pos();
    let light_color = light.get_color();
    let mut d = (light_pos - intersection.point)
        .normalize()
        .dot(intersection.normal);

    if d < 0.0 {
        d = 0.0;
    }

    let r_diffuse = if direct_light {
        light_color.0 * point_material.diffuse_color.0 * d
    } else {
        0.0
    };
    let g_diffuse = if direct_light {
        light_color.1 * point_material.diffuse_color.1 * d
    } else {
        0.0
    };
    let b_diffuse = if direct_light {
        light_color.2 * point_material.diffuse_color.2 * d
    } else {
        0.0
    };

    let r_ambient = point_material.ambient_color.0;
    let g_ambient = point_material.ambient_color.1;
    let b_ambient = point_material.ambient_color.2;

    let v = (view_point - intersection.point).normalize();
    let lt = (light_pos - intersection.point).normalize();
    let r = intersection.normal * (2.0 * intersection.normal.dot(lt)) - lt;

    let mut s = r.dot(v).powf(point_material.shininess);
    if s < 0.0 {
        s = 0.0;
    }

    let r_specular = if direct_light {
        light_color.0 * point_material.specular_color.0 * s
    } else {
        0.0
    };
    let g_specular = if direct_light {
        light_color.1 * point_material.specular_color.1 * s
    } else {
        0.0
    };
    let b_specular = if direct_light {
        light_color.2 * point_material.specular_color.2 * s
    } else {
        0.0
    };

    let mut r_sum = r_ambient + r_diffuse + r_specular;
    let mut g_sum = g_ambient + g_diffuse + g_specular;
    let mut b_sum = b_ambient + b_diffuse + b_specular;

    if r_sum > 1.0 {
        r_sum = 1.0;
    }

    if g_sum > 1.0 {
        g_sum = 1.0;
    }

    if b_sum > 1.0 {
        b_sum = 1.0;
    }

    Color(r_sum, g_sum, b_sum)
}
<|endoftext|>


<commit_before>//! Debug Logging
//!
//! To use in a debug build, set the env var `RAYON_RS_LOG=1`.  In a
//! release build, logs are compiled out. You will have to change
//! `DUMP_LOGS` to be `true`.

use std::env;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};

#[derive(Debug)]
pub enum Event {
    Tickle { worker: usize, old_state: usize },
    GetSleepy { worker: usize, state: usize },
    GotSleepy { worker: usize, old_state: usize, new_state: usize },
    GotAwoken { worker: usize },
    FellAsleep { worker: usize },
    GotInterrupted { worker: usize },
    FoundWork { worker: usize, yields: usize },
    DidNotFindWork { worker: usize, yields: usize },
    StoleWork { worker: usize, victim: usize },
    UninjectedWork { worker: usize },
    WaitUntil { worker: usize },
    LatchSet { worker: usize },
    InjectJobs { count: usize },
    Join { worker: usize },
    PoppedJob { worker: usize },
    PoppedRhs { worker: usize },
    LostJob { worker: usize },
    JobCompletedOk { owner_thread: usize },
    JobPanickedErrorStored { owner_thread: usize },
    JobPanickedErrorNotStored { owner_thread: usize },
    ScopeCompletePanicked { owner_thread: usize },
    ScopeCompleteNoPanic { owner_thread: usize },

    FutureExecute { state: usize },
    FutureExecuteReady,
    FutureExecuteNotReady,
    FutureExecuteErr,
    FutureInstallWaitingTask { state: usize },
    FutureUnparkWaitingTask,
    FutureComplete,
}

pub const DUMP_LOGS: bool = cfg!(debug_assertions);

lazy_static! {
    pub static ref LOG_ENV: bool = env::var("RAYON_RS_LOG").is_ok();
}

macro_rules! log {
    ($event:expr) => {
        if ::log::DUMP_LOGS { if *::log::LOG_ENV { println!("{:?}", $event); } }
    }
}

pub static STOLEN_JOB: AtomicUsize = ATOMIC_USIZE_INIT;

macro_rules! stat_stolen {
    () => {
        ::log::STOLEN_JOB.fetch_add(1, ::std::sync::atomic::Ordering::SeqCst);
    }
}

pub static POPPED_JOB: AtomicUsize = ATOMIC_USIZE_INIT;

macro_rules! stat_popped {
    () => {
        ::log::POPPED_JOB.fetch_add(1, ::std::sync::atomic::Ordering::SeqCst);
    }
}

macro_rules! dump_stats {
    () => {
        {
            let stolen = ::log::STOLEN_JOB.load(::std::sync::atomic::Ordering::SeqCst);
            println!("Jobs stolen: {:?}", stolen);
            let popped = ::log::POPPED_JOB.load(::std::sync::atomic::Ordering::SeqCst);
            println!("Jobs popped: {:?}", popped);
        }
    }
}
<commit_msg>Change logging controlling env var to RAYON_LOG<commit_after>//! Debug Logging
//!
//! To use in a debug build, set the env var `RAYON_LOG=1`.  In a
//! release build, logs are compiled out. You will have to change
//! `DUMP_LOGS` to be `true`.
//!
//! **Old environment variable:** `RAYON_LOG` is a one-to-one
//! replacement of the now deprecated `RAYON_RS_LOG` environment
//! variable, which is still supported for backwards compatibility.

use std::env;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};

#[derive(Debug)]
pub enum Event {
    Tickle { worker: usize, old_state: usize },
    GetSleepy { worker: usize, state: usize },
    GotSleepy { worker: usize, old_state: usize, new_state: usize },
    GotAwoken { worker: usize },
    FellAsleep { worker: usize },
    GotInterrupted { worker: usize },
    FoundWork { worker: usize, yields: usize },
    DidNotFindWork { worker: usize, yields: usize },
    StoleWork { worker: usize, victim: usize },
    UninjectedWork { worker: usize },
    WaitUntil { worker: usize },
    LatchSet { worker: usize },
    InjectJobs { count: usize },
    Join { worker: usize },
    PoppedJob { worker: usize },
    PoppedRhs { worker: usize },
    LostJob { worker: usize },
    JobCompletedOk { owner_thread: usize },
    JobPanickedErrorStored { owner_thread: usize },
    JobPanickedErrorNotStored { owner_thread: usize },
    ScopeCompletePanicked { owner_thread: usize },
    ScopeCompleteNoPanic { owner_thread: usize },

    FutureExecute { state: usize },
    FutureExecuteReady,
    FutureExecuteNotReady,
    FutureExecuteErr,
    FutureInstallWaitingTask { state: usize },
    FutureUnparkWaitingTask,
    FutureComplete,
}

pub const DUMP_LOGS: bool = cfg!(debug_assertions);

lazy_static! {
    pub static ref LOG_ENV: bool = env::var("RAYON_LOG").is_ok() || env::var("RAYON_RS_LOG").is_ok();
}

macro_rules! log {
    ($event:expr) => {
        if ::log::DUMP_LOGS { if *::log::LOG_ENV { println!("{:?}", $event); } }
    }
}

pub static STOLEN_JOB: AtomicUsize = ATOMIC_USIZE_INIT;

macro_rules! stat_stolen {
    () => {
        ::log::STOLEN_JOB.fetch_add(1, ::std::sync::atomic::Ordering::SeqCst);
    }
}

pub static POPPED_JOB: AtomicUsize = ATOMIC_USIZE_INIT;

macro_rules! stat_popped {
    () => {
        ::log::POPPED_JOB.fetch_add(1, ::std::sync::atomic::Ordering::SeqCst);
    }
}

macro_rules! dump_stats {
    () => {
        {
            let stolen = ::log::STOLEN_JOB.load(::std::sync::atomic::Ordering::SeqCst);
            println!("Jobs stolen: {:?}", stolen);
            let popped = ::log::POPPED_JOB.load(::std::sync::atomic::Ordering::SeqCst);
            println!("Jobs popped: {:?}", popped);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Sam Doshi (sam@metal-fish.co.uk)
//
// Licensed under the MIT License <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use constants::*;
use types::{U7, Channel};
use raw_message::{RawMessage};
use RawMessage::*;
use message::{Message};
use Message::*;
use utils::{mask7, status_byte, u14_to_msb_lsb};

/// Convert `self` to `Vec<RawMessage>`
///
/// A `Vec<RawMessage>` represents ordered Midi data that must be sent as a contigious
/// block, this is useful for representing `Message::SysEx` and `Message::NRPN14`,
/// note that midi clock messages are allowed to interrupt sysex messages as part of the spec.
pub trait ToRawMessages {
    fn to_raw_messages(&self) -> Vec<RawMessage>;
}

impl ToRawMessages for RawMessage {
    fn to_raw_messages(&self) -> Vec<RawMessage> {
        vec!(*self)
    }
}

impl ToRawMessages for Message {
    fn to_raw_messages(&self) -> Vec<RawMessage> {
        match self {
            // System realtime
            &Start => vec!(Status(START)),
            &TimingClock => vec!(Status(TIMING_CLOCK)),
            &Continue => vec!(Status(CONTINUE)),
            &Stop => vec!(Status(STOP)),
            &ActiveSensing => vec!(Status(ACTIVE_SENSING)),
            &SystemReset => vec!(Status(SYSTEM_RESET)),

            // Channel mode
            &AllSoundOff(ch) => ControlChange(ch, 120, 0).to_raw_messages(),
            &ResetAllControllers(ch) => ControlChange(ch, 121, 0).to_raw_messages(),
            &LocalControlOff(ch) => ControlChange(ch, 122, 0).to_raw_messages(),
            &LocalControlOn(ch) => ControlChange(ch, 122, 127).to_raw_messages(),
            &AllNotesOff(ch) => ControlChange(ch, 123, 0).to_raw_messages(),

            // Channel voice
            &ProgramChange(ch, no) => {
                let sb = status_byte(PROGRAM_CHANGE, ch);
                vec!(StatusData(sb, mask7(no)))
            },
            &ControlChange(ch, no, val) => {
                vec!(cc(ch, mask7(no), mask7(val)))
            },
            &RPN7(ch, rpn, val) => {
                let (rpn_msb, rpn_lsb) = u14_to_msb_lsb(rpn);
                vec!(
                    cc(ch, CC_RPN_MSB, rpn_msb),
                    cc(ch, CC_RPN_LSB, rpn_lsb),
                    cc(ch, CC_DATA_ENTRY_MSB, mask7(val))
                )
            },
            &RPN14(ch, rpn, val) => {
                let (rpn_msb, rpn_lsb) = u14_to_msb_lsb(rpn);
                let (val_msb, val_lsb) = u14_to_msb_lsb(val);
                vec!(
                    cc(ch, CC_RPN_MSB, rpn_msb),
                    cc(ch, CC_RPN_LSB, rpn_lsb),
                    cc(ch, CC_DATA_ENTRY_MSB, val_msb),
                    cc(ch, CC_DATA_ENTRY_LSB, val_lsb)
                )
            },
            &NRPN7(ch, nrpn, val) => {
                let (nrpn_msb, nrpn_lsb) = u14_to_msb_lsb(nrpn);
                vec!(
                    cc(ch, CC_NRPN_MSB, nrpn_msb),
                    cc(ch, CC_NRPN_LSB, nrpn_lsb),
                    cc(ch, CC_DATA_ENTRY_MSB, mask7(val))
                )
            },
            &NRPN14(ch, nrpn, val) => {
                let (nrpn_msb, nrpn_lsb) = u14_to_msb_lsb(nrpn);
                let (val_msb, val_lsb) = u14_to_msb_lsb(val);
                vec!(
                    cc(ch, CC_NRPN_MSB, nrpn_msb),
                    cc(ch, CC_NRPN_LSB, nrpn_lsb),
                    cc(ch, CC_DATA_ENTRY_MSB, val_msb),
                    cc(ch, CC_DATA_ENTRY_LSB, val_lsb)
                )
            },
            &SysEx(manufacturer, ref data) => {
                let mut output = Vec::new();
                output.push(SYSEX);
                output.push_all(manufacturer.to_u7s().as_slice());
                output.extend(data.iter().map(|d| mask7(*d)));
                output.push(SYSEX_EOX);
                output.into_iter().map(|d| Raw(d)).collect()
            },
            &NoteOff(ch, no, vel) => {
                let sb = status_byte(NOTE_OFF, ch);
                vec!(StatusDataData(sb, mask7(no), mask7(vel)))
            },
            &NoteOn(ch, no, vel) => {
                let sb = status_byte(NOTE_ON, ch);
                vec!(StatusDataData(sb, mask7(no), mask7(vel)))
            },
            &PitchBend(ch, bend) => {
                let sb = status_byte(PITCH_BEND, ch);
                let (msb, lsb) = u14_to_msb_lsb(bend);
                vec!(StatusDataData(sb, lsb, msb))
            }
            &PolyphonicPressure(ch, no, vel) => {
                let sb = status_byte(POLYPHONIC_PRESSURE, ch);
                vec!(StatusDataData(sb, mask7(no), mask7(vel)))
            },
            &ChannelPressure(ch, vel) => {
                let sb = status_byte(CHANNEL_PRESSURE, ch);
                vec!(StatusData(sb, mask7(vel)))
            }
        }
    }
}

// we need to generate a lot of CC messages...
fn cc(ch: Channel, cc_no: U7, val: U7) -> RawMessage {
    let sb = status_byte(CONTROL_CHANGE, ch);
    StatusDataData(sb, cc_no, val)
}
<commit_msg>add test_message_to_raw_messages()<commit_after>// Copyright 2015 Sam Doshi (sam@metal-fish.co.uk)
//
// Licensed under the MIT License <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use constants::*;
use types::{U7, Channel};
use raw_message::{RawMessage};
use RawMessage::*;
use message::{Message};
use Message::*;
use utils::{mask7, status_byte, u14_to_msb_lsb};

/// Convert `self` to `Vec<RawMessage>`
///
/// A `Vec<RawMessage>` represents ordered Midi data that must be sent as a contigious
/// block, this is useful for representing `Message::SysEx` and `Message::NRPN14`,
/// note that midi clock messages are allowed to interrupt sysex messages as part of the spec.
pub trait ToRawMessages {
    fn to_raw_messages(&self) -> Vec<RawMessage>;
}

impl ToRawMessages for RawMessage {
    fn to_raw_messages(&self) -> Vec<RawMessage> {
        vec!(*self)
    }
}

impl ToRawMessages for Message {
    fn to_raw_messages(&self) -> Vec<RawMessage> {
        match self {
            // System realtime
            &Start => vec!(Status(START)),
            &TimingClock => vec!(Status(TIMING_CLOCK)),
            &Continue => vec!(Status(CONTINUE)),
            &Stop => vec!(Status(STOP)),
            &ActiveSensing => vec!(Status(ACTIVE_SENSING)),
            &SystemReset => vec!(Status(SYSTEM_RESET)),

            // Channel mode
            &AllSoundOff(ch) => ControlChange(ch, 120, 0).to_raw_messages(),
            &ResetAllControllers(ch) => ControlChange(ch, 121, 0).to_raw_messages(),
            &LocalControlOff(ch) => ControlChange(ch, 122, 0).to_raw_messages(),
            &LocalControlOn(ch) => ControlChange(ch, 122, 127).to_raw_messages(),
            &AllNotesOff(ch) => ControlChange(ch, 123, 0).to_raw_messages(),

            // Channel voice
            &ProgramChange(ch, no) => {
                let sb = status_byte(PROGRAM_CHANGE, ch);
                vec!(StatusData(sb, mask7(no)))
            },
            &ControlChange(ch, no, val) => {
                vec!(cc(ch, mask7(no), mask7(val)))
            },
            &RPN7(ch, rpn, val) => {
                let (rpn_msb, rpn_lsb) = u14_to_msb_lsb(rpn);
                vec!(
                    cc(ch, CC_RPN_MSB, rpn_msb),
                    cc(ch, CC_RPN_LSB, rpn_lsb),
                    cc(ch, CC_DATA_ENTRY_MSB, mask7(val))
                )
            },
            &RPN14(ch, rpn, val) => {
                let (rpn_msb, rpn_lsb) = u14_to_msb_lsb(rpn);
                let (val_msb, val_lsb) = u14_to_msb_lsb(val);
                vec!(
                    cc(ch, CC_RPN_MSB, rpn_msb),
                    cc(ch, CC_RPN_LSB, rpn_lsb),
                    cc(ch, CC_DATA_ENTRY_MSB, val_msb),
                    cc(ch, CC_DATA_ENTRY_LSB, val_lsb)
                )
            },
            &NRPN7(ch, nrpn, val) => {
                let (nrpn_msb, nrpn_lsb) = u14_to_msb_lsb(nrpn);
                vec!(
                    cc(ch, CC_NRPN_MSB, nrpn_msb),
                    cc(ch, CC_NRPN_LSB, nrpn_lsb),
                    cc(ch, CC_DATA_ENTRY_MSB, mask7(val))
                )
            },
            &NRPN14(ch, nrpn, val) => {
                let (nrpn_msb, nrpn_lsb) = u14_to_msb_lsb(nrpn);
                let (val_msb, val_lsb) = u14_to_msb_lsb(val);
                vec!(
                    cc(ch, CC_NRPN_MSB, nrpn_msb),
                    cc(ch, CC_NRPN_LSB, nrpn_lsb),
                    cc(ch, CC_DATA_ENTRY_MSB, val_msb),
                    cc(ch, CC_DATA_ENTRY_LSB, val_lsb)
                )
            },
            &SysEx(manufacturer, ref data) => {
                let mut output = Vec::new();
                output.push(SYSEX);
                output.push_all(manufacturer.to_u7s().as_slice());
                output.extend(data.iter().map(|d| mask7(*d)));
                output.push(SYSEX_EOX);
                output.into_iter().map(|d| Raw(d)).collect()
            },
            &NoteOff(ch, no, vel) => {
                let sb = status_byte(NOTE_OFF, ch);
                vec!(StatusDataData(sb, mask7(no), mask7(vel)))
            },
            &NoteOn(ch, no, vel) => {
                let sb = status_byte(NOTE_ON, ch);
                vec!(StatusDataData(sb, mask7(no), mask7(vel)))
            },
            &PitchBend(ch, bend) => {
                let sb = status_byte(PITCH_BEND, ch);
                let (msb, lsb) = u14_to_msb_lsb(bend);
                vec!(StatusDataData(sb, lsb, msb))
            }
            &PolyphonicPressure(ch, no, vel) => {
                let sb = status_byte(POLYPHONIC_PRESSURE, ch);
                vec!(StatusDataData(sb, mask7(no), mask7(vel)))
            },
            &ChannelPressure(ch, vel) => {
                let sb = status_byte(CHANNEL_PRESSURE, ch);
                vec!(StatusData(sb, mask7(vel)))
            }
        }
    }
}

// we need to generate a lot of CC messages...
fn cc(ch: Channel, cc_no: U7, val: U7) -> RawMessage {
    let sb = status_byte(CONTROL_CHANGE, ch);
    StatusDataData(sb, cc_no, val)
}

#[cfg(test)]
mod test {
    use super::ToRawMessages;
    use message::Message::*;
    use raw_message::RawMessage::*;
    use manufacturer::Manufacturer::*;
    use types::Channel::*;

    #[test]
    fn test_message_to_raw_messages() {
        // Where possible these numbers have been pasted in from
        // http://www.midi.org/techspecs/midimessages.php

        // Start
        assert_eq!(Start.to_raw_messages(), vec![Status(0b11111010)]);

        // TimingClock
        assert_eq!(TimingClock.to_raw_messages(), vec![Status(0b11111000)]);

        // Continue
        assert_eq!(Continue.to_raw_messages(), vec![Status(0b11111011)]);

        // Stop
        assert_eq!(Stop.to_raw_messages(), vec![Status(0b11111100)]);

        // ActiveSensing
        assert_eq!(ActiveSensing.to_raw_messages(), vec![Status(0b11111110)]);

        // SystemReset
        assert_eq!(SystemReset.to_raw_messages(), vec![Status(0b11111111)]);

        // AllSoundOff
        assert_eq!(AllSoundOff(Ch1).to_raw_messages(), vec![StatusDataData(176, 120, 0)]);

        // ResetAllControllers
        assert_eq!(ResetAllControllers(Ch1).to_raw_messages(), vec![StatusDataData(176, 121, 0)]);

        // LocalControlOff
        assert_eq!(LocalControlOff(Ch1).to_raw_messages(), vec![StatusDataData(176, 122, 0)]);

        // LocalControlOn
        assert_eq!(LocalControlOn(Ch1).to_raw_messages(), vec![StatusDataData(176, 122, 127)]);

        // AllNotesOff
        assert_eq!(AllNotesOff(Ch1).to_raw_messages(), vec![StatusDataData(176, 123, 0)]);

        // ProgramChange
        assert_eq!(ProgramChange(Ch1, 0).to_raw_messages(), vec![StatusData(192, 0)]);
        assert_eq!(ProgramChange(Ch1, 127).to_raw_messages(), vec![StatusData(192, 127)]);
        assert_eq!(ProgramChange(Ch1, 128).to_raw_messages(), vec![StatusData(192, 0)]);

        // ControlChange
        assert_eq!(ControlChange(Ch1, 0, 0).to_raw_messages(), vec![StatusDataData(176, 0, 0)]);
        assert_eq!(ControlChange(Ch1, 0, 127).to_raw_messages(), vec![StatusDataData(176, 0, 127)]);
        assert_eq!(ControlChange(Ch1, 0, 128).to_raw_messages(), vec![StatusDataData(176, 0, 0)]);
        assert_eq!(ControlChange(Ch1, 127, 0).to_raw_messages(), vec![StatusDataData(176, 127, 0)]);
        assert_eq!(ControlChange(Ch1, 128, 0).to_raw_messages(), vec![StatusDataData(176, 0, 0)]);

        // RPN7
        assert_eq!(RPN7(Ch1, 1000, 0).to_raw_messages(), vec![StatusDataData(176, 101, 7),
                                                              StatusDataData(176, 100, 104),
                                                              StatusDataData(176, 6, 0)]);

        // RPN14
        assert_eq!(RPN14(Ch1, 1000, 1001).to_raw_messages(), vec![StatusDataData(176, 101, 7),
                                                                  StatusDataData(176, 100, 104),
                                                                  StatusDataData(176, 6, 7),
                                                                  StatusDataData(176, 38, 105)]);

        // NRPN7
        assert_eq!(NRPN7(Ch1, 1000, 0).to_raw_messages(), vec![StatusDataData(176, 99, 7),
                                                               StatusDataData(176, 98, 104),
                                                               StatusDataData(176, 6, 0)]);

        // NRPN14
        assert_eq!(NRPN14(Ch1, 1000, 1001).to_raw_messages(), vec![StatusDataData(176, 99, 7),
                                                                   StatusDataData(176, 98, 104),
                                                                   StatusDataData(176, 6, 7),
                                                                   StatusDataData(176, 38, 105)]);

        // SysEx
        assert_eq!(SysEx(OneByte(100), vec![1, 2, 3, 4]).to_raw_messages(),
                   vec![Raw(0b11110000),
                        Raw(100),
                        Raw(1), Raw(2), Raw(3), Raw(4),
                        Raw(0b11110111)]);

        assert_eq!(SysEx(OneByte(128), vec![1, 2, 3, 4, 128]).to_raw_messages(),
                   vec![Raw(0b11110000),
                        Raw(0),
                        Raw(1), Raw(2), Raw(3), Raw(4), Raw(0),
                        Raw(0b11110111)]);

        assert_eq!(SysEx(ThreeByte(100, 101, 128), vec![1, 2, 3, 4]).to_raw_messages(),
                   vec![Raw(0b11110000),
                        Raw(100), Raw(101), Raw(0),
                        Raw(1), Raw(2), Raw(3), Raw(4),
                        Raw(0b11110111)]);

        // NoteOff
        assert_eq!(NoteOff(Ch1, 0, 0).to_raw_messages(), vec![StatusDataData(128, 0, 0)]);
        assert_eq!(NoteOff(Ch2, 127, 127).to_raw_messages(), vec![StatusDataData(129, 127, 127)]);
        assert_eq!(NoteOff(Ch3, 128, 128).to_raw_messages(), vec![StatusDataData(130, 0, 0)]);

        // NoteOn
        assert_eq!(NoteOn(Ch4, 0, 0).to_raw_messages(), vec![StatusDataData(147, 0, 0)]);
        assert_eq!(NoteOn(Ch5, 127, 127).to_raw_messages(), vec![StatusDataData(148, 127, 127)]);
        assert_eq!(NoteOn(Ch6, 128, 128).to_raw_messages(), vec![StatusDataData(149, 0, 0)]);

        // PitchBend
        assert_eq!(PitchBend(Ch7, 0).to_raw_messages(), vec![StatusDataData(230, 0, 0)]);
        assert_eq!(PitchBend(Ch8, 1000).to_raw_messages(), vec![StatusDataData(231, 104, 7)]);
        assert_eq!(PitchBend(Ch9, 45000).to_raw_messages(), vec![StatusDataData(232, 72, 95)]);
        assert_eq!(PitchBend(Ch10, 12232).to_raw_messages(), vec![StatusDataData(233, 72, 95)]);

        // PolyphonicPressure
        assert_eq!(PolyphonicPressure(Ch11, 0, 0).to_raw_messages(),
                   vec![StatusDataData(170, 0, 0)]);
        assert_eq!(PolyphonicPressure(Ch12, 127, 127).to_raw_messages(),
                   vec![StatusDataData(171, 127, 127)]);
        assert_eq!(PolyphonicPressure(Ch13, 128, 128).to_raw_messages(),
                   vec![StatusDataData(172, 0, 0)]);

        // ChannelPressure
        assert_eq!(ChannelPressure(Ch14, 0).to_raw_messages(), vec![StatusData(221, 0)]);
        assert_eq!(ChannelPressure(Ch15, 127).to_raw_messages(), vec![StatusData(222, 127)]);
        assert_eq!(ChannelPressure(Ch16, 128).to_raw_messages(), vec![StatusData(223, 0)]);
    }
}

<|endoftext|>