<commit_before>//! The `html_parse` module parses a string of html into dom node datastructures.

use dom::{AttrMap, ElementData, Node, NodeType};

use std::iter::Peekable;
use std::str::Chars;

pub struct HtmlParser<'a> {
    chars: Peekable<Chars<'a>>,
}

impl<'a> HtmlParser<'a> {
    /// Constructs a new HtmlParser.
    ///
    /// full_html: the complete html to parse.
    pub fn new(full_html: &str) -> HtmlParser {
        HtmlParser { chars: full_html.chars().peekable() }
    }

    /// Entry point to parsing html, recursively parses html nodes.
    /// TODO check tags match, deal with self closing tags
    pub fn parse_nodes(&mut self) -> Vec<Node> {
        let mut nodes = Vec::new();

        while self.chars.peek().is_some() {
            self.consume_while(char::is_whitespace);
            if self.chars.peek().map_or(false, |c| *c == '<') {
                self.chars.next();
                if self.chars.peek().map_or(false, |c| *c == '/') {
                    self.consume_while(|x| x != '>');
                    self.chars.next();
                    break;
                } else if self.chars.peek().map_or(false, |c| *c == '!') {
                    self.chars.next();
                    nodes.push(self.parse_comment_node());
                } else {
                    nodes.push(self.parse_node());
                }
            } else {
                nodes.push(self.parse_text_node());
            }
        }
        nodes
    }

    /// Parse a single html node and recursively call parse_nodes on children.
    fn parse_node(&mut self) -> Node {
        // is an valid tagname
        let tagname = self.consume_while(|x| x.is_digit(36));
        let attributes = self.parse_attributes();

        let elem = ElementData::new(tagname, attributes);
        let children = self.parse_nodes();
        Node::new(NodeType::Element(elem), children)
    }

    /// Consume the text between '>' and '<' to produce a text node.
    /// TODO deal with control characters and U+0000
    fn parse_text_node(&mut self) -> Node {
        let mut text_content = String::new();

        while self.chars.peek().map_or(false, |c| *c != '<') {
            let whitespace = self.consume_while(char::is_whitespace);
            if whitespace.len() > 0 {
                text_content.push(' ');
            }
            let text_part = self.consume_while(|x| !x.is_whitespace() && x != '<');
            text_content.push_str(&text_part);
        }
        Node::new(NodeType::Text(text_content), Vec::new())
    }

    /// Consume text between "" to produce a comment node.
    fn parse_comment_node(&mut self) -> Node {
        let mut comment_content = String::new();

        if self.chars.peek().map_or(false, |c| *c == '-') {
            self.chars.next();
            if self.chars.peek().map_or(false, |c| *c == '-') {
                self.chars.next();
                if self.chars.peek().map_or(false, |c| *c == '>') {
                    // invalid comment format
                    self.chars.next();
                    return Node::new(NodeType::Comment(comment_content), Vec::new());
                } else if self.chars.peek().map_or(false, |c| *c == '-') {
                    self.chars.next();
                    if self.chars.peek().map_or(false, |c| *c == '>') {
                        // invalid comment format
                        self.chars.next();
                        return Node::new(NodeType::Comment(comment_content), Vec::new());
                    } else {
                        comment_content.push('-');
                    }
                }
                while self.chars.peek().is_some() {
                    comment_content.push_str(&self.consume_while(|x| x != '-'));
                    if self.chars.peek().map_or(false, |c| *c == '-') {
                        self.chars.next();
                        if self.chars.peek().map_or(false, |c| *c == '-') {
                            self.consume_while(|x| x != '>');
                            self.chars.next();
                            break;
                        } else {
                            comment_content.push('-')
                        }
                    }
                }
            }
        }
        Node::new(NodeType::Comment(comment_content), Vec::new())
    }

    /// Consume characters after a tagname until '>' and return a map.
    /// TODO normalize caps
    fn parse_attributes(&mut self) -> AttrMap {
        let mut attributes = AttrMap::new();

        while self.chars.peek().map_or(false, |c| *c != '>') {
            self.consume_while(char::is_whitespace);
            let name = self.consume_while(|c| is_valid_attr_name(c));
            self.consume_while(char::is_whitespace);

            let value = if self.chars.peek().map_or(false, |c| *c == '=') {
                self.chars.next(); // consume the '='
                self.consume_while(char::is_whitespace);
                let s = self.parse_attr_value();
                self.consume_while(|c| !c.is_whitespace() && c != '>');
                self.consume_while(char::is_whitespace);
                s
            } else {
                "".to_string()
            };
            attributes.insert(name, value);
        }
        self.chars.next(); // consume the '>' if it exists.

        attributes
    }

    /// Consume an attribute value (<tagname attrname=value>) and return it.
    /// TODO proper validation and error recovery
    fn parse_attr_value(&mut self) -> String {
        self.consume_while(char::is_whitespace);

        let result = match self.chars.peek() {
            Some(&c) if c == '"' || c == '\'' => {
                self.chars.next();
                let ret = self.consume_while(|x| x != c);
                self.chars.next(); // consume the quote
                ret
            },
            _ => self.consume_while(is_valid_attr_value),
        };

        result
    }

    /// Consumes characters until condition is false or there are no more chars left.
    /// Returns a string of the consumed characters.
    fn consume_while<F>(&mut self, condition: F) -> String where F : Fn(char) -> bool {
        let mut result = String::new();
        while self.chars.peek().map_or(false, |c| condition(*c)) {
            // the check above guarentees there is a value to be consumed
            result.push(self.chars.next().unwrap());
        }

        result
    }
}

/// Utility to check if a character can be used for an attribute name.
fn is_valid_attr_name(c: char) -> bool {
    !is_excluded_name(c) && !is_control(c)
}

fn is_control(ch: char) -> bool {
    match ch {
        '\u{007F}' => true,
        c if c >= '\u{0000}' && c <= '\u{001F}' => true,
        c if c >= '\u{0080}' && c <= '\u{009F}' => true,
        _ => false,
    }

}

fn is_excluded_name(c: char) -> bool {
    match c {
        ' '|'"'|'\''|'>'|'/'|'=' => true,
        _ => false,
    }
}

/// Utility to check if a character can be used for an attribute value.
/// TODO no ambiguous ampersand
fn is_valid_attr_value(c: char) -> bool {
    match c {
        ' '|'"'|'\''|'='|'<'|'>'|'`' => false,
        _ => true
    }
}

//TODO
//  -check and consume function that takes a condition
//  -script tags/link tags
//  -parse character references

/// Tests ----------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;
    use super::is_control;
    use dom::AttrMap;

    use std::iter::Peekable;
    use std::str::Chars;

    /// Test a parser is constructed correctly.
    #[test]
    fn new_parser() {
        let (parser, mut expected_chars) = test_parser("
lel
");

        for character in parser.chars {
            assert_eq!(character, expected_chars.next().unwrap());
        }

        assert_eq!(None, expected_chars.peek());
    }

    /// Test an empty attr value is parsed correctly.
    #[test]
    fn attr_value_empty() {
        let (mut parser, _) = test_parser("");
        assert_eq!("", parser.parse_attr_value());
    }

    /// Test an empty attr value is parsed correctly.
    #[test]
    fn attr_value_end() {
        let (mut parser, _) = test_parser(">");
        assert_eq!("", parser.parse_attr_value());
    }

    /// Test an regular attr value is parsed correctly.
    #[test]
    fn attr_value_reg() {
        let (mut parser, _) = test_parser("regularValue");
        assert_eq!("regularValue", parser.parse_attr_value());

        let (mut parser, _) = test_parser("regularValue>");
        assert_eq!("regularValue", parser.parse_attr_value());

        let (mut parser, _) = test_parser("regularValue ");
        assert_eq!("regularValue", parser.parse_attr_value());

        let (mut parser, _) = test_parser("regular<Value");
        assert_eq!("regular", parser.parse_attr_value());

        let (mut parser, _) = test_parser("regular'Value");
        assert_eq!("regular", parser.parse_attr_value());
    }

    /// Test an quoted attr value is parsed correctly.
    #[test]
    fn attr_value_quote() {
        let (mut parser, _) = test_parser("'regularValue'");
        assert_eq!("regularValue", parser.parse_attr_value());

        let (mut parser, _) = test_parser("\"regular'>< -_=Value\"");
        assert_eq!("regular'>< -_=Value", parser.parse_attr_value());

        let (mut parser, _) = test_parser("'regular\">< -_=Value'");
        assert_eq!("regular\">< -_=Value", parser.parse_attr_value());

        let (mut parser, _) = test_parser("\"regular\">< -_=Value\"");
        assert_eq!("regular", parser.parse_attr_value());

        let (mut parser, _) = test_parser("'regular'>< -_=Value'");
        assert_eq!("regular", parser.parse_attr_value());
    }

    /// Test empty attributes are parsed correctly.
    #[test]
    fn attrs_empty() {
        let (mut parser, _) = test_parser("");
        assert_eq!(AttrMap::new(), parser.parse_attributes());
    }

    /// Test end attributes are parsed correctly.
    #[test]
    fn attrs_end() {
        let (mut parser, _) = test_parser(">");
        assert_eq!(AttrMap::new(), parser.parse_attributes());
    }

    /// Test end attributes are parsed correctly.
    #[test]
    fn attrs_regular() {
        let (mut parser, _) = test_parser("name0 name1=value1 kek name2  ='value2' name3  = \"value3\"  ");
        let mut expected = AttrMap::new();
        expected.insert("name0".to_string(), "".to_string());
        expected.insert("kek".to_string(), "".to_string());
        expected.insert("name1".to_string(), "value1".to_string());
        expected.insert("name2".to_string(), "value2".to_string());
        expected.insert("name3".to_string(), "value3".to_string());

        assert_eq!(expected, parser.parse_attributes());
    }

    /// Test invalid attributes
    #[test]
    fn attrs_invalid() {
        let (mut parser, _) = test_parser("name0 name1=val'ue1 name2='va l ue2'");
        let mut expected = AttrMap::new();
        expected.insert("name0".to_string(), "".to_string());
        expected.insert("name1".to_string(), "val".to_string());
        expected.insert("name2".to_string(), "va l ue2".to_string());

        assert_eq!(expected, parser.parse_attributes());
    }

    /// Test if a character is a control character
    #[test]
    fn control_characters() {
        assert!(is_control('\u{0001}'));
        assert!(is_control('\u{007F}'));
        assert!(is_control('\u{0081}'));
        assert!(!is_control(' '));
    }

    /// Utility to return a parser for tests.
    fn test_parser<'a>(mock_html: &'a str) -> (HtmlParser, Peekable<Chars<'a>>) {
        let parser = HtmlParser::new(mock_html);
        let expected_chars = mock_html.chars().peekable();
        (parser, expected_chars)
    }
}
<commit_msg>start comment node tests<commit_after>//! The `html_parse` module parses a string of html into dom node datastructures.

