<commit_before>use std::collections::{BitSet, BTreeSet};
use std::cell::{RefCell, Ref, RefMut};

use value::{Id};
use relation::{Change, Relation};
use view::{View, Table};
use compiler;

#[derive(Clone, Debug)]
pub struct Node {
    pub id: Id,
    pub view: View,
    pub upstream: Vec<usize>,
    pub downstream: Vec<usize>,
}

pub type Changes = Vec<(Id, Change)>;

#[derive(Clone, Debug)]
pub struct Flow {
    pub nodes: Vec<Node>,
    pub outputs: Vec<RefCell<Relation>>,
    pub dirty: BitSet,
    pub needs_recompile: bool,
}

impl Flow {
    pub fn new() -> Self {
        let flow = Flow {
            nodes: Vec::new(),
            outputs: Vec::new(),
            dirty: BitSet::new(),
            needs_recompile: true,
        };
        compiler::bootstrap(flow)
    }

    pub fn get_ix(&self, id: &str) -> Option<usize> {
        self.nodes.iter().position(|node| &node.id[..] == id)
    }

    pub fn get_output(&self, id: &str) -> Ref<Relation> {
        self.outputs[self.get_ix(id).unwrap()].borrow()
    }

    pub fn get_output_mut(&self, id: &str) -> RefMut<Relation> {
        self.outputs[self.get_ix(id).unwrap()].borrow_mut()
    }

    pub fn overwrite_output(&self, id: &str) -> RefMut<Relation> {
        let mut output = self.get_output_mut(id);
        output.index = BTreeSet::new();
        output
    }

    pub fn change(&mut self, changes: Changes) {
        let code_schema = compiler::code_schema();
        for (id, changes) in changes.into_iter() {
            match self.get_ix(&*id) {
                Some(ix) => match self.nodes[ix].view {
                    View::Table(_) => {
                        // TODO should we be checking diffs after the fact?
                        self.outputs[ix].borrow_mut().change(changes);
                        if code_schema.iter().find(|&&(ref code_id, _)| **code_id == id).is_some() {
                            self.needs_recompile = true;
                        }
                        for ix in self.nodes[ix].downstream.iter() {
                            self.dirty.insert(*ix);
                        }
                    }
                    _ => panic!("Tried to insert into a non-table view with id: {:?}", id),
                },
                None => panic!("Tried to insert into a non-existent view with id: {:?}", id),
            }
        }
    }

    pub fn as_changes(&self) -> Changes {
        (0..self.nodes.len()).map(|ix|
            (
                self.nodes[ix].id.clone(),
                self.outputs[ix].borrow().as_insert()
            )
        ).collect()
    }

    pub fn changes_from(&self, old_self: Self) -> Changes {
        let mut changes = Vec::new();
        for (ix, node) in self.nodes.iter().enumerate() {
            match old_self.get_ix(&node.id[..]) {
                Some(old_ix) => {
                    let new_output = self.outputs[ix].borrow();
                    let old_output = old_self.outputs[old_ix].borrow();
                    if new_output.fields == old_output.fields {
                        let change = new_output.change_from(&*old_output);
                        changes.push((node.id.clone(), change));
                    } else {
                        changes.push((node.id.clone(), old_output.as_remove()));
                        changes.push((node.id.clone(), new_output.as_insert()));
                    }
                }
                None => {
                    let new_output = self.outputs[ix].borrow();
                    changes.push((node.id.clone(), new_output.as_insert()));
                }
            }
        }
        for (old_ix, old_node) in old_self.nodes.iter().enumerate() {
            match self.get_ix(&old_node.id[..]) {
                Some(_) => {
                    () // already handled above
                }
                None => {
                    let old_output = old_self.outputs[old_ix].borrow();
                    changes.push((old_node.id.clone(), old_output.as_remove()));
                }
            }
        }
        changes
    }

    pub fn recalculate(&mut self) {
        let Flow{ref nodes, ref mut outputs, ref mut dirty, ..} = *self;
        let code_schema = compiler::code_schema();
        while let Some(ix) = dirty.iter().next() {
            dirty.remove(&ix);
            let node = &nodes[ix];
            let new_output = {
                let upstream = node.upstream.iter().map(|&ix| outputs[ix].borrow()).collect::<Vec<_>>();
                let inputs = upstream.iter().map(|borrowed| &**borrowed).collect::<Vec<_>>();
                node.view.run(&*outputs[ix].borrow(), &inputs[..])
            };
            match new_output {
                None => (), // view does not want to update
                Some(new_output) => {
                    let change = new_output.change_from(&*outputs[ix].borrow());
                    if (change.insert.len() != 0) || (change.remove.len() != 0) {
                        if code_schema.iter().find(|&&(ref code_id, _)| **code_id == node.id).is_some() {
                            self.needs_recompile = true;
                        }
                        for ix in node.downstream.iter() {
                            dirty.insert(*ix);
                        }
                    }
                    outputs[ix] = RefCell::new(new_output);
                }
            }
        }
    }

    pub fn tick(&mut self) -> bool {
        let code_schema = compiler::code_schema();
        let mut flow_changed = false;
        for (ix, node) in self.nodes.iter().enumerate() {
            match node.view {
                View::Table(Table{ref insert, ref remove}) => {
                    let mut view_changed = false;
                    {
                        let upstream = node.upstream.iter().map(|ix| self.outputs[*ix].borrow()).collect::<Vec<_>>();
                        let inputs = upstream.iter().map(|borrowed| &**borrowed).collect::<Vec<_>>();
                        let mut inserts = match *insert {
                            Some(ref select) => select.select(&inputs[..]),
                            None => vec![],
                        };
                        let mut removes = match *remove {
                            Some(ref select) => select.select(&inputs[..]),
                            None => vec![],
                        };
                        inserts.sort();
                        removes.sort();
                        inserts.retain(|insert| removes.binary_search(&insert).is_err());
                        removes.retain(|remove| inserts.binary_search(&remove).is_err());
                        let mut output = self.outputs[ix].borrow_mut();
                        let mut index = &mut output.index;
                        for insert in inserts {
                            view_changed = view_changed || index.insert(insert);
                        }
                        for remove in removes {
                            view_changed = view_changed || !index.remove(&remove);
                        }
                    }
                    if view_changed {
                        if code_schema.iter().find(|&&(ref code_id, _)| **code_id == node.id).is_some() {
                            self.needs_recompile = true;
                        }
                        for ix in node.downstream.iter() {
                            self.dirty.insert(*ix);
                        }
                    }
                    flow_changed = flow_changed || view_changed;
                }
                _ => () // only tables tick
            }
        }
        flow_changed
    }

    pub fn quiesce(mut self, changes: Changes) -> Self {
        time!("changing", {
            self.change(changes);
        });
        loop {
            if self.needs_recompile {
                time!("compiling", {
                    self = compiler::recompile(self);
                });
            }
            time!("calculating", {
                self.recalculate();
            });
            let changed = self.tick();
            if !changed {
                break
            }
        }
        self
    }
}
<commit_msg>Be more generous when handling changes to unknown views<commit_after>use std::collections::{BitSet, BTreeSet};
use std::cell::{RefCell, Ref, RefMut};

use value::{Id};
use relation::{Change, Relation};
use view::{View, Table};
use compiler;

#[derive(Clone, Debug)]
pub struct Node {
    pub id: Id,
    pub view: View,
    pub upstream: Vec<usize>,
    pub downstream: Vec<usize>,
}

pub type Changes = Vec<(Id, Change)>;

#[derive(Clone, Debug)]
pub struct Flow {
    pub nodes: Vec<Node>,
    pub outputs: Vec<RefCell<Relation>>,
    pub dirty: BitSet,
    pub needs_recompile: bool,
}

impl Flow {
    pub fn new() -> Self {
        let flow = Flow {
            nodes: Vec::new(),
            outputs: Vec::new(),
            dirty: BitSet::new(),
            needs_recompile: true,
        };
        compiler::bootstrap(flow)
    }

    pub fn get_ix(&self, id: &str) -> Option<usize> {
        self.nodes.iter().position(|node| &node.id[..] == id)
    }

    pub fn get_output(&self, id: &str) -> Ref<Relation> {
        self.outputs[self.get_ix(id).unwrap()].borrow()
    }

    pub fn get_output_mut(&self, id: &str) -> RefMut<Relation> {
        self.outputs[self.get_ix(id).unwrap()].borrow_mut()
    }

    pub fn overwrite_output(&self, id: &str) -> RefMut<Relation> {
        let mut output = self.get_output_mut(id);
        output.index = BTreeSet::new();
        output
    }

    pub fn change(&mut self, changes: Changes) {
        let code_schema = compiler::code_schema();
        for (id, change) in changes.into_iter() {
            match self.get_ix(&*id) {
                Some(ix) => {
                    match self.nodes[ix].view {
                        View::Table(_) => (),
                        _ => println!("Warning: changing a non-table view with id: {:?}", id),
                    }
                    // TODO should we be checking diffs after the fact?
                    self.outputs[ix].borrow_mut().change(change);
                    if code_schema.iter().find(|&&(ref code_id, _)| **code_id == id).is_some() {
                        self.needs_recompile = true;
                    }
                    for ix in self.nodes[ix].downstream.iter() {
                        self.dirty.insert(*ix);
                    }
                }
                None => {
                    println!("Warning: creating a dummy view because you tried to change a non-existing view with id: {:?}", id);
                    self.nodes.push(Node{
                        id: id.to_owned(),
                        view: View::Table(Table{
                            insert:None,
                            remove:None,
                        }),
                        upstream: Vec::new(),
                        downstream: Vec::new(),
                    });
                    let fields = change.fields.clone();
                    // compiler tables will never be missing, so it's safe to just put dummy names in here
                    let names = change.fields.iter().map(|_| "".to_owned()).collect();
                    self.outputs.push(RefCell::new(Relation::new(id.to_owned(), fields, names)));
                    self.outputs[self.outputs.len()-1].borrow_mut().change(change);
                }
            }
        }
    }

