<commit_before>extern crate dot;

use std::borrow::Cow;
use std::collections::{BTreeMap, HashSet};

use super::common::{Node, Weight};

#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Arc(pub Node, pub Node);

pub type Nodes = HashSet<Node>;
type Arcs = BTreeMap<Arc, Weight>;

#[derive(Clone, Debug)]
pub struct Graph {
    nodes: Nodes,
    arcs: Arcs,
}

impl Graph {
    pub fn new() -> Graph {
        Graph {
            nodes: Nodes::new(),
            arcs: Arcs::new(),
        }
    }

    pub fn nodes(&self) -> &Nodes {
        &self.nodes
    }

    pub fn arcs(&self) -> &Arcs {
        &self.arcs
    }

    pub fn arc_weight(&self, arc: &Arc) -> Weight {
        self.arcs[arc]
    }

    pub fn add_node(&mut self, id: Node) {
        self.nodes.insert(id);
    }

    pub fn rm_node(&mut self, id: Node) {
        self.nodes.remove(&id);
    }

    pub fn add_arc(&mut self, src: Node, dst: Node, weight: Weight) -> Arc {
        assert!(self.nodes.contains(&src));
        assert!(self.nodes.contains(&dst));
        let arc = Arc(src, dst);
        self.arcs.insert(arc.clone(), weight);
        arc
    }

    pub fn rm_arc(&mut self, arc: &Arc) {
        self.arcs.remove(arc);
    }

    pub fn union(&self, other: &Graph) -> Graph {
        let nodes = self.nodes.union(&other.nodes).cloned().collect();
        let self_arcs = self.arcs.keys().cloned().collect::<HashSet<_>>();
        let other_arcs = other.arcs.keys().cloned().collect::<HashSet<_>>();
        let arcs = self_arcs.union(&other_arcs).cloned().into_iter()
            .map(|arc| {
                let self_w = self.arcs.get(&arc);
                let other_w = other.arcs.get(&arc);
                let n = self_w.is_some() as i64 + other_w.is_some() as i64;
                let weight = (self_w.unwrap_or(&0.0) + other_w.unwrap_or(&0.0)) / n as Weight;
                (arc, weight)
            }).collect();
        Graph {nodes: nodes, arcs: arcs}
    }
}

impl<'a> dot::Labeller<'a, Node, Arc> for Graph {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("neural_network").unwrap()
    }

    fn node_id(&'a self, node: &Node) -> dot::Id<'a> {
        let &Node(id) = node;
        dot::Id::new(format!("node_{}", id)).unwrap()
    }

    fn node_label<'b>(&'b self, node: &Node) -> dot::LabelText<'b> {
        let &Node(id) = node;
        dot::LabelText::LabelStr(Cow::Owned(format!("{}", id)))
    }

    fn edge_label<'b>(&'b self, arc: &Arc) -> dot::LabelText<'b> {
        let weight = self.arc_weight(arc);
        dot::LabelText::LabelStr(Cow::Owned(format!("{}", weight)))
    }
}

impl<'a> dot::GraphWalk<'a, Node, Arc> for Graph {
    fn nodes(&self) -> dot::Nodes<'a, Node> {
        Cow::Owned(self.nodes().iter().cloned().collect::<Vec<Node>>())
    }

    fn edges(&'a self) -> dot::Edges<'a, Arc> {
        Cow::Owned(self.arcs().keys().cloned().collect::<Vec<Arc>>())
    }

    fn source(&self, arc: &Arc) -> Node {
        let &Arc(src, _) = arc;
        src
    }

    fn target(&self, arc: &Arc) -> Node {
        let &Arc(_, dst) = arc;
        dst
    }
}

#[test]
fn test_create_should_succeed() {
    Graph::new();
}

#[test]
fn test_add_node_should_succeed() {
    let mut graph = Graph::new();
    graph.add_node(Node(42));
    assert!(graph.nodes().contains(&Node(42)));
}

#[test]
fn test_rm_node_should_succeed() {
    let mut graph = Graph::new();
    graph.add_node(Node(42));
    graph.rm_node(Node(42));
    assert!(graph.nodes().is_empty());
}

#[test]
fn test_add_arc_should_succeed() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    let weight = 0.3;
    graph.add_node(src);
    graph.add_node(dst);
    let arc = graph.add_arc(src, dst, weight);
    assert_eq!(arc, Arc(src, dst));
    assert_eq!(graph.arcs()[&arc], weight);
}

#[test]
fn test_rm_arc_should_succeed() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    let weight = 0.3;
    graph.add_node(src);
    graph.add_node(dst);
    let arc = graph.add_arc(src, dst, weight);
    graph.rm_arc(&arc);
    assert!(graph.arcs().is_empty());
}

#[test]
#[should_panic]
fn test_add_arc_with_nonexistent_nodes_should_panic() {
    Graph::new().add_arc(Node(1), Node(2), 0.3);
}

#[test]
fn test_union_should_succeed() {
    let node1 = Node(1);
    let node2 = Node(2);
    let node3 = Node(3);
    let node4 = Node(4);
    let mut graph1 = Graph::new();
    graph1.add_node(node1);
    graph1.add_node(node2);
    graph1.add_node(node3);
    graph1.add_arc(node1, node2, 1.0);
    graph1.add_arc(node2, node3, 1.0);
    let mut graph2 = Graph::new();
    graph2.add_node(node1);
    graph2.add_node(node2);
    graph2.add_node(node4);
    graph2.add_arc(node1, node2, 0.5);
    graph2.add_arc(node1, node4, 1.0);
    let union = graph1.union(&graph2);
    assert_eq!(union.nodes(), &[node1, node2, node3, node4].iter().cloned().collect::<Nodes>());
    assert_eq!(union.arcs(), &[
        (Arc(node1, node2), 0.75),
        (Arc(node2, node3), 1.0),
        (Arc(node1, node4), 1.0),
    ].iter().cloned().collect::<Arcs>());
}
<commit_msg>Fix warning<commit_after>extern crate dot;

use std::borrow::Cow;
use std::collections::{BTreeMap, HashSet};

use super::common::{Node, Weight};

#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Arc(pub Node, pub Node);

pub type Nodes = HashSet<Node>;
pub type Arcs = BTreeMap<Arc, Weight>;

#[derive(Clone, Debug)]
pub struct Graph {
    nodes: Nodes,
    arcs: Arcs,
}

impl Graph {
    pub fn new() -> Graph {
        Graph {
            nodes: Nodes::new(),
            arcs: Arcs::new(),
        }
    }

    pub fn nodes(&self) -> &Nodes {
        &self.nodes
    }

    pub fn arcs(&self) -> &Arcs {
        &self.arcs
    }

    pub fn arc_weight(&self, arc: &Arc) -> Weight {
        self.arcs[arc]
    }

    pub fn add_node(&mut self, id: Node) {
        self.nodes.insert(id);
    }

    pub fn rm_node(&mut self, id: Node) {
        self.nodes.remove(&id);
    }

    pub fn add_arc(&mut self, src: Node, dst: Node, weight: Weight) -> Arc {
        assert!(self.nodes.contains(&src));
        assert!(self.nodes.contains(&dst));
        let arc = Arc(src, dst);
        self.arcs.insert(arc.clone(), weight);
        arc
    }

    pub fn rm_arc(&mut self, arc: &Arc) {
        self.arcs.remove(arc);
    }

    pub fn union(&self, other: &Graph) -> Graph {
        let nodes = self.nodes.union(&other.nodes).cloned().collect();
        let self_arcs = self.arcs.keys().cloned().collect::<HashSet<_>>();
        let other_arcs = other.arcs.keys().cloned().collect::<HashSet<_>>();
        let arcs = self_arcs.union(&other_arcs).cloned().into_iter()
            .map(|arc| {
                let self_w = self.arcs.get(&arc);
                let other_w = other.arcs.get(&arc);
                let n = self_w.is_some() as i64 + other_w.is_some() as i64;
                let weight = (self_w.unwrap_or(&0.0) + other_w.unwrap_or(&0.0)) / n as Weight;
                (arc, weight)
            }).collect();
        Graph {nodes: nodes, arcs: arcs}
    }
}

impl<'a> dot::Labeller<'a, Node, Arc> for Graph {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("neural_network").unwrap()
    }

    fn node_id(&'a self, node: &Node) -> dot::Id<'a> {
        let &Node(id) = node;
        dot::Id::new(format!("node_{}", id)).unwrap()
    }

    fn node_label<'b>(&'b self, node: &Node) -> dot::LabelText<'b> {
        let &Node(id) = node;
        dot::LabelText::LabelStr(Cow::Owned(format!("{}", id)))
    }

    fn edge_label<'b>(&'b self, arc: &Arc) -> dot::LabelText<'b> {
        let weight = self.arc_weight(arc);
        dot::LabelText::LabelStr(Cow::Owned(format!("{}", weight)))
    }
}

impl<'a> dot::GraphWalk<'a, Node, Arc> for Graph {
    fn nodes(&self) -> dot::Nodes<'a, Node> {
        Cow::Owned(self.nodes().iter().cloned().collect::<Vec<Node>>())
    }

    fn edges(&'a self) -> dot::Edges<'a, Arc> {
        Cow::Owned(self.arcs().keys().cloned().collect::<Vec<Arc>>())
    }

    fn source(&self, arc: &Arc) -> Node {
        let &Arc(src, _) = arc;
        src
    }

    fn target(&self, arc: &Arc) -> Node {
        let &Arc(_, dst) = arc;
        dst
    }
}

#[test]
fn test_create_should_succeed() {
    Graph::new();
}

#[test]
fn test_add_node_should_succeed() {
    let mut graph = Graph::new();
    graph.add_node(Node(42));
    assert!(graph.nodes().contains(&Node(42)));
}

#[test]
fn test_rm_node_should_succeed() {
    let mut graph = Graph::new();
    graph.add_node(Node(42));
    graph.rm_node(Node(42));
    assert!(graph.nodes().is_empty());
}

#[test]
fn test_add_arc_should_succeed() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    let weight = 0.3;
    graph.add_node(src);
    graph.add_node(dst);
    let arc = graph.add_arc(src, dst, weight);
    assert_eq!(arc, Arc(src, dst));
    assert_eq!(graph.arcs()[&arc], weight);
}

#[test]
fn test_rm_arc_should_succeed() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    let weight = 0.3;
    graph.add_node(src);
    graph.add_node(dst);
    let arc = graph.add_arc(src, dst, weight);
    graph.rm_arc(&arc);
    assert!(graph.arcs().is_empty());
}

#[test]
#[should_panic]
fn test_add_arc_with_nonexistent_nodes_should_panic() {
    Graph::new().add_arc(Node(1), Node(2), 0.3);
}