use dom::{AttrMap, ElementData, Node, NodeType};

use std::iter::Peekable;
use std::str::Chars;

pub struct HtmlParser<'a> {
    chars: Peekable<Chars<'a>>,
}

impl<'a> HtmlParser<'a> {
    /// Constructs a new HtmlParser.
    ///
    /// full_html: the complete html to parse.
    pub fn new(full_html: &str) -> HtmlParser {
        HtmlParser { chars: full_html.chars().peekable() }
    }

    /// Entry point to parsing html, recursively parses html nodes.
    /// TODO check tags match, deal with self closing tags
    pub fn parse_nodes(&mut self) -> Vec<Node> {
        let mut nodes = Vec::new();

        while self.chars.peek().is_some() {
            self.consume_while(char::is_whitespace);
            if self.chars.peek().map_or(false, |c| *c == '<') {
                self.chars.next();
                if self.chars.peek().map_or(false, |c| *c == '/') {
                    self.consume_while(|x| x != '>');
                    self.chars.next();
                    break;
                } else if self.chars.peek().map_or(false, |c| *c == '!') {
                    self.chars.next();
                    nodes.push(self.parse_comment_node());
                } else {
                    nodes.push(self.parse_node());
                }
            } else {
                nodes.push(self.parse_text_node());
            }
        }
        nodes
    }

    /// Parse a single html node and recursively call parse_nodes on children.
    fn parse_node(&mut self) -> Node {
        // is an valid tagname
        let tagname = self.consume_while(|x| x.is_digit(36));
        let attributes = self.parse_attributes();

        let elem = ElementData::new(tagname, attributes);
        let children = self.parse_nodes();
        Node::new(NodeType::Element(elem), children)
    }

    /// Consume the text between '>' and '<' to produce a text node.
    /// TODO deal with control characters and U+0000
    fn parse_text_node(&mut self) -> Node {
        let mut text_content = String::new();

        while self.chars.peek().map_or(false, |c| *c != '<') {
            let whitespace = self.consume_while(char::is_whitespace);
            if whitespace.len() > 0 {
                text_content.push(' ');
            }
            let text_part = self.consume_while(|x| !x.is_whitespace() && x != '<');
            text_content.push_str(&text_part);
        }
        Node::new(NodeType::Text(text_content), Vec::new())
    }

    /// Consume text between "" to produce a comment node.
    fn parse_comment_node(&mut self) -> Node {
        let mut comment_content = String::new();

        if self.chars.peek().map_or(false, |c| *c == '-') {
            self.chars.next();
            if self.chars.peek().map_or(false, |c| *c == '-') {
                self.chars.next();
                if self.chars.peek().map_or(false, |c| *c == '>') {
                    // invalid comment format
                    self.chars.next();
                    return Node::new(NodeType::Comment(comment_content), Vec::new());
                } else if self.chars.peek().map_or(false, |c| *c == '-') {
                    self.chars.next();
                    if self.chars.peek().map_or(false, |c| *c == '>') {
                        // invalid comment format
                        self.chars.next();
                        return Node::new(NodeType::Comment(comment_content), Vec::new());
                    } else {
                        comment_content.push('-');
                    }
                }
                while self.chars.peek().is_some() {
                    comment_content.push_str(&self.consume_while(|x| x != '-'));
                    if self.chars.peek().map_or(false, |c| *c == '-') {
                        self.chars.next();
                        if self.chars.peek().map_or(false, |c| *c == '-') {
                            self.consume_while(|x| x != '>');
                            self.chars.next();
                            break;
                        } else {
                            comment_content.push('-')
                        }
                    }
                }
            }
        }
        Node::new(NodeType::Comment(comment_content), Vec::new())
    }

    /// Consume characters after a tagname until '>' and return a map.
    fn parse_attributes(&mut self) -> AttrMap {
        let mut attributes = AttrMap::new();

        while self.chars.peek().map_or(false, |c| *c != '>') {
            self.consume_while(char::is_whitespace);
            let name = self.consume_while(|c| is_valid_attr_name(c)).to_lowercase();
            self.consume_while(char::is_whitespace);

            let value = if self.chars.peek().map_or(false, |c| *c == '=') {
                self.chars.next(); // consume the '='
                self.consume_while(char::is_whitespace);
                let s = self.parse_attr_value();
                // cleans up aftere any invalid characters
                self.consume_while(|c| !c.is_whitespace() && c != '>');
                self.consume_while(char::is_whitespace);
                s
            } else {
                "".to_string()
            };
            attributes.insert(name, value);
        }
        self.chars.next(); // consume the '>' if it exists.

        attributes
    }

    /// Consume an attribute value and return it.
    fn parse_attr_value(&mut self) -> String {
        self.consume_while(char::is_whitespace);

        let result = match self.chars.peek() {
            Some(&c) if c == '"' || c == '\'' => {
                self.chars.next();
                let ret = self.consume_while(|x| x != c);
                self.chars.next(); // consume the quote
                ret
            },
            _ => self.consume_while(is_valid_attr_value),
        };

        result
    }

    /// Consumes characters until condition is false or there are no more chars left.
    /// Returns a string of the consumed characters.
    fn consume_while<F>(&mut self, condition: F) -> String where F : Fn(char) -> bool {
        let mut result = String::new();
        while self.chars.peek().map_or(false, |c| condition(*c)) {
            // the check above guarentees there is a value to be consumed
            result.push(self.chars.next().unwrap());
        }

        result
    }
}

/// Utility to check if a character can be used for an attribute name.
fn is_valid_attr_name(c: char) -> bool {
    !is_excluded_name(c) && !is_control(c)
}

fn is_control(ch: char) -> bool {
    match ch {
        '\u{007F}' => true,
        c if c >= '\u{0000}' && c <= '\u{001F}' => true,
        c if c >= '\u{0080}' && c <= '\u{009F}' => true,
        _ => false,
    }

}

fn is_excluded_name(c: char) -> bool {
    match c {
        ' '|'"'|'\''|'>'|'/'|'=' => true,
        _ => false,
    }
}

/// Utility to check if a character can be used for an attribute value.
/// TODO no ambiguous ampersand
fn is_valid_attr_value(c: char) -> bool {
    match c {
        ' '|'"'|'\''|'='|'<'|'>'|'`' => false,
        _ => true
    }
}

//TODO
//  -check and consume function that takes a condition
//  -script tags/link tags
//  -parse character references