    pub fn as_changes(&self) -> Changes {
        (0..self.nodes.len()).map(|ix|
            (
                self.nodes[ix].id.clone(),
                self.outputs[ix].borrow().as_insert()
            )
        ).collect()
    }

    pub fn changes_from(&self, old_self: Self) -> Changes {
        let mut changes = Vec::new();
        for (ix, node) in self.nodes.iter().enumerate() {
            match old_self.get_ix(&node.id[..]) {
                Some(old_ix) => {
                    let new_output = self.outputs[ix].borrow();
                    let old_output = old_self.outputs[old_ix].borrow();
                    if new_output.fields == old_output.fields {
                        let change = new_output.change_from(&*old_output);
                        changes.push((node.id.clone(), change));
                    } else {
                        changes.push((node.id.clone(), old_output.as_remove()));
                        changes.push((node.id.clone(), new_output.as_insert()));
                    }
                }
                None => {
                    let new_output = self.outputs[ix].borrow();
                    changes.push((node.id.clone(), new_output.as_insert()));
                }
            }
        }
        for (old_ix, old_node) in old_self.nodes.iter().enumerate() {
            match self.get_ix(&old_node.id[..]) {
                Some(_) => {
                    () // already handled above
                }
                None => {
                    let old_output = old_self.outputs[old_ix].borrow();
                    changes.push((old_node.id.clone(), old_output.as_remove()));
                }
            }
        }
        changes
    }

    pub fn recalculate(&mut self) {
        let Flow{ref nodes, ref mut outputs, ref mut dirty, ..} = *self;
        let code_schema = compiler::code_schema();
        while let Some(ix) = dirty.iter().next() {
            dirty.remove(&ix);
            let node = &nodes[ix];
            let new_output = {
                let upstream = node.upstream.iter().map(|&ix| outputs[ix].borrow()).collect::<Vec<_>>();
                let inputs = upstream.iter().map(|borrowed| &**borrowed).collect::<Vec<_>>();
                node.view.run(&*outputs[ix].borrow(), &inputs[..])
            };
            match new_output {
                None => (), // view does not want to update
                Some(new_output) => {
                    let change = new_output.change_from(&*outputs[ix].borrow());
                    if (change.insert.len() != 0) || (change.remove.len() != 0) {
                        if code_schema.iter().find(|&&(ref code_id, _)| **code_id == node.id).is_some() {
                            self.needs_recompile = true;
                        }
                        for ix in node.downstream.iter() {
                            dirty.insert(*ix);
                        }
                    }
                    outputs[ix] = RefCell::new(new_output);
                }
            }
        }
    }

    pub fn tick(&mut self) -> bool {
        let code_schema = compiler::code_schema();
        let mut flow_changed = false;
        for (ix, node) in self.nodes.iter().enumerate() {
            match node.view {
                View::Table(Table{ref insert, ref remove}) => {
                    let mut view_changed = false;
                    {
                        let upstream = node.upstream.iter().map(|ix| self.outputs[*ix].borrow()).collect::<Vec<_>>();
                        let inputs = upstream.iter().map(|borrowed| &**borrowed).collect::<Vec<_>>();
                        let mut inserts = match *insert {
                            Some(ref select) => select.select(&inputs[..]),
                            None => vec![],
                        };
                        let mut removes = match *remove {
                            Some(ref select) => select.select(&inputs[..]),
                            None => vec![],
                        };
                        inserts.sort();
                        removes.sort();
                        inserts.retain(|insert| removes.binary_search(&insert).is_err());
                        removes.retain(|remove| inserts.binary_search(&remove).is_err());
                        let mut output = self.outputs[ix].borrow_mut();
                        let mut index = &mut output.index;
                        for insert in inserts {
                            view_changed = view_changed || index.insert(insert);
                        }
                        for remove in removes {
                            view_changed = view_changed || !index.remove(&remove);
                        }
                    }
                    if view_changed {
                        if code_schema.iter().find(|&&(ref code_id, _)| **code_id == node.id).is_some() {
                            self.needs_recompile = true;
                        }
                        for ix in node.downstream.iter() {
                            self.dirty.insert(*ix);
                        }
                    }
                    flow_changed = flow_changed || view_changed;
                }
                _ => () // only tables tick
            }
        }
        flow_changed
    }

    pub fn quiesce(mut self, changes: Changes) -> Self {
        time!("changing", {
            self.change(changes);
        });
        loop {
            if self.needs_recompile {
                time!("compiling", {
                    self = compiler::recompile(self);
                });
            }
            time!("calculating", {
                self.recalculate();
            });
            let changed = self.tick();
            if !changed {
                break
            }
        }
        self
    }
}
<|endoftext|>


<commit_before>// Options
// Space before /
// Quote style
// Ordering of attributes

#[cfg(test)]
mod test {
    use std::io::{IoResult,MemWriter};

    use super::super::{Document,Element,Text,Comment,ProcessingInstruction};
    use super::super::{ElementElementChild,TextElementChild,CommentElementChild,PIElementChild};

    macro_rules! assert_str_eq(
        ($l:expr, $r:expr) => (assert_eq!($l.as_slice(), $r.as_slice()));
    )

    enum Content {
        WElement(Element),
        WElementEnd(String),
        WText(Text),
        WComment(Comment),
        WProcessingInstruction(ProcessingInstruction),
    }

    fn format_element<W : Writer>(element: Element, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        try!(write!(writer, "<{}", element.name()));

        for attr in element.attributes().iter() {
            try!(write!(writer, " {}='{}'", attr.name(), attr.value()));
        }

        let mut children = element.children();
        if children.is_empty() {
            writer.write_str("/>")
        } else {
            try!(writer.write_str(">"));

            todo.push(WElementEnd(element.name()));
            children.reverse();
            let x = children.move_iter().map(|c| match c {
                ElementElementChild(element) => WElement(element),
                TextElementChild(t) => WText(t),
                CommentElementChild(c) => WComment(c),
                PIElementChild(p) => WProcessingInstruction(p),
            }).collect();
            todo.push_all_move(x);

            Ok(())
        }
    }

    fn format_processing_instruction<W : Writer>(pi: ProcessingInstruction, writer: &mut W) -> IoResult<()> {
        match pi.value() {
            None    => write!(writer, "<?{}?>", pi.target()),
            Some(v) => write!(writer, "<?{} {}?>", pi.target(), v),
        }
    }

    fn format_document<W : Writer>(doc: &Document, writer: &mut W) -> IoResult<()> {
        let mut todo = Vec::new();

        try!(writer.write_str("<?xml version='1.0'?>"));
        let e = doc.root().children()[0].element().expect("not an element?");

        todo.push(WElement(e));

        while ! todo.is_empty() {
            match todo.pop().unwrap() {
                WElement(e) => try!(format_element(e, &mut todo, writer)),
                WElementEnd(name) => try!(write!(writer, "</{}>", name)),
                WText(t) => try!(writer.write_str(t.text().as_slice())),
                WComment(c) => try!(write!(writer, "<!--{}-->", c.text())),
                WProcessingInstruction(p) => try!(format_processing_instruction(p, writer)),
            }
        }

        Ok(())
    }

    fn format_xml(doc: &Document) -> String {
        let mut w = MemWriter::new();
        format_document(doc, &mut w).ok().expect("Not formatted");
        String::from_utf8(w.unwrap()).ok().expect("Not a string")
    }

    #[test]
    fn top_element() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello/>");
    }

    #[test]
    fn element_with_attributes() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        e.set_attribute("a".to_string(), "b".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello a='b'/>");
    }

    #[test]
    fn nested_element() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let world = d.new_element("world".to_string());
        hello.append_child(world);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><world/></hello>");
    }

    #[test]
    fn nested_text() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let text = d.new_text("A fine day to you!".to_string());
        hello.append_child(text);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello>A fine day to you!</hello>");
    }

    #[test]
    fn nested_comment() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let comment = d.new_comment(" Fill this in ".to_string());
        hello.append_child(comment);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><!-- Fill this in --></hello>");
    }

    #[test]
    fn nested_processing_instruction_without_value() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let pi = d.new_processing_instruction("display".to_string(), None);
        hello.append_child(pi);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><?display?></hello>");
    }

    #[test]
    fn nested_processing_instruction_with_value() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let pi = d.new_processing_instruction("display".to_string(), Some("screen".to_string()));
        hello.append_child(pi);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><?display screen?></hello>");
    }
}
<commit_msg>Refactoring<commit_after>// Options
// Space before /
// Quote style
// Ordering of attributes

#[cfg(test)]
mod test {
    use std::io::{IoResult,MemWriter};

    use super::super::{Document,Element,Text,Comment,ProcessingInstruction};
    use super::super::{ElementElementChild,TextElementChild,CommentElementChild,PIElementChild};

    macro_rules! assert_str_eq(
        ($l:expr, $r:expr) => (assert_eq!($l.as_slice(), $r.as_slice()));
    )

    enum Content {
        WElement(Element),
        WElementEnd(String),
        WText(Text),
        WComment(Comment),
        WProcessingInstruction(ProcessingInstruction),
    }

    fn format_element<W : Writer>(element: Element, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        try!(write!(writer, "<{}", element.name()));

        for attr in element.attributes().iter() {
            try!(write!(writer, " {}='{}'", attr.name(), attr.value()));
        }

        let mut children = element.children();
        if children.is_empty() {
            writer.write_str("/>")
        } else {
            try!(writer.write_str(">"));

            todo.push(WElementEnd(element.name()));
            children.reverse();
            let x = children.move_iter().map(|c| match c {
                ElementElementChild(element) => WElement(element),
                TextElementChild(t) => WText(t),
                CommentElementChild(c) => WComment(c),
                PIElementChild(p) => WProcessingInstruction(p),
            }).collect();
            todo.push_all_move(x);

            Ok(())
        }
    }

    fn format_processing_instruction<W : Writer>(pi: ProcessingInstruction, writer: &mut W) -> IoResult<()> {
        match pi.value() {
            None    => write!(writer, "<?{}?>", pi.target()),
            Some(v) => write!(writer, "<?{} {}?>", pi.target(), v),
        }
    }