#[test]
fn test_union_should_succeed() {
    let node1 = Node(1);
    let node2 = Node(2);
    let node3 = Node(3);
    let node4 = Node(4);
    let mut graph1 = Graph::new();
    graph1.add_node(node1);
    graph1.add_node(node2);
    graph1.add_node(node3);
    graph1.add_arc(node1, node2, 1.0);
    graph1.add_arc(node2, node3, 1.0);
    let mut graph2 = Graph::new();
    graph2.add_node(node1);
    graph2.add_node(node2);
    graph2.add_node(node4);
    graph2.add_arc(node1, node2, 0.5);
    graph2.add_arc(node1, node4, 1.0);
    let union = graph1.union(&graph2);
    assert_eq!(union.nodes(), &[node1, node2, node3, node4].iter().cloned().collect::<Nodes>());
    assert_eq!(union.arcs(), &[
        (Arc(node1, node2), 0.75),
        (Arc(node2, node3), 1.0),
        (Arc(node1, node4), 1.0),
    ].iter().cloned().collect::<Arcs>());
}
<|endoftext|>


<commit_before>use std::io::{ IoResult, Reader };
use std::io::fs::File;
use std::path::Path;
use super::ResourcesLoader;

#[deriving(Clone)]
pub struct DirLoader {
	directory: Path
}

impl DirLoader {
	pub fn new(directory: Path) -> DirLoader {
		DirLoader { directory: directory }
	}
}

impl ResourcesLoader for DirLoader {
	fn load(&self, resourceName: &str)
		-> IoResult<Box<Reader>>
	{
		let pathToSearch = self.directory.join(resourceName).join(".*");

		match ::glob::glob(format!("{}", pathToSearch.display()).as_slice()).next()
		{
			None =>
				Err(::std::io::IoError{
					kind: ::std::io::FileNotFound,
					desc: "Could not find resource",
					detail: None
				}),

			Some(file) =>
				Ok(box try!(File::open(&file)) as Box<Reader>)
		}
	}
}

<commit_msg>Fix DirLoader not working<commit_after>use std::io::{ IoResult, Reader };
use std::io::fs::File;
use std::path::Path;
use super::ResourcesLoader;

#[deriving(Clone)]
pub struct DirLoader {
	directory: Path
}

impl DirLoader {
	pub fn new(directory: Path) -> DirLoader {
		DirLoader { directory: directory }
	}
}

impl ResourcesLoader for DirLoader {
	fn load(&self, resourceName: &str)
		-> IoResult<Box<Reader>>
	{
		let pathToSearch = self.directory.join(format!("{}.*", resourceName));

		match ::glob::glob(format!("{}", pathToSearch.display()).as_slice()).next()
		{
			None =>
				Err(::std::io::IoError{
					kind: ::std::io::FileNotFound,
					desc: "Could not find resource",
					detail: Some(format!("Could not find resource \"{}\"", resourceName))
				}),

			Some(file) =>
				Ok(box try!(File::open(&file)) as Box<Reader>)
		}
	}
}

<|endoftext|>


<commit_before>use std::path::Path;

use html5ever::{LocalName, Namespace, QualName};
use kuchiki::traits::TendrilSink;
use kuchiki::{parse_fragment, Attribute, ExpandedName, NodeRef};

use crate::page::Page;
use crate::settings::Settings;
use crate::web;

use crate::string_ext::StringExt;

const START_HTML: &str = "";
const END_HTML: &str = "";

// Enhance the HTML
pub fn enhance_markup(html: &str, settings: &Settings) -> String {
    let doc = parse_markup(html);

    link_headings(&doc);
    process_custom_elements(&doc, &settings.pages_path);
    trim_pre_whitespace(&doc);

    let mut enhanced_html = doc.to_string();
    // HACK: The document ends up serialised with a wrapping `<html>` element around the content
    // strip that here. E.g.
    // https://github.com/kuchiki-rs/kuchiki/blob/f652e38b12cb0d33f7bb0565b6933a6e2823a0c5/src/tests.rs#L66
    if enhanced_html.ends_with(END_HTML) {
        enhanced_html.truncate(enhanced_html.len() - END_HTML.len());
    }
    if enhanced_html.starts_with(START_HTML) {
        enhanced_html[START_HTML.len()..].to_string()
    } else {
        enhanced_html
    }
}

fn link_headings(doc: &NodeRef) {
    for heading in doc.select("h1,h2,h3,h4,h5,h6").unwrap() {
        let identifier = heading.text_contents().to_slug();

        let a = NodeRef::new_element(
            el_name("a"),
            [
                attr("class", String::from("anchor")),
                attr("href", format!("#{}", identifier)),
                attr("id", identifier),
            ],
        );
        let span = NodeRef::new_element(
            el_name("span"),
            [attr("class", String::from("link-icon monospace"))],
        );
        span.append(NodeRef::new_text("#"));
        a.append(span);

        heading.as_node().prepend(a)
    }
}

fn process_custom_elements(doc: &NodeRef, basepath: &Path) {
    RecentlyChangedList::process(doc, basepath);
}

fn trim_pre_whitespace(doc: &NodeRef) {
    for code in doc.select("pre code").unwrap() {
        let mut next_node = code.as_node().first_child();
        while let Some(ref node) = next_node {
            let tmp_next = node.next_sibling();
            if node.as_text().is_some() {
                node.detach();
            }
            next_node = tmp_next;
        }
    }
}

struct RecentlyChangedList;

const RECENTLY_MODIFIED_LIMIT: usize = 5;

impl RecentlyChangedList {
    fn process(doc: &NodeRef, basepath: &Path) {
        let p_qual = QualName::new(None, ns!(html), local_name!("p"));
        for elem in doc.select("recently-changed-list").unwrap() {
            // comrak wraps the element in
 tag so we need to replace it to avoid generating
            // invalid markup
            let parent = elem.as_node().parent();
            let node_to_replace = match parent {
                Some(ref parent) if parent.as_element().map_or(false, |e| e.name == p_qual) => {
                    parent
                }
                Some(_) | None => elem.as_node(),
            };

            let list = NodeRef::new_element(el_name("ul"), []);
            for page in Page::recently_modified(RECENTLY_MODIFIED_LIMIT, basepath) {
                let li = NodeRef::new_element(el_name("li"), []);
                let link = NodeRef::new_element(
                    el_name("a"),
                    [attr(
                        "href",
                        uri!(web::page::show(name = &page.name)).to_string(),
                    )],
                );
                link.append(NodeRef::new_text(page.title()));
                let span = NodeRef::new_element(
                    el_name("span"),
                    [attr("class", String::from("smaller-font lighten"))],
                );
                span.append(NodeRef::new_text(" updated "));
                let abbr =
                    NodeRef::new_element(el_name("abbr"), [attr("title", page.mtime_rfc3339())]);
                abbr.append(NodeRef::new_text(page.mtime_human()));

                span.append(abbr);
                li.append(link);
                li.append(span);
                list.append(li);
            }

            // replace elem with the list
            node_to_replace.insert_after(list);
            node_to_replace.detach();
        }
    }
}

fn attr(name: &str, value: String) -> (ExpandedName, Attribute) {
    (
        ExpandedName::new(Namespace::from(""), LocalName::from(name)),
        Attribute {
            prefix: None,
            value,
        },
    )
}

fn el_name(name: &str) -> QualName {
    QualName::new(None, ns!(html), LocalName::from(name))
}

fn parse_markup(html: &str) -> NodeRef {
    let ctx_name = QualName::new(None, ns!(html), local_name!("div"));
    info!("{}", html);
    parse_fragment(ctx_name.clone(), vec![]).one(html)
}

#[cfg(test)]
mod tests {
    use super::*;
    use regex::Regex;
    use rocket::form::validate::Contains;
    use std::path::PathBuf;

    const HTML: &str = "
Test
<recently-changed-list></recently-changed-list>";

    fn test_settings() -> Settings {
        Settings {
            pages_path: pages_path(),
            author: "Test".to_string(),
            author_url: "https://example.com/".to_string(),
            copyright_start_year: 2020,
            name: "Test Site".to_string(),
            domain: "example.com".to_string(),
            tagline: "For testing".to_string(),
            sentry_dsn: None,
        }
    }

    fn pages_path() -> PathBuf {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.extend(&["tests", "fixtures", "pages"]);
        path
    }