/// Tests ----------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;
    use super::is_control;
    use dom::AttrMap;

    use std::iter::Peekable;
    use std::str::Chars;

    /// Test a parser is constructed correctly.
    #[test]
    fn new_parser() {
        let (parser, mut expected_chars) = test_parser("
lel
");

        for character in parser.chars {
            assert_eq!(character, expected_chars.next().unwrap());
        }

        assert_eq!(None, expected_chars.peek());
    }

    /// Test an empty attr value is parsed correctly.
    #[test]
    fn attr_value_empty() {
        let (mut parser, _) = test_parser("");
        assert_eq!("", parser.parse_attr_value());
    }

    /// Test an empty attr value is parsed correctly.
    #[test]
    fn attr_value_end() {
        let (mut parser, _) = test_parser(">");
        assert_eq!("", parser.parse_attr_value());
    }

    /// Test an regular attr value is parsed correctly.
    #[test]
    fn attr_value_reg() {
        let (mut parser, _) = test_parser("regularValue");
        assert_eq!("regularValue", parser.parse_attr_value());

        let (mut parser, _) = test_parser("regularValue>");
        assert_eq!("regularValue", parser.parse_attr_value());

        let (mut parser, _) = test_parser("regularValue ");
        assert_eq!("regularValue", parser.parse_attr_value());

        let (mut parser, _) = test_parser("regular<Value");
        assert_eq!("regular", parser.parse_attr_value());

        let (mut parser, _) = test_parser("regular'Value");
        assert_eq!("regular", parser.parse_attr_value());
    }

    /// Test an quoted attr value is parsed correctly.
    #[test]
    fn attr_value_quote() {
        let (mut parser, _) = test_parser("'regularValue'");
        assert_eq!("regularValue", parser.parse_attr_value());

        let (mut parser, _) = test_parser("\"regular'>< -_=Value\"");
        assert_eq!("regular'>< -_=Value", parser.parse_attr_value());

        let (mut parser, _) = test_parser("'regular\">< -_=Value'");
        assert_eq!("regular\">< -_=Value", parser.parse_attr_value());

        let (mut parser, _) = test_parser("\"regular\">< -_=Value\"");
        assert_eq!("regular", parser.parse_attr_value());

        let (mut parser, _) = test_parser("'regular'>< -_=Value'");
        assert_eq!("regular", parser.parse_attr_value());
    }

    /// Test empty attributes are parsed correctly.
    #[test]
    fn attrs_empty() {
        let (mut parser, _) = test_parser("");
        assert_eq!(AttrMap::new(), parser.parse_attributes());
    }

    /// Test end attributes are parsed correctly.
    #[test]
    fn attrs_end() {
        let (mut parser, _) = test_parser(">");
        assert_eq!(AttrMap::new(), parser.parse_attributes());
    }

    /// Test regular well formed attributes are parsed correctly.
    #[test]
    fn attrs_regular() {
        let (mut parser, _) = test_parser("name0 name1=value1 kek name2  ='value2' name3  = \"value3\"  ");
        let mut expected = AttrMap::new();
        expected.insert("name0".to_string(), "".to_string());
        expected.insert("kek".to_string(), "".to_string());
        expected.insert("name1".to_string(), "value1".to_string());
        expected.insert("name2".to_string(), "value2".to_string());
        expected.insert("name3".to_string(), "value3".to_string());

        assert_eq!(expected, parser.parse_attributes());
    }

    /// Test an invalid attribute.
    #[test]
    fn attrs_invalid() {
        let (mut parser, _) = test_parser("name0 name1=val'ue1 name2='va l ue2'");
        let mut expected = AttrMap::new();
        expected.insert("name0".to_string(), "".to_string());
        expected.insert("name1".to_string(), "val".to_string());
        expected.insert("name2".to_string(), "va l ue2".to_string());

        assert_eq!(expected, parser.parse_attributes());
    }

    /// Test case insensitivity for attr names and case sensitivity for attr values.
    #[test]
    fn attrs_case() {
        let (mut parser, _) = test_parser("NameZero NAMEone=VALUEone NAMETWO='VALUETWO' namethree=valuethree");
        let mut expected = AttrMap::new();
        expected.insert("namezero".to_string(), "".to_string());
        expected.insert("nameone".to_string(), "VALUEone".to_string());
        expected.insert("nametwo".to_string(), "VALUETWO".to_string());
        expected.insert("namethree".to_string(), "valuethree".to_string());

        assert_eq!(expected, parser.parse_attributes());
    }

    /// Test empty comment node.
    #[test]
    fn comment_empty() {

    }

    /// Test end comment node.
    #[test]
    fn comment_end() {

    }

    /// Test regular comment node.
    #[test]
    fn comment_regular() {

    }

    /// Test if a character is a control character
    #[test]
    fn control_characters() {
        assert!(is_control('\u{0001}'));
        assert!(is_control('\u{007F}'));
        assert!(is_control('\u{0081}'));
        assert!(!is_control(' '));
    }

    /// Utility to return a parser for tests.
    fn test_parser<'a>(mock_html: &'a str) -> (HtmlParser, Peekable<Chars<'a>>) {
        let parser = HtmlParser::new(mock_html);
        let expected_chars = mock_html.chars().peekable();
        (parser, expected_chars)
    }
}
<|endoftext|>


<commit_before>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::io::Read;
use std::path::Path;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const WHITELIST: &[&str] = &[
    "E0183",
    "E0227",
    "E0279",
    "E0280",
    "E0311",
    "E0313",
    "E0314",
    "E0315",
    "E0377",
    "E0456",
    "E0461",
    "E0462",
    "E0464",
    "E0465",
    "E0472",
    "E0473",
    "E0474",
    "E0475",
    "E0476",
    "E0479",
    "E0480",
    "E0481",
    "E0482",
    "E0483",
    "E0484",
    "E0485",
    "E0486",
    "E0487",
    "E0488",
    "E0489",
    "E0514",
    "E0519",
    "E0523",
    "E0554",
    "E0570",
    "E0629",
    "E0630",
    "E0640",
    "E0717",
    "E0727",
    "E0729",
];

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, bool>,
    err_code: String,
) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
            error_codes.insert(err_code, true);
            return;
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            error_codes.get_mut(&err_code).map(|x| *x = true);
            return;
        }
    }
}

macro_rules! some_or_continue {
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => continue,
        }
    );
}

fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>, path: &Path) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            if let Some(err_code) = s.splitn(2, ':').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    error_codes.insert(err_code.clone(), false);
                }
                // Now we extract the tests from the markdown file!
                let md = some_or_continue!(s.splitn(2, "include_str!(\"").skip(1).next());
                let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
                let path = some_or_continue!(path.parent()).join(md_file_name);
                match read_to_string(&path) {
                    Ok(content) => {
                        check_error_code_explanation(
                            &content,
                            error_codes,
                            err_code,
                        );
                    }
                    Err(e) => {
                        eprintln!("Couldn't read `{}`: {}", path.display(), e);
                    }
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            if let Some(err_code) = s.splitn(2, ',').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) { // this check should *never* fail!
                    error_codes.insert(err_code, false);
                }
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            if let Some(err_code) = s.splitn(2, ']').next() {
                if let Some(err_code) = err_code.splitn(2, '[').skip(1).next() {
                    let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
                    *nb = true;
                }
            }
        }
    }
}

pub fn check(path: &Path, bad: &mut bool) {
    println!("Checking which error codes lack tests...");
    let mut error_codes: HashMap<String, bool> = HashMap::new();
    super::walk(path,
                &mut |path| super::filter_dirs(path),
                &mut |entry, contents| {
        let file_name = entry.file_name();
        if file_name == "error_codes.rs" {
            extract_error_codes(contents, &mut error_codes, entry.path());
        } else if entry.path().extension() == Some(OsStr::new("stderr")) {
            extract_error_codes_from_tests(contents, &mut error_codes);
        }
    });
    println!("Found {} error codes", error_codes.len());

    let mut errors = Vec::new();
    for (err_code, nb) in &error_codes {
        if !*nb && !WHITELIST.contains(&err_code.as_str()) {
            errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<commit_msg>Rollup merge of #66698 - petrochenkov:tidywarn, r=Mark-Simulacrum<commit_after>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const WHITELIST: &[&str] = &[
    "E0183",
    "E0227",
    "E0279",
    "E0280",
    "E0311",
    "E0313",
    "E0314",
    "E0315",
    "E0377",
    "E0456",
    "E0461",
    "E0462",
    "E0464",
    "E0465",
    "E0472",
    "E0473",
    "E0474",
    "E0475",
    "E0476",
    "E0479",
    "E0480",
    "E0481",
    "E0482",
    "E0483",
    "E0484",
    "E0485",
    "E0486",
    "E0487",
    "E0488",
    "E0489",
    "E0514",
    "E0519",
    "E0523",
    "E0554",
    "E0570",
    "E0629",
    "E0630",
    "E0640",
    "E0717",
    "E0727",
    "E0729",
];

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, bool>,
    err_code: String,
) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
            error_codes.insert(err_code, true);
            return;
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            error_codes.get_mut(&err_code).map(|x| *x = true);
            return;
        }
    }
}

macro_rules! some_or_continue {
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => continue,
        }
    );
}

fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>, path: &Path) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            if let Some(err_code) = s.splitn(2, ':').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    error_codes.insert(err_code.clone(), false);
                }
                // Now we extract the tests from the markdown file!
                let md = some_or_continue!(s.splitn(2, "include_str!(\"").skip(1).next());
                let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
                let path = some_or_continue!(path.parent()).join(md_file_name);
                match read_to_string(&path) {
                    Ok(content) => {
                        check_error_code_explanation(
                            &content,
                            error_codes,
                            err_code,
                        );
                    }
                    Err(e) => {
                        eprintln!("Couldn't read `{}`: {}", path.display(), e);
                    }
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            if let Some(err_code) = s.splitn(2, ',').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) { // this check should *never* fail!
                    error_codes.insert(err_code, false);
                }
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            if let Some(err_code) = s.splitn(2, ']').next() {
                if let Some(err_code) = err_code.splitn(2, '[').skip(1).next() {
                    let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
                    *nb = true;
                }
            }
        }
    }
}