    fn format_one<W : Writer>(content: Content, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        match content {
            WElement(e)               => format_element(e, todo, writer),
            WElementEnd(name)         => write!(writer, "</{}>", name),
            WText(t)                  => writer.write_str(t.text().as_slice()),
            WComment(c)               => write!(writer, "<!--{}-->", c.text()),
            WProcessingInstruction(p) => format_processing_instruction(p, writer),
        }
    }

    fn format_document<W : Writer>(doc: &Document, writer: &mut W) -> IoResult<()> {
        let mut todo = Vec::new();

        try!(writer.write_str("<?xml version='1.0'?>"));
        let e = doc.root().children()[0].element().expect("not an element?");

        todo.push(WElement(e));

        while ! todo.is_empty() {
            try!(format_one(todo.pop().unwrap(), &mut todo, writer));
        }

        Ok(())
    }

    fn format_xml(doc: &Document) -> String {
        let mut w = MemWriter::new();
        format_document(doc, &mut w).ok().expect("Not formatted");
        String::from_utf8(w.unwrap()).ok().expect("Not a string")
    }

    #[test]
    fn top_element() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello/>");
    }

    #[test]
    fn element_with_attributes() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        e.set_attribute("a".to_string(), "b".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello a='b'/>");
    }

    #[test]
    fn nested_element() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let world = d.new_element("world".to_string());
        hello.append_child(world);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><world/></hello>");
    }

    #[test]
    fn nested_text() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let text = d.new_text("A fine day to you!".to_string());
        hello.append_child(text);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello>A fine day to you!</hello>");
    }

    #[test]
    fn nested_comment() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let comment = d.new_comment(" Fill this in ".to_string());
        hello.append_child(comment);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><!-- Fill this in --></hello>");
    }

    #[test]
    fn nested_processing_instruction_without_value() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let pi = d.new_processing_instruction("display".to_string(), None);
        hello.append_child(pi);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><?display?></hello>");
    }

    #[test]
    fn nested_processing_instruction_with_value() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let pi = d.new_processing_instruction("display".to_string(), Some("screen".to_string()));
        hello.append_child(pi);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><?display screen?></hello>");
    }
}
<|endoftext|>


<commit_before>//! Utils for testing and debugging.

use definitions::{
    VecBuffer
};

use image::{
    DynamicImage,
    GenericImage,
    GrayImage,
    ImageBuffer,
    Luma,
    open,
    Pixel,
    Primitive,
    Rgb,
    RgbImage
};

use quickcheck::{
    Arbitrary,
    Gen
};

use rand::{
    Rand
};

use std::fmt;
use std::path::Path;

/// Human readable description of some of the pixels that differ
/// between left and right, or None if all pixels match.
pub fn pixel_diff_summary<I, J, P>(actual: &I, expected: &J)
    -> Option<String>
    where P: Pixel + PartialEq + fmt::Debug,
          I: GenericImage<Pixel=P>,
          J: GenericImage<Pixel=P>
{
    significant_pixel_diff_summary(actual, expected, |p, q| p != q)
}

/// Human readable description of some of the pixels that differ
/// signifcantly (according to provided function) between left
/// and right, or None if all pixels match.
pub fn significant_pixel_diff_summary<I, J, F, P>(actual: &I,
                                                  expected: &J,
                                                  is_significant_diff: F)
    -> Option<String>
    where P: Pixel + fmt::Debug,
          I: GenericImage<Pixel=P>,
          J: GenericImage<Pixel=P>,
          F: Fn((u32, u32, I::Pixel), (u32, u32, J::Pixel)) -> bool
{
    if actual.dimensions() != expected.dimensions() {
        return Some("dimensions do not match. \
            actual: {:?}, expected: {:?}", actual.dimensions(), expected.dimensions())
    }
    let diffs = pixel_diffs(actual, expected, is_significant_diff);
    if diffs.is_empty() {
        return None;
    }
    Some(describe_pixel_diffs(diffs.into_iter()))
}

/// Panics if any pixels differ between the two input images.
#[macro_export]
macro_rules! assert_pixels_eq {
    ($actual:expr, $expected:expr) => ({
        assert_dimensions_match!($actual, $expected);
        match $crate::utils::pixel_diff_summary(&$actual, &$expected) {
            None => {},
            Some(err) => panic!(err)
        };
     })
}

/// Panics if any pixels differ between the two images by more than the
/// given tolerance in a single channel.
#[macro_export]
macro_rules! assert_pixels_eq_within {
    ($actual:expr, $expected:expr, $channel_tolerance:expr) => ({

        assert_dimensions_match!($actual, $expected);
        let diffs = $crate::utils::pixel_diffs(&$actual, &$expected, |p, q| {

            use image::Pixel;
            let cp = p.2.channels();
            let cq = q.2.channels();
            if cp.len() != cq.len() {
                panic!("pixels have different channel counts. \
                    actual: {:?}, expected: {:?}", cp.len(), cq.len())
            }

            let mut large_diff = false;
            for i in 0..cp.len() {
                let sp = cp[i];
                let sq = cq[i];
                // Handle unsigned subpixels
                let diff = if sp > sq {sp - sq} else {sq - sp};
                if diff > $channel_tolerance {
                    large_diff = true;
                    break;
                }
            }

            large_diff
        });
        if !diffs.is_empty() {
            panic!($crate::utils::describe_pixel_diffs(diffs.into_iter()))
        }
    })
}

/// Panics if image dimensions do not match.
#[macro_export]
macro_rules! assert_dimensions_match {
    ($actual:expr, $expected:expr) => ({

        let actual_dim = $actual.dimensions();
        let expected_dim = $expected.dimensions();

        if actual_dim != expected_dim {
            panic!("dimensions do not match. \
                actual: {:?}, expected: {:?}", actual_dim, expected_dim)
        }
     })
}

/// Lists pixels that differ between left and right images.
pub fn pixel_diffs<I, J, F, P>(left: &I, right: &J, is_diff: F)
        -> Vec<((u32, u32, I::Pixel), (u32, u32, I::Pixel))>
    where P: Pixel,
          I: GenericImage<Pixel=P>,
          J: GenericImage<Pixel=P>,
          F: Fn((u32, u32, I::Pixel), (u32, u32, J::Pixel)) -> bool
{
    if is_empty(left) || is_empty(right) {
        return vec![];
    }

    // Can't just call $image.pixels(), as that needn't hit the
    // trait pixels method - ImageBuffer defines its own pixels
    // method with a different signature
    GenericImage::pixels(left)
        .zip(GenericImage::pixels(right))
        .filter(|&(p, q)| is_diff(p, q))
        .collect::<Vec<_>>()
}

fn is_empty<I: GenericImage>(image: &I) -> bool {
    image.width() == 0 || image.height() == 0
}

/// Gives a summary description of a list of pixel diffs for use in error messages.
pub fn describe_pixel_diffs<I, P>(diffs: I) -> String
    where I: Iterator<Item=(P, P)>,
          P: fmt::Debug
{
    let mut err = "pixels do not match. ".to_string();
    err.push_str(&(diffs
        .take(5)
        .map(|d| format!("\nactual: {:?}, expected {:?} ", d.0, d.1))
        .collect::<Vec<_>>()
        .join("")));
    err
}

/// Loads image at given path, panicking on failure.
pub fn load_image_or_panic(path: &Path) -> DynamicImage {
     open(path)
         .ok()
         .expect(&format!("Could not load image at {:?}", path))
}

/// Gray image to use in benchmarks. This is neither noise nor
/// similar to natural images - it's just a convenience method
/// to produce an image that's not constant.
pub fn gray_bench_image(width: u32, height: u32) -> GrayImage {
    let mut image = GrayImage::new(width, height);
    for y in 0..image.height() {
        for x in 0..image.width() {
            let intensity = (x % 7 + y % 6) as u8;
            image.put_pixel(x, y, Luma([intensity]));
        }
    }
    image
}

/// RGB image to use in benchmarks. See comment on gray_bench_image.
pub fn rgb_bench_image(width: u32, height: u32) -> RgbImage {
    use std::cmp;
    let mut image = RgbImage::new(width, height);
    for y in 0..image.height() {
        for x in 0..image.width() {
            let r = (x % 7 + y % 6) as u8;
            let g = 255u8 - r;
            let b = cmp::min(r, g);
            image.put_pixel(x, y, Rgb([r, g, b]));
        }
    }
    image
}

/// Wrapper for image buffers to allow us to write an Arbitrary instance.
#[derive(Clone)]
pub struct TestBuffer<T: Pixel>(pub VecBuffer<T>);

/// 8bpp grayscale TestBuffer.
pub type GrayTestImage = TestBuffer<Luma<u8>>;

/// 24bpp RGB TestBuffer.
pub type RgbTestImage = TestBuffer<Rgb<u8>>;

impl<T: Pixel + ArbitraryPixel + Send + 'static> Arbitrary for TestBuffer<T>
    where <T as Pixel>::Subpixel: Send
{
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let (width, height) = small_image_dimensions(g);
        let mut image = ImageBuffer::new(width, height);
        for y in 0..height {
            for x in 0..width {
                let pix: T = ArbitraryPixel::arbitrary(g);
                image.put_pixel(x, y, pix);
            }
        }
        TestBuffer(image)
    }

    fn shrink(&self) -> Box<Iterator<Item=TestBuffer<T>>> {
        Box::new(shrink(&self.0).map(|x| TestBuffer(x)))
    }
}

/// Workaround for not being able to define Arbitrary instances for pixel types
/// defines in other modules.
pub trait ArbitraryPixel {
    fn arbitrary<G: Gen>(g: &mut G) -> Self;
}