    #[test]
    fn recently_changed_list() {
        // Test that it replaces the custom element with a list of pages
        let doc = parse_markup(HTML);
        RecentlyChangedList::process(&doc, &pages_path());

        let processed = doc.to_string();
        let regex = Regex::new(r#"
(
[^<]+ updated [^<]+
)+
"#).unwrap();
        assert!(regex.is_match(&processed));
    }

    #[test]
    fn recently_changed_list_strips_parent_p_tag() {
        // When rendering the Markdown to HTML comrak wraps the custom element in a
, test that
        // this is stripped.
        let html = "
Recently Updated Pages
\n
<recently-changed-list></recently-changed-list>
\n";
        let doc = parse_markup(html);
        RecentlyChangedList::process(&doc, &pages_path());

        let processed = doc.to_string();
        assert!(!processed.contains("
"));
    }

    #[test]
    fn enhancing_markup_does_not_add_html_tag() {
        let markup = "
no HTML tag please
";
        let enhanced = enhance_markup(markup, &test_settings());
        assert_eq!(markup, enhanced);
    }
}
<commit_msg>Fix whitespace trimming of code blocks<commit_after>use std::path::Path;

use html5ever::{LocalName, Namespace, QualName};
use kuchiki::traits::TendrilSink;
use kuchiki::{parse_fragment, Attribute, ExpandedName, NodeRef};

use crate::page::Page;
use crate::settings::Settings;
use crate::web;

use crate::string_ext::StringExt;

const START_HTML: &str = "";
const END_HTML: &str = "";

// Enhance the HTML
pub fn enhance_markup(html: &str, settings: &Settings) -> String {
    let doc = parse_markup(html);

    link_headings(&doc);
    process_custom_elements(&doc, &settings.pages_path);
    trim_pre_whitespace(&doc);

    let mut enhanced_html = doc.to_string();
    // HACK: The document ends up serialised with a wrapping `<html>` element around the content
    // strip that here. E.g.
    // https://github.com/kuchiki-rs/kuchiki/blob/f652e38b12cb0d33f7bb0565b6933a6e2823a0c5/src/tests.rs#L66
    if enhanced_html.ends_with(END_HTML) {
        enhanced_html.truncate(enhanced_html.len() - END_HTML.len());
    }
    if enhanced_html.starts_with(START_HTML) {
        enhanced_html[START_HTML.len()..].to_string()
    } else {
        enhanced_html
    }
}

fn link_headings(doc: &NodeRef) {
    for heading in doc.select("h1,h2,h3,h4,h5,h6").unwrap() {
        let identifier = heading.text_contents().to_slug();

        let a = NodeRef::new_element(
            el_name("a"),
            [
                attr("class", String::from("anchor")),
                attr("href", format!("#{}", identifier)),
                attr("id", identifier),
            ],
        );
        let span = NodeRef::new_element(
            el_name("span"),
            [attr("class", String::from("link-icon monospace"))],
        );
        span.append(NodeRef::new_text("#"));
        a.append(span);

        heading.as_node().prepend(a)
    }
}

fn process_custom_elements(doc: &NodeRef, basepath: &Path) {
    RecentlyChangedList::process(doc, basepath);
}

fn trim_pre_whitespace(doc: &NodeRef) {
    info!("{}", doc.to_string());
    // Have to collect notes to remove otherwise iteration stop after one pass through the block
    // https://github.com/kuchiki-rs/kuchiki/issues/57
    let mut to_detach = Vec::new();
    for code in doc.select("pre > code").unwrap() {
        let mut next_node = code.as_node().first_child();
        while let Some(node) = next_node {
            let tmp_next = node.next_sibling();
            if node.as_text().is_some() {
                to_detach.push(node);
            }
            next_node = tmp_next;
        }
    }
    to_detach.iter().for_each(|node| node.detach())
}

struct RecentlyChangedList;

const RECENTLY_MODIFIED_LIMIT: usize = 5;

impl RecentlyChangedList {
    fn process(doc: &NodeRef, basepath: &Path) {
        let p_qual = QualName::new(None, ns!(html), local_name!("p"));
        for elem in doc.select("recently-changed-list").unwrap() {
            // comrak wraps the element in
 tag so we need to replace it to avoid generating
            // invalid markup
            let parent = elem.as_node().parent();
            let node_to_replace = match parent {
                Some(ref parent) if parent.as_element().map_or(false, |e| e.name == p_qual) => {
                    parent
                }
                Some(_) | None => elem.as_node(),
            };

            let list = NodeRef::new_element(el_name("ul"), []);
            for page in Page::recently_modified(RECENTLY_MODIFIED_LIMIT, basepath) {
                let li = NodeRef::new_element(el_name("li"), []);
                let link = NodeRef::new_element(
                    el_name("a"),
                    [attr(
                        "href",
                        uri!(web::page::show(name = &page.name)).to_string(),
                    )],
                );
                link.append(NodeRef::new_text(page.title()));
                let span = NodeRef::new_element(
                    el_name("span"),
                    [attr("class", String::from("smaller-font lighten"))],
                );
                span.append(NodeRef::new_text(" updated "));
                let abbr =
                    NodeRef::new_element(el_name("abbr"), [attr("title", page.mtime_rfc3339())]);
                abbr.append(NodeRef::new_text(page.mtime_human()));

                span.append(abbr);
                li.append(link);
                li.append(span);
                list.append(li);
            }

            // replace elem with the list
            node_to_replace.insert_after(list);
            node_to_replace.detach();
        }
    }
}

fn attr(name: &str, value: String) -> (ExpandedName, Attribute) {
    (
        ExpandedName::new(Namespace::from(""), LocalName::from(name)),
        Attribute {
            prefix: None,
            value,
        },
    )
}

fn el_name(name: &str) -> QualName {
    QualName::new(None, ns!(html), LocalName::from(name))
}

fn parse_markup(html: &str) -> NodeRef {
    let ctx_name = QualName::new(None, ns!(html), local_name!("div"));
    info!("{}", html);
    parse_fragment(ctx_name.clone(), vec![]).one(html)
}

#[cfg(test)]
mod tests {
    use super::*;
    use regex::Regex;
    use rocket::form::validate::Contains;
    use std::path::PathBuf;

    const HTML: &str = "
Test
<recently-changed-list></recently-changed-list>";

    fn test_settings() -> Settings {
        Settings {
            pages_path: pages_path(),
            author: "Test".to_string(),
            author_url: "https://example.com/".to_string(),
            copyright_start_year: 2020,
            name: "Test Site".to_string(),
            domain: "example.com".to_string(),
            tagline: "For testing".to_string(),
            sentry_dsn: None,
        }
    }

    fn pages_path() -> PathBuf {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.extend(&["tests", "fixtures", "pages"]);
        path
    }

    #[test]
    fn recently_changed_list() {
        // Test that it replaces the custom element with a list of pages
        let doc = parse_markup(HTML);
        RecentlyChangedList::process(&doc, &pages_path());

        let processed = doc.to_string();
        let regex = Regex::new(r#"
(
[^<]+ updated [^<]+
)+
"#).unwrap();
        assert!(regex.is_match(&processed));
    }

    #[test]
    fn recently_changed_list_strips_parent_p_tag() {
        // When rendering the Markdown to HTML comrak wraps the custom element in a
, test that
        // this is stripped.
        let html = "
Recently Updated Pages
\n
<recently-changed-list></recently-changed-list>
\n";
        let doc = parse_markup(html);
        RecentlyChangedList::process(&doc, &pages_path());

        let processed = doc.to_string();
        assert!(!processed.contains("
"));
    }

    #[test]
    fn enhancing_markup_does_not_add_html_tag() {
        let markup = "
no HTML tag please
";
        let enhanced = enhance_markup(markup, &test_settings());
        assert_eq!(markup, enhanced);
    }
}
<|endoftext|>


<commit_before>use cgmath::Point2;
use game::constants::{TERRAIN_OBJECTS, TILES_PCS_H, TILES_PCS_W};
use game::get_rand_from_range;
use graphics::coords_to_tile_offset;
use pathfinding::{astar::astar, utils::absdiff};
use shaders::Position;

fn tiles(p: &Point2<i32>, impassable_tiles: &[[usize; 2]]) -> Vec<(Point2<i32>, i32)> {
  let e = Point2::new(p.x as i32, p.y as i32);
  let neighbours: Vec<Point2<i32>> = vec![Point2::new(e.x - 1, e.y),
                                          Point2::new(e.x - 1, e.y - 1),
                                          Point2::new(e.x, e.y - 1),
                                          Point2::new(e.x + 1, e.y),
                                          Point2::new(e.x + 1, e.y + 1),
                                          Point2::new(e.x, e.y + 1),
                                          Point2::new(e.x - 1, e.y + 1),
                                          Point2::new(e.x + 1, e.y - 1)
  ];

  neighbours.iter()
            .filter(|ref e| e.x >= 0 && e.x < TILES_PCS_W as i32 && e.y >= 0 && e.y < TILES_PCS_H as i32)
            .filter(|ref e| !impassable_tiles.contains(&[e.x as usize, e.y as usize]))
            .map(|p| (Point2::new(p.x, p.y), 1))
            .collect()
}

fn find_next_best_endpoint(end_point: Point2<i32>) -> Point2<i32> {
  let player_and_its_neighbour_tiles: Vec<[usize; 2]> = vec![Point2::new(end_point.x - 1, end_point.y),
                                                             Point2::new(end_point.x - 1, end_point.y - 1),
                                                             Point2::new(end_point.x, end_point.y - 1),
                                                             Point2::new(end_point.x + 1, end_point.y),
                                                             Point2::new(end_point.x + 1, end_point.y + 1),
                                                             Point2::new(end_point.x, end_point.y + 1),
                                                             Point2::new(end_point.x - 1, end_point.y + 1),
                                                             Point2::new(end_point.x + 1, end_point.y - 1)]
    .iter()
    .map(|p| [p.x as usize, p.y as usize])
    .collect::<Vec<_>>();

  if player_and_its_neighbour_tiles.iter().any(|e| e[0] == end_point.x as usize && e[1] == end_point.y as usize) {
    let neighbours = tiles(&end_point, player_and_its_neighbour_tiles.as_slice());
    neighbours[0].0
  } else {
    end_point
  }
}

pub fn calc_route(start_point: Position, end_point: Position, impassable_tiles: &[[usize; 2]]) -> Option<(Vec<Point2<i32>>, i32)> {
  let start = coords_to_tile_offset(start_point);
  let end = find_next_best_endpoint(coords_to_tile_offset(end_point));

  astar(&start,
        |p: &Point2<i32>| tiles(p, &impassable_tiles),
        |p: &Point2<i32>| absdiff(p.x, end.x) + absdiff(p.y, end.y),
        |p: &Point2<i32>| p.x == end.x && p.y == end.y)
}

pub fn calc_next_movement(start_point: Position, end_point: Position) -> i32 {
  let next_step: Point2<i32> = calc_route(start_point, end_point, &TERRAIN_OBJECTS)
    .map_or_else(|| Point2::new(0 as i32, 0 as i32),
                 |(route, _)| {
                   if route.len() > 1 {
                     Point2::new(route[1].x as i32, route[1].y as i32)
                   } else {
                     Point2::new(route[0].x as i32, route[0].y as i32)
                   }
                 });

  let start = coords_to_tile_offset(start_point);
  let diff: (i32, i32) = (next_step.x - start.x, next_step.y - start.y);

  match diff {
    (1, 0) => 315,
    (1, 1) => 270,
    (0, 1) => 225,
    (-1, 1) => 180,
    (-1, 0) => 135,
    (-1, -1) => 90,
    (0, -1) => 45,
    (1, -1) => 0,
    _ => get_rand_from_range(0, 359),
  }
}
<commit_msg>Extract "neighbours" function<commit_after>use cgmath::Point2;
use game::constants::{TERRAIN_OBJECTS, TILES_PCS_H, TILES_PCS_W};
use game::get_rand_from_range;
use graphics::coords_to_tile_offset;
use pathfinding::{astar::astar, utils::absdiff};
use shaders::Position;

fn neighbours(curr_pos: Point2<i32>) -> Vec<Point2<i32>> {
  vec![Point2::new(curr_pos.x - 1, curr_pos.y),
       Point2::new(curr_pos.x - 1, curr_pos.y - 1),
       Point2::new(curr_pos.x, curr_pos.y - 1),
       Point2::new(curr_pos.x + 1, curr_pos.y),
       Point2::new(curr_pos.x + 1, curr_pos.y + 1),
       Point2::new(curr_pos.x, curr_pos.y + 1),
       Point2::new(curr_pos.x - 1, curr_pos.y + 1),
       Point2::new(curr_pos.x + 1, curr_pos.y - 1)
  ]
}

fn tiles(p: &Point2<i32>, impassable_tiles: &[[usize; 2]]) -> Vec<(Point2<i32>, i32)> {
  neighbours(Point2::new(p.x as i32, p.y as i32)).iter()
                                                 .filter(|ref e| e.x >= 0 && e.x < TILES_PCS_W as i32 && e.y >= 0 && e.y < TILES_PCS_H as i32)
                                                 .filter(|ref e| !impassable_tiles.contains(&[e.x as usize, e.y as usize]))
                                                 .map(|p| (Point2::new(p.x, p.y), 1))
                                                 .collect()
}

fn find_next_best_endpoint(end_point: Point2<i32>) -> Point2<i32> {
  let player_and_its_neighbour_tiles: Vec<[usize; 2]> = neighbours(end_point)
    .iter()
    .map(|p| [p.x as usize, p.y as usize])
    .collect();

  if player_and_its_neighbour_tiles.iter().any(|e| e[0] == end_point.x as usize && e[1] == end_point.y as usize) {
    let neighbours = tiles(&end_point, player_and_its_neighbour_tiles.as_slice());
    neighbours[0].0
  } else {
    end_point
  }
}

pub fn calc_route(start_point: Position, end_point: Position, impassable_tiles: &[[usize; 2]]) -> Option<(Vec<Point2<i32>>, i32)> {
  let start = coords_to_tile_offset(start_point);
  let end = find_next_best_endpoint(coords_to_tile_offset(end_point));

  astar(&start,
        |p: &Point2<i32>| tiles(p, &impassable_tiles),
        |p: &Point2<i32>| absdiff(p.x, end.x) + absdiff(p.y, end.y),
        |p: &Point2<i32>| p.x == end.x && p.y == end.y)
}

pub fn calc_next_movement(start_point: Position, end_point: Position) -> i32 {
  let next_step: Point2<i32> = calc_route(start_point, end_point, &TERRAIN_OBJECTS)
    .map_or_else(|| Point2::new(0 as i32, 0 as i32),
                 |(route, _)| {
                   if route.len() > 1 {
                     Point2::new(route[1].x as i32, route[1].y as i32)
                   } else {
                     Point2::new(route[0].x as i32, route[0].y as i32)
                   }
                 });

  let start = coords_to_tile_offset(start_point);
  let diff: (i32, i32) = (next_step.x - start.x, next_step.y - start.y);

  match diff {
    (1, 0) => 315,
    (1, 1) => 270,
    (0, 1) => 225,
    (-1, 1) => 180,
    (-1, 0) => 135,
    (-1, -1) => 90,
    (0, -1) => 45,
    (1, -1) => 0,
    _ => get_rand_from_range(0, 359),
  }
}
<|endoftext|>


<commit_before>use proc_macro2::{Span, TokenStream};
use syn::{Ident, Type};

pub fn bundle_impl(
    trait_name: &str,
    type_name: Ident,
    internal: bool,
    block: TokenStream,
) -> TokenStream {
    let const_name = Ident::new(
        &format!("_palette_derive_{}_for_{}", trait_name, type_name),
        Span::call_site(),
    );

    if internal {
        quote!{
            #[allow(non_snake_case, unused_attributes, unused_qualifications)]
            mod #const_name {
                use float::Float as _FloatTrait;
                use super::*;
                #block
            }
        }
    } else {
        quote!{
            #[allow(non_snake_case, unused_attributes, unused_qualifications)]
            mod #const_name {
                extern crate palette as _palette;
                use self::_palette::float::Float as _FloatTrait;
                use super::*;
                #block
            }
        }
    }
}

pub fn path(path: &[&str], internal: bool) -> TokenStream {
    let path = path
        .into_iter()
        .map(|&ident| Ident::new(ident, Span::call_site()));

    if internal {
        quote!{::#(#path)::*}
    } else {
        quote!{self::_palette::#(#path)::*}
    }
}

pub fn path_type(path: &[&str], internal: bool) -> Type {
    let path = path
        .into_iter()
        .map(|&ident| Ident::new(ident, Span::call_site()));

    if internal {
        parse_quote!{::#(#path)::*}
    } else {
        parse_quote!{self::_palette::#(#path)::*}
    }
}

pub fn color_path(color: &str, internal: bool) -> TokenStream {
    match color {
        "Luma" => path(&["luma", "Luma"], internal),
        "Rgb" => path(&["rgb", "Rgb"], internal),
        _ => path(&[color], internal),
    }
}
<commit_msg>Re-disable import warnings in derived blocks<commit_after>use proc_macro2::{Span, TokenStream};
use syn::{Ident, Type};

pub fn bundle_impl(
    trait_name: &str,
    type_name: Ident,
    internal: bool,
    block: TokenStream,
) -> TokenStream {
    let const_name = Ident::new(
        &format!("_palette_derive_{}_for_{}", trait_name, type_name),
        Span::call_site(),
    );

    if internal {
        quote!{
            #[allow(non_snake_case, unused_attributes, unused_qualifications, unused_imports)]
            mod #const_name {
                use float::Float as _FloatTrait;
                use super::*;
                #block
            }
        }
    } else {
        quote!{
            #[allow(non_snake_case, unused_attributes, unused_qualifications, unused_imports)]
            mod #const_name {
                extern crate palette as _palette;
                use self::_palette::float::Float as _FloatTrait;
                use super::*;
                #block
            }
        }
    }
}

pub fn path(path: &[&str], internal: bool) -> TokenStream {
    let path = path
        .into_iter()
        .map(|&ident| Ident::new(ident, Span::call_site()));

    if internal {
        quote!{::#(#path)::*}
    } else {
        quote!{self::_palette::#(#path)::*}
    }
}

pub fn path_type(path: &[&str], internal: bool) -> Type {
    let path = path
        .into_iter()
        .map(|&ident| Ident::new(ident, Span::call_site()));

    if internal {
        parse_quote!{::#(#path)::*}
    } else {
        parse_quote!{self::_palette::#(#path)::*}
    }
}

pub fn color_path(color: &str, internal: bool) -> TokenStream {
    match color {
        "Luma" => path(&["luma", "Luma"], internal),
        "Rgb" => path(&["rgb", "Rgb"], internal),
        _ => path(&[color], internal),
    }
}
<|endoftext|>


<commit_before><commit_msg>removed arch folder - obsolete now<commit_after><|endoftext|>


<commit_before>//#![deny(warnings)]
#![feature(io)]

extern crate extra;

use std::env::args;
use std::io::{self, Read, Write};
use std::process::exit;

use extra::option::OptionalExt;
use extra::io::{fail, WriteExt};

static HELP: &'static str = r#"
NAME
    mtxt - a simple tool to manipulate text from standard input.

SYNOPSIS
    mtxt [-h | --help | -u | --to-uppercase-w | -l | --to-lowercase | -a | --strip-non-ascii]

DESCRIPTION
    This utility will manipulate UTF-8 encoded text from the standard input. Unicode is supported.
    The flags are composable, unless otherwise stated.

OPTIONS
    -h
    --help
        Print this manual page.

    -u
    --to-uppercase
        Convert the input to UPPERCASE. Works correctly with Unicode. Incompatible with '-l'.

    -l
    --lowercase
        Convert the input to lowercase. Works correctly with Unicode.

    -a
    --strip-non-ascii
        Strip the input for non-ASCII bytes, outputting a valid ASCII string.

EXAMPLES
    $ echo Δ | mtxt -l
    > δ
    $ echo we got deltas Δ right | mtxt -a
    > we got deltas  right
    $ echo Japanese scripts do not have case thus 山will stay unchanged | mtxt -u
    > JAPANESE SCRIPTS DO NOT HAVE CASE THUS 山WILL STAY UNCHANGED

AUTHOR
    This program was written by Ticki for Redox OS. Bugs, issues, or feature requests should be
    reported in the Github repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 Ticki

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    Do you actually read the license? wat?
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

fn main() {
    let stdin = io::stdin();
    let stdin = stdin.lock();
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();

    // These are the options.
    let mut to_uppercase = false;
    let mut to_lowercase = false;
    let mut strip_non_ascii = false;

    for arg in args().skip(1) {
        match arg.as_str() {
            "-u" | "--to-uppercase" => to_uppercase = true,
            "-l" | "--to-lowercase" => to_lowercase = true,
            "-a" | "--strip-non-ascii" => strip_non_ascii = true,
            "-h" | "--help" => {
                // The help page.
                stdout.write(HELP.as_bytes()).try(&mut stderr);
            }
            a => {
                // The argument, a, is unknown.
                stderr.write(b"error: unknown argument, ").try(&mut stderr);
                stderr.write(a.as_bytes()).try(&mut stderr);
                stderr.write(b".\n").try(&mut stderr);
                stderr.flush().try(&mut stderr);
                exit(1);
            }
        }
    }

    if to_lowercase && to_uppercase {
        // Fail, since -u and -l are incompatible.
        fail("-u and -l are incompatible. Aborting.", &mut stderr);
    }

    // My eyes bleed.
    //
    // Anyone?
    // ...
    //
    // Silence.
    //
    // If you see this, rewrite the code below. Now, you can't say no. Too late.
    // Muhahahaha.
    for i in stdin.chars() {
        let i = i.try(&mut stderr);

        // TODO handle -a more efficient

        // If -u is set, convert to uppercase
        if to_uppercase {
            for uppercase in i.to_uppercase()
                .filter(|x| !strip_non_ascii || x.is_ascii())
            {
                stdout.write_char(uppercase).try(&mut stderr);
            }
        // If -l is set, convert to lowercase
        } else if to_lowercase {
            for lowercase in i.to_lowercase()
                .filter(|x| !strip_non_ascii || x.is_ascii())
            {
                stdout.write_char(lowercase).try(&mut stderr);
            }
        // If -a is set, strip non-ASCII.
        } else if !strip_non_ascii || strip_non_ascii && i.is_ascii() {
            stdout.write_char(i).try(&mut stderr);
        }
    }
}
<commit_msg>add /* @MANSTART{mdless} */<commit_after>//#![deny(warnings)]
#![feature(io)]

extern crate extra;

use std::env::args;
use std::io::{self, Read, Write};
use std::process::exit;

use extra::option::OptionalExt;
use extra::io::{fail, WriteExt};

static HELP: &'static str = /* @MANSTART{mtxt} */ r#"
NAME
    mtxt - a simple tool to manipulate text from standard input.

SYNOPSIS
    mtxt [-h | --help | -u | --to-uppercase-w | -l | --to-lowercase | -a | --strip-non-ascii]

DESCRIPTION
    This utility will manipulate UTF-8 encoded text from the standard input. Unicode is supported.
    The flags are composable, unless otherwise stated.

OPTIONS
    -h
    --help
        Print this manual page.

    -u
    --to-uppercase
        Convert the input to UPPERCASE. Works correctly with Unicode. Incompatible with '-l'.

    -l
    --lowercase
        Convert the input to lowercase. Works correctly with Unicode.

    -a
    --strip-non-ascii
        Strip the input for non-ASCII bytes, outputting a valid ASCII string.

EXAMPLES
    $ echo Δ | mtxt -l
    > δ
    $ echo we got deltas Δ right | mtxt -a
    > we got deltas  right
    $ echo Japanese scripts do not have case thus 山will stay unchanged | mtxt -u
    > JAPANESE SCRIPTS DO NOT HAVE CASE THUS 山WILL STAY UNCHANGED

AUTHOR
    This program was written by Ticki for Redox OS. Bugs, issues, or feature requests should be
    reported in the Github repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 Ticki

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    Do you actually read the license? wat?
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

fn main() {
    let stdin = io::stdin();
    let stdin = stdin.lock();
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();

    // These are the options.
    let mut to_uppercase = false;
    let mut to_lowercase = false;
    let mut strip_non_ascii = false;

    for arg in args().skip(1) {
        match arg.as_str() {
            "-u" | "--to-uppercase" => to_uppercase = true,
            "-l" | "--to-lowercase" => to_lowercase = true,
            "-a" | "--strip-non-ascii" => strip_non_ascii = true,
            "-h" | "--help" => {
                // The help page.
                stdout.write(HELP.as_bytes()).try(&mut stderr);
            }
            a => {
                // The argument, a, is unknown.
                stderr.write(b"error: unknown argument, ").try(&mut stderr);
                stderr.write(a.as_bytes()).try(&mut stderr);
                stderr.write(b".\n").try(&mut stderr);
                stderr.flush().try(&mut stderr);
                exit(1);
            }
        }
    }

    if to_lowercase && to_uppercase {
        // Fail, since -u and -l are incompatible.
        fail("-u and -l are incompatible. Aborting.", &mut stderr);
    }

    // My eyes bleed.
    //
    // Anyone?
    // ...
    //
    // Silence.
    //
    // If you see this, rewrite the code below. Now, you can't say no. Too late.
    // Muhahahaha.
    for i in stdin.chars() {
        let i = i.try(&mut stderr);

        // TODO handle -a more efficient

        // If -u is set, convert to uppercase
        if to_uppercase {
            for uppercase in i.to_uppercase()
                .filter(|x| !strip_non_ascii || x.is_ascii())
            {
                stdout.write_char(uppercase).try(&mut stderr);
            }
        // If -l is set, convert to lowercase
        } else if to_lowercase {
            for lowercase in i.to_lowercase()
                .filter(|x| !strip_non_ascii || x.is_ascii())
            {
                stdout.write_char(lowercase).try(&mut stderr);
            }
        // If -a is set, strip non-ASCII.
        } else if !strip_non_ascii || strip_non_ascii && i.is_ascii() {
            stdout.write_char(i).try(&mut stderr);
        }
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(test, allow(dead_code))]

#![feature(collections)]

extern crate document;

use std::env;
use std::cmp::min;
use std::fs::File;
use std::io::{self,Read,Write};

use document::parser::Parser;

fn pretty_error(xml: &str, position: usize) -> &str {
    let s = &xml[position..];
    let l = s.chars().count();
    s.slice_chars(0, min(l, 15))
}

fn process_input<R>(input: R)
    where R: Read
{
    let mut input = input;
    let mut data = String::new();

    if let Err(x) = input.read_to_string(&mut data) {
        panic!("Can't read: {}", x);
    }

    let p = Parser::new();

    let package = match p.parse(&data) {
        Ok(d) => d,
        Err((point, _)) => panic!("Unable to parse: {}", pretty_error(&data, point)),
    };

    let mut out = io::stdout();
    let d = package.as_document();

    document::writer::format_document(&d, &mut out).ok().expect("I can't output");
    // Remove when we move back to stdout_raw + buffer or when stdout flushed at program exit
    out.flush().unwrap();
}

fn main() {
    let mut args: Vec<_> = env::args().collect();

    let filename = args.remove(1);

    if filename == "-" {
        process_input(io::stdin());
    } else {
        let file = File::open(&filename).unwrap();
        process_input(file);
    }
}
<commit_msg>Remove unstable use of slice_chars<commit_after>#![cfg_attr(test, allow(dead_code))]

extern crate document;

use std::env;
use std::cmp::min;
use std::fs::File;
use std::io::{self,Read,Write};

use document::parser::Parser;

fn pretty_error(xml: &str, position: usize) -> String {
    let s = &xml[position..];
    let l = s.chars().count();
    s.chars().take(min(l, 15)).collect()
}

fn process_input<R>(input: R)
    where R: Read
{
    let mut input = input;
    let mut data = String::new();

    if let Err(x) = input.read_to_string(&mut data) {
        panic!("Can't read: {}", x);
    }

    let p = Parser::new();

    let package = match p.parse(&data) {
        Ok(d) => d,
        Err((point, _)) => panic!("Unable to parse: {}", pretty_error(&data, point)),
    };

    let mut out = io::stdout();
    let d = package.as_document();

    document::writer::format_document(&d, &mut out).ok().expect("I can't output");
    // Remove when we move back to stdout_raw + buffer or when stdout flushed at program exit
    out.flush().unwrap();
}

fn main() {
    let mut args: Vec<_> = env::args().collect();

    let filename = args.remove(1);

    if filename == "-" {
        process_input(io::stdin());
    } else {
        let file = File::open(&filename).unwrap();
        process_input(file);
    }
}
<|endoftext|>


<commit_before>// struct to hold current and next fib nr
struct Fibonacci {
    current: u32,
    next: u32,
}

// sequence generator
fn fibonacci() -> Fibonacci {
    Fibonacci {
        current: 1,
        next: 1,
    }
}

//TODO implement the iterator for Fibonacci

fn main() {
   // sum up even elements of Fibonacci sequence
   // which not exceed 4.000.000
   let (a, b) = (1, 1);

   println!("The sum of all even Fibonacci elements with value below 4.000.000 equals to: {}", 0);
}
<commit_msg>updated problem 2<commit_after>struct Fibonacci {
    current: u32,
    next: u32,
}

impl Iterator for Fibonacci {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        let temp = self.current +self.next;
        self.current = self.next;
        self.next = temp;
        Some(self.current)
    }
}

fn fibonacci() -> Fibonacci {
    Fibonacci {
        current: 1,
        next: 1,
    }
}

fn main() {
    //TODO print sum
    //let sum = fibonacci()
    //    .filter(|f| f % 2 == 0) // even elements
    //    .take_while(|n| n <= &bla) // below 4 mil
    //    .sum();
    println!("The sum of all even Fibonacci elements with value below 4.000.000 equals to: {}", sum);
}
<|endoftext|>


<commit_before>use std::{fmt, cmp};

mod tests;

#[derive(Debug, PartialEq)]
pub struct Card {
    pub rank: Rank,
    pub suit: Suit
}

impl fmt::Display for Card {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.rank, self.suit)
    }
}

impl cmp::Ord for Card {
    fn cmp(&self, other: &Card) -> cmp::Ordering {
        self.rank.cmp(&other.rank)
    }
}

impl cmp::Eq for Card { }

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

impl Card {
    pub fn new(rank: Rank, suit: Suit) -> Card {
        Card {
            rank: rank,
            suit: suit
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum Rank {
    Two     =   2,
    Three   =   3,
    Four    =   4,
    Five    =   5,
    Six     =   6,
    Seven   =   7,
    Eight   =   8,
    Nine    =   9,
    Ten     =   10,
    Jack    =   11,
    Queen   =   12,
    King    =   13,
    Ace     =   14
}

impl fmt::Display for Rank {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match *self {
            Rank::Two      =>  "2",
            Rank::Three    =>  "3",
            Rank::Four     =>  "4",
            Rank::Five     =>  "5",
            Rank::Six      =>  "6",
            Rank::Seven    =>  "7",
            Rank::Eight    =>  "8",
            Rank::Nine     =>  "9",
            Rank::Ten      =>  "T",
            Rank::Jack     =>  "J",
            Rank::Queen    =>  "Q",
            Rank::King     =>  "K",
            Rank::Ace      =>  "A"
        };
        write!(f, "{}", value)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Suit {
    Diamonds,
    Clubs,
    Hearts,
    Spades
}

impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let suit = match *self {
            Suit::Diamonds  =>  "♢",
            Suit::Clubs     =>  "♣",
            Suit::Hearts    =>  "♡",
            Suit::Spades    =>  "♠"
        };
        write!(f, "{}", suit)
    }
}
<commit_msg>Removed explicit enum values for Card Ranks<commit_after>use std::{fmt, cmp};

mod tests;

#[derive(Debug, PartialEq)]
pub struct Card {
    pub rank: Rank,
    pub suit: Suit
}

impl fmt::Display for Card {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.rank, self.suit)
    }
}

impl cmp::Ord for Card {
    fn cmp(&self, other: &Card) -> cmp::Ordering {
        self.rank.cmp(&other.rank)
    }
}

impl cmp::Eq for Card { }

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

impl Card {
    pub fn new(rank: Rank, suit: Suit) -> Card {
        Card {
            rank: rank,
            suit: suit
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum Rank {
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
    Ace
}

impl fmt::Display for Rank {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match *self {
            Rank::Two      =>  "2",
            Rank::Three    =>  "3",
            Rank::Four     =>  "4",
            Rank::Five     =>  "5",
            Rank::Six      =>  "6",
            Rank::Seven    =>  "7",
            Rank::Eight    =>  "8",
            Rank::Nine     =>  "9",
            Rank::Ten      =>  "T",
            Rank::Jack     =>  "J",
            Rank::Queen    =>  "Q",
            Rank::King     =>  "K",
            Rank::Ace      =>  "A"
        };
        write!(f, "{}", value)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Suit {
    Diamonds,
    Clubs,
    Hearts,
    Spades
}

impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let suit = match *self {
            Suit::Diamonds  =>  "♢",
            Suit::Clubs     =>  "♣",
            Suit::Hearts    =>  "♡",
            Suit::Spades    =>  "♠"
        };
        write!(f, "{}", suit)
    }
}
<|endoftext|>


<commit_before>extern crate digitalocean;
#[macro_use] extern crate log;
#[macro_use] extern crate serde_json;
extern crate url;
extern crate url_serde;

mod utils;

use serde_json::Value;

use digitalocean::api::LoadBalancer;
use digitalocean::request::Request;
use digitalocean::method::{Get, List, Update, Create, Delete};

use utils::before;

#[test]
fn list_produces_correct_request() {
    before();

    let correct_url = "https://api.digitalocean.com/v2/load_balancers";

    let req: Request<List, Vec<LoadBalancer>> = LoadBalancer::list();
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, Value::Null);
}

#[test]
fn get_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}", load_balancer_id);

    let req: Request<Get, LoadBalancer> = LoadBalancer::get(load_balancer_id);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, Value::Null);
}

#[test]
fn create_produces_correct_request() {
    before();

    let correct_url = "https://api.digitalocean.com/v2/load_balancers";
    let (name, region, algo) = ("test", "tor1", "least_connections");
    let (e_protocol, e_port, t_protocol, t_port) = ("tcp", 22, "tcp", 22);
    let (e_protocol_2, e_port_2, t_protocol_2, t_port_2) = ("http", 443, "http", 443);

    let req: Request<Create, LoadBalancer> = LoadBalancer::create(name, region)
        .algorithm(algo)
        .forwarding_rule(
            e_protocol.clone(),
            e_port,
            t_protocol.clone(),
            t_port,
            None,
            None)
        .forwarding_rule(
            e_protocol_2.clone(),
            e_port_2,
            t_protocol_2.clone(),
            t_port_2,
            None,
            None);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, json!({
        "name": name,
        "region": region,
        "algorithm": algo,
        "forwarding_rules": [
            {
                "entry_protocol": e_protocol,
                "entry_port": e_port,
                "target_protocol": t_protocol,
                "target_port": t_port,
            },
            {
                "entry_protocol": e_protocol_2,
                "entry_port": e_port_2,
                "target_protocol": t_protocol_2,
                "target_port": t_port_2,
            },
        ],
    }));
}

#[test]
fn update_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}", load_balancer_id);
    let (name, region, tag) = ("test", "tor1", "tag");
    let (e_protocol, e_port, t_protocol, t_port) = ("tcp", 22, "tcp", 22);

    let req: Request<Update, LoadBalancer> = LoadBalancer::update(load_balancer_id)
        .name(name)
        .region(region)
        .tag(tag)
        .forwarding_rule(
            e_protocol.clone(),
            e_port,
            t_protocol.clone(),
            t_port,
            None,
            None);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, json!({
        "name": name,
        "region": region,
        "tag": tag,
        "forwarding_rules": [
            {
                "entry_protocol": e_protocol,
                "entry_port": e_port,
                "target_protocol": t_protocol,
                "target_port": t_port,
            },
        ],
    }));
}

#[test]
fn delete_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}", load_balancer_id);