pub fn check(path: &Path, bad: &mut bool) {
    println!("Checking which error codes lack tests...");
    let mut error_codes: HashMap<String, bool> = HashMap::new();
    super::walk(path,
                &mut |path| super::filter_dirs(path),
                &mut |entry, contents| {
        let file_name = entry.file_name();
        if file_name == "error_codes.rs" {
            extract_error_codes(contents, &mut error_codes, entry.path());
        } else if entry.path().extension() == Some(OsStr::new("stderr")) {
            extract_error_codes_from_tests(contents, &mut error_codes);
        }
    });
    println!("Found {} error codes", error_codes.len());

    let mut errors = Vec::new();
    for (err_code, nb) in &error_codes {
        if !*nb && !WHITELIST.contains(&err_code.as_str()) {
            errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_must_use, unused_variables, unused_imports)]

use std::thread::{self,Thread,Builder};
use std::sync::mpsc::{self,channel,Receiver};
use mio::tcp::*;
use mio::*;
use bytes::{ByteBuf,MutByteBuf};
use std::collections::HashMap;
use std::io::{self,Read,ErrorKind};
use nom::HexDisplay;
use std::error::Error;
use mio::util::Slab;
use std::io::Write;
use std::str::FromStr;
use std::marker::PhantomData;
use std::fmt::Debug;
use time::precise_time_ns;
use rand::random;

use network::{ClientResult,ServerMessage,ConnectionError,SocketType,socketType,ProxyOrder};
use network::metrics::{METRICS,ProxyMetrics};

use messages::{TcpFront,Command,Instance};

const SERVER: Token = Token(0);
type ClientToken = Token;

pub trait ProxyClient<Server:Handler> {
  fn front_socket(&self) -> &TcpStream;
  fn back_socket(&self)  -> Option<&TcpStream>;
  fn front_token(&self)  -> Option<Token>;
  fn back_token(&self)   -> Option<Token>;
  fn set_back_socket(&mut self, TcpStream);
  fn set_front_token(&mut self, token: Token);
  fn set_back_token(&mut self, token: Token);
  fn set_tokens(&mut self, token: Token, backend: Token);
  fn front_hup(&mut self) -> ClientResult;
  fn back_hup(&mut self) -> ClientResult;
  fn readable(&mut self, event_loop: &mut EventLoop<Server>) -> ClientResult;
  fn writable(&mut self, event_loop: &mut EventLoop<Server>) -> ClientResult;
  fn back_readable(&mut self, event_loop: &mut EventLoop<Server>) -> ClientResult;
  fn back_writable(&mut self, event_loop: &mut EventLoop<Server>) -> ClientResult;
  fn remove_backend(&mut self);
}

pub trait ProxyConfiguration<Server:Handler,Client> {
  fn add_tcp_front(&mut self, port: u16, app_id: &str, event_loop: &mut EventLoop<Server>) -> Option<Token>;
  fn connect_to_backend(&mut self, client:&mut Client) ->Result<TcpStream,ConnectionError>;
  fn notify(&mut self, event_loop: &mut EventLoop<Server>, message: ProxyOrder);
  fn accept(&mut self, token: Token) -> Option<(Client, bool)>;
}

pub struct Server<ServerConfiguration,Client> {
  configuration:   ServerConfiguration,
  clients:         Slab<Client>,
  backend:         Slab<ClientToken>,
  max_listeners:   usize,
  max_connections: usize,
}

impl<ServerConfiguration:ProxyConfiguration<Server<ServerConfiguration,Client>, Client>,Client:ProxyClient<Server<ServerConfiguration,Client>>> Server<ServerConfiguration,Client> {
  pub fn new(max_listeners: usize, max_connections: usize, configuration: ServerConfiguration) -> Self {
    Server {
      configuration:   configuration,
      clients:         Slab::new_starting_at(Token(max_listeners), max_connections),
      backend:         Slab::new_starting_at(Token(max_listeners+max_connections), max_connections),
      max_listeners:   max_listeners,
      max_connections: max_connections,
    }
  }

  pub fn configuration(&mut self) -> &mut ServerConfiguration {
    &mut self.configuration
  }

  pub fn close_client(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
    self.clients[token].front_socket().shutdown(Shutdown::Both);
    event_loop.deregister(self.clients[token].front_socket());
    if let Some(sock) = self.clients[token].back_socket() {
      sock.shutdown(Shutdown::Both);
      event_loop.deregister(sock);
    }

    if let Some(backend_token) = self.clients[token].back_token() {
      if self.backend.contains(backend_token) {
        self.backend.remove(backend_token);
      }
    }
    self.clients.remove(token);
  }

  pub fn close_backend(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
    if let Some(backend_token) = self.clients[token].back_token() {
      if self.backend.contains(backend_token) {
        self.backend.remove(backend_token);
        self.clients[token].remove_backend();
      }
    }
  }

  pub fn accept(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
    if let Some((client, should_connect)) = self.configuration.accept(token) {
      if let Ok(client_token) = self.clients.insert(client) {
        event_loop.register(self.clients[client_token].front_socket(), client_token, EventSet::readable(), PollOpt::edge());
        &self.clients[client_token].set_front_token(client_token);
        METRICS.lock().unwrap().gauge("accept", 1);
        if should_connect {
          self.connect_to_backend(event_loop, client_token);
        }
      } else {
        error!("could not add client to slab");
      }
    } else {
      error!("could not create a client");
    }
  }

  pub fn connect_to_backend(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
    if let Ok(socket) = self.configuration.connect_to_backend(&mut self.clients[token]) {
      if let Ok(backend_token) = self.backend.insert(token) {
        self.clients[token].set_back_socket(socket);
        self.clients[token].set_back_token(backend_token);

        if let Some(sock) = self.clients[token].back_socket() {
          event_loop.register(sock, backend_token, EventSet::writable(), PollOpt::edge());
        }
        return;
      }
    } else {
      // ToDo serve page explaining what happened
      // - "domain not found" (aka "404 clever") page
      // - 503 "service unavailable" (aka "your app is deploying") page
      // - check other failing conditions
    }
    self.close_client(event_loop, token);
  }

  pub fn get_client_token(&self, token: Token) -> Option<Token> {
    if token.as_usize() < self.max_listeners {
      None
    } else if token.as_usize() < self.max_listeners + self.max_connections && self.clients.contains(token) {
      Some(token)
    } else if token.as_usize() < self.max_listeners + 2 * self.max_connections && self.backend.contains(token) {
      if self.clients.contains(self.backend[token]) {
        Some(self.backend[token])
      } else {
        None
      }
    } else {
      None
    }
  }

  pub fn interpret_client_order(&mut self, event_loop: &mut EventLoop<Self>, token: Token, order: ClientResult) {
    match order {
      ClientResult::CloseClient      => self.close_client(event_loop, token),
      ClientResult::CloseBackend     => self.close_backend(event_loop, token),
      ClientResult::CloseBothSuccess => self.close_client(event_loop, token),
      ClientResult::CloseBothFailure => self.close_client(event_loop, token),
      ClientResult::ConnectBackend   => self.connect_to_backend(event_loop, token),
      ClientResult::Continue         => {}
    }
  }
}

impl<ServerConfiguration:ProxyConfiguration<Server<ServerConfiguration,Client>, Client>,Client:ProxyClient<Server<ServerConfiguration,Client>>> Handler for Server<ServerConfiguration,Client> {
  type Timeout = usize;
  type Message = ProxyOrder;

  fn ready(&mut self, event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
    trace!("{:?} got events: {:?}", token, events);
    if events.is_readable() {
      trace!("{:?} is readable", token);

      match socket_type(token, self.max_listeners, self.max_connections) {
        Some(SocketType::Listener) => {
          self.accept(event_loop, token)
        }

        Some(SocketType::FrontClient) => {
          if self.clients.contains(token) {
            let order = self.clients[token].readable(event_loop);
            self.interpret_client_order(event_loop, token, order);
          } else {
            info!("client {:?} was removed", token);
          }
        }

        Some(SocketType::BackClient) => {
          if let Some(tok) = self.get_client_token(token) {
            let order = self.clients[tok].back_readable(event_loop);
            self.interpret_client_order(event_loop, tok, order);
          }
        }

        None => {}
      }
    }

    if events.is_writable() {
      trace!("{:?} is writable", token);

      match socket_type(token, self.max_listeners, self.max_connections) {
        Some(SocketType::Listener) => {
          error!("received writable for listener {:?}, this should not happen", token);
        }

        Some(SocketType::FrontClient) => {
          if self.clients.contains(token) {
            let order = self.clients[token].writable(event_loop);
            trace!("interpreting client order {:?}", order);
            self.interpret_client_order(event_loop, token, order);
          } else {
            info!("client {:?} was removed", token);
          }
        }

        Some(SocketType::BackClient) => {
          if let Some(tok) = self.get_client_token(token) {
            let order = self.clients[tok].back_writable(event_loop);
            self.interpret_client_order(event_loop, tok, order);
          }
        }

        None => {}
      }
    }

    if events.is_hup() {
      match socket_type(token, self.max_listeners, self.max_connections) {
        Some(SocketType::Listener) => {
          error!("should not happen: server {:?} closed", token);
        }

        Some(SocketType::FrontClient) => {
          if self.clients.contains(token) {
            if self.clients[token].front_hup() == ClientResult::CloseClient {
              self.close_client(event_loop, token);
            }
          } else {
            info!("client {:?} was removed", token);
          }
        }

        Some(SocketType::BackClient) => {
          if let Some(tok) = self.get_client_token(token) {
            if self.clients[tok].front_hup() == ClientResult::CloseClient {
              self.close_client(event_loop, tok);
            }
          }
        }

        None => {}
      }
    }
  }

  fn notify(&mut self, event_loop: &mut EventLoop<Self>, message: Self::Message) {
    self.configuration.notify(event_loop, message);
  }

  fn timeout(&mut self, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
    warn!("timeout");
  }

  fn interrupted(&mut self, event_loop: &mut EventLoop<Self>) {
    warn!("interrupted");
  }
}

<commit_msg>Fix rebase issue<commit_after>#![allow(dead_code, unused_must_use, unused_variables, unused_imports)]

use std::thread::{self,Thread,Builder};
use std::sync::mpsc::{self,channel,Receiver};
use mio::tcp::*;
use mio::*;
use bytes::{ByteBuf,MutByteBuf};
use std::collections::HashMap;
use std::io::{self,Read,ErrorKind};
use nom::HexDisplay;
use std::error::Error;
use mio::util::Slab;
use std::io::Write;
use std::str::FromStr;
use std::marker::PhantomData;
use std::fmt::Debug;
use time::precise_time_ns;
use rand::random;

use network::{ClientResult,ServerMessage,ConnectionError,SocketType,socket_type,ProxyOrder};
use network::metrics::{METRICS,ProxyMetrics};

use messages::{TcpFront,Command,Instance};

const SERVER: Token = Token(0);
type ClientToken = Token;

pub trait ProxyClient<Server:Handler> {
  fn front_socket(&self) -> &TcpStream;
  fn back_socket(&self)  -> Option<&TcpStream>;
  fn front_token(&self)  -> Option<Token>;
  fn back_token(&self)   -> Option<Token>;
  fn set_back_socket(&mut self, TcpStream);
  fn set_front_token(&mut self, token: Token);
  fn set_back_token(&mut self, token: Token);
  fn set_tokens(&mut self, token: Token, backend: Token);
  fn front_hup(&mut self) -> ClientResult;
  fn back_hup(&mut self) -> ClientResult;
  fn readable(&mut self, event_loop: &mut EventLoop<Server>) -> ClientResult;
  fn writable(&mut self, event_loop: &mut EventLoop<Server>) -> ClientResult;
  fn back_readable(&mut self, event_loop: &mut EventLoop<Server>) -> ClientResult;
  fn back_writable(&mut self, event_loop: &mut EventLoop<Server>) -> ClientResult;
  fn remove_backend(&mut self);
}

pub trait ProxyConfiguration<Server:Handler,Client> {
  fn add_tcp_front(&mut self, port: u16, app_id: &str, event_loop: &mut EventLoop<Server>) -> Option<Token>;
  fn connect_to_backend(&mut self, client:&mut Client) ->Result<TcpStream,ConnectionError>;
  fn notify(&mut self, event_loop: &mut EventLoop<Server>, message: ProxyOrder);
  fn accept(&mut self, token: Token) -> Option<(Client, bool)>;
}

pub struct Server<ServerConfiguration,Client> {
  configuration:   ServerConfiguration,
  clients:         Slab<Client>,
  backend:         Slab<ClientToken>,
  max_listeners:   usize,
  max_connections: usize,
}

impl<ServerConfiguration:ProxyConfiguration<Server<ServerConfiguration,Client>, Client>,Client:ProxyClient<Server<ServerConfiguration,Client>>> Server<ServerConfiguration,Client> {
  pub fn new(max_listeners: usize, max_connections: usize, configuration: ServerConfiguration) -> Self {
    Server {
      configuration:   configuration,
      clients:         Slab::new_starting_at(Token(max_listeners), max_connections),
      backend:         Slab::new_starting_at(Token(max_listeners+max_connections), max_connections),
      max_listeners:   max_listeners,
      max_connections: max_connections,
    }
  }

  pub fn configuration(&mut self) -> &mut ServerConfiguration {
    &mut self.configuration
  }

  pub fn close_client(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
    self.clients[token].front_socket().shutdown(Shutdown::Both);
    event_loop.deregister(self.clients[token].front_socket());
    if let Some(sock) = self.clients[token].back_socket() {
      sock.shutdown(Shutdown::Both);
      event_loop.deregister(sock);
    }

    if let Some(backend_token) = self.clients[token].back_token() {
      if self.backend.contains(backend_token) {
        self.backend.remove(backend_token);
      }
    }
    self.clients.remove(token);
  }

  pub fn close_backend(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
    if let Some(backend_token) = self.clients[token].back_token() {
      if self.backend.contains(backend_token) {
        self.backend.remove(backend_token);
        self.clients[token].remove_backend();
      }
    }
  }

  pub fn accept(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
    if let Some((client, should_connect)) = self.configuration.accept(token) {
      if let Ok(client_token) = self.clients.insert(client) {
        event_loop.register(self.clients[client_token].front_socket(), client_token, EventSet::readable(), PollOpt::edge());
        &self.clients[client_token].set_front_token(client_token);
        METRICS.lock().unwrap().gauge("accept", 1);
        if should_connect {
          self.connect_to_backend(event_loop, client_token);
        }
      } else {
        error!("could not add client to slab");
      }
    } else {
      error!("could not create a client");
    }
  }

  pub fn connect_to_backend(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
    if let Ok(socket) = self.configuration.connect_to_backend(&mut self.clients[token]) {
      if let Ok(backend_token) = self.backend.insert(token) {
        self.clients[token].set_back_socket(socket);
        self.clients[token].set_back_token(backend_token);

        if let Some(sock) = self.clients[token].back_socket() {
          event_loop.register(sock, backend_token, EventSet::writable(), PollOpt::edge());
        }
        return;
      }
    } else {
      // ToDo serve page explaining what happened
      // - "domain not found" (aka "404 clever") page
      // - 503 "service unavailable" (aka "your app is deploying") page
      // - check other failing conditions
    }
    self.close_client(event_loop, token);
  }

  pub fn get_client_token(&self, token: Token) -> Option<Token> {
    if token.as_usize() < self.max_listeners {
      None
    } else if token.as_usize() < self.max_listeners + self.max_connections && self.clients.contains(token) {
      Some(token)
    } else if token.as_usize() < self.max_listeners + 2 * self.max_connections && self.backend.contains(token) {
      if self.clients.contains(self.backend[token]) {
        Some(self.backend[token])
      } else {
        None
      }
    } else {
      None
    }
  }

  pub fn interpret_client_order(&mut self, event_loop: &mut EventLoop<Self>, token: Token, order: ClientResult) {
    match order {
      ClientResult::CloseClient      => self.close_client(event_loop, token),
      ClientResult::CloseBackend     => self.close_backend(event_loop, token),
      ClientResult::CloseBothSuccess => self.close_client(event_loop, token),
      ClientResult::CloseBothFailure => self.close_client(event_loop, token),
      ClientResult::ConnectBackend   => self.connect_to_backend(event_loop, token),
      ClientResult::Continue         => {}
    }
  }
}

impl<ServerConfiguration:ProxyConfiguration<Server<ServerConfiguration,Client>, Client>,Client:ProxyClient<Server<ServerConfiguration,Client>>> Handler for Server<ServerConfiguration,Client> {
  type Timeout = usize;
  type Message = ProxyOrder;

  fn ready(&mut self, event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
    trace!("{:?} got events: {:?}", token, events);
    if events.is_readable() {
      trace!("{:?} is readable", token);

      match socket_type(token, self.max_listeners, self.max_connections) {
        Some(SocketType::Listener) => {
          self.accept(event_loop, token)
        }

        Some(SocketType::FrontClient) => {
          if self.clients.contains(token) {
            let order = self.clients[token].readable(event_loop);
            self.interpret_client_order(event_loop, token, order);
          } else {
            info!("client {:?} was removed", token);
          }
        }

        Some(SocketType::BackClient) => {
          if let Some(tok) = self.get_client_token(token) {
            let order = self.clients[tok].back_readable(event_loop);
            self.interpret_client_order(event_loop, tok, order);
          }
        }

        None => {}
      }
    }

    if events.is_writable() {
      trace!("{:?} is writable", token);

      match socket_type(token, self.max_listeners, self.max_connections) {
        Some(SocketType::Listener) => {
          error!("received writable for listener {:?}, this should not happen", token);
        }

        Some(SocketType::FrontClient) => {
          if self.clients.contains(token) {
            let order = self.clients[token].writable(event_loop);
            trace!("interpreting client order {:?}", order);
            self.interpret_client_order(event_loop, token, order);
          } else {
            info!("client {:?} was removed", token);
          }
        }

        Some(SocketType::BackClient) => {
          if let Some(tok) = self.get_client_token(token) {
            let order = self.clients[tok].back_writable(event_loop);
            self.interpret_client_order(event_loop, tok, order);
          }
        }

        None => {}
      }
    }

    if events.is_hup() {
      match socket_type(token, self.max_listeners, self.max_connections) {
        Some(SocketType::Listener) => {
          error!("should not happen: server {:?} closed", token);
        }

        Some(SocketType::FrontClient) => {
          if self.clients.contains(token) {
            if self.clients[token].front_hup() == ClientResult::CloseClient {
              self.close_client(event_loop, token);
            }
          } else {
            info!("client {:?} was removed", token);
          }
        }

        Some(SocketType::BackClient) => {
          if let Some(tok) = self.get_client_token(token) {
            if self.clients[tok].front_hup() == ClientResult::CloseClient {
              self.close_client(event_loop, tok);
            }
          }
        }

        None => {}
      }
    }
  }

  fn notify(&mut self, event_loop: &mut EventLoop<Self>, message: Self::Message) {
    self.configuration.notify(event_loop, message);
  }

  fn timeout(&mut self, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
    warn!("timeout");
  }

  fn interrupted(&mut self, event_loop: &mut EventLoop<Self>) {
    warn!("interrupted");
  }
}

<|endoftext|>


<commit_before>use criterion::*;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use rav1e::bench::frame::AsRegion;
use rav1e::bench::rdo;
use rav1e::bench::tiling::Area;
use rav1e::prelude::Plane;

fn init_plane_u8(width: usize, height: usize, seed: u8) -> Plane<u8> {
  let mut ra = ChaChaRng::from_seed([seed; 32]);
  let data: Vec<u8> = (0..(width * height)).map(|_| ra.gen()).collect();
  Plane::wrap(data, width)
}

pub fn cdef_dist_wxh_8x8(c: &mut Criterion) {
  let src1 = init_plane_u8(8, 8, 1);
  let src2 = init_plane_u8(8, 8, 2);

  c.bench_function("cdef_dist_wxh_8x8", move |b| {
    b.iter(|| {
      rdo::cdef_dist_wxh(
        &src1.region(Area::Rect { x: 0, y: 0, width: 8, height: 8 }),
        &src2.region(Area::Rect { x: 0, y: 0, width: 8, height: 8 }),
        8,
        8,
        8,
        |_, _| 1.0,
      )
    })
  });
}

criterion_group!(rdo, cdef_dist_wxh_8x8);
<commit_msg>Add benchmarks for sse_wxh<commit_after>use criterion::*;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use rav1e::bench::frame::AsRegion;
use rav1e::bench::rdo;
use rav1e::bench::tiling::Area;
use rav1e::prelude::Plane;

fn init_plane_u8(width: usize, height: usize, seed: u8) -> Plane<u8> {
  let mut ra = ChaChaRng::from_seed([seed; 32]);
  let data: Vec<u8> = (0..(width * height)).map(|_| ra.gen()).collect();
  Plane::wrap(data, width)
}

pub fn cdef_dist_wxh_8x8(c: &mut Criterion) {
  let src1 = init_plane_u8(8, 8, 1);
  let src2 = init_plane_u8(8, 8, 2);

  c.bench_function("cdef_dist_wxh_8x8", move |b| {
    b.iter(|| {
      rdo::cdef_dist_wxh(
        &src1.region(Area::Rect { x: 0, y: 0, width: 8, height: 8 }),
        &src2.region(Area::Rect { x: 0, y: 0, width: 8, height: 8 }),
        8,
        8,
        8,
        |_, _| 1.0,
      )
    })
  });
}

pub fn sse_wxh_8x8(c: &mut Criterion) {
  let src1 = init_plane_u8(8, 8, 1);
  let src2 = init_plane_u8(8, 8, 2);

  c.bench_function("sse_wxh_8x8", move |b| {
    b.iter(|| {
      rdo::sse_wxh(
        &src1.region(Area::Rect { x: 0, y: 0, width: 8, height: 8 }),
        &src2.region(Area::Rect { x: 0, y: 0, width: 8, height: 8 }),
        8,
        8,
        |_, _| 1.0,
      )
    })
  });
}

pub fn sse_wxh_4x4(c: &mut Criterion) {
  let src1 = init_plane_u8(8, 8, 1);
  let src2 = init_plane_u8(8, 8, 2);

  c.bench_function("sse_wxh_4z4", move |b| {
    b.iter(|| {
      rdo::sse_wxh(
        &src1.region(Area::Rect { x: 0, y: 0, width: 4, height: 4 }),
        &src2.region(Area::Rect { x: 0, y: 0, width: 4, height: 4 }),
        4,
        4,
        |_, _| 1.0,
      )
    })
  });
}

pub fn sse_wxh_2x2(c: &mut Criterion) {
  let mut src1 = init_plane_u8(8, 8, 1);
  let mut src2 = init_plane_u8(8, 8, 2);
  src1.cfg.xdec = 1;
  src1.cfg.ydec = 1;
  src2.cfg.xdec = 1;
  src2.cfg.ydec = 1;

  c.bench_function("sse_wxh_2x2", move |b| {
    b.iter(|| {
      rdo::sse_wxh(
        &src1.region(Area::Rect { x: 0, y: 0, width: 4, height: 4 }),
        &src2.region(Area::Rect { x: 0, y: 0, width: 4, height: 4 }),
        4,
        4,
        |_, _| 1.0,
      )
    })
  });
}

criterion_group!(
  rdo,
  cdef_dist_wxh_8x8,
  sse_wxh_8x8,
  sse_wxh_4x4,
  sse_wxh_2x2
);
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

pub use model::TwitchLinks;
pub use model::UrlString;
pub use model::DateString;
pub use model::LocaleString;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Channel {
    #[serde(rename="_links")]
    links: BTreeMap<String, String>,
    #[serde(rename="_id")]
    id: u64,
    name: String,
    display_name: String,
    game: String,
    status: String,
    mature: bool,
    delay: Option<u32>,
    language: LocaleString,
    broadcaster_language: LocaleString,
    created_at: DateString,
    updated_at: DateString,
    logo: Option<UrlString>,
    banner: Option<UrlString>,
    video_banner: Option<UrlString>,
    background: Option<UrlString>,
    profile_banner: Option<UrlString>,
    profile_banner_background_color: Option<UrlString>,
    partner: bool,
    url: UrlString,
    views: u32,
    followers: u32,
}

impl TwitchLinks for Channel {
    fn links(&self) -> &BTreeMap<String, String> {
        &self.links
    }
}

impl Channel {
    pub fn link_self(&self) -> &String {
        self.get_expected_link("self")
    }
    pub fn link_follows(&self) -> &String {
        self.get_expected_link("follows")
    }
    pub fn link_commercial(&self) -> &String {
        self.get_expected_link("commercial")
    }
    pub fn link_stream_key(&self) -> &String {
        self.get_expected_link("stream_key")
    }
    pub fn link_chat(&self) -> &String {
        self.get_expected_link("chat")
    }
    pub fn link_features(&self) -> &String {
        self.get_expected_link("features")
    }
    pub fn link_subscriptions(&self) -> &String {
        self.get_expected_link("subscriptions")
    }
    pub fn link_editors(&self) -> &String {
        self.get_expected_link("editors")
    }
    pub fn link_teams(&self) -> &String {
        self.get_expected_link("teams")
    }
    pub fn link_videos(&self) -> &String {
        self.get_expected_link("videos")
    }
    pub fn id(&self) -> u64 {
        self.id
    }
    pub fn name(&self) -> &String {
        &self.name
    }
    pub fn display_name(&self) -> &String {
        &self.display_name
    }
    pub fn game(&self) -> &String {
        &self.game
    }
    pub fn status(&self) -> &String {
        &self.status
    }
    pub fn mature(&self) -> bool {
        self.mature
    }
    pub fn delay(&self) -> Option<u32> {
        self.delay
    }
    pub fn language(&self) -> &LocaleString {
        &self.language
    }
    pub fn broadcaster_language(&self) -> &LocaleString {
        &self.broadcaster_language
    }
    pub fn created_at(&self) -> &DateString {
        &self.created_at
    }
    pub fn updated_at(&self) -> &DateString {
        &self.updated_at
    }
    pub fn logo(&self) -> &Option<UrlString> {
        &self.logo
    }
    pub fn banner(&self) -> &Option<UrlString> {
        &self.banner
    }
    pub fn video_banner(&self) -> &Option<UrlString> {
        &self.video_banner
    }
    pub fn background(&self) -> &Option<UrlString> {
        &self.background
    }
    pub fn profile_banner(&self) -> &Option<UrlString> {
        &self.profile_banner
    }
    pub fn profile_banner_background_color(&self) -> &Option<UrlString> {
        &self.profile_banner_background_color
    }
    pub fn partner(&self) -> bool {
        self.partner
    }
    pub fn url(&self) -> &UrlString {
        &self.url
    }
    pub fn views(&self) -> u32 {
        self.views
    }
    pub fn followers(&self) -> u32 {
        self.followers
    }
}
<commit_msg>Fixed channel mature flag possibly being null<commit_after>use std::collections::BTreeMap;

pub use model::TwitchLinks;
pub use model::UrlString;
pub use model::DateString;
pub use model::LocaleString;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Channel {
    #[serde(rename="_links")]
    links: BTreeMap<String, String>,
    #[serde(rename="_id")]
    id: u64,
    name: String,
    display_name: String,
    game: String,
    status: String,
    mature: Option<bool>,
    delay: Option<u32>,
    language: LocaleString,
    broadcaster_language: LocaleString,
    created_at: DateString,
    updated_at: DateString,
    logo: Option<UrlString>,
    banner: Option<UrlString>,
    video_banner: Option<UrlString>,
    background: Option<UrlString>,
    profile_banner: Option<UrlString>,
    profile_banner_background_color: Option<UrlString>,
    partner: bool,
    url: UrlString,
    views: u32,
    followers: u32,
}

impl TwitchLinks for Channel {
    fn links(&self) -> &BTreeMap<String, String> {
        &self.links
    }
}

impl Channel {
    pub fn link_self(&self) -> &String {
        self.get_expected_link("self")
    }
    pub fn link_follows(&self) -> &String {
        self.get_expected_link("follows")
    }
    pub fn link_commercial(&self) -> &String {
        self.get_expected_link("commercial")
    }
    pub fn link_stream_key(&self) -> &String {
        self.get_expected_link("stream_key")
    }
    pub fn link_chat(&self) -> &String {
        self.get_expected_link("chat")
    }
    pub fn link_features(&self) -> &String {
        self.get_expected_link("features")
    }
    pub fn link_subscriptions(&self) -> &String {
        self.get_expected_link("subscriptions")
    }
    pub fn link_editors(&self) -> &String {
        self.get_expected_link("editors")
    }
    pub fn link_teams(&self) -> &String {
        self.get_expected_link("teams")
    }
    pub fn link_videos(&self) -> &String {
        self.get_expected_link("videos")
    }
    pub fn id(&self) -> u64 {
        self.id
    }
    pub fn name(&self) -> &String {
        &self.name
    }
    pub fn display_name(&self) -> &String {
        &self.display_name
    }
    pub fn game(&self) -> &String {
        &self.game
    }
    pub fn status(&self) -> &String {
        &self.status
    }
    pub fn mature(&self) -> Option<bool> {
        self.mature
    }
    pub fn delay(&self) -> Option<u32> {
        self.delay
    }
    pub fn language(&self) -> &LocaleString {
        &self.language
    }
    pub fn broadcaster_language(&self) -> &LocaleString {
        &self.broadcaster_language
    }
    pub fn created_at(&self) -> &DateString {
        &self.created_at
    }
    pub fn updated_at(&self) -> &DateString {
        &self.updated_at
    }
    pub fn logo(&self) -> &Option<UrlString> {
        &self.logo
    }
    pub fn banner(&self) -> &Option<UrlString> {
        &self.banner
    }
    pub fn video_banner(&self) -> &Option<UrlString> {
        &self.video_banner
    }
    pub fn background(&self) -> &Option<UrlString> {
        &self.background
    }
    pub fn profile_banner(&self) -> &Option<UrlString> {
        &self.profile_banner
    }
    pub fn profile_banner_background_color(&self) -> &Option<UrlString> {
        &self.profile_banner_background_color
    }
    pub fn partner(&self) -> bool {
        self.partner
    }
    pub fn url(&self) -> &UrlString {
        &self.url
    }
    pub fn views(&self) -> u32 {
        self.views
    }
    pub fn followers(&self) -> u32 {
        self.followers
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};

use render::Renderer;
use routing::{DagHandle, Edge, GraphWatcher, NodeData, NodeHandle};
use util::unpack_f32;

type NodeMap = HashMap<NodeHandle, Node>;

#[derive(Default)]
pub struct RefRenderer {
    nodes: NodeMap,
}

struct Node {
    data: MyNodeData,
    inbound: HashSet<Edge>
}

enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(NodeMap),
    /// This node is an instance of another DAG.
    Graph(DagHandle),
    /// Primitive Delay effect
    Delay,
    /// Primitive Constant effect.
    /// Also serves as a unit step;
    /// Returns the float value for t >= 0, else 0.
    F32Const,
    /// Primitive effect to multiply TWO input streams sample-wise.
    Multiply,
    /// Primitive effect to calculate A/B.
    /// Note: because not all floating point numbers have inverses,
    /// A * (1/B) != A/B. Hence, we need division (not inversion) for proper
    /// precision.
    Divide,
    /// Primitive effect to calculate A%B (true modulo; not remainder.
    /// Result, y,  is always positive: y is bounded by [0, B).
    Modulo,
    /// This node is a DAG definition. i.e. it holds the output edges of a DAG.
    DagIO,
}

impl Renderer for RefRenderer {
    fn get_sample(&mut self, time: u64, ch: u8) -> f32 {
        // Try to find the edge that goes to -> (Null, slot=0, ch=ch)
        let root_handle = NodeHandle::toplevel();
        // empty graph is 0 = silence
        self.nodes.get(&root_handle).map_or(0f32, |node| {
            // find all edges to ([Null], slot=0, ch=ch)
            self.sum_input_to_slot(&self.nodes, node, time, 0, ch, &Vec::new()) as f32
        })
    }
}
impl RefRenderer {
    /// Get the value on an edge at a particular time
    /// When backtracking from the output, we push each Node onto the context if we enter inside of
    ///   it (i.e. if it's a nested DAG) & pop when exiting.
    fn get_value(&self, nodes: &NodeMap, edge: &Edge, time: u64, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let from = edge.from_full();
        if *from.node_handle() == None {
            // Reading from one of the inputs to the top of `context`
            // TODO: we can avoid cloning by reversing the pop after recursing.
            let mut new_context = context.clone();
            let (new_nodes, head) = new_context.pop().unwrap();
            // Sum the inputs to the matching slot/ch
            self.sum_input_to_slot(new_nodes, &new_nodes[&head], time, edge.from_slot(), edge.from_ch(), &new_context)
        } else {
            // Reading from another node within the DAG
            let node = &nodes[&from];
            match node.data {
                MyNodeData::UserNode(ref new_nodes) => {
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    // Now find the *output* of the sub dag (or 0 if the sub dag has no outputs)
                    new_nodes.get(&NodeHandle::toplevel()).map_or(0f64, |root_node| {
                        self.sum_input_to_slot(&new_nodes, root_node, time, edge.from_slot(), edge.from_ch(), &new_context)
                    })
                },
                // Output = sum of all edges to Null of the same slot & ch, within the given DAG.
                MyNodeData::Graph(ref dag_handle) => {
                    // TODO: we can avoid cloning by reversing the push after recursing.
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    let subdag = &nodes[&NodeHandle::new_dag(dag_handle.clone())];
                    self.sum_input_to_slot(nodes, &subdag, time, edge.from_slot(), edge.from_ch(), &new_context)
                }
                // Output = sum of all inputs to slot 0 of the same ch.
                MyNodeData::Delay => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Delay slot != 0");
                        0f64
                    } else {
                        let delay_frames = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        // Clamp delay value to [0, u64::max]
                        let delay_int = if delay_frames < 0f64 {
                            0u64
                        } else if delay_frames > u64::max_value() as f64 {
                            // TODO: u64::max isn't precisely representable in f64;
                            // will this cause issues?
                            u64::max_value()
                        } else {
                            // Note: this conversion is flooring.
                            delay_frames as u64
                        };
                        // t<0 -> value is 0.
                        time.checked_sub(delay_int).map_or(0f64, |origin_time| {
                            self.sum_input_to_slot(nodes, node, origin_time, 0, edge.from_ch(), context)
                        })
                    }
                },
                MyNodeData::F32Const => {
                    // Float value is encoded via the slot.
                    let value = edge.from_slot();
                    unpack_f32(value) as f64
                },
                MyNodeData::Multiply => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Multiply slot != 0");
                        0f64
                    } else {
                        // Sum all inputs from slot=0 and slot=2 into two separate
                        // variables, then multiply them.
                        let val_a = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        let val_b = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        val_a * val_b
                    }
                },
                MyNodeData::Divide => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from MultInv slot != 0");
                        0f64
                    } else {
                        // Sum all inputs
                        let dividend = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        let divisor = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        dividend / divisor
                    }
                },
                MyNodeData::Modulo => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Modulo slot != 0");
                        0f64
                    } else {
                        // Sum all dividends
                        let dividend = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        // Sum all divisors
                        let divisor = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        let rem = dividend % divisor;
                        if rem < 0f64 {
                            // TODO: We may be losing precision here, if rem is small.
                            // We should find a way to do true modulus.
                            rem + divisor
                        } else {
                            rem
                        }
                    }
                },
                _ => panic!("Internal RefRenderer error: illegal node type"),
            }
        }
    }
    /// Return the sum of all inputs into a specific slot/channel of the given
    /// node at the given time.
    fn sum_input_to_slot(&self, nodes: &NodeMap, node: &Node, time: u64, slot: u32, ch: u8, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let edges_in = node.inbound.iter().filter(|in_edge| {
            in_edge.to_slot() == slot && in_edge.to_ch() == ch
        });
        edges_in.map(|edge| self.get_value(nodes, edge, time, context)).sum()
    }

    fn make_node(&self, data: &NodeData) -> MyNodeData {
        match *data {
            NodeData::Graph(ref handle) => MyNodeData::Graph(handle.clone()),
            NodeData::Effect(ref effect) => {
                match effect.meta().get_primitive_url() {
                    Some(ref url) => {
                        match url.path() {
                            "/F32Const" => MyNodeData::F32Const,
                            "/Delay" => MyNodeData::Delay,
                            "/Multiply" => MyNodeData::Multiply,
                            "/Divide" => MyNodeData::Divide,
                            "/Modulo" => MyNodeData::Modulo,
                            _ => panic!("Unrecognized primitive effect: {} (full url: {})", url.path(), url),
                        }
                    }
                    None => {
                        let graph = effect.routegraph().as_ref().unwrap();
                        let mut nodes = HashMap::new();
                        for (node, data) in graph.iter_nodes() {
                            nodes.insert(node.clone(), Node::new(self.make_node(data)));
                        }
                        for edge in graph.iter_edges() {
                            nodes.entry(edge.to_full()).or_insert_with(|| {
                                Node::new(MyNodeData::DagIO)
                            }).inbound.insert(edge.clone());
                        }
                        MyNodeData::UserNode(nodes)
                    }
                }
            }
        }
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(handle.clone(), Node::new(my_node_data));
        // If the node is part of a new DAG, allocate data so that future edges
        // to null within the DAG can be held.
        self.nodes.entry(NodeHandle::new_dag(handle.dag_handle().clone())).or_insert_with(|| {
            Node::new(MyNodeData::DagIO)
        });
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.remove(edge);
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: HashSet::new(),
        }
    }
}
<commit_msg>Implement 'min' effect.<commit_after>use std::collections::{HashMap, HashSet};