fn shrink<I>(image: &I) -> Box<Iterator<Item=VecBuffer<I::Pixel>>>
    where I: GenericImage,
          I::Pixel: 'static
{
    let mut subs = vec![];

    let w = image.width();
    let h = image.height();

    if w > 0 {
        let left = copy_sub(image, 0, 0, w - 1, h);
        subs.push(left);
        let right = copy_sub(image, 1, 0, w - 1, h);
        subs.push(right);
    }
    if h > 0 {
        let top = copy_sub(image, 0, 0, w, h - 1);
        subs.push(top);
        let bottom = copy_sub(image, 0, 1, w, h - 1);
        subs.push(bottom);
    }

    Box::new(subs.into_iter())
}

fn copy_sub<I>(image: &I, x: u32, y: u32, width: u32, height: u32) -> VecBuffer<I::Pixel>
    where I: GenericImage,
          I::Pixel: 'static
{
    let mut out = ImageBuffer::new(width, height);
    for dy in 0..height {
        let oy = y + dy;
        for dx in 0..width {
            let ox = x + dx;
            out.put_pixel(dx, dy, image.get_pixel(ox, oy));
        }
    }
    out
}

impl<T: fmt::Debug + Pixel + 'static> fmt::Debug for TestBuffer<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "width: {}, height: {}, data: {:?}",
            self.0.width(), self.0.height(), self.0.enumerate_pixels().collect::<Vec<_>>())
    }
}

fn small_image_dimensions<G: Gen>(g: &mut G) -> (u32, u32) {
    let dims: (u8, u8) = Arbitrary::arbitrary(g);
    ((dims.0 % 10) as u32, (dims.1 % 10) as u32)
}

impl<T: Rand + Send + Primitive> ArbitraryPixel for Rgb<T> {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let red: T = g.gen();
        let green: T = g.gen();
        let blue: T = g.gen();
        Rgb([red, green, blue])
    }
}

impl<T: Rand + Send + Primitive> ArbitraryPixel for Luma<T> {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let val: T = g.gen();
        Luma([val])
    }
}

#[cfg(test)]
mod test {

    use image::{
        GrayImage,
        ImageBuffer
    };

    #[test]
    fn test_assert_pixels_eq_passes() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 01, 02,
            10, 11, 12]).unwrap();

        assert_pixels_eq!(image, image);
    }

    #[test]
    #[should_panic]
    fn test_assert_pixels_eq_fails() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 01, 02,
            10, 11, 12]).unwrap();

        let diff: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 11, 02,
            10, 11, 12]).unwrap();

        assert_pixels_eq!(diff, image);
    }

    #[test]
    fn test_assert_pixels_eq_within_passes() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 01, 02,
            10, 11, 12]).unwrap();

        let diff: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 02, 02,
            10, 11, 12]).unwrap();

        assert_pixels_eq_within!(diff, image, 1);
    }

    #[test]
    #[should_panic]
    fn test_assert_pixels_eq_within_fails() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 01, 02,
            10, 11, 12]).unwrap();

        let diff: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 03, 02,
            10, 11, 12]).unwrap();

        assert_pixels_eq_within!(diff, image, 1);
    }
}
<commit_msg>Fix typo<commit_after>//! Utils for testing and debugging.

use definitions::{
    VecBuffer
};

use image::{
    DynamicImage,
    GenericImage,
    GrayImage,
    ImageBuffer,
    Luma,
    open,
    Pixel,
    Primitive,
    Rgb,
    RgbImage
};

use quickcheck::{
    Arbitrary,
    Gen
};

use rand::{
    Rand
};

use std::fmt;
use std::path::Path;

/// Human readable description of some of the pixels that differ
/// between left and right, or None if all pixels match.
pub fn pixel_diff_summary<I, J, P>(actual: &I, expected: &J)
    -> Option<String>
    where P: Pixel + PartialEq + fmt::Debug,
          I: GenericImage<Pixel=P>,
          J: GenericImage<Pixel=P>
{
    significant_pixel_diff_summary(actual, expected, |p, q| p != q)
}

/// Human readable description of some of the pixels that differ
/// signifcantly (according to provided function) between left
/// and right, or None if all pixels match.
pub fn significant_pixel_diff_summary<I, J, F, P>(actual: &I,
                                                  expected: &J,
                                                  is_significant_diff: F)
    -> Option<String>
    where P: Pixel + fmt::Debug,
          I: GenericImage<Pixel=P>,
          J: GenericImage<Pixel=P>,
          F: Fn((u32, u32, I::Pixel), (u32, u32, J::Pixel)) -> bool
{
    if actual.dimensions() != expected.dimensions() {
        return Some(format!("dimensions do not match. \
            actual: {:?}, expected: {:?}", actual.dimensions(), expected.dimensions()));
    }
    let diffs = pixel_diffs(actual, expected, is_significant_diff);
    if diffs.is_empty() {
        return None;
    }
    Some(describe_pixel_diffs(diffs.into_iter()))
}

/// Panics if any pixels differ between the two input images.
#[macro_export]
macro_rules! assert_pixels_eq {
    ($actual:expr, $expected:expr) => ({
        assert_dimensions_match!($actual, $expected);
        match $crate::utils::pixel_diff_summary(&$actual, &$expected) {
            None => {},
            Some(err) => panic!(err)
        };
     })
}

/// Panics if any pixels differ between the two images by more than the
/// given tolerance in a single channel.
#[macro_export]
macro_rules! assert_pixels_eq_within {
    ($actual:expr, $expected:expr, $channel_tolerance:expr) => ({

        assert_dimensions_match!($actual, $expected);
        let diffs = $crate::utils::pixel_diffs(&$actual, &$expected, |p, q| {

            use image::Pixel;
            let cp = p.2.channels();
            let cq = q.2.channels();
            if cp.len() != cq.len() {
                panic!("pixels have different channel counts. \
                    actual: {:?}, expected: {:?}", cp.len(), cq.len())
            }

            let mut large_diff = false;
            for i in 0..cp.len() {
                let sp = cp[i];
                let sq = cq[i];
                // Handle unsigned subpixels
                let diff = if sp > sq {sp - sq} else {sq - sp};
                if diff > $channel_tolerance {
                    large_diff = true;
                    break;
                }
            }

            large_diff
        });
        if !diffs.is_empty() {
            panic!($crate::utils::describe_pixel_diffs(diffs.into_iter()))
        }
    })
}

/// Panics if image dimensions do not match.
#[macro_export]
macro_rules! assert_dimensions_match {
    ($actual:expr, $expected:expr) => ({

        let actual_dim = $actual.dimensions();
        let expected_dim = $expected.dimensions();

        if actual_dim != expected_dim {
            panic!("dimensions do not match. \
                actual: {:?}, expected: {:?}", actual_dim, expected_dim)
        }
     })
}

/// Lists pixels that differ between left and right images.
pub fn pixel_diffs<I, J, F, P>(left: &I, right: &J, is_diff: F)
        -> Vec<((u32, u32, I::Pixel), (u32, u32, I::Pixel))>
    where P: Pixel,
          I: GenericImage<Pixel=P>,
          J: GenericImage<Pixel=P>,
          F: Fn((u32, u32, I::Pixel), (u32, u32, J::Pixel)) -> bool
{
    if is_empty(left) || is_empty(right) {
        return vec![];
    }

    // Can't just call $image.pixels(), as that needn't hit the
    // trait pixels method - ImageBuffer defines its own pixels
    // method with a different signature
    GenericImage::pixels(left)
        .zip(GenericImage::pixels(right))
        .filter(|&(p, q)| is_diff(p, q))
        .collect::<Vec<_>>()
}

fn is_empty<I: GenericImage>(image: &I) -> bool {
    image.width() == 0 || image.height() == 0
}

/// Gives a summary description of a list of pixel diffs for use in error messages.
pub fn describe_pixel_diffs<I, P>(diffs: I) -> String
    where I: Iterator<Item=(P, P)>,
          P: fmt::Debug
{
    let mut err = "pixels do not match. ".to_string();
    err.push_str(&(diffs
        .take(5)
        .map(|d| format!("\nactual: {:?}, expected {:?} ", d.0, d.1))
        .collect::<Vec<_>>()
        .join("")));
    err
}

/// Loads image at given path, panicking on failure.
pub fn load_image_or_panic(path: &Path) -> DynamicImage {
     open(path)
         .ok()
         .expect(&format!("Could not load image at {:?}", path))
}

/// Gray image to use in benchmarks. This is neither noise nor
/// similar to natural images - it's just a convenience method
/// to produce an image that's not constant.
pub fn gray_bench_image(width: u32, height: u32) -> GrayImage {
    let mut image = GrayImage::new(width, height);
    for y in 0..image.height() {
        for x in 0..image.width() {
            let intensity = (x % 7 + y % 6) as u8;
            image.put_pixel(x, y, Luma([intensity]));
        }
    }
    image
}

/// RGB image to use in benchmarks. See comment on gray_bench_image.
pub fn rgb_bench_image(width: u32, height: u32) -> RgbImage {
    use std::cmp;
    let mut image = RgbImage::new(width, height);
    for y in 0..image.height() {
        for x in 0..image.width() {
            let r = (x % 7 + y % 6) as u8;
            let g = 255u8 - r;
            let b = cmp::min(r, g);
            image.put_pixel(x, y, Rgb([r, g, b]));
        }
    }
    image
}

/// Wrapper for image buffers to allow us to write an Arbitrary instance.
#[derive(Clone)]
pub struct TestBuffer<T: Pixel>(pub VecBuffer<T>);

/// 8bpp grayscale TestBuffer.
pub type GrayTestImage = TestBuffer<Luma<u8>>;

/// 24bpp RGB TestBuffer.
pub type RgbTestImage = TestBuffer<Rgb<u8>>;

impl<T: Pixel + ArbitraryPixel + Send + 'static> Arbitrary for TestBuffer<T>
    where <T as Pixel>::Subpixel: Send
{
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let (width, height) = small_image_dimensions(g);
        let mut image = ImageBuffer::new(width, height);
        for y in 0..height {
            for x in 0..width {
                let pix: T = ArbitraryPixel::arbitrary(g);
                image.put_pixel(x, y, pix);
            }
        }
        TestBuffer(image)
    }

    fn shrink(&self) -> Box<Iterator<Item=TestBuffer<T>>> {
        Box::new(shrink(&self.0).map(|x| TestBuffer(x)))
    }
}