    let req: Request<Delete, ()> = LoadBalancer::delete(load_balancer_id);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, Value::Null);
}
<commit_msg>More load balancer tests<commit_after>extern crate digitalocean;
#[macro_use] extern crate log;
#[macro_use] extern crate serde_json;
extern crate url;
extern crate url_serde;

mod utils;

use serde_json::Value;

use digitalocean::api::LoadBalancer;
use digitalocean::request::Request;
use digitalocean::method::{Get, List, Update, Create, Delete};

use utils::before;

#[test]
fn list_produces_correct_request() {
    before();

    let correct_url = "https://api.digitalocean.com/v2/load_balancers";

    let req: Request<List, Vec<LoadBalancer>> = LoadBalancer::list();
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, Value::Null);
}

#[test]
fn get_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}", load_balancer_id);

    let req: Request<Get, LoadBalancer> = LoadBalancer::get(load_balancer_id);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, Value::Null);
}

#[test]
fn create_produces_correct_request() {
    before();

    let correct_url = "https://api.digitalocean.com/v2/load_balancers";
    let (name, region, algo) = ("test", "tor1", "least_connections");
    let (e_protocol, e_port, t_protocol, t_port) = ("tcp", 22, "tcp", 22);
    let (e_protocol_2, e_port_2, t_protocol_2, t_port_2) = ("http", 443, "http", 443);

    let req: Request<Create, LoadBalancer> = LoadBalancer::create(name, region)
        .algorithm(algo)
        .forwarding_rule(
            e_protocol.clone(),
            e_port,
            t_protocol.clone(),
            t_port,
            None,
            None)
        .forwarding_rule(
            e_protocol_2.clone(),
            e_port_2,
            t_protocol_2.clone(),
            t_port_2,
            None,
            None);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, json!({
        "name": name,
        "region": region,
        "algorithm": algo,
        "forwarding_rules": [
            {
                "entry_protocol": e_protocol,
                "entry_port": e_port,
                "target_protocol": t_protocol,
                "target_port": t_port,
            },
            {
                "entry_protocol": e_protocol_2,
                "entry_port": e_port_2,
                "target_protocol": t_protocol_2,
                "target_port": t_port_2,
            },
        ],
    }));
}