use render::Renderer;
use routing::{DagHandle, Edge, GraphWatcher, NodeData, NodeHandle};
use util::unpack_f32;

type NodeMap = HashMap<NodeHandle, Node>;

#[derive(Default)]
pub struct RefRenderer {
    nodes: NodeMap,
}

struct Node {
    data: MyNodeData,
    inbound: HashSet<Edge>
}

enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(NodeMap),
    /// This node is an instance of another DAG.
    Graph(DagHandle),
    /// Primitive Delay effect
    Delay,
    /// Primitive Constant effect.
    /// Also serves as a unit step;
    /// Returns the float value for t >= 0, else 0.
    F32Const,
    /// Primitive effect to multiply TWO input streams sample-wise.
    Multiply,
    /// Primitive effect to calculate A/B.
    /// Note: because not all floating point numbers have inverses,
    /// A * (1/B) != A/B. Hence, we need division (not inversion) for proper
    /// precision.
    Divide,
    /// Primitive effect to calculate A%B (true modulo; not remainder.
    /// Result, y,  is always positive: y is bounded by [0, B).
    Modulo,
    /// Primitive effect to return the sample-wise minimum of two input streams.
    /// Max(A, B) can be implemented as -Min(-A, -B).
    /// The choice to define Min instead of Max was mostly arbitrary,
    /// and chosen because Min is more common in linear programming to avoid dealing
    /// with Inf.
    Min,
    /// This node is a DAG definition. i.e. it holds the output edges of a DAG.
    DagIO,
}