/// Workaround for not being able to define Arbitrary instances for pixel types
/// defines in other modules.
pub trait ArbitraryPixel {
    fn arbitrary<G: Gen>(g: &mut G) -> Self;
}

fn shrink<I>(image: &I) -> Box<Iterator<Item=VecBuffer<I::Pixel>>>
    where I: GenericImage,
          I::Pixel: 'static
{
    let mut subs = vec![];

    let w = image.width();
    let h = image.height();

    if w > 0 {
        let left = copy_sub(image, 0, 0, w - 1, h);
        subs.push(left);
        let right = copy_sub(image, 1, 0, w - 1, h);
        subs.push(right);
    }
    if h > 0 {
        let top = copy_sub(image, 0, 0, w, h - 1);
        subs.push(top);
        let bottom = copy_sub(image, 0, 1, w, h - 1);
        subs.push(bottom);
    }

    Box::new(subs.into_iter())
}

fn copy_sub<I>(image: &I, x: u32, y: u32, width: u32, height: u32) -> VecBuffer<I::Pixel>
    where I: GenericImage,
          I::Pixel: 'static
{
    let mut out = ImageBuffer::new(width, height);
    for dy in 0..height {
        let oy = y + dy;
        for dx in 0..width {
            let ox = x + dx;
            out.put_pixel(dx, dy, image.get_pixel(ox, oy));
        }
    }
    out
}

impl<T: fmt::Debug + Pixel + 'static> fmt::Debug for TestBuffer<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "width: {}, height: {}, data: {:?}",
            self.0.width(), self.0.height(), self.0.enumerate_pixels().collect::<Vec<_>>())
    }
}

fn small_image_dimensions<G: Gen>(g: &mut G) -> (u32, u32) {
    let dims: (u8, u8) = Arbitrary::arbitrary(g);
    ((dims.0 % 10) as u32, (dims.1 % 10) as u32)
}

impl<T: Rand + Send + Primitive> ArbitraryPixel for Rgb<T> {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let red: T = g.gen();
        let green: T = g.gen();
        let blue: T = g.gen();
        Rgb([red, green, blue])
    }
}

impl<T: Rand + Send + Primitive> ArbitraryPixel for Luma<T> {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let val: T = g.gen();
        Luma([val])
    }
}

#[cfg(test)]
mod test {

    use image::{
        GrayImage,
        ImageBuffer
    };

    #[test]
    fn test_assert_pixels_eq_passes() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 01, 02,
            10, 11, 12]).unwrap();

        assert_pixels_eq!(image, image);
    }

    #[test]
    #[should_panic]
    fn test_assert_pixels_eq_fails() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 01, 02,
            10, 11, 12]).unwrap();

        let diff: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 11, 02,
            10, 11, 12]).unwrap();

        assert_pixels_eq!(diff, image);
    }

    #[test]
    fn test_assert_pixels_eq_within_passes() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 01, 02,
            10, 11, 12]).unwrap();

        let diff: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 02, 02,
            10, 11, 12]).unwrap();

        assert_pixels_eq_within!(diff, image, 1);
    }

    #[test]
    #[should_panic]
    fn test_assert_pixels_eq_within_fails() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 01, 02,
            10, 11, 12]).unwrap();

        let diff: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            00, 03, 02,
            10, 11, 12]).unwrap();

        assert_pixels_eq_within!(diff, image, 1);
    }
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;

/// A wrapper around a value, used with [`Args`] so that a [`MessagePart`]
/// can access the original value when necessary.
///
/// These are not typically created directly, but are created automatically
/// via the standard library's `From` trait when creating [`Args`].
///
/// [`Args`]: struct.Args.html
/// [`MessagePart`]: trait.MessagePart.html
#[derive(Debug, PartialEq)]
pub enum Value<'a> {
    /// Wrap an `i64`.
    Number(i64),
    /// Wrap an `&str`.
    Str(&'a str),
}

impl<'a> From<i32> for Value<'a> {
    fn from(value: i32) -> Value<'a> {
        Value::Number(value as i64)
    }
}

impl<'a> From<u32> for Value<'a> {
    fn from(value: u32) -> Value<'a> {
        Value::Number(value as i64)
    }
}

impl<'a> From<i64> for Value<'a> {
    fn from(value: i64) -> Value<'a> {
        Value::Number(value)
    }
}

impl<'a> From<u64> for Value<'a> {
    fn from(value: u64) -> Value<'a> {
        Value::Number(value as i64)
    }
}

impl<'a> From<usize> for Value<'a> {
    fn from(value: usize) -> Value<'a> {
        Value::Number(value as i64)
    }
}

impl<'a> From<&'a str> for Value<'a> {
    fn from(value: &'a str) -> Value<'a> {
        Value::Str(value)
    }
}

impl<'a> fmt::Display for Value<'a> {
    /// Forward `fmt::Display` to the underlying value.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Value::Number(i) => i.fmt(f),
            Value::Str(s) => s.fmt(f),
        }
    }
}
<commit_msg>Fix clippy warnings.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;

/// A wrapper around a value, used with [`Args`] so that a [`MessagePart`]
/// can access the original value when necessary.
///
/// These are not typically created directly, but are created automatically
/// via the standard library's `From` trait when creating [`Args`].
///
/// [`Args`]: struct.Args.html
/// [`MessagePart`]: trait.MessagePart.html
#[derive(Debug, PartialEq)]
pub enum Value<'a> {
    /// Wrap an `i64`.
    Number(i64),
    /// Wrap an `&str`.
    Str(&'a str),
}

impl<'a> From<i32> for Value<'a> {
    fn from(value: i32) -> Value<'a> {
        Value::Number(i64::from(value))
    }
}

impl<'a> From<u32> for Value<'a> {
    fn from(value: u32) -> Value<'a> {
        Value::Number(i64::from(value))
    }
}

impl<'a> From<i64> for Value<'a> {
    fn from(value: i64) -> Value<'a> {
        Value::Number(value)
    }
}

impl<'a> From<u64> for Value<'a> {
    fn from(value: u64) -> Value<'a> {
        Value::Number(value as i64)
    }
}

impl<'a> From<usize> for Value<'a> {
    fn from(value: usize) -> Value<'a> {
        Value::Number(value as i64)
    }
}

impl<'a> From<&'a str> for Value<'a> {
    fn from(value: &'a str) -> Value<'a> {
        Value::Str(value)
    }
}

impl<'a> fmt::Display for Value<'a> {
    /// Forward `fmt::Display` to the underlying value.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Value::Number(i) => i.fmt(f),
            Value::Str(s) => s.fmt(f),
        }
    }
}
<|endoftext|>


<commit_before>// Unused mut warning is false in this context
#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_stream;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_tls;
extern crate tokio_service;
extern crate curl;
extern crate webbrowser;
extern crate termion;
extern crate native_tls;
extern crate regex;

mod utils;
mod ui;
mod decoding;
mod logging_utils;
mod formatting;
mod core;

use core::app::*;
use core::models::*;
use core::client;
use ui::cli;

use std::cmp::min;
use tokio_core::reactor::Core;
use std::io::{self, BufRead};
use std::thread::spawn;
use std::process;
use futures::{Stream, Sink, Future};
use futures::sync::mpsc;
use ui::backend::UiCommand;

fn main() {

    let mut app_domain = AppDomain::new();
    let mut app_cache: AppCache = AppCache::new();
    let mut app_state_machine: AppStateMachine = AppStateMachine::new();
    let mut main_core = Core::new().expect("Failed to create core");

    info!(&app_domain.logger, "Application started");
    app_cache.retrieved_top_stories =
        client::get_top_story_ids(&mut app_domain, &mut app_state_machine).ok();
    print_ten_stories(&mut app_domain, &mut app_cache, &mut app_state_machine);
    let (sender, receiver) = mpsc::channel(1);

    spawn(move || {
        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let sender = sender.clone();
            sender.send(line).wait().unwrap();
        }
    });
    let listener = receiver.for_each(|verb| {
        let option_cmd = UiCommand::parse(verb);
        match option_cmd {
            Some(cmd) => {
                logging_utils::log_cmd(&app_domain.logger, &cmd);
                gui_listener(cmd, &mut app_domain, &mut app_cache, &mut app_state_machine)
            }
            None => {
                println!("Could not parse command");
                Err(())
            }
        }

    });

    let result = main_core.run(listener);
}

fn gui_listener(cmd: UiCommand,
                app_domain: &mut AppDomain,
                app_cache: &mut AppCache,
                app_state_machine: &mut AppStateMachine)
                -> Result<(), ()> {

    if cmd.command.is_some() {

        let verb: String = cmd.command.unwrap();
        let mut numb: usize = 0;
        let mut has_numb = false;
        if cmd.number.is_some() {
            numb = cmd.number.unwrap() - 1; // UI is designed as index starting from 1
            has_numb = true;
        }

        if verb == "next" {
            if (app_state_machine.viewing_stories()) {
                handle_next_stories(app_domain, app_cache, app_state_machine);
            } else if (app_state_machine.viewing_comments()) {
                handle_next_comments(app_domain, app_cache, app_state_machine);
            } else {
                // todo wrong state, something went wrong
            }
        } else if verb == "back" {
            if (app_state_machine.viewing_stories()) {
                handle_previous_stories(app_domain, app_cache, app_state_machine);
            } else if (app_state_machine.viewing_comments()) {
                handle_previous_comments(app_domain, app_cache, app_state_machine);
            } else {
                // todo wrong state, something went wrong
            }
        } else if verb == "top" {
            print_ten_stories(app_domain, app_cache, app_state_machine);
            app_state_machine.register_viewing_stories();
        } else if verb == "exit" {
            logging_utils::log_exit(&app_domain.logger);
            process::exit(0);
        } else if verb == "comments" && has_numb {
            safe_load_story(numb, app_domain, app_cache, app_state_machine).and_then(|item| {
                handle_comments(item, app_domain, app_cache, app_state_machine);
                app_state_machine.register_viewing_comments();
                Some(0)
            });
        } else if verb == "expand" && has_numb {
            safe_load_comment(numb, app_domain, app_cache, app_state_machine).and_then(|item| {
                app_state_machine.comments_page_index = 0; // reset comments index
                handle_comments(item, app_domain, app_cache, app_state_machine);
                app_state_machine.register_expanded_comment();
                Some(0)
            });
        } else if verb == "load" && has_numb {
            download_page(numb, app_domain, app_cache, app_state_machine);
        } else if verb == "open" && has_numb {
            open_page(numb, app_domain, app_cache, app_state_machine);
            app_state_machine.register_opened_story();
        }
        // todo print help
        else {
            cli::print_invalid_command();
        }
    }
    Ok(())
}