#[test]
fn update_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}", load_balancer_id);
    let (name, region, tag) = ("test", "tor1", "tag");
    let (e_protocol, e_port, t_protocol, t_port) = ("tcp", 22, "tcp", 22);

    let req: Request<Update, LoadBalancer> = LoadBalancer::update(load_balancer_id)
        .name(name)
        .region(region)
        .tag(tag)
        .forwarding_rule(
            e_protocol.clone(),
            e_port,
            t_protocol.clone(),
            t_port,
            None,
            None);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, json!({
        "name": name,
        "region": region,
        "tag": tag,
        "forwarding_rules": [
            {
                "entry_protocol": e_protocol,
                "entry_port": e_port,
                "target_protocol": t_protocol,
                "target_port": t_port,
            },
        ],
    }));
}

#[test]
fn delete_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}", load_balancer_id);

    let req: Request<Delete, ()> = LoadBalancer::delete(load_balancer_id);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, Value::Null);
}

#[test]
fn add_droplets_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}/droplets", load_balancer_id);
    let droplet_ids = vec![123, 456, 789];

    let req: Request<Create, ()> = LoadBalancer::get(load_balancer_id)
        .add_droplets(droplet_ids.clone());
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, json!({
        "droplet_ids": droplet_ids,
    }));
}