impl Renderer for RefRenderer {
    fn get_sample(&mut self, time: u64, ch: u8) -> f32 {
        // Try to find the edge that goes to -> (Null, slot=0, ch=ch)
        let root_handle = NodeHandle::toplevel();
        // empty graph is 0 = silence
        self.nodes.get(&root_handle).map_or(0f32, |node| {
            // find all edges to ([Null], slot=0, ch=ch)
            self.sum_input_to_slot(&self.nodes, node, time, 0, ch, &Vec::new()) as f32
        })
    }
}
impl RefRenderer {
    /// Get the value on an edge at a particular time
    /// When backtracking from the output, we push each Node onto the context if we enter inside of
    ///   it (i.e. if it's a nested DAG) & pop when exiting.
    fn get_value(&self, nodes: &NodeMap, edge: &Edge, time: u64, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let from = edge.from_full();
        if *from.node_handle() == None {
            // Reading from one of the inputs to the top of `context`
            // TODO: we can avoid cloning by reversing the pop after recursing.
            let mut new_context = context.clone();
            let (new_nodes, head) = new_context.pop().unwrap();
            // Sum the inputs to the matching slot/ch
            self.sum_input_to_slot(new_nodes, &new_nodes[&head], time, edge.from_slot(), edge.from_ch(), &new_context)
        } else {
            // Reading from another node within the DAG
            let node = &nodes[&from];
            match node.data {
                MyNodeData::UserNode(ref new_nodes) => {
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    // Now find the *output* of the sub dag (or 0 if the sub dag has no outputs)
                    new_nodes.get(&NodeHandle::toplevel()).map_or(0f64, |root_node| {
                        self.sum_input_to_slot(&new_nodes, root_node, time, edge.from_slot(), edge.from_ch(), &new_context)
                    })
                },
                // Output = sum of all edges to Null of the same slot & ch, within the given DAG.
                MyNodeData::Graph(ref dag_handle) => {
                    // TODO: we can avoid cloning by reversing the push after recursing.
                    let mut new_context = context.clone();
                    new_context.push((nodes, from));
                    let subdag = &nodes[&NodeHandle::new_dag(dag_handle.clone())];
                    self.sum_input_to_slot(nodes, &subdag, time, edge.from_slot(), edge.from_ch(), &new_context)
                }
                // Output = sum of all inputs to slot 0 of the same ch.
                MyNodeData::Delay => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Delay slot != 0");
                        0f64
                    } else {
                        let delay_frames = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        // Clamp delay value to [0, u64::max]
                        let delay_int = if delay_frames < 0f64 {
                            0u64
                        } else if delay_frames > u64::max_value() as f64 {
                            // TODO: u64::max isn't precisely representable in f64;
                            // will this cause issues?
                            u64::max_value()
                        } else {
                            // Note: this conversion is flooring.
                            delay_frames as u64
                        };
                        // t<0 -> value is 0.
                        time.checked_sub(delay_int).map_or(0f64, |origin_time| {
                            self.sum_input_to_slot(nodes, node, origin_time, 0, edge.from_ch(), context)
                        })
                    }
                },
                MyNodeData::F32Const => {
                    // Float value is encoded via the slot.
                    let value = edge.from_slot();
                    unpack_f32(value) as f64
                },
                MyNodeData::Multiply => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Multiply slot != 0");
                        0f64
                    } else {
                        // Sum all inputs from slot=0 and slot=2 into two separate
                        // variables, then multiply them.
                        let val_a = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        let val_b = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        val_a * val_b
                    }
                },
                MyNodeData::Divide => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from MultInv slot != 0");
                        0f64
                    } else {
                        // Sum all inputs
                        let dividend = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        let divisor = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        dividend / divisor
                    }
                },
                MyNodeData::Min => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Modulo slot != 0");
                        0f64
                    } else {
                        let input_a = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        let input_b = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        input_a.min(input_b)
                    }
                }
                MyNodeData::Modulo => {
                    // The only nonzero output is slot=0.
                    if edge.from_slot() != 0 {
                        println!("Warning: attempt to read from Modulo slot != 0");
                        0f64
                    } else {
                        // Sum all dividends
                        let dividend = self.sum_input_to_slot(nodes, node, time, 0, edge.from_ch(), context);
                        // Sum all divisors
                        let divisor = self.sum_input_to_slot(nodes, node, time, 1, edge.from_ch(), context);
                        let rem = dividend % divisor;
                        if rem < 0f64 {
                            // TODO: We may be losing precision here, if rem is small.
                            // We should find a way to do true modulus.
                            rem + divisor
                        } else {
                            rem
                        }
                    }
                },
                _ => panic!("Internal RefRenderer error: illegal node type"),
            }
        }
    }
    /// Return the sum of all inputs into a specific slot/channel of the given
    /// node at the given time.
    fn sum_input_to_slot(&self, nodes: &NodeMap, node: &Node, time: u64, slot: u32, ch: u8, context: &Vec<(&NodeMap, NodeHandle)>) -> f64 {
        let edges_in = node.inbound.iter().filter(|in_edge| {
            in_edge.to_slot() == slot && in_edge.to_ch() == ch
        });
        edges_in.map(|edge| self.get_value(nodes, edge, time, context)).sum()
    }

    fn make_node(&self, data: &NodeData) -> MyNodeData {
        match *data {
            NodeData::Graph(ref handle) => MyNodeData::Graph(handle.clone()),
            NodeData::Effect(ref effect) => {
                match effect.meta().get_primitive_url() {
                    Some(ref url) => {
                        match url.path() {
                            "/F32Const" => MyNodeData::F32Const,
                            "/Delay" => MyNodeData::Delay,
                            "/Multiply" => MyNodeData::Multiply,
                            "/Divide" => MyNodeData::Divide,
                            "/Modulo" => MyNodeData::Modulo,
                            "/Min" => MyNodeData::Min,
                            _ => panic!("Unrecognized primitive effect: {} (full url: {})", url.path(), url),
                        }
                    }
                    None => {
                        let graph = effect.routegraph().as_ref().unwrap();
                        let mut nodes = HashMap::new();
                        for (node, data) in graph.iter_nodes() {
                            nodes.insert(node.clone(), Node::new(self.make_node(data)));
                        }
                        for edge in graph.iter_edges() {
                            nodes.entry(edge.to_full()).or_insert_with(|| {
                                Node::new(MyNodeData::DagIO)
                            }).inbound.insert(edge.clone());
                        }
                        MyNodeData::UserNode(nodes)
                    }
                }
            }
        }
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(handle.clone(), Node::new(my_node_data));
        // If the node is part of a new DAG, allocate data so that future edges
        // to null within the DAG can be held.
        self.nodes.entry(NodeHandle::new_dag(handle.dag_handle().clone())).or_insert_with(|| {
            Node::new(MyNodeData::DagIO)
        });
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.insert(edge.clone());
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        self.nodes.get_mut(&edge.to_full()).unwrap().inbound.remove(edge);
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: HashSet::new(),
        }
    }
}
<|endoftext|>