fn handle_next_comments(app_domain: &mut AppDomain,
                        app_cache: &mut AppCache,
                        app_state_machine: &mut AppStateMachine) {
    app_state_machine.comments_page_index += 1;
    print_ten_comments(app_domain, app_cache, app_state_machine);
}

fn handle_previous_comments(app_domain: &mut AppDomain,
                            app_cache: &mut AppCache,
                            app_state_machine: &mut AppStateMachine) {
    if app_state_machine.comments_page_index >= 0 {
        app_state_machine.comments_page_index -= 1;
    }
    print_ten_comments(app_domain, app_cache, app_state_machine);
}

fn handle_next_stories(app_domain: &mut AppDomain,
                       app_cache: &mut AppCache,
                       app_state_machine: &mut AppStateMachine) {
    app_state_machine.listing_page_index += 1;
    print_and_log_stories(app_domain, app_cache, app_state_machine);
}

fn handle_previous_stories(app_domain: &mut AppDomain,
                           app_cache: &mut AppCache,
                           app_state_machine: &mut AppStateMachine) {
    if app_state_machine.listing_page_index >= 0 {
        app_state_machine.listing_page_index -= 1;
    }
    print_and_log_stories(app_domain, app_cache, app_state_machine);
}

fn handle_comments(item: HnItem,
                   app_domain: &mut AppDomain,
                   app_cache: &mut AppCache,
                   app_state_machine: &mut AppStateMachine) {
    // todo This overrides the cached comments even if they were spam, probably fixed once the back traverse for comments is implemented
    retrieve_comments_for_item(item, app_domain, app_cache, app_state_machine);
    print_ten_comments(app_domain, app_cache, app_state_machine);
}

fn print_and_log_stories(app_domain: &mut AppDomain,
                         app_cache: &mut AppCache,
                         app_state_machine: &mut AppStateMachine) {
    print_ten_stories(app_domain, app_cache, app_state_machine);
    logging_utils::log_stories_page_with_index(&app_domain.logger,
                                               app_state_machine.listing_page_index);
    app_state_machine.register_viewing_stories();

}

fn check_numb_against_stories(numb: usize, app_cache: &mut AppCache) -> Option<usize> {
    match app_cache.stories_len() {
        Some(l) => Some(min((l - 1), numb)),
        None => None,
    }
}

fn check_numb_against_comments(numb: usize, app_cache: &mut AppCache) -> Option<usize> {
    match app_cache.comments_len() {
        Some(l) => Some(min((l - 1), numb)),
        None => None,
    }
}

fn safe_load_story(numb: usize,
                   app_domain: &mut AppDomain,
                   app_cache: &mut AppCache,
                   app_state_machine: &mut AppStateMachine)
                   -> Option<HnItem> {
    let opt_numb = check_numb_against_stories(numb, app_cache);
    if opt_numb.is_none() {
        cli::print_invalid_numb();
    } else {
        let act_numb = opt_numb.unwrap();
        if act_numb != numb {
            cli::print_over_limit_but_using_index(act_numb + 1);
        }
        let parent_opt = retrieve_story(act_numb, app_domain, app_cache, app_state_machine);
        match parent_opt {
            Some(parent) => {
                return Some(parent); // retrieve_comments_for_item(parent, app_domain, app_cache, app_state_machine)
            }
            None => cli::print_could_not_get_story(act_numb + 1),
        }
    }
    None
}

fn safe_load_comment(numb: usize,
                     app_domain: &mut AppDomain,
                     mut app_cache: &mut AppCache,
                     app_state_machine: &mut AppStateMachine)
                     -> Option<HnItem> {

    let opt_numb = check_numb_against_comments(numb, app_cache);
    if opt_numb.is_none() {
        cli::print_invalid_numb();
    } else {
        let act_numb = opt_numb.unwrap(); // todo did this handle index somewhere?
        if act_numb != numb {
            cli::print_over_limit_but_using_index(act_numb + 1);
        }
        return app_cache.get_comment_if_kids(act_numb);
    }
    None
}

fn retrieve_comments_for_item(parent: HnItem,
                              app_domain: &mut AppDomain,
                              app_cache: &mut AppCache,
                              app_state_machine: &mut AppStateMachine)
                              -> bool {
    let comments = client::get_comments_for_item(&parent, app_domain, app_state_machine);
    match comments {
        Some(comments_vector) => {
            app_cache.last_parent_items.push_back(parent); // move parent to this location
            app_cache.last_retrieved_comments = Some(comments_vector); // return comments and return
            true
        }
        None => false,
    }
}

fn retrieve_story(numb: usize,
                  app_domain: &mut AppDomain,
                  app_cache: &mut AppCache,
                  app_state_machine: &mut AppStateMachine)
                  -> Option<HnItem> {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).ok();
    item
}

fn open_page(numb: usize,
             app_domain: &mut AppDomain,
             app_cache: &mut AppCache,
             app_state_machine: &mut AppStateMachine) {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
    if item.url.is_some() && webbrowser::open(&item.url.as_ref().unwrap()).is_ok() {
        // todo cleanup
        logging_utils::log_open_page(&app_domain.logger, item.url.as_ref().unwrap());
        println!("{} {}", "Opened browser to url", item.url.as_ref().unwrap()) // todo move to cli
    }
}

fn download_page(numb: usize,
                 app_domain: &mut AppDomain,
                 app_cache: &mut AppCache,
                 app_state_machine: &mut AppStateMachine) {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
    let filen = client::download_page_from_item(&item, app_domain, app_state_machine);
    match filen {
        Ok(n) => {
            cli::print_filename_of_loaded_page(&n, item.title.as_ref().unwrap());
            logging_utils::log_loaded_page_locally(&app_domain.logger,
                                                   item.url.as_ref().unwrap(),
                                                   &n)
        }
        Err(e) => {
            cli::could_not_load_page(item.title.as_ref().unwrap());
            warn!(&app_domain.logger,
                  format!("Could not load page to file {}", e));
        }
    }

}

fn print_ten_comments(app_domain: &mut AppDomain,
                      app_cache: &mut AppCache,
                      app_state_machine: &mut AppStateMachine) {

    let skipped: usize = (app_state_machine.comments_page_index * 10) as usize;
    let mut partition: Option<Vec<&HnItem>> = None;
    match app_cache.last_retrieved_comments {
        Some(ref comments) => {
            let mut p: Vec<&HnItem> = Vec::new();
            for comment in comments.iter().skip(skipped).take(10) {
                p.push(comment);
            }
            partition = Some(p);
        }
        None => (),
    }
    cli::print_comment_and_parent(app_cache.last_parent_items.back(),
                                  &partition,
                                  &app_domain.formatters,
                                  skipped);
}

fn print_ten_stories(app_domain: &mut AppDomain,
                     app_cache: &mut AppCache,
                     app_state_machine: &mut AppStateMachine) {

    // This probably should not need all the parameters
    let top_stories = app_cache.retrieved_top_stories.as_ref().unwrap();
    logging_utils::log_loaded_top_stories(&app_domain.logger, top_stories.values.len());
    let skipped: usize = (app_state_machine.listing_page_index * 10) as usize;
    let mut index = 0 + skipped as i32;

    for item_id in top_stories.values.iter().skip(skipped).take(10) {
        index += 1;
        let s = format!("{}", item_id);
        let item: HnItem = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
        cli::print_headline_with_author(&item, &index);
    }
}
<commit_msg>Made the line easier to read<commit_after>// Unused mut warning is false in this context
#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_stream;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_tls;
extern crate tokio_service;
extern crate curl;
extern crate webbrowser;
extern crate termion;
extern crate native_tls;
extern crate regex;

mod utils;
mod ui;
mod decoding;
mod logging_utils;
mod formatting;
mod core;

use core::app::*;
use core::models::*;
use core::client;
use ui::cli;

use std::cmp::min;
use tokio_core::reactor::Core;
use std::io::{self, BufRead};
use std::thread::spawn;
use std::process;
use futures::{Stream, Sink, Future};
use futures::sync::mpsc;
use ui::backend::UiCommand;

fn main() {

    let mut app_domain = AppDomain::new();
    let mut app_cache: AppCache = AppCache::new();
    let mut app_state_machine: AppStateMachine = AppStateMachine::new();
    let mut main_core = Core::new().expect("Failed to create core");

    info!(&app_domain.logger, "Application started");
    app_cache.retrieved_top_stories =
        client::get_top_story_ids(&mut app_domain, &mut app_state_machine).ok();
    print_ten_stories(&mut app_domain, &mut app_cache, &mut app_state_machine);
    let (sender, receiver) = mpsc::channel(1);

    spawn(move || {
        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let sender = sender.clone();
            sender.send(line).wait().unwrap();
        }
    });
    let listener = receiver.for_each(|verb| {
        let option_cmd = UiCommand::parse(verb);
        match option_cmd {
            Some(cmd) => {
                logging_utils::log_cmd(&app_domain.logger, &cmd);
                gui_listener(cmd, &mut app_domain, &mut app_cache, &mut app_state_machine)
            }
            None => {
                println!("Could not parse command");
                Err(())
            }
        }

    });

    let result = main_core.run(listener);
}