#[test]
fn remove_droplets_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}/droplets", load_balancer_id);
    let droplet_ids = vec![123, 456, 789];

    let req: Request<Delete, ()> = LoadBalancer::get(load_balancer_id)
        .remove_droplets(droplet_ids.clone());
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, json!({
        "droplet_ids": droplet_ids,
    }));
}

#[test]
fn add_forwarding_rule_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}/forwarding_rules", load_balancer_id);
    let (e_protocol, e_port, t_protocol, t_port) = ("tcp", 22, "tcp", 22);

    let req: Request<Create, ()> = LoadBalancer::get(load_balancer_id)
        .add_forwarding_rule(e_protocol, e_port, t_protocol, t_port, None, None);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, json!({
        "forwarding_rules": [
            {
                "entry_protocol": e_protocol,
                "entry_port": e_port,
                "target_protocol": t_protocol,
                "target_port": t_port,
            },
        ],
    }));
}

#[test]
fn remove_forwarding_rule_produces_correct_request() {
    before();

    let load_balancer_id = "123";
    let correct_url = format!("https://api.digitalocean.com/v2/load_balancers/{}/forwarding_rules", load_balancer_id);
    let (e_protocol, e_port, t_protocol, t_port) = ("tcp", 22, "tcp", 22);

    let req: Request<Delete, ()> = LoadBalancer::get(load_balancer_id)
        .remove_forwarding_rule(e_protocol, e_port, t_protocol, t_port, None, None);
    info!("{:#?}", req);

    assert_eq!(req.url.as_str(), correct_url);
    assert_eq!(req.body, json!({
        "forwarding_rules": [
            {
                "entry_protocol": e_protocol,
                "entry_port": e_port,
                "target_protocol": t_protocol,
                "target_port": t_port,
            },
        ],
    }));
}<|endoftext|>


<commit_before>use location::Location;
use world;

#[derive(RustcEncodable, RustcDecodable)]
pub struct Flags {
    pub seed: u32,
    pub camera: Location,
    pub tick: u64,
    pub player_acted: bool,
}

impl Flags {
    pub fn new(seed: u32) -> Flags {
        Flags {
            seed: seed,
            camera: Location::new(0, 0, 0),
            tick: 0,
            player_acted: false,
        }
    }
}

/// Get the location where the current view should be centered.
pub fn camera() -> Location {
    world::with(|w| w.flags.camera)
}

/// Move the current view location.
pub fn set_camera(loc: Location) {
    world::with_mut(|w| w.flags.camera = loc);
}

/// Return the frame count since the start of the game.
pub fn get_tick() -> u64 {
    world::with(|w| w.flags.tick)
}
<commit_msg>Derive Copy for Flags<commit_after>use location::Location;
use world;

#[derive(Copy, RustcEncodable, RustcDecodable)]
pub struct Flags {
    pub seed: u32,
    pub camera: Location,
    pub tick: u64,
    pub player_acted: bool,
}

impl Flags {
    pub fn new(seed: u32) -> Flags {
        Flags {
            seed: seed,
            camera: Location::new(0, 0, 0),
            tick: 0,
            player_acted: false,
        }
    }
}

/// Get the location where the current view should be centered.
pub fn camera() -> Location {
    world::with(|w| w.flags.camera)
}

/// Move the current view location.
pub fn set_camera(loc: Location) {
    world::with_mut(|w| w.flags.camera = loc);
}

/// Return the frame count since the start of the game.
pub fn get_tick() -> u64 {
    world::with(|w| w.flags.tick)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017-2020 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

//! Core primitive types for relm.
//!
//! The primary type is `EventStream`.

#![warn(
    missing_docs,
    trivial_casts,
    trivial_numeric_casts,
    unused_extern_crates,
    unused_import_braces,
    unused_qualifications,
)]

mod source;

use std::cell::RefCell;
use std::collections::VecDeque;
use std::marker::PhantomData;
use std::rc::{Rc, Weak};
use std::sync::mpsc::{self, Receiver, SendError};

use self::source::{SourceFuncs, new_source, source_get};

use glib::{
    MainContext,
    Source,
    SourceId,
};

/// Handle to a EventStream to emit messages.
pub struct StreamHandle<MSG> {
    stream: Weak<RefCell<_EventStream<MSG>>>,
}

impl<MSG> Clone for StreamHandle<MSG> {
    fn clone(&self) -> Self {
        Self {
            stream: self.stream.clone(),
        }
    }
}

impl<MSG> StreamHandle<MSG> {
    fn new(stream: Weak<RefCell<_EventStream<MSG>>>) -> Self {
        Self {
            stream,
        }
    }

    /// Same as clone(). Useful for the macro relm_observer_new.
    pub fn stream(&self) -> Self {
        self.clone()
    }

    /// Send the `event` message to the stream and the observers.
    pub fn emit(&self, msg: MSG) {
        if let Some(ref stream) = self.stream.upgrade() {
            emit(stream, msg);
        }
        else {
            panic!("Trying to call emit() on a dropped EventStream");
        }
    }

    /// Lock the stream (don't emit message) until the `Lock` goes out of scope.
    pub fn lock(&self) -> Lock<MSG> {
        if let Some(ref stream) = self.stream.upgrade() {
            stream.borrow_mut().locked = true;
            Lock {
                stream: self.clone(),
            }
        }
        else {
            panic!("Trying to call lock() on a dropped EventStream");
        }
    }

    fn unlock(&self) {
        if let Some(ref stream) = self.stream.upgrade() {
            stream.borrow_mut().locked = false;
        }
        else {
            panic!("Trying to call unlock() on a dropped EventStream");
        }
    }

    /// Add an observer to the event stream.
    /// This callback will be called every time a message is emmited.
    pub fn observe<CALLBACK: Fn(&MSG) + 'static>(&self, callback: CALLBACK) {
        if let Some(ref stream) = self.stream.upgrade() {
            stream.borrow_mut().observers.push(Rc::new(callback));
        }
        else {
            panic!("Trying to call observe() on a dropped EventStream");
        }
    }
}

/// A lock is used to temporarily stop emitting messages.
#[must_use]
pub struct Lock<MSG> {
    stream: StreamHandle<MSG>,
}

impl<MSG> Drop for Lock<MSG> {
    fn drop(&mut self) {
        self.stream.unlock();
    }
}

struct ChannelData<MSG> {
    callback: Box<dyn FnMut(MSG)>,
    peeked_value: Option<MSG>,
    receiver: Receiver<MSG>,
}

/// A wrapper over a `std::sync::mpsc::Sender` to wakeup the glib event loop when sending a
/// message.
pub struct Sender<MSG> {
    sender: mpsc::Sender<MSG>,
}

impl<MSG> Clone for Sender<MSG> {
    fn clone(&self) -> Self {
        Self {
            sender: self.sender.clone(),
        }
    }
}

impl<MSG> Sender<MSG> {
    /// Send a message and wakeup the event loop.
    pub fn send(&self, msg: MSG) -> Result<(), SendError<MSG>> {
        let result = self.sender.send(msg);
        let context = MainContext::default();
        context.wakeup();
        result
    }
}

/// A channel to send a message to a relm widget from another thread.
pub struct Channel<MSG> {
    _source: Source,
    _phantom: PhantomData<MSG>,
}

impl<MSG> Channel<MSG> {
    /// Create a new channel with a callback that will be called when a message is received.
    pub fn new<CALLBACK: FnMut(MSG) + 'static>(callback: CALLBACK) -> (Self, Sender<MSG>) {
        let (sender, receiver) = mpsc::channel();
        let source = new_source(RefCell::new(ChannelData {
            callback: Box::new(callback),
            peeked_value: None,
            receiver,
        }));
        let main_context = MainContext::default();
        source.attach(Some(&main_context));
        (Self {
            _source: source,
            _phantom: PhantomData,
        }, Sender {
            sender,
        })
    }
}

impl<MSG> SourceFuncs for RefCell<ChannelData<MSG>> {
    fn dispatch(&self) -> bool {
        // TODO: show errors.
        let msg = self.borrow_mut().peeked_value.take().or_else(|| {
            self.borrow().receiver.try_recv().ok()
        });
        if let Some(msg) = msg {
            let callback = &mut self.borrow_mut().callback;
            callback(msg);
        }
        true
    }

    fn prepare(&self) -> (bool, Option<u32>) {
        if self.borrow().peeked_value.is_some() {
            return (true, None);
        }
        let peek_val = self.borrow().receiver.try_recv().ok();
        self.borrow_mut().peeked_value = peek_val;
        (self.borrow().peeked_value.is_some(), None)
    }

}

struct _EventStream<MSG> {
    events: VecDeque<MSG>,
    locked: bool,
    // We use an Rc here to be able to clone the function to call it so that we don't borrow the
    // stream while calling the function. Otherwise, calling an observer could trigger a
    // borrow_mut() which would result in a panic.
    observers: Vec<Rc<dyn Fn(&MSG)>>,
}

impl<MSG> SourceFuncs for SourceData<MSG> {
    fn dispatch(&self) -> bool {
        let event = self.stream.borrow_mut().events.pop_front();
        if let (Some(event), Some(callback)) = (event, self.callback.borrow_mut().as_mut()) {
            callback(event);
        }
        true
    }

    fn prepare(&self) -> (bool, Option<u32>) {
        (!self.stream.borrow().events.is_empty(), None)
    }

}

type Callback<MSG> = Rc<RefCell<Option<Box<dyn FnMut(MSG)>>>>;

struct SourceData<MSG> {
    callback: Callback<MSG>,
    stream: Rc<RefCell<_EventStream<MSG>>>,
}

fn emit<MSG>(stream: &Rc<RefCell<_EventStream<MSG>>>, msg: MSG) {
    if !stream.borrow().locked {
        let len = stream.borrow().observers.len();
        for i in 0..len {
            let observer = stream.borrow().observers[i].clone();
            observer(&msg);
        }

        stream.borrow_mut().events.push_back(msg);
    }
}

/// A stream of messages to be used for widget/signal communication and inter-widget communication.
/// EventStream cannot be send to another thread. Use a `Channel` `Sender` instead.
pub struct EventStream<MSG> {
    source: Source,
    source_id: Option<SourceId>,
    _phantom: PhantomData<*mut MSG>,
}

impl<MSG> Drop for EventStream<MSG> {
    fn drop(&mut self) {
        // Ignore error since we're in a destructor.
        let _ = self.source_id.take().expect("source id").remove();
        self.close();
    }
}

impl<MSG> EventStream<MSG> {
    fn get_callback(&self) -> Callback<MSG> {
        source_get::<SourceData<MSG>>(&self.source).callback.clone()
    }

    fn get_stream(&self) -> &Rc<RefCell<_EventStream<MSG>>> {
        &source_get::<SourceData<MSG>>(&self.source).stream
    }
}

impl<MSG> EventStream<MSG> {
    /// Create a new event stream.
    pub fn new() -> Self {
        let event_stream: _EventStream<MSG> = _EventStream {
            events: VecDeque::new(),
            locked: false,
            observers: vec![],
        };
        let source = new_source(SourceData {
            callback: Rc::new(RefCell::new(None)),
            stream: Rc::new(RefCell::new(event_stream)),
        });
        let main_context = MainContext::default();
        let source_id = Some(source.attach(Some(&main_context)));
        EventStream {
            source,
            source_id,
            _phantom: PhantomData,
        }
    }

    /// Close the event stream, i.e. stop processing messages.
    pub fn close(&self) {
        self.source.destroy();
    }

    /// Synonym for downgrade().
    pub fn stream(&self) -> StreamHandle<MSG> {
        self.downgrade()
    }

    /// Create a Clone-able EventStream handle.
    pub fn downgrade(&self) -> StreamHandle<MSG> {
        StreamHandle::new(Rc::downgrade(self.get_stream()))
    }

    /// Send the `event` message to the stream and the observers.
    pub fn emit(&self, event: MSG) {
        let stream = self.get_stream();
        emit(stream, event)
    }

    /// Lock the stream (don't emit message) until the `Lock` goes out of scope.
    pub fn lock(&self) -> Lock<MSG> {
        let stream = self.get_stream();
        stream.borrow_mut().locked = true;
        Lock {
            stream: self.stream(),
        }
    }