fn gui_listener(cmd: UiCommand,
                app_domain: &mut AppDomain,
                app_cache: &mut AppCache,
                app_state_machine: &mut AppStateMachine)
                -> Result<(), ()> {

    if cmd.command.is_some() {

        let verb: String = cmd.command.unwrap();
        let mut numb: usize = 0;
        let mut has_numb = false;
        if cmd.number.is_some() {
            numb = cmd.number.unwrap() - 1; // UI is designed as index starting from 1
            has_numb = true;
        }

        if verb == "next" {
            if (app_state_machine.viewing_stories()) {
                handle_next_stories(app_domain, app_cache, app_state_machine);
            } else if (app_state_machine.viewing_comments()) {
                handle_next_comments(app_domain, app_cache, app_state_machine);
            } else {
                // todo wrong state, something went wrong
            }
        } else if verb == "back" {
            if (app_state_machine.viewing_stories()) {
                handle_previous_stories(app_domain, app_cache, app_state_machine);
            } else if (app_state_machine.viewing_comments()) {
                handle_previous_comments(app_domain, app_cache, app_state_machine);
            } else {
                // todo wrong state, something went wrong
            }
        } else if verb == "top" {
            print_ten_stories(app_domain, app_cache, app_state_machine);
            app_state_machine.register_viewing_stories();
        } else if verb == "exit" {
            logging_utils::log_exit(&app_domain.logger);
            process::exit(0);
        } else if verb == "comments" && has_numb {
            safe_load_story(numb, app_domain, app_cache, app_state_machine).and_then(|item| {
                handle_comments(item, app_domain, app_cache, app_state_machine);
                app_state_machine.register_viewing_comments();
                Some(0)
            });
        } else if verb == "expand" && has_numb {
            safe_load_comment(numb, app_domain, app_cache, app_state_machine).and_then(|item| {
                app_state_machine.comments_page_index = 0; // reset comments index
                handle_comments(item, app_domain, app_cache, app_state_machine);
                app_state_machine.register_expanded_comment();
                Some(0)
            });
        } else if verb == "load" && has_numb {
            download_page(numb, app_domain, app_cache, app_state_machine);
        } else if verb == "open" && has_numb {
            open_page(numb, app_domain, app_cache, app_state_machine);
            app_state_machine.register_opened_story();
        }
        // todo print help
        else {
            cli::print_invalid_command();
        }
    }
    Ok(())
}

fn handle_next_comments(app_domain: &mut AppDomain,
                        app_cache: &mut AppCache,
                        app_state_machine: &mut AppStateMachine) {
    app_state_machine.comments_page_index += 1;
    print_ten_comments(app_domain, app_cache, app_state_machine);
}

fn handle_previous_comments(app_domain: &mut AppDomain,
                            app_cache: &mut AppCache,
                            app_state_machine: &mut AppStateMachine) {
    if app_state_machine.comments_page_index >= 0 {
        app_state_machine.comments_page_index -= 1;
    }
    print_ten_comments(app_domain, app_cache, app_state_machine);
}

fn handle_next_stories(app_domain: &mut AppDomain,
                       app_cache: &mut AppCache,
                       app_state_machine: &mut AppStateMachine) {
    app_state_machine.listing_page_index += 1;
    print_and_log_stories(app_domain, app_cache, app_state_machine);
}

fn handle_previous_stories(app_domain: &mut AppDomain,
                           app_cache: &mut AppCache,
                           app_state_machine: &mut AppStateMachine) {
    if app_state_machine.listing_page_index >= 0 {
        app_state_machine.listing_page_index -= 1;
    }
    print_and_log_stories(app_domain, app_cache, app_state_machine);
}

fn handle_comments(item: HnItem,
                   app_domain: &mut AppDomain,
                   app_cache: &mut AppCache,
                   app_state_machine: &mut AppStateMachine) {
    // todo This overrides the cached comments even if they were spam, probably fixed once the back traverse for comments is implemented
    retrieve_comments_for_item(item, app_domain, app_cache, app_state_machine);
    print_ten_comments(app_domain, app_cache, app_state_machine);
}

fn print_and_log_stories(app_domain: &mut AppDomain,
                         app_cache: &mut AppCache,
                         app_state_machine: &mut AppStateMachine) {
    print_ten_stories(app_domain, app_cache, app_state_machine);
    logging_utils::log_stories_page_with_index(&app_domain.logger,
                                               app_state_machine.listing_page_index);
    app_state_machine.register_viewing_stories();

}

fn check_numb_against_stories(numb: usize, app_cache: &mut AppCache) -> Option<usize> {
    match app_cache.stories_len() {
        Some(l) => Some(min((l - 1), numb)),
        None => None,
    }
}

fn check_numb_against_comments(numb: usize, app_cache: &mut AppCache) -> Option<usize> {
    match app_cache.comments_len() {
        Some(l) => Some(min((l - 1), numb)),
        None => None,
    }
}

fn safe_load_story(numb: usize,
                   app_domain: &mut AppDomain,
                   app_cache: &mut AppCache,
                   app_state_machine: &mut AppStateMachine)
                   -> Option<HnItem> {
    let opt_numb = check_numb_against_stories(numb, app_cache);
    if opt_numb.is_none() {
        cli::print_invalid_numb();
    } else {
        let act_numb = opt_numb.unwrap();
        if act_numb != numb {
            cli::print_over_limit_but_using_index(act_numb + 1);
        }
        let parent_opt = retrieve_story(act_numb, app_domain, app_cache, app_state_machine);
        match parent_opt {
            Some(parent) => {
                return Some(parent); // retrieve_comments_for_item(parent, app_domain, app_cache, app_state_machine)
            }
            None => cli::print_could_not_get_story(act_numb + 1),
        }
    }
    None
}

fn safe_load_comment(numb: usize,
                     app_domain: &mut AppDomain,
                     mut app_cache: &mut AppCache,
                     app_state_machine: &mut AppStateMachine)
                     -> Option<HnItem> {

    let opt_numb = check_numb_against_comments(numb, app_cache);
    if opt_numb.is_none() {
        cli::print_invalid_numb();
    } else {
        let act_numb = opt_numb.unwrap(); // todo did this handle index somewhere?
        if act_numb != numb {
            cli::print_over_limit_but_using_index(act_numb + 1);
        }
        return app_cache.get_comment_if_kids(act_numb);
    }
    None
}

fn retrieve_comments_for_item(parent: HnItem,
                              app_domain: &mut AppDomain,
                              app_cache: &mut AppCache,
                              app_state_machine: &mut AppStateMachine)
                              -> bool {
    let comments = client::get_comments_for_item(&parent, app_domain, app_state_machine);
    match comments {
        Some(comments_vector) => {
            app_cache.last_parent_items.push_back(parent); // move parent to this location
            app_cache.last_retrieved_comments = Some(comments_vector); // return comments and return
            true
        }
        None => false,
    }
}

fn retrieve_story(numb: usize,
                  app_domain: &mut AppDomain,
                  app_cache: &mut AppCache,
                  app_state_machine: &mut AppStateMachine)
                  -> Option<HnItem> {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).ok();
    item
}

fn open_page(numb: usize,
             app_domain: &mut AppDomain,
             app_cache: &mut AppCache,
             app_state_machine: &mut AppStateMachine) {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
    if item.url.is_some() && webbrowser::open(&item.url.as_ref().unwrap()).is_ok() {
        // todo cleanup
        logging_utils::log_open_page(&app_domain.logger, item.url.as_ref().unwrap());
        println!("{} {}", "Opened browser to url", item.url.as_ref().unwrap()) // todo move to cli
    }
}

fn download_page(numb: usize,
                 app_domain: &mut AppDomain,
                 app_cache: &mut AppCache,
                 app_state_machine: &mut AppStateMachine) {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
    let filen = client::download_page_from_item(&item, app_domain, app_state_machine);
    match filen {
        Ok(n) => {
            cli::print_filename_of_loaded_page(&n, item.title.as_ref().unwrap());
            logging_utils::log_loaded_page_locally(&app_domain.logger,
                                                   item.url.as_ref().unwrap(),
                                                   &n)
        }
        Err(e) => {
            cli::could_not_load_page(item.title.as_ref().unwrap());
            warn!(&app_domain.logger,
                  format!("Could not load page to file {}", e));
        }
    }

}

fn print_ten_comments(app_domain: &mut AppDomain,
                      app_cache: &mut AppCache,
                      app_state_machine: &mut AppStateMachine) {

    let skipped: usize = (app_state_machine.comments_page_index * 10) as usize;
    let mut partition: Option<Vec<&HnItem>> = None;
    match app_cache.last_retrieved_comments {
        Some(ref comments) => {
            partition = Some(comments.iter().skip(skipped).take(10).collect());
        }
        None => (),
    }
    cli::print_comment_and_parent(app_cache.last_parent_items.back(),
                                  &partition,
                                  &app_domain.formatters,
                                  skipped);
}

fn print_ten_stories(app_domain: &mut AppDomain,
                     app_cache: &mut AppCache,
                     app_state_machine: &mut AppStateMachine) {

    // This probably should not need all the parameters
    let top_stories = app_cache.retrieved_top_stories.as_ref().unwrap();
    logging_utils::log_loaded_top_stories(&app_domain.logger, top_stories.values.len());
    let skipped: usize = (app_state_machine.listing_page_index * 10) as usize;
    let mut index = 0 + skipped as i32;

    for item_id in top_stories.values.iter().skip(skipped).take(10) {
        index += 1;
        let s = format!("{}", item_id);
        let item: HnItem = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
        cli::print_headline_with_author(&item, &index);
    }
}
<|endoftext|>


<commit_before>use super::loader::Loader;
use super::util;
use crate::error::{Error, Result};
use std::io::{self, Write};
use std::path::Path;
use std::{fs, str};
use tree_sitter_tags::TagsContext;

pub fn generate_tags(loader: &Loader, scope: Option<&str>, paths: &[String]) -> Result<()> {
    let mut lang = None;
    if let Some(scope) = scope {
        lang = loader.language_configuration_for_scope(scope)?;
        if lang.is_none() {
            return Error::err(format!("Unknown scope '{}'", scope));
        }
    }

    let mut context = TagsContext::new();
    let cancellation_flag = util::cancel_on_stdin();
    let stdout = io::stdout();
    let mut stdout = stdout.lock();

    for path in paths {
        let path = Path::new(&path);
        let (language, language_config) = match lang {
            Some(v) => v,
            None => match loader.language_configuration_for_file_name(path)? {
                Some(v) => v,
                None => {
                    eprintln!("No language found for path {:?}", path);
                    continue;
                }
            },
        };

        if let Some(tags_config) = language_config.tags_config(language)? {
            let path_str = format!("{:?}", path);
            writeln!(&mut stdout, "{}", &path_str[1..path_str.len() - 1])?;

            let source = fs::read(path)?;
            for tag in context.generate_tags(tags_config, &source, Some(&cancellation_flag))? {
                let tag = tag?;
                write!(
                    &mut stdout,
                    "  {:<8} {:<40}\t [{}] {:>9}-{:<9}",
                    tag.syntax_type,
                    str::from_utf8(&source[tag.name_range]).unwrap_or(""),
                    if tag.is_definition { "def" } else { "ref" },
                    tag.span.start,
                    tag.span.end,
                )?;
                if let Some(docs) = tag.docs {
                    if docs.len() > 120 {
                        write!(&mut stdout, "\t{:?}...", &docs[0..120])?;
                    } else {
                        write!(&mut stdout, "\t{:?}", &docs)?;
                    }
                }
                writeln!(&mut stdout, "")?;
            }
        } else {
            eprintln!("No tags config found for path {:?}", path);
        }
    }

    Ok(())
}
<commit_msg>Minor output changes<commit_after>use super::loader::Loader;
use super::util;
use crate::error::{Error, Result};
use std::io::{self, Write};
use std::path::Path;
use std::{fs, str};
use tree_sitter_tags::TagsContext;

pub fn generate_tags(loader: &Loader, scope: Option<&str>, paths: &[String]) -> Result<()> {
    let mut lang = None;
    if let Some(scope) = scope {
        lang = loader.language_configuration_for_scope(scope)?;
        if lang.is_none() {
            return Error::err(format!("Unknown scope '{}'", scope));
        }
    }

    let mut context = TagsContext::new();
    let cancellation_flag = util::cancel_on_stdin();
    let stdout = io::stdout();
    let mut stdout = stdout.lock();

    for path in paths {
        let path = Path::new(&path);
        let (language, language_config) = match lang {
            Some(v) => v,
            None => match loader.language_configuration_for_file_name(path)? {
                Some(v) => v,
                None => {
                    eprintln!("No language found for path {:?}", path);
                    continue;
                }
            },
        };

        if let Some(tags_config) = language_config.tags_config(language)? {
            let ident = if paths.len() > 1 {
                let path_str = format!("{:?}", path);
                writeln!(&mut stdout, "{}", &path_str[1..path_str.len() - 1])?;
                "\t"
            } else {
                ""
            };

            let source = fs::read(path)?;
            for tag in context.generate_tags(tags_config, &source, Some(&cancellation_flag))? {
                let tag = tag?;
                write!(
                    &mut stdout,
                    "{}{:<10}\t | {:<8}\t{} {} - {} `{}`",
                    ident,
                    str::from_utf8(&source[tag.name_range]).unwrap_or(""),
                    tag.syntax_type,
                    if tag.is_definition { "def" } else { "ref" },
                    tag.span.start,
                    tag.span.end,
                    str::from_utf8(&source[tag.line_range]).unwrap_or(""),
                )?;
                if let Some(docs) = tag.docs {
                    if docs.len() > 120 {
                        write!(&mut stdout, "\t{:?}...", &docs[0..120])?;
                    } else {
                        write!(&mut stdout, "\t{:?}", &docs)?;
                    }
                }
                writeln!(&mut stdout, "")?;
            }
        } else {
            eprintln!("No tags config found for path {:?}", path);
        }
    }

    Ok(())
}
<|endoftext|>


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

//! General — Library initialization and miscellaneous functions

use std::cell::Cell;
use std::ptr;
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

use ffi;

thread_local! {
    static IS_MAIN_THREAD: Cell<bool> = Cell::new(false)
}

static INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT;

/// Asserts that this is the main thread and either `gdk::init` or `gtk::init` has been called.
macro_rules! assert_initialized_main_thread {
    () => (
        if !::rt::is_initialized_main_thread() {
            if ::rt::is_initialized() {
                panic!("GDK may only be used from the main thread.");
            }
            else {
                panic!("GDK has not been initialized. Call `gdk::init` or `gtk::init` first.");
            }
        }
    )
}

/// No-op.
macro_rules! skip_assert_initialized {
    () => ()
}

/// Asserts that neither `gdk::init` nor `gtk::init` has been called.
macro_rules! assert_not_initialized {
    () => (
        if ::rt::is_initialized() {
            panic!("This function has to be called before `gdk::init` or `gtk::init`.");
        }
    )
}

macro_rules! callback_guard {
    () => (
        let _guard = ::glib::CallbackGuard::new();
        if cfg!(debug_assertions) {
            assert_initialized_main_thread!();
        }
    )
}

/// Returns `true` if GDK has been initialized.
#[inline]
pub fn is_initialized() -> bool {
    skip_assert_initialized!();
    INITIALIZED.load(Ordering::Acquire)
}

/// Returns `true` if GDK has been initialized and this is the main thread.
#[inline]
pub fn is_initialized_main_thread() -> bool {
    skip_assert_initialized!();
    IS_MAIN_THREAD.with(|c| c.get())
}

/// Informs this crate that GDK has been initialized and the current thread is the main one.
pub unsafe fn set_initialized() {
    skip_assert_initialized!();
    if is_initialized_main_thread() {
        return;
    }
    else if is_initialized() {
        panic!("Attempted to initialize GDK from two different threads.");
    }
    INITIALIZED.store(true, Ordering::Release);
    IS_MAIN_THREAD.with(|c| c.set(true));
}

pub fn init() {
    assert_not_initialized!();
    unsafe {
        ffi::gdk_init(ptr::null_mut(), ptr::null_mut());
        set_initialized();
    }
}
<commit_msg>Remove callback_guard in manual part<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! General — Library initialization and miscellaneous functions

use std::cell::Cell;
use std::ptr;
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

use ffi;

thread_local! {
    static IS_MAIN_THREAD: Cell<bool> = Cell::new(false)
}

static INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT;

/// Asserts that this is the main thread and either `gdk::init` or `gtk::init` has been called.
macro_rules! assert_initialized_main_thread {
    () => (
        if !::rt::is_initialized_main_thread() {
            if ::rt::is_initialized() {
                panic!("GDK may only be used from the main thread.");
            }
            else {
                panic!("GDK has not been initialized. Call `gdk::init` or `gtk::init` first.");
            }
        }
    )
}

/// No-op.
macro_rules! skip_assert_initialized {
    () => ()
}

/// Asserts that neither `gdk::init` nor `gtk::init` has been called.
macro_rules! assert_not_initialized {
    () => (
        if ::rt::is_initialized() {
            panic!("This function has to be called before `gdk::init` or `gtk::init`.");
        }
    )
}

/// Returns `true` if GDK has been initialized.
#[inline]
pub fn is_initialized() -> bool {
    skip_assert_initialized!();
    INITIALIZED.load(Ordering::Acquire)
}

/// Returns `true` if GDK has been initialized and this is the main thread.
#[inline]
pub fn is_initialized_main_thread() -> bool {
    skip_assert_initialized!();
    IS_MAIN_THREAD.with(|c| c.get())
}

/// Informs this crate that GDK has been initialized and the current thread is the main one.
pub unsafe fn set_initialized() {
    skip_assert_initialized!();
    if is_initialized_main_thread() {
        return;
    }
    else if is_initialized() {
        panic!("Attempted to initialize GDK from two different threads.");
    }
    INITIALIZED.store(true, Ordering::Release);
    IS_MAIN_THREAD.with(|c| c.set(true));
}

pub fn init() {
    assert_not_initialized!();
    unsafe {
        ffi::gdk_init(ptr::null_mut(), ptr::null_mut());
        set_initialized();
    }
}
<|endoftext|>


<commit_before>/// The `mutate` trait returns a mutation version of a type that implements it.
///
/// Implementer are should try to create persisted data-structures

pub trait Mutate {
    fn mutate(&self) -> Self;
}

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

    #[derive(PartialEq,Debug)]
    enum Dummy {
        A, B,
    }

    impl super::Mutate for Dummy {
        fn mutate(&self) -> Dummy {
            match *self {
                Dummy::A => Dummy::B,
                Dummy::B => Dummy::A
            }
        }
    }

    #[test]
    fn should_mutate_mutate_dummy() {
        let original = Dummy::A;

        let mutation = original.mutate();

        assert_eq!(mutation, Dummy::B);
    }
}
<commit_msg>Implement Mutate for Command<commit_after>/// The `mutate` trait returns a mutation version of a type that implements it.
///
/// Implementer are should try to create persisted data-structures

extern crate rand;

use super::super::structure::{Command};

pub trait Mutate {
    fn mutate(&self) -> Self;
}

impl Mutate for Command {
    fn mutate(&self) -> Command {
        let mutation: Command = rand::random();
        mutation
    }
}

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

    #[derive(PartialEq,Debug)]
    enum Dummy {
        A, B,
    }

    impl super::Mutate for Dummy {
        fn mutate(&self) -> Dummy {
            match *self {
                Dummy::A => Dummy::B,
                Dummy::B => Dummy::A
            }
        }
    }

    #[test]
    fn should_mutate_mutate_dummy() {
        let original = Dummy::A;

        let mutation = original.mutate();

        assert_eq!(mutation, Dummy::B);
    }
}
<|endoftext|>