    /// Add an observer to the event stream.
    /// This callback will be called every time a message is emmited.
    pub fn observe<CALLBACK: Fn(&MSG) + 'static>(&self, callback: CALLBACK) {
        let stream = self.get_stream();
        stream.borrow_mut().observers.push(Rc::new(callback));
    }

    /// Add a callback to the event stream.
    /// This is the main callback and received a owned version of the message, in contrast to
    /// observe().
    pub fn set_callback<CALLBACK: FnMut(MSG) + 'static>(&self, callback: CALLBACK) {
        let source_callback = self.get_callback();
        *source_callback.borrow_mut() = Some(Box::new(callback));
    }
}
<commit_msg>add must_use suggested by clippy<commit_after>/*
 * Copyright (c) 2017-2020 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

//! Core primitive types for relm.
//!
//! The primary type is `EventStream`.

#![warn(
    missing_docs,
    trivial_casts,
    trivial_numeric_casts,
    unused_extern_crates,
    unused_import_braces,
    unused_qualifications,
)]

mod source;

use std::cell::RefCell;
use std::collections::VecDeque;
use std::marker::PhantomData;
use std::rc::{Rc, Weak};
use std::sync::mpsc::{self, Receiver, SendError};

use self::source::{SourceFuncs, new_source, source_get};

use glib::{
    MainContext,
    Source,
    SourceId,
};

/// Handle to a EventStream to emit messages.
pub struct StreamHandle<MSG> {
    stream: Weak<RefCell<_EventStream<MSG>>>,
}

impl<MSG> Clone for StreamHandle<MSG> {
    fn clone(&self) -> Self {
        Self {
            stream: self.stream.clone(),
        }
    }
}

impl<MSG> StreamHandle<MSG> {
    fn new(stream: Weak<RefCell<_EventStream<MSG>>>) -> Self {
        Self {
            stream,
        }
    }

    /// Same as clone(). Useful for the macro relm_observer_new.
    #[must_use]
    pub fn stream(&self) -> Self {
        self.clone()
    }

    /// Send the `event` message to the stream and the observers.
    pub fn emit(&self, msg: MSG) {
        if let Some(ref stream) = self.stream.upgrade() {
            emit(stream, msg);
        }
        else {
            panic!("Trying to call emit() on a dropped EventStream");
        }
    }

    /// Lock the stream (don't emit message) until the `Lock` goes out of scope.
    pub fn lock(&self) -> Lock<MSG> {
        if let Some(ref stream) = self.stream.upgrade() {
            stream.borrow_mut().locked = true;
            Lock {
                stream: self.clone(),
            }
        }
        else {
            panic!("Trying to call lock() on a dropped EventStream");
        }
    }

    fn unlock(&self) {
        if let Some(ref stream) = self.stream.upgrade() {
            stream.borrow_mut().locked = false;
        }
        else {
            panic!("Trying to call unlock() on a dropped EventStream");
        }
    }

    /// Add an observer to the event stream.
    /// This callback will be called every time a message is emmited.
    pub fn observe<CALLBACK: Fn(&MSG) + 'static>(&self, callback: CALLBACK) {
        if let Some(ref stream) = self.stream.upgrade() {
            stream.borrow_mut().observers.push(Rc::new(callback));
        }
        else {
            panic!("Trying to call observe() on a dropped EventStream");
        }
    }
}

/// A lock is used to temporarily stop emitting messages.
#[must_use]
pub struct Lock<MSG> {
    stream: StreamHandle<MSG>,
}

impl<MSG> Drop for Lock<MSG> {
    fn drop(&mut self) {
        self.stream.unlock();
    }
}

struct ChannelData<MSG> {
    callback: Box<dyn FnMut(MSG)>,
    peeked_value: Option<MSG>,
    receiver: Receiver<MSG>,
}

/// A wrapper over a `std::sync::mpsc::Sender` to wakeup the glib event loop when sending a
/// message.
pub struct Sender<MSG> {
    sender: mpsc::Sender<MSG>,
}

impl<MSG> Clone for Sender<MSG> {
    fn clone(&self) -> Self {
        Self {
            sender: self.sender.clone(),
        }
    }
}

impl<MSG> Sender<MSG> {
    /// Send a message and wakeup the event loop.
    pub fn send(&self, msg: MSG) -> Result<(), SendError<MSG>> {
        let result = self.sender.send(msg);
        let context = MainContext::default();
        context.wakeup();
        result
    }
}

/// A channel to send a message to a relm widget from another thread.
pub struct Channel<MSG> {
    _source: Source,
    _phantom: PhantomData<MSG>,
}

impl<MSG> Channel<MSG> {
    /// Create a new channel with a callback that will be called when a message is received.
    pub fn new<CALLBACK: FnMut(MSG) + 'static>(callback: CALLBACK) -> (Self, Sender<MSG>) {
        let (sender, receiver) = mpsc::channel();
        let source = new_source(RefCell::new(ChannelData {
            callback: Box::new(callback),
            peeked_value: None,
            receiver,
        }));
        let main_context = MainContext::default();
        source.attach(Some(&main_context));
        (Self {
            _source: source,
            _phantom: PhantomData,
        }, Sender {
            sender,
        })
    }
}

impl<MSG> SourceFuncs for RefCell<ChannelData<MSG>> {
    fn dispatch(&self) -> bool {
        // TODO: show errors.
        let msg = self.borrow_mut().peeked_value.take().or_else(|| {
            self.borrow().receiver.try_recv().ok()
        });
        if let Some(msg) = msg {
            let callback = &mut self.borrow_mut().callback;
            callback(msg);
        }
        true
    }

    fn prepare(&self) -> (bool, Option<u32>) {
        if self.borrow().peeked_value.is_some() {
            return (true, None);
        }
        let peek_val = self.borrow().receiver.try_recv().ok();
        self.borrow_mut().peeked_value = peek_val;
        (self.borrow().peeked_value.is_some(), None)
    }

}

struct _EventStream<MSG> {
    events: VecDeque<MSG>,
    locked: bool,
    // We use an Rc here to be able to clone the function to call it so that we don't borrow the
    // stream while calling the function. Otherwise, calling an observer could trigger a
    // borrow_mut() which would result in a panic.
    observers: Vec<Rc<dyn Fn(&MSG)>>,
}

impl<MSG> SourceFuncs for SourceData<MSG> {
    fn dispatch(&self) -> bool {
        let event = self.stream.borrow_mut().events.pop_front();
        if let (Some(event), Some(callback)) = (event, self.callback.borrow_mut().as_mut()) {
            callback(event);
        }
        true
    }

    fn prepare(&self) -> (bool, Option<u32>) {
        (!self.stream.borrow().events.is_empty(), None)
    }

}

type Callback<MSG> = Rc<RefCell<Option<Box<dyn FnMut(MSG)>>>>;

struct SourceData<MSG> {
    callback: Callback<MSG>,
    stream: Rc<RefCell<_EventStream<MSG>>>,
}

fn emit<MSG>(stream: &Rc<RefCell<_EventStream<MSG>>>, msg: MSG) {
    if !stream.borrow().locked {
        let len = stream.borrow().observers.len();
        for i in 0..len {
            let observer = stream.borrow().observers[i].clone();
            observer(&msg);
        }

        stream.borrow_mut().events.push_back(msg);
    }
}

/// A stream of messages to be used for widget/signal communication and inter-widget communication.
/// EventStream cannot be send to another thread. Use a `Channel` `Sender` instead.
pub struct EventStream<MSG> {
    source: Source,
    source_id: Option<SourceId>,
    _phantom: PhantomData<*mut MSG>,
}

impl<MSG> Drop for EventStream<MSG> {
    fn drop(&mut self) {
        // Ignore error since we're in a destructor.
        let _ = self.source_id.take().expect("source id").remove();
        self.close();
    }
}

impl<MSG> EventStream<MSG> {
    fn get_callback(&self) -> Callback<MSG> {
        source_get::<SourceData<MSG>>(&self.source).callback.clone()
    }

    fn get_stream(&self) -> &Rc<RefCell<_EventStream<MSG>>> {
        &source_get::<SourceData<MSG>>(&self.source).stream
    }
}

impl<MSG> EventStream<MSG> {
    /// Create a new event stream.
    pub fn new() -> Self {
        let event_stream: _EventStream<MSG> = _EventStream {
            events: VecDeque::new(),
            locked: false,
            observers: vec![],
        };
        let source = new_source(SourceData {
            callback: Rc::new(RefCell::new(None)),
            stream: Rc::new(RefCell::new(event_stream)),
        });
        let main_context = MainContext::default();
        let source_id = Some(source.attach(Some(&main_context)));
        EventStream {
            source,
            source_id,
            _phantom: PhantomData,
        }
    }

    /// Close the event stream, i.e. stop processing messages.
    pub fn close(&self) {
        self.source.destroy();
    }

    /// Synonym for downgrade().
    pub fn stream(&self) -> StreamHandle<MSG> {
        self.downgrade()
    }

    /// Create a Clone-able EventStream handle.
    pub fn downgrade(&self) -> StreamHandle<MSG> {
        StreamHandle::new(Rc::downgrade(self.get_stream()))
    }

    /// Send the `event` message to the stream and the observers.
    pub fn emit(&self, event: MSG) {
        let stream = self.get_stream();
        emit(stream, event)
    }

    /// Lock the stream (don't emit message) until the `Lock` goes out of scope.
    pub fn lock(&self) -> Lock<MSG> {
        let stream = self.get_stream();
        stream.borrow_mut().locked = true;
        Lock {
            stream: self.stream(),
        }
    }

    /// Add an observer to the event stream.
    /// This callback will be called every time a message is emmited.
    pub fn observe<CALLBACK: Fn(&MSG) + 'static>(&self, callback: CALLBACK) {
        let stream = self.get_stream();
        stream.borrow_mut().observers.push(Rc::new(callback));
    }

    /// Add a callback to the event stream.
    /// This is the main callback and received a owned version of the message, in contrast to
    /// observe().
    pub fn set_callback<CALLBACK: FnMut(MSG) + 'static>(&self, callback: CALLBACK) {
        let source_callback = self.get_callback();
        *source_callback.borrow_mut() = Some(Box::new(callback));
    }
}
<|endoftext|>


<commit_before>extern crate handlebars;
#[macro_use]
extern crate serde_json;

use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::Arc;

use handlebars::{Context, Handlebars, Helper, HelperDef, RenderContext, RenderError, ScopedJson};

#[test]
fn test_subexpression() {
    let hbs = Handlebars::new();

    let data = json!({"a": 1, "b": 0, "c": 2});

    assert_eq!(
        hbs.render_template("{{#if (gt a b)}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Success"
    );

    assert_eq!(
        hbs.render_template("{{#if (gt a c)}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Failed"
    );

    assert_eq!(
        hbs.render_template("{{#if (not (gt a c))}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Success"
    );

    assert_eq!(
        hbs.render_template("{{#if (not (gt a b))}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Failed"
    );

    // no argument provided for not
    assert!(hbs
        .render_template("{{#if (not)}}Success{{else}}Failed{{/if}}", &data)
        .is_err());

    // json literal
    assert_eq!(
        hbs.render_template("{{#if (not true)}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Failed"
    );
    assert_eq!(
        hbs.render_template("{{#if (not false)}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Success"
    );
}

#[test]
fn test_strict_mode() {
    let mut hbs = Handlebars::new();
    hbs.set_strict_mode(true);

    let data = json!({"a": 1});

    assert!(hbs.render_template("{{#if (eq a 1)}}Success{{else}}Failed{{/if}}", &data).is_ok());
    assert!(hbs.render_template("{{#if (eq z 1)}}Success{{else}}Failed{{/if}}", &data).is_err())
}

#[test]
fn invalid_json_path() {
    // The data here is not important
    let data = &Vec::<()>::new();

    let hbs = Handlebars::new();

    let error = hbs.render_template("{{x[]@this}}", &data).unwrap_err();

    let expected = "Error rendering \"Unnamed template\" line 1, col 1: Helper not defined: \"x\"";

    assert_eq!(format!("{}", error), expected);
}

struct MyHelper;

impl HelperDef for MyHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        _: &Helper<'reg, 'rc>,
        _: &'reg Handlebars,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        Ok(ScopedJson::Derived(json!({
            "a": 1,
            "b": 2,
        })))
    }
}

#[test]
fn test_lookup_with_subexpression() {
    let mut registry = Handlebars::new();
    registry.register_helper("myhelper", Box::new(MyHelper {}));
    registry
        .register_template_string("t", "{{ lookup (myhelper) \"a\" }}")
        .unwrap();

    let result = registry.render("t", &json!({})).unwrap();

    assert_eq!("1", result);
}

struct CallCounterHelper {
    pub(crate) c: Arc<AtomicU16>,
}

impl HelperDef for CallCounterHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        _: &'reg Handlebars,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        // inc counter
        self.c.fetch_add(1, Ordering::SeqCst);

        if let Some(_) = h.param(0) {
            Ok(json!({
                "a": 1,
            })
            .into())
        } else {
            Ok(json!(null).into())
        }
    }
}

#[test]
fn test_helper_call_count() {
    let mut registry = Handlebars::new();

    let counter = Arc::new(AtomicU16::new(0));
    let helper = Box::new(CallCounterHelper { c: counter.clone() });

    registry.register_helper("myhelper", helper);

    registry
        .render_template(
            "{{#if (myhelper a)}}something{{else}}nothing{{/if}}",
            &json!(null),
        ) // If returns true
        .unwrap();

    assert_eq!(1, counter.load(Ordering::SeqCst));

    registry
        .render_template(
            "{{#if (myhelper)}}something{{else}}nothing{{/if}}",
            &json!(null),
        ) // If returns false
        .unwrap();

    assert_eq!(2, counter.load(Ordering::SeqCst));
}
<commit_msg>(style) format code<commit_after>extern crate handlebars;
#[macro_use]
extern crate serde_json;

use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::Arc;

use handlebars::{Context, Handlebars, Helper, HelperDef, RenderContext, RenderError, ScopedJson};

#[test]
fn test_subexpression() {
    let hbs = Handlebars::new();

    let data = json!({"a": 1, "b": 0, "c": 2});

    assert_eq!(
        hbs.render_template("{{#if (gt a b)}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Success"
    );

    assert_eq!(
        hbs.render_template("{{#if (gt a c)}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Failed"
    );

    assert_eq!(
        hbs.render_template("{{#if (not (gt a c))}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Success"
    );

    assert_eq!(
        hbs.render_template("{{#if (not (gt a b))}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Failed"
    );

    // no argument provided for not
    assert!(hbs
        .render_template("{{#if (not)}}Success{{else}}Failed{{/if}}", &data)
        .is_err());

    // json literal
    assert_eq!(
        hbs.render_template("{{#if (not true)}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Failed"
    );
    assert_eq!(
        hbs.render_template("{{#if (not false)}}Success{{else}}Failed{{/if}}", &data)
            .unwrap(),
        "Success"
    );
}

#[test]
fn test_strict_mode() {
    let mut hbs = Handlebars::new();
    hbs.set_strict_mode(true);

    let data = json!({"a": 1});

    assert!(hbs
        .render_template("{{#if (eq a 1)}}Success{{else}}Failed{{/if}}", &data)
        .is_ok());
    assert!(hbs
        .render_template("{{#if (eq z 1)}}Success{{else}}Failed{{/if}}", &data)
        .is_err())
}

#[test]
fn invalid_json_path() {
    // The data here is not important
    let data = &Vec::<()>::new();

    let hbs = Handlebars::new();

    let error = hbs.render_template("{{x[]@this}}", &data).unwrap_err();

    let expected = "Error rendering \"Unnamed template\" line 1, col 1: Helper not defined: \"x\"";

    assert_eq!(format!("{}", error), expected);
}

struct MyHelper;

impl HelperDef for MyHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        _: &Helper<'reg, 'rc>,
        _: &'reg Handlebars,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        Ok(ScopedJson::Derived(json!({
            "a": 1,
            "b": 2,
        })))
    }
}

#[test]
fn test_lookup_with_subexpression() {
    let mut registry = Handlebars::new();
    registry.register_helper("myhelper", Box::new(MyHelper {}));
    registry
        .register_template_string("t", "{{ lookup (myhelper) \"a\" }}")
        .unwrap();

    let result = registry.render("t", &json!({})).unwrap();

    assert_eq!("1", result);
}

struct CallCounterHelper {
    pub(crate) c: Arc<AtomicU16>,
}

impl HelperDef for CallCounterHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        _: &'reg Handlebars,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        // inc counter
        self.c.fetch_add(1, Ordering::SeqCst);

        if let Some(_) = h.param(0) {
            Ok(json!({
                "a": 1,
            })
            .into())
        } else {
            Ok(json!(null).into())
        }
    }
}

#[test]
fn test_helper_call_count() {
    let mut registry = Handlebars::new();

    let counter = Arc::new(AtomicU16::new(0));
    let helper = Box::new(CallCounterHelper { c: counter.clone() });

    registry.register_helper("myhelper", helper);

    registry
        .render_template(
            "{{#if (myhelper a)}}something{{else}}nothing{{/if}}",
            &json!(null),
        ) // If returns true
        .unwrap();

    assert_eq!(1, counter.load(Ordering::SeqCst));

    registry
        .render_template(
            "{{#if (myhelper)}}something{{else}}nothing{{/if}}",
            &json!(null),
        ) // If returns false
        .unwrap();

    assert_eq!(2, counter.load(Ordering::SeqCst));
}
<|endoftext|>