<commit_before>use rustc_serialize::json::Json;

use analysis::Analyzer;
use term::Term;

use query::{Query, TermMatcher};
use query::parser::{QueryParseContext, QueryParseError};
use query::parser::utils::{parse_string, parse_float, Operator, parse_operator, parse_field_and_boost};

pub fn parse(context: &QueryParseContext, json: &Json) -> Result<Query, QueryParseError> {
    let object = try!(json.as_object().ok_or(QueryParseError::ExpectedObject));

    // Get configuration
    let mut fields_with_boosts = Vec::new();
    let mut query = String::new();
    let mut boost = 1.0f64;
    let mut operator = Operator::Or;

    let mut has_fields_key = false;
    let mut has_query_key = false;

    for (key, val) in object.iter() {
        match key.as_ref() {
            "fields" => {
                has_fields_key = true;

                match *val {
                    Json::Array(ref array) => {
                        for field in array.iter() {
                            fields_with_boosts.push(try!(parse_field_and_boost(field)));
                        }
                    }
                    _ => return Err(QueryParseError::ExpectedArray)
                }
            }
            "query" => {
                has_query_key = true;
                query = try!(parse_string(val));
            }
            "boost" => {
                boost = try!(parse_float(val));
            }
            "operator" => {
                operator = try!(parse_operator(val))
            }
            _ => return Err(QueryParseError::UnrecognisedKey(key.clone()))
        }
    }

    if !has_fields_key {
        return Err(QueryParseError::ExpectedKey("fields"))
    }

    if !has_query_key {
        return Err(QueryParseError::ExpectedKey("query"))
    }

    // Convert query string into term query objects
    let mut field_queries = Vec::new();
    for (field_name, field_boost) in fields_with_boosts {
        let mut term_queries = Vec::new();
        for token in Analyzer::Standard.run(query.clone()) {
            term_queries.push(Query::MatchTerm {
                field: field_name.clone(),
                term: token.term,
                matcher: TermMatcher::Exact,
            });
        }

        let mut field_query = match operator {
            Operator::Or => {
                Query::Bool {
                    must: vec![],
                    must_not: vec![],
                    should: term_queries,
                    filter: vec![],
                    minimum_should_match: 1,
                }
            }
            Operator::And => {
                Query::Bool {
                    must: term_queries,
                    must_not: vec![],
                    should: vec![],
                    filter: vec![],
                    minimum_should_match: 0,
                }
            }
        };

        // Add boost
        if field_boost != 1.0f64 {
            field_query = Query::BoostScore {
                query: Box::new(field_query),
                boost: field_boost,
            };
        }

        field_queries.push(field_query);
    }

    let mut query = Query::DisjunctionMax {
        queries: field_queries,
    };

    // Add boost
    if boost != 1.0f64 {
        query = Query::BoostScore {
            query: Box::new(query),
            boost: boost,
        };
    }

    return Ok(query);
}

#[cfg(test)]
mod tests {
    use rustc_serialize::json::Json;

    use term::Term;
    use query::{Query, TermMatcher};
    use query::parser::{QueryParseContext, QueryParseError};
    use index::Index;

    use super::parse;

    #[test]
    fn test_multi_match_query() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Bool {
                    must: vec![],
                    must_not: vec![],
                    should: vec![
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                    filter: vec![],
                    minimum_should_match: 1,
                },
                Query::Bool {
                    must: vec![],
                    must_not: vec![],
                    should: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                    filter: vec![],
                    minimum_should_match: 1,
                }
            ],
        }));
    }

    #[test]
    fn test_multi_term_multi_match_query() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"hello world\",
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Bool {
                    must: vec![],
                    must_not: vec![],
                    should: vec![
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("hello".to_string()),
                            matcher: TermMatcher::Exact,
                        },
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("world".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                    filter: vec![],
                    minimum_should_match: 1,
                },
                Query::Bool {
                    must: vec![],
                    must_not: vec![],
                    should: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("hello".to_string()),
                            matcher: TermMatcher::Exact,
                        },
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("world".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                    filter: vec![],
                    minimum_should_match: 1,
                }
            ],
        }));
    }

    #[test]
    fn test_with_boost() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": 2.0
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::BoostScore {
            query: Box::new(Query::DisjunctionMax {
                queries: vec![
                    Query::Bool {
                        must: vec![],
                        must_not: vec![],
                        should: vec![
                            Query::MatchTerm {
                                field: "bar".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                        filter: vec![],
                        minimum_should_match: 1,
                    },
                    Query::Bool {
                        must: vec![],
                        must_not: vec![],
                        should: vec![
                            Query::MatchTerm {
                                field: "baz".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                        filter: vec![],
                        minimum_should_match: 1,
                    }
                ],
            }),
            boost: 2.0f64,
        }));
    }

    #[test]
    fn test_with_boost_integer() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": 2
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::BoostScore {
            query: Box::new(Query::DisjunctionMax {
                queries: vec![
                    Query::Bool {
                        must: vec![],
                        must_not: vec![],
                        should: vec![
                            Query::MatchTerm {
                                field: "bar".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                        filter: vec![],
                        minimum_should_match: 1,
                    },
                    Query::Bool {
                        must: vec![],
                        must_not: vec![],
                        should: vec![
                            Query::MatchTerm {
                                field: "baz".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                        filter: vec![],
                        minimum_should_match: 1,
                    }
                ],
            }),
            boost: 2.0f64,
        }));
    }

    #[test]
    fn test_with_field_boost() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar^2\", \"baz^1.0\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::BoostScore {
                    query: Box::new(Query::Bool {
                        must: vec![],
                        must_not: vec![],
                        should: vec![
                            Query::MatchTerm {
                                field: "bar".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                        filter: vec![],
                        minimum_should_match: 1,
                    }),
                    boost: 2.0f64,
                },
                Query::Bool {
                    must: vec![],
                    must_not: vec![],
                    should: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                    filter: vec![],
                    minimum_should_match: 1,
                }
            ],
        }));
    }

    #[test]
    fn test_with_and_operator() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"operator\": \"and\"
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Bool {
                    must: vec![
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                    must_not: vec![],
                    should: vec![],
                    filter: vec![],
                    minimum_should_match: 0,
                },
                Query::Bool {
                    must: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                    must_not: vec![],
                    should: vec![],
                    filter: vec![],
                    minimum_should_match: 0,
                }
            ],
        }));
    }

    #[test]
    fn test_gives_error_for_incorrect_type() {
        // String
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        \"foo\"
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Array
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        [
            \"foo\"
        ]
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Integer
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        123
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Float
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        123.1234
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));
    }

    #[test]
    fn test_gives_error_for_incorrect_query_type() {
        // Object
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": {
                \"foo\": \"bar\"
            },
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Array
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": [\"foo\"],
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Integer
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": 123,
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Float
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": 123.456,
            \"fields\": [\"bar\", \"baz\"]
        }        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));
    }

    #[test]
    fn test_gives_error_for_incorrect_fields_type() {
        // String
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": \"bar\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Object
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": {
                \"value\": [\"bar\", \"baz\"]
            }
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Integer
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": 123
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Float
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": 123.456
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));
    }

    #[test]
    fn test_gives_error_for_incorrect_boost_type() {
        // String
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": \"2\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));

        // Array
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": [2]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));

        // Object
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": {
                \"value\": 2
            }
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));
    }

    #[test]
    fn test_gives_error_for_missing_query() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedKey("query")));
    }

    #[test]
    fn test_gives_error_for_missing_fields() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedKey("fields")));
    }

    #[test]
    fn test_gives_error_for_extra_key() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"hello\": \"world\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::UnrecognisedKey("hello".to_string())));
    }
}
<commit_msg>Rewrite multi_match query to not use Bool query<commit_after>use rustc_serialize::json::Json;

use analysis::Analyzer;
use term::Term;

use query::{Query, TermMatcher};
use query::parser::{QueryParseContext, QueryParseError};
use query::parser::utils::{parse_string, parse_float, Operator, parse_operator, parse_field_and_boost};

pub fn parse(context: &QueryParseContext, json: &Json) -> Result<Query, QueryParseError> {
    let object = try!(json.as_object().ok_or(QueryParseError::ExpectedObject));

    // Get configuration
    let mut fields_with_boosts = Vec::new();
    let mut query = String::new();
    let mut boost = 1.0f64;
    let mut operator = Operator::Or;

    let mut has_fields_key = false;
    let mut has_query_key = false;

    for (key, val) in object.iter() {
        match key.as_ref() {
            "fields" => {
                has_fields_key = true;

                match *val {
                    Json::Array(ref array) => {
                        for field in array.iter() {
                            fields_with_boosts.push(try!(parse_field_and_boost(field)));
                        }
                    }
                    _ => return Err(QueryParseError::ExpectedArray)
                }
            }
            "query" => {
                has_query_key = true;
                query = try!(parse_string(val));
            }
            "boost" => {
                boost = try!(parse_float(val));
            }
            "operator" => {
                operator = try!(parse_operator(val))
            }
            _ => return Err(QueryParseError::UnrecognisedKey(key.clone()))
        }
    }

    if !has_fields_key {
        return Err(QueryParseError::ExpectedKey("fields"))
    }

    if !has_query_key {
        return Err(QueryParseError::ExpectedKey("query"))
    }

    // Convert query string into term query objects
    let mut field_queries = Vec::new();
    for (field_name, field_boost) in fields_with_boosts {
        let mut term_queries = Vec::new();
        for token in Analyzer::Standard.run(query.clone()) {
            term_queries.push(Query::MatchTerm {
                field: field_name.clone(),
                term: token.term,
                matcher: TermMatcher::Exact,
            });
        }

        let mut field_query = match operator {
            Operator::Or => {
                Query::Or {
                    queries: term_queries,
                }
            }
            Operator::And => {
                Query::And {
                    queries: term_queries,
                }
            }
        };

        // Add boost
        if field_boost != 1.0f64 {
            field_query = Query::BoostScore {
                query: Box::new(field_query),
                boost: field_boost,
            };
        }

        field_queries.push(field_query);
    }

    let mut query = Query::DisjunctionMax {
        queries: field_queries,
    };

    // Add boost
    if boost != 1.0f64 {
        query = Query::BoostScore {
            query: Box::new(query),
            boost: boost,
        };
    }

    return Ok(query);
}

#[cfg(test)]
mod tests {
    use rustc_serialize::json::Json;

    use term::Term;
    use query::{Query, TermMatcher};
    use query::parser::{QueryParseContext, QueryParseError};
    use index::Index;

    use super::parse;

    #[test]
    fn test_multi_match_query() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Or {
                    queries: vec![
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                },
                Query::Or {
                    queries: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                }
            ],
        }));
    }

    #[test]
    fn test_multi_term_multi_match_query() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"hello world\",
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Or {
                    queries: vec![
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("hello".to_string()),
                            matcher: TermMatcher::Exact,
                        },
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("world".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                },
                Query::Or {
                    queries: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("hello".to_string()),
                            matcher: TermMatcher::Exact,
                        },
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("world".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                }
            ],
        }));
    }

    #[test]
    fn test_with_boost() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": 2.0
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::BoostScore {
            query: Box::new(Query::DisjunctionMax {
                queries: vec![
                    Query::Or {
                        queries: vec![
                            Query::MatchTerm {
                                field: "bar".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                    },
                    Query::Or {
                        queries: vec![
                            Query::MatchTerm {
                                field: "baz".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                    }
                ],
            }),
            boost: 2.0f64,
        }));
    }

    #[test]
    fn test_with_boost_integer() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": 2
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::BoostScore {
            query: Box::new(Query::DisjunctionMax {
                queries: vec![
                    Query::Or {
                        queries: vec![
                            Query::MatchTerm {
                                field: "bar".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                    },
                    Query::Or {
                        queries: vec![
                            Query::MatchTerm {
                                field: "baz".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                    }
                ],
            }),
            boost: 2.0f64,
        }));
    }

    #[test]
    fn test_with_field_boost() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar^2\", \"baz^1.0\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::BoostScore {
                    query: Box::new(Query::Or {
                        queries: vec![
                            Query::MatchTerm {
                                field: "bar".to_string(),
                                term: Term::String("foo".to_string()),
                                matcher: TermMatcher::Exact,
                            }
                        ],
                    }),
                    boost: 2.0f64,
                },
                Query::Or {
                    queries: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                }
            ],
        }));
    }

    #[test]
    fn test_with_and_operator() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"operator\": \"and\"
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::And {
                    queries: vec![
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                },
                Query::And {
                    queries: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                        }
                    ],
                }
            ],
        }));
    }

    #[test]
    fn test_gives_error_for_incorrect_type() {
        // String
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        \"foo\"
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Array
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        [
            \"foo\"
        ]
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Integer
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        123
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Float
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        123.1234
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));
    }

    #[test]
    fn test_gives_error_for_incorrect_query_type() {
        // Object
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": {
                \"foo\": \"bar\"
            },
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Array
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": [\"foo\"],
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Integer
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": 123,
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Float
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": 123.456,
            \"fields\": [\"bar\", \"baz\"]
        }        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));
    }

    #[test]
    fn test_gives_error_for_incorrect_fields_type() {
        // String
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": \"bar\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Object
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": {
                \"value\": [\"bar\", \"baz\"]
            }
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Integer
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": 123
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Float
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": 123.456
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));
    }

    #[test]
    fn test_gives_error_for_incorrect_boost_type() {
        // String
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": \"2\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));

        // Array
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": [2]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));

        // Object
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": {
                \"value\": 2
            }
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));
    }

    #[test]
    fn test_gives_error_for_missing_query() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedKey("query")));
    }

    #[test]
    fn test_gives_error_for_missing_fields() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedKey("fields")));
    }

    #[test]
    fn test_gives_error_for_extra_key() {
        let query = parse(&QueryParseContext::new(&Index::new()), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"hello\": \"world\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::UnrecognisedKey("hello".to_string())));
    }
}
<|endoftext|>


<commit_before>use std::time::{Duration,
                Instant};

/// How long to wait for an Ack after we ping
const PING_TIMING_DEFAULT_MS: u64 = 1000;
/// How long to wait for an Ack after we PingReq - should be at least 2x the PING_TIMING_DEFAULT_MS
const PINGREQ_TIMING_DEFAULT_MS: u64 = 2100;
/// How many protocol periods before a suspect member is marked as confirmed.
const SUSPICION_TIMEOUT_DEFAULT_PROTOCOL_PERIODS: u64 = 3;
/// How long is the gossip period
const GOSSIP_PERIOD_DEFAULT_MS: u64 = 1000;
/// How long before we set a confirmed member to a departed member, removing them from quorums
///   just for your own sanity - this is 3 days.
const DEPARTURE_TIMEOUT_DEFAULT_MS: u64 = 259_200_000;

/// The timing of the outbound threads.
#[derive(Debug, Clone)]
pub struct Timing {
    pub ping_ms: u64,
    pub pingreq_ms: u64,
    pub gossip_period_ms: u64,
    pub suspicion_timeout_protocol_periods: u64,
    pub departure_timeout_ms: u64,
}

impl Default for Timing {
    fn default() -> Timing {
        Timing { ping_ms: PING_TIMING_DEFAULT_MS,
                 pingreq_ms: PINGREQ_TIMING_DEFAULT_MS,
                 gossip_period_ms: GOSSIP_PERIOD_DEFAULT_MS,
                 suspicion_timeout_protocol_periods: SUSPICION_TIMEOUT_DEFAULT_PROTOCOL_PERIODS,
                 departure_timeout_ms: DEPARTURE_TIMEOUT_DEFAULT_MS, }
    }
}

impl Timing {
    /// When should this gossip period expire
    pub fn gossip_timeout(&self) -> Instant {
        Instant::now() + Duration::from_millis(self.gossip_period_ms)
    }

    /// How long is a protocol period, in millis.
    pub fn protocol_period_ms(&self) -> u64 { self.ping_ms + self.pingreq_ms }

    /// When should this ping record time out?
    pub fn ping_timeout(&self) -> Instant { Instant::now() + Duration::from_millis(self.ping_ms) }

    /// When should this pingreq timeout?
    pub fn pingreq_timeout(&self) -> Instant {
        Instant::now() + Duration::from_millis(self.pingreq_ms)
    }

    /// How long before the next scheduled protocol period
    pub fn next_protocol_period(&self) -> Instant {
        Instant::now() + Duration::from_millis(self.ping_ms + self.pingreq_ms)
    }

    /// How long before this suspect entry times out
    pub fn suspicion_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.protocol_period_ms() * self.suspicion_timeout_protocol_periods)
    }

    pub fn departure_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.departure_timeout_ms)
    }
}
<commit_msg>None of the Timing struct members need to be public<commit_after>use std::time::{Duration,
                Instant};

/// How long to wait for an Ack after we ping
const PING_TIMING_DEFAULT_MS: u64 = 1000;
/// How long to wait for an Ack after we PingReq - should be at least 2x the PING_TIMING_DEFAULT_MS
const PINGREQ_TIMING_DEFAULT_MS: u64 = 2100;
/// How many protocol periods before a suspect member is marked as confirmed.
const SUSPICION_TIMEOUT_DEFAULT_PROTOCOL_PERIODS: u64 = 3;
/// How long is the gossip period
const GOSSIP_PERIOD_DEFAULT_MS: u64 = 1000;
/// How long before we set a confirmed member to a departed member, removing them from quorums
///   just for your own sanity - this is 3 days.
const DEPARTURE_TIMEOUT_DEFAULT_MS: u64 = 259_200_000;

/// The timing of the outbound threads.
#[derive(Debug, Clone)]
pub struct Timing {
    ping_ms: u64,
    pingreq_ms: u64,
    gossip_period_ms: u64,
    suspicion_timeout_protocol_periods: u64,
    departure_timeout_ms: u64,
}

impl Default for Timing {
    fn default() -> Timing {
        Timing { ping_ms: PING_TIMING_DEFAULT_MS,
                 pingreq_ms: PINGREQ_TIMING_DEFAULT_MS,
                 gossip_period_ms: GOSSIP_PERIOD_DEFAULT_MS,
                 suspicion_timeout_protocol_periods: SUSPICION_TIMEOUT_DEFAULT_PROTOCOL_PERIODS,
                 departure_timeout_ms: DEPARTURE_TIMEOUT_DEFAULT_MS, }
    }
}

impl Timing {
    /// When should this gossip period expire
    pub fn gossip_timeout(&self) -> Instant {
        Instant::now() + Duration::from_millis(self.gossip_period_ms)
    }

    /// How long is a protocol period, in millis.
    pub fn protocol_period_ms(&self) -> u64 { self.ping_ms + self.pingreq_ms }

    /// When should this ping record time out?
    pub fn ping_timeout(&self) -> Instant { Instant::now() + Duration::from_millis(self.ping_ms) }

    /// When should this pingreq timeout?
    pub fn pingreq_timeout(&self) -> Instant {
        Instant::now() + Duration::from_millis(self.pingreq_ms)
    }

    /// How long before the next scheduled protocol period
    pub fn next_protocol_period(&self) -> Instant {
        Instant::now() + Duration::from_millis(self.ping_ms + self.pingreq_ms)
    }

    /// How long before this suspect entry times out
    pub fn suspicion_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.protocol_period_ms() * self.suspicion_timeout_protocol_periods)
    }

    pub fn departure_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.departure_timeout_ms)
    }
}
<|endoftext|>


<commit_before>//! Kata OS command line interface

// This brief bootstrap of Rust-in-Kata prototypes a minimal modular design
// for the DebugConsole CLI use case.
//
// * kata_io Read/Write interface (or move to std::, but that requires alloc)
// * kata_uart_client implementation of the kata_io interface
// * kata_line_reader
// * kata_shell
// * kata_debug_console main entry point fn run()

#![no_std]

#[cfg(not(test))]
extern crate kata_panic;

use kata_allocator;
use kata_logger::KataLogger;
use kata_shell;
use kata_uart_client;
use log::trace;

#[no_mangle]
pub extern "C" fn pre_init() {
    static KATA_LOGGER: KataLogger = KataLogger;
    log::set_logger(&KATA_LOGGER).unwrap();
    // NB: set to Trace for early-boot msgs
    log::set_max_level(log::LevelFilter::Debug);

    // TODO(sleffler): temp until we integrate with seL4
    static mut HEAP_MEMORY: [u8; 16 * 1024] = [0; 16 * 1024];
    unsafe {
        kata_allocator::ALLOCATOR.init(HEAP_MEMORY.as_mut_ptr() as usize, HEAP_MEMORY.len());
        trace!(
            "setup heap: start_addr {:p} size {}",
            HEAP_MEMORY.as_ptr(),
            HEAP_MEMORY.len()
        );
    }
}

/// Entry point for DebugConsole. Runs the shell with UART IO.
#[no_mangle]
pub extern "C" fn run() -> ! {
    trace!("run");
    let mut tx = kata_uart_client::Tx {};
    let mut rx = kata_uart_client::Rx {};
    kata_shell::repl(&mut tx, &mut rx);
}
<commit_msg>Increases DebugConsole heap to 1MB<commit_after>//! Kata OS command line interface

// This brief bootstrap of Rust-in-Kata prototypes a minimal modular design
// for the DebugConsole CLI use case.
//
// * kata_io Read/Write interface (or move to std::, but that requires alloc)
// * kata_uart_client implementation of the kata_io interface
// * kata_line_reader
// * kata_shell
// * kata_debug_console main entry point fn run()

#![no_std]

#[cfg(not(test))]
extern crate kata_panic;

use kata_allocator;
use kata_logger::KataLogger;
use kata_shell;
use kata_uart_client;
use log::trace;

#[no_mangle]
pub extern "C" fn pre_init() {
    static KATA_LOGGER: KataLogger = KataLogger;
    log::set_logger(&KATA_LOGGER).unwrap();
    // NB: set to Trace for early-boot msgs
    log::set_max_level(log::LevelFilter::Debug);

    // TODO(b/200946906): Review per-component heap allocations, including this one.
    const HEAP_SIZE: usize = 1 << 20;
    static mut HEAP_MEMORY: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
    unsafe {
        kata_allocator::ALLOCATOR.init(HEAP_MEMORY.as_mut_ptr() as usize, HEAP_MEMORY.len());
        trace!(
            "setup heap: start_addr {:p} size {}",
            HEAP_MEMORY.as_ptr(),
            HEAP_MEMORY.len()
        );
    }
}

/// Entry point for DebugConsole. Runs the shell with UART IO.
#[no_mangle]
pub extern "C" fn run() -> ! {
    trace!("run");
    let mut tx = kata_uart_client::Tx {};
    let mut rx = kata_uart_client::Rx {};
    kata_shell::repl(&mut tx, &mut rx);
}
<|endoftext|>


<commit_before>use std::cmp;

pub fn lev_distance(me: &str, t: &str) -> usize {
    if me.is_empty() {
        return t.chars().count();
    }
    if t.is_empty() {
        return me.chars().count();
    }

    let mut dcol = (0..t.len() + 1).collect::<Vec<_>>();
    let mut t_last = 0;

    for (i, sc) in me.chars().enumerate() {
        let mut current = i;
        dcol[0] = current + 1;

        for (j, tc) in t.chars().enumerate() {
            let next = dcol[j + 1];

            if sc == tc {
                dcol[j + 1] = current;
            } else {
                dcol[j + 1] = cmp::min(current, next);
                dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1;
            }

            current = next;
            t_last = j;
        }
    }

    dcol[t_last + 1]
}

#[test]
fn test_lev_distance() {
    use std::char::{from_u32, MAX};
    // Test bytelength agnosticity
    for c in (0u32..MAX as u32)
        .filter_map(|i| from_u32(i))
        .map(|i| i.to_string())
    {
        assert_eq!(lev_distance(&c, &c), 0);
    }

    let a = "\nMäry häd ä little lämb\n\nLittle lämb\n";
    let b = "\nMary häd ä little lämb\n\nLittle lämb\n";
    let c = "Mary häd ä little lämb\n\nLittle lämb\n";
    assert_eq!(lev_distance(a, b), 1);
    assert_eq!(lev_distance(b, a), 1);
    assert_eq!(lev_distance(a, c), 2);
    assert_eq!(lev_distance(c, a), 2);
    assert_eq!(lev_distance(b, c), 1);
    assert_eq!(lev_distance(c, b), 1);
}
<commit_msg>Resolve a redundant_closure lint warning<commit_after>use std::cmp;

pub fn lev_distance(me: &str, t: &str) -> usize {
    if me.is_empty() {
        return t.chars().count();
    }
    if t.is_empty() {
        return me.chars().count();
    }

    let mut dcol = (0..t.len() + 1).collect::<Vec<_>>();
    let mut t_last = 0;

    for (i, sc) in me.chars().enumerate() {
        let mut current = i;
        dcol[0] = current + 1;

        for (j, tc) in t.chars().enumerate() {
            let next = dcol[j + 1];

            if sc == tc {
                dcol[j + 1] = current;
            } else {
                dcol[j + 1] = cmp::min(current, next);
                dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1;
            }

            current = next;
            t_last = j;
        }
    }

    dcol[t_last + 1]
}

#[test]
fn test_lev_distance() {
    use std::char::{from_u32, MAX};
    // Test bytelength agnosticity
    for c in (0u32..MAX as u32)
        .filter_map(from_u32)
        .map(|i| i.to_string())
    {
        assert_eq!(lev_distance(&c, &c), 0);
    }

    let a = "\nMäry häd ä little lämb\n\nLittle lämb\n";
    let b = "\nMary häd ä little lämb\n\nLittle lämb\n";
    let c = "Mary häd ä little lämb\n\nLittle lämb\n";
    assert_eq!(lev_distance(a, b), 1);
    assert_eq!(lev_distance(b, a), 1);
    assert_eq!(lev_distance(a, c), 2);
    assert_eq!(lev_distance(c, a), 2);
    assert_eq!(lev_distance(b, c), 1);
    assert_eq!(lev_distance(c, b), 1);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::Attr;
use dom::attr::AttrValue;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::element::AttributeMutation;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::image::base::Image;
use net_traits::image_cache_task::{ImageResponder, ImageResponse};
use script_task::ScriptTaskEventCategory::UpdateReplacedElement;
use script_task::{CommonScriptMsg, Runnable, ScriptChan};
use std::sync::Arc;
use string_cache::Atom;
use url::{Url, UrlParser};
use util::str::DOMString;

#[dom_struct]
pub struct HTMLImageElement {
    htmlelement: HTMLElement,
    url: DOMRefCell<Option<Url>>,
    image: DOMRefCell<Option<Arc<Image>>>,
}

impl HTMLImageElement {
    pub fn get_url(&self) -> Option<Url>{
        self.url.borrow().clone()
    }
}

struct ImageResponseHandlerRunnable {
    element: Trusted<HTMLImageElement>,
    image: ImageResponse,
}

impl ImageResponseHandlerRunnable {
    fn new(element: Trusted<HTMLImageElement>, image: ImageResponse)
           -> ImageResponseHandlerRunnable {
        ImageResponseHandlerRunnable {
            element: element,
            image: image,
        }
    }
}

impl Runnable for ImageResponseHandlerRunnable {
    fn handler(self: Box<Self>) {
        // Update the image field
        let element = self.element.root();
        let element_ref = element.r();
        *element_ref.image.borrow_mut() = match self.image {
            ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => {
                Some(image)
            }
            ImageResponse::None => None,
        };

        // Mark the node dirty
        let document = document_from_node(&*element);
        document.content_changed(element.upcast(), NodeDamage::OtherNodeDamage);

        // Fire image.onload
        let window = window_from_node(document.r());
        let event = Event::new(GlobalRef::Window(window.r()),
                               DOMString::from("load"),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable);
        event.fire(element.upcast());

        // Trigger reflow
        window.add_pending_reflow();
    }
}

impl HTMLImageElement {
    /// Makes the local `image` member match the status of the `src` attribute and starts
    /// prefetching the image. This method must be called after `src` is changed.
    fn update_image(&self, value: Option<(DOMString, Url)>) {
        let document = document_from_node(self);
        let window = document.window();
        let image_cache = window.image_cache_task();
        match value {
            None => {
                *self.url.borrow_mut() = None;
                *self.image.borrow_mut() = None;
            }
            Some((src, base_url)) => {
                let img_url = UrlParser::new().base_url(&base_url).parse(&src);
                // FIXME: handle URL parse errors more gracefully.
                let img_url = img_url.unwrap();
                *self.url.borrow_mut() = Some(img_url.clone());

                let trusted_node = Trusted::new(window.get_cx(), self, window.script_chan());
                let (responder_sender, responder_receiver) = ipc::channel().unwrap();
                let script_chan = window.script_chan();
                let wrapper = window.get_runnable_wrapper();
                ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
                    // Return the image via a message to the script task, which marks the element
                    // as dirty and triggers a reflow.
                    let image_response = message.to().unwrap();
                    let runnable = ImageResponseHandlerRunnable::new(
                        trusted_node.clone(), image_response);
                    let runnable = wrapper.wrap_runnable(runnable);
                    script_chan.send(CommonScriptMsg::RunnableMsg(
                        UpdateReplacedElement, runnable)).unwrap();
                });

                image_cache.request_image(img_url,
                                          window.image_cache_chan(),
                                          Some(ImageResponder::new(responder_sender)));
            }
        }
    }

    fn new_inherited(localName: Atom, prefix: Option<DOMString>, document: &Document) -> HTMLImageElement {
        HTMLImageElement {
            htmlelement: HTMLElement::new_inherited(localName, prefix, document),
            url: DOMRefCell::new(None),
            image: DOMRefCell::new(None),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: Atom,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLImageElement> {
        let element = HTMLImageElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLImageElementBinding::Wrap)
    }

    pub fn Image(global: GlobalRef,
                 width: Option<u32>,
                 height: Option<u32>) -> Fallible<Root<HTMLImageElement>> {
        let document = global.as_window().Document();
        let image = HTMLImageElement::new(atom!("img"), None, document.r());
        if let Some(w) = width {
            image.SetWidth(w);
        }
        if let Some(h) = height {
            image.SetHeight(h);
        }

        Ok(image)
    }
}

pub trait LayoutHTMLImageElementHelpers {
    #[allow(unsafe_code)]
    unsafe fn image(&self) -> Option<Arc<Image>>;

    #[allow(unsafe_code)]
    unsafe fn image_url(&self) -> Option<Url>;
}

impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> {
    #[allow(unsafe_code)]
    unsafe fn image(&self) -> Option<Arc<Image>> {
        (*self.unsafe_get()).image.borrow_for_layout().clone()
    }

    #[allow(unsafe_code)]
    unsafe fn image_url(&self) -> Option<Url> {
        (*self.unsafe_get()).url.borrow_for_layout().clone()
    }
}

impl HTMLImageElementMethods for HTMLImageElement {
    // https://html.spec.whatwg.org/multipage/#dom-img-alt
    make_getter!(Alt, "alt");
    // https://html.spec.whatwg.org/multipage/#dom-img-alt
    make_setter!(SetAlt, "alt");

    // https://html.spec.whatwg.org/multipage/#dom-img-src
    make_url_getter!(Src, "src");
    // https://html.spec.whatwg.org/multipage/#dom-img-src
    make_setter!(SetSrc, "src");

    // https://html.spec.whatwg.org/multipage/#dom-img-usemap
    make_getter!(UseMap, "usemap");
    // https://html.spec.whatwg.org/multipage/#dom-img-usemap
    make_setter!(SetUseMap, "usemap");

    // https://html.spec.whatwg.org/multipage/#dom-img-ismap
    make_bool_getter!(IsMap, "ismap");
    // https://html.spec.whatwg.org/multipage/#dom-img-ismap
    make_bool_setter!(SetIsMap, "ismap");

    // https://html.spec.whatwg.org/multipage/#dom-img-width
    fn Width(&self) -> u32 {
        let node = self.upcast::<Node>();
        let rect = node.get_bounding_content_box();
        rect.size.width.to_px() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-width
    make_uint_setter!(SetWidth, "width");

    // https://html.spec.whatwg.org/multipage/#dom-img-height
    fn Height(&self) -> u32 {
        let node = self.upcast::<Node>();
        let rect = node.get_bounding_content_box();
        rect.size.height.to_px() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-height
    make_uint_setter!(SetHeight, "height");

    // https://html.spec.whatwg.org/multipage/#dom-img-naturalwidth
    fn NaturalWidth(&self) -> u32 {
        let image = self.image.borrow();

        match *image {
            Some(ref image) => image.width,
            None => 0,
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-naturalheight
    fn NaturalHeight(&self) -> u32 {
        let image = self.image.borrow();

        match *image {
            Some(ref image) => image.height,
            None => 0,
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-complete
    fn Complete(&self) -> bool {
        let image = self.image.borrow();
        image.is_some()
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-name
    make_getter!(Name, "name");

    // https://html.spec.whatwg.org/multipage/#dom-img-name
    make_atomic_setter!(SetName, "name");

    // https://html.spec.whatwg.org/multipage/#dom-img-align
    make_getter!(Align, "align");

    // https://html.spec.whatwg.org/multipage/#dom-img-align
    make_setter!(SetAlign, "align");

    // https://html.spec.whatwg.org/multipage/#dom-img-hspace
    make_uint_getter!(Hspace, "hspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-hspace
    make_uint_setter!(SetHspace, "hspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-vspace
    make_uint_getter!(Vspace, "vspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-vspace
    make_uint_setter!(SetVspace, "vspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
    make_getter!(LongDesc, "longdesc");

    // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
    make_setter!(SetLongDesc, "longdesc");

    // https://html.spec.whatwg.org/multipage/#dom-img-border
    make_getter!(Border, "border");

    // https://html.spec.whatwg.org/multipage/#dom-img-border
    make_setter!(SetBorder, "border");
}

impl VirtualMethods for HTMLImageElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match attr.local_name() {
            &atom!("src") => {
                self.update_image(mutation.new_value(attr).map(|value| {
                    // FIXME(ajeffrey): convert directly from AttrValue to DOMString
                    (DOMString::from(&**value), window_from_node(self).get_url())
                }));
            },
            _ => {},
        }
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("name") => AttrValue::from_atomic(value),
            &atom!("width") | &atom!("height") |
            &atom!("hspace") | &atom!("vspace") => AttrValue::from_u32(value, 0),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}
<commit_msg>Make IPC image cache listener silently drop failures instead of catastrophically panicking. Resolves #8817.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::Attr;
use dom::attr::AttrValue;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::element::AttributeMutation;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::image::base::Image;
use net_traits::image_cache_task::{ImageResponder, ImageResponse};
use script_task::ScriptTaskEventCategory::UpdateReplacedElement;
use script_task::{CommonScriptMsg, Runnable, ScriptChan};
use std::sync::Arc;
use string_cache::Atom;
use url::{Url, UrlParser};
use util::str::DOMString;

#[dom_struct]
pub struct HTMLImageElement {
    htmlelement: HTMLElement,
    url: DOMRefCell<Option<Url>>,
    image: DOMRefCell<Option<Arc<Image>>>,
}

impl HTMLImageElement {
    pub fn get_url(&self) -> Option<Url>{
        self.url.borrow().clone()
    }
}

struct ImageResponseHandlerRunnable {
    element: Trusted<HTMLImageElement>,
    image: ImageResponse,
}

impl ImageResponseHandlerRunnable {
    fn new(element: Trusted<HTMLImageElement>, image: ImageResponse)
           -> ImageResponseHandlerRunnable {
        ImageResponseHandlerRunnable {
            element: element,
            image: image,
        }
    }
}

impl Runnable for ImageResponseHandlerRunnable {
    fn handler(self: Box<Self>) {
        // Update the image field
        let element = self.element.root();
        let element_ref = element.r();
        *element_ref.image.borrow_mut() = match self.image {
            ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => {
                Some(image)
            }
            ImageResponse::None => None,
        };

        // Mark the node dirty
        let document = document_from_node(&*element);
        document.content_changed(element.upcast(), NodeDamage::OtherNodeDamage);

        // Fire image.onload
        let window = window_from_node(document.r());
        let event = Event::new(GlobalRef::Window(window.r()),
                               DOMString::from("load"),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable);
        event.fire(element.upcast());

        // Trigger reflow
        window.add_pending_reflow();
    }
}

impl HTMLImageElement {
    /// Makes the local `image` member match the status of the `src` attribute and starts
    /// prefetching the image. This method must be called after `src` is changed.
    fn update_image(&self, value: Option<(DOMString, Url)>) {
        let document = document_from_node(self);
        let window = document.window();
        let image_cache = window.image_cache_task();
        match value {
            None => {
                *self.url.borrow_mut() = None;
                *self.image.borrow_mut() = None;
            }
            Some((src, base_url)) => {
                let img_url = UrlParser::new().base_url(&base_url).parse(&src);
                // FIXME: handle URL parse errors more gracefully.
                let img_url = img_url.unwrap();
                *self.url.borrow_mut() = Some(img_url.clone());

                let trusted_node = Trusted::new(window.get_cx(), self, window.script_chan());
                let (responder_sender, responder_receiver) = ipc::channel().unwrap();
                let script_chan = window.script_chan();
                let wrapper = window.get_runnable_wrapper();
                ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
                    // Return the image via a message to the script task, which marks the element
                    // as dirty and triggers a reflow.
                    let image_response = message.to().unwrap();
                    let runnable = ImageResponseHandlerRunnable::new(
                        trusted_node.clone(), image_response);
                    let runnable = wrapper.wrap_runnable(runnable);
                    let _ = script_chan.send(CommonScriptMsg::RunnableMsg(
                        UpdateReplacedElement, runnable));
                });

                image_cache.request_image(img_url,
                                          window.image_cache_chan(),
                                          Some(ImageResponder::new(responder_sender)));
            }
        }
    }

    fn new_inherited(localName: Atom, prefix: Option<DOMString>, document: &Document) -> HTMLImageElement {
        HTMLImageElement {
            htmlelement: HTMLElement::new_inherited(localName, prefix, document),
            url: DOMRefCell::new(None),
            image: DOMRefCell::new(None),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: Atom,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLImageElement> {
        let element = HTMLImageElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLImageElementBinding::Wrap)
    }

    pub fn Image(global: GlobalRef,
                 width: Option<u32>,
                 height: Option<u32>) -> Fallible<Root<HTMLImageElement>> {
        let document = global.as_window().Document();
        let image = HTMLImageElement::new(atom!("img"), None, document.r());
        if let Some(w) = width {
            image.SetWidth(w);
        }
        if let Some(h) = height {
            image.SetHeight(h);
        }

        Ok(image)
    }
}

pub trait LayoutHTMLImageElementHelpers {
    #[allow(unsafe_code)]
    unsafe fn image(&self) -> Option<Arc<Image>>;

    #[allow(unsafe_code)]
    unsafe fn image_url(&self) -> Option<Url>;
}

impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> {
    #[allow(unsafe_code)]
    unsafe fn image(&self) -> Option<Arc<Image>> {
        (*self.unsafe_get()).image.borrow_for_layout().clone()
    }

    #[allow(unsafe_code)]
    unsafe fn image_url(&self) -> Option<Url> {
        (*self.unsafe_get()).url.borrow_for_layout().clone()
    }
}

impl HTMLImageElementMethods for HTMLImageElement {
    // https://html.spec.whatwg.org/multipage/#dom-img-alt
    make_getter!(Alt, "alt");
    // https://html.spec.whatwg.org/multipage/#dom-img-alt
    make_setter!(SetAlt, "alt");

    // https://html.spec.whatwg.org/multipage/#dom-img-src
    make_url_getter!(Src, "src");
    // https://html.spec.whatwg.org/multipage/#dom-img-src
    make_setter!(SetSrc, "src");

    // https://html.spec.whatwg.org/multipage/#dom-img-usemap
    make_getter!(UseMap, "usemap");
    // https://html.spec.whatwg.org/multipage/#dom-img-usemap
    make_setter!(SetUseMap, "usemap");

    // https://html.spec.whatwg.org/multipage/#dom-img-ismap
    make_bool_getter!(IsMap, "ismap");
    // https://html.spec.whatwg.org/multipage/#dom-img-ismap
    make_bool_setter!(SetIsMap, "ismap");

    // https://html.spec.whatwg.org/multipage/#dom-img-width
    fn Width(&self) -> u32 {
        let node = self.upcast::<Node>();
        let rect = node.get_bounding_content_box();
        rect.size.width.to_px() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-width
    make_uint_setter!(SetWidth, "width");

    // https://html.spec.whatwg.org/multipage/#dom-img-height
    fn Height(&self) -> u32 {
        let node = self.upcast::<Node>();
        let rect = node.get_bounding_content_box();
        rect.size.height.to_px() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-height
    make_uint_setter!(SetHeight, "height");

    // https://html.spec.whatwg.org/multipage/#dom-img-naturalwidth
    fn NaturalWidth(&self) -> u32 {
        let image = self.image.borrow();

        match *image {
            Some(ref image) => image.width,
            None => 0,
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-naturalheight
    fn NaturalHeight(&self) -> u32 {
        let image = self.image.borrow();

        match *image {
            Some(ref image) => image.height,
            None => 0,
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-complete
    fn Complete(&self) -> bool {
        let image = self.image.borrow();
        image.is_some()
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-name
    make_getter!(Name, "name");

    // https://html.spec.whatwg.org/multipage/#dom-img-name
    make_atomic_setter!(SetName, "name");

    // https://html.spec.whatwg.org/multipage/#dom-img-align
    make_getter!(Align, "align");

    // https://html.spec.whatwg.org/multipage/#dom-img-align
    make_setter!(SetAlign, "align");

    // https://html.spec.whatwg.org/multipage/#dom-img-hspace
    make_uint_getter!(Hspace, "hspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-hspace
    make_uint_setter!(SetHspace, "hspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-vspace
    make_uint_getter!(Vspace, "vspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-vspace
    make_uint_setter!(SetVspace, "vspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
    make_getter!(LongDesc, "longdesc");

    // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
    make_setter!(SetLongDesc, "longdesc");

    // https://html.spec.whatwg.org/multipage/#dom-img-border
    make_getter!(Border, "border");

    // https://html.spec.whatwg.org/multipage/#dom-img-border
    make_setter!(SetBorder, "border");
}

impl VirtualMethods for HTMLImageElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match attr.local_name() {
            &atom!("src") => {
                self.update_image(mutation.new_value(attr).map(|value| {
                    // FIXME(ajeffrey): convert directly from AttrValue to DOMString
                    (DOMString::from(&**value), window_from_node(self).get_url())
                }));
            },
            _ => {},
        }
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("name") => AttrValue::from_atomic(value),
            &atom!("width") | &atom!("height") |
            &atom!("hspace") | &atom!("vspace") => AttrValue::from_u32(value, 0),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(missing_docs)]

use cookie_rs::Cookie;
use euclid::rect::Rect;
use hyper_serde::Serde;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId;
use rustc_serialize::json::{Json, ToJson};
use servo_url::ServoUrl;

#[derive(Deserialize, Serialize)]
pub enum WebDriverScriptCommand {
    AddCookie(
        #[serde(deserialize_with = "::hyper_serde::deserialize",
                serialize_with = "::hyper_serde::serialize")]
        Cookie,
        IpcSender<Result<(), WebDriverCookieError>>
    ),
    ExecuteScript(String, IpcSender<WebDriverJSResult>),
    ExecuteAsyncScript(String, IpcSender<WebDriverJSResult>),
    FindElementCSS(String, IpcSender<Result<Option<String>, ()>>),
    FindElementsCSS(String, IpcSender<Result<Vec<String>, ()>>),
    FocusElement(String, IpcSender<Result<(), ()>>),
    GetActiveElement(IpcSender<Option<String>>),
    GetCookie(String, IpcSender<Vec<Serde<Cookie>>>),
    GetCookies(IpcSender<Vec<Serde<Cookie>>>),
    GetElementAttribute(String, String, IpcSender<Result<Option<String>, ()>>),
    GetElementCSS(String, String, IpcSender<Result<String, ()>>),
    GetElementRect(String, IpcSender<Result<Rect<f64>, ()>>),
    GetElementTagName(String, IpcSender<Result<String, ()>>),
    GetElementText(String, IpcSender<Result<String, ()>>),
    GetFrameId(WebDriverFrameId, IpcSender<Result<Option<PipelineId>, ()>>),
    GetUrl(IpcSender<ServoUrl>),
    IsEnabled(String, IpcSender<Result<bool, ()>>),
    IsSelected(String, IpcSender<Result<bool, ()>>),
    GetTitle(IpcSender<String>)
}

#[derive(Deserialize, Serialize)]
pub enum WebDriverCookieError {
    InvalidDomain,
    UnableToSetCookie
}

#[derive(Deserialize, Serialize)]
pub enum WebDriverJSValue {
    Undefined,
    Null,
    Boolean(bool),
    Number(f64),
    String(String),
    // TODO: Object and WebElement
}

#[derive(Deserialize, Serialize)]
pub enum WebDriverJSError {
    Timeout,
    UnknownType,
    /// Occurs when handler received an event message for a layout channel that is not
    /// associated with the current script thread
    BrowsingContextNotFound
}

pub type WebDriverJSResult = Result<WebDriverJSValue, WebDriverJSError>;

#[derive(Deserialize, Serialize)]
pub enum WebDriverFrameId {
    Short(u16),
    Element(String),
    Parent
}

impl ToJson for WebDriverJSValue {
    fn to_json(&self) -> Json {
        match *self {
            WebDriverJSValue::Undefined => Json::Null,
            WebDriverJSValue::Null => Json::Null,
            WebDriverJSValue::Boolean(ref x) => x.to_json(),
            WebDriverJSValue::Number(ref x) => x.to_json(),
            WebDriverJSValue::String(ref x) => x.to_json()
        }
    }
}

#[derive(Deserialize, Serialize)]
pub enum LoadStatus {
    LoadComplete,
    LoadTimeout
}
<commit_msg>Reformat WebDriverScriptCommand::AddCookie.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(missing_docs)]

use cookie_rs::Cookie;
use euclid::rect::Rect;
use hyper_serde::Serde;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId;
use rustc_serialize::json::{Json, ToJson};
use servo_url::ServoUrl;

#[derive(Deserialize, Serialize)]
pub enum WebDriverScriptCommand {
    AddCookie(#[serde(deserialize_with = "::hyper_serde::deserialize",
                      serialize_with = "::hyper_serde::serialize")]
              Cookie,
              IpcSender<Result<(), WebDriverCookieError>>),
    ExecuteScript(String, IpcSender<WebDriverJSResult>),
    ExecuteAsyncScript(String, IpcSender<WebDriverJSResult>),
    FindElementCSS(String, IpcSender<Result<Option<String>, ()>>),
    FindElementsCSS(String, IpcSender<Result<Vec<String>, ()>>),
    FocusElement(String, IpcSender<Result<(), ()>>),
    GetActiveElement(IpcSender<Option<String>>),
    GetCookie(String, IpcSender<Vec<Serde<Cookie>>>),
    GetCookies(IpcSender<Vec<Serde<Cookie>>>),
    GetElementAttribute(String, String, IpcSender<Result<Option<String>, ()>>),
    GetElementCSS(String, String, IpcSender<Result<String, ()>>),
    GetElementRect(String, IpcSender<Result<Rect<f64>, ()>>),
    GetElementTagName(String, IpcSender<Result<String, ()>>),
    GetElementText(String, IpcSender<Result<String, ()>>),
    GetFrameId(WebDriverFrameId, IpcSender<Result<Option<PipelineId>, ()>>),
    GetUrl(IpcSender<ServoUrl>),
    IsEnabled(String, IpcSender<Result<bool, ()>>),
    IsSelected(String, IpcSender<Result<bool, ()>>),
    GetTitle(IpcSender<String>)
}

#[derive(Deserialize, Serialize)]
pub enum WebDriverCookieError {
    InvalidDomain,
    UnableToSetCookie
}

#[derive(Deserialize, Serialize)]
pub enum WebDriverJSValue {
    Undefined,
    Null,
    Boolean(bool),
    Number(f64),
    String(String),
    // TODO: Object and WebElement
}

#[derive(Deserialize, Serialize)]
pub enum WebDriverJSError {
    Timeout,
    UnknownType,
    /// Occurs when handler received an event message for a layout channel that is not
    /// associated with the current script thread
    BrowsingContextNotFound
}

pub type WebDriverJSResult = Result<WebDriverJSValue, WebDriverJSError>;

#[derive(Deserialize, Serialize)]
pub enum WebDriverFrameId {
    Short(u16),
    Element(String),
    Parent
}

impl ToJson for WebDriverJSValue {
    fn to_json(&self) -> Json {
        match *self {
            WebDriverJSValue::Undefined => Json::Null,
            WebDriverJSValue::Null => Json::Null,
            WebDriverJSValue::Boolean(ref x) => x.to_json(),
            WebDriverJSValue::Number(ref x) => x.to_json(),
            WebDriverJSValue::String(ref x) => x.to_json()
        }
    }
}

#[derive(Deserialize, Serialize)]
pub enum LoadStatus {
    LoadComplete,
    LoadTimeout
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Processes that are directly spawned by shared mempool runtime initialization

use crate::{
    core_mempool::{CoreMempool, TimelineState},
    counters,
    logging::{LogEntry, LogEvent, LogSchema},
    network::{MempoolNetworkEvents, MempoolSyncMsg},
    shared_mempool::{
        tasks,
        types::{notify_subscribers, ScheduledBroadcast, SharedMempool, SharedMempoolNotification},
    },
    CommitNotification, ConsensusRequest, SubmissionStatus,
};
use ::network::protocols::network::Event;
use anyhow::Result;
use bounded_executor::BoundedExecutor;
use channel::diem_channel;
use diem_config::{config::PeerNetworkId, network_id::NodeNetworkId};
use diem_infallible::Mutex;
use diem_logger::prelude::*;
use diem_trace::prelude::*;
use diem_types::{
    mempool_status::MempoolStatus, on_chain_config::OnChainConfigPayload,
    transaction::SignedTransaction, vm_status::DiscardedVMStatus,
};
use futures::{
    channel::{mpsc, oneshot},
    stream::{select_all, FuturesUnordered},
    StreamExt,
};
use std::{
    sync::Arc,
    time::{Duration, SystemTime},
};
use tokio::{runtime::Handle, time::interval};
use tokio_stream::wrappers::IntervalStream;
use vm_validator::vm_validator::TransactionValidation;

/// Coordinator that handles inbound network events and outbound txn broadcasts.
pub(crate) async fn coordinator<V>(
    mut smp: SharedMempool<V>,
    executor: Handle,
    network_events: Vec<(NodeNetworkId, MempoolNetworkEvents)>,
    mut client_events: mpsc::Receiver<(
        SignedTransaction,
        oneshot::Sender<Result<SubmissionStatus>>,
    )>,
    mut consensus_requests: mpsc::Receiver<ConsensusRequest>,
    mut state_sync_requests: mpsc::Receiver<CommitNotification>,
    mut mempool_reconfig_events: diem_channel::Receiver<(), OnChainConfigPayload>,
) where
    V: TransactionValidation,
{
    info!(LogSchema::event_log(
        LogEntry::CoordinatorRuntime,
        LogEvent::Start
    ));
    let smp_events: Vec<_> = network_events
        .into_iter()
        .map(|(network_id, events)| events.map(move |e| (network_id.clone(), e)))
        .collect();
    let mut events = select_all(smp_events).fuse();
    let mempool = smp.mempool.clone();
    let mut scheduled_broadcasts = FuturesUnordered::new();

    // Use a BoundedExecutor to restrict only `workers_available` concurrent
    // worker tasks that can process incoming transactions.
    let workers_available = smp.config.shared_mempool_max_concurrent_inbound_syncs;
    let bounded_executor = BoundedExecutor::new(workers_available, executor.clone());

    loop {
        let _timer = counters::MAIN_LOOP.start_timer();
        ::futures::select! {
            (msg, callback) = client_events.select_next_some() => {
                handle_client_event(&mut smp, &bounded_executor, msg, callback).await;
            },
            msg = consensus_requests.select_next_some() => {
                tasks::process_consensus_request(&mempool, msg).await;
            }
            msg = state_sync_requests.select_next_some() => {
                handle_state_sync_request(&mut smp, msg);
            }
            config_update = mempool_reconfig_events.select_next_some() => {
                handle_mempool_reconfig_event(&mut smp, &bounded_executor, config_update).await;
            },
            (peer, backoff) = scheduled_broadcasts.select_next_some() => {
                tasks::execute_broadcast(peer, backoff, &mut smp, &mut scheduled_broadcasts, executor.clone());
            },
            (network_id, event) = events.select_next_some() => {
                handle_event(&executor, &bounded_executor, &mut scheduled_broadcasts, &mut smp, network_id, event).await;
            },
            complete => break,
        }
    }
    error!(LogSchema::event_log(
        LogEntry::CoordinatorRuntime,
        LogEvent::Terminated
    ));
}

async fn handle_client_event<V>(
    smp: &mut SharedMempool<V>,
    bounded_executor: &BoundedExecutor,
    msg: SignedTransaction,
    callback: oneshot::Sender<anyhow::Result<(MempoolStatus, Option<DiscardedVMStatus>)>>,
) where
    V: TransactionValidation,
{
    trace_event!("mempool::client_event", {"txn", msg.sender(), msg.sequence_number()});
    // This timer measures how long it took for the bounded executor to *schedule* the
    // task.
    let _timer = counters::TASK_SPAWN_LATENCY
        .with_label_values(&[counters::CLIENT_EVENT_LABEL, counters::SPAWN_LABEL])
        .start_timer();
    // This timer measures how long it took for the task to go from scheduled to started.
    let task_start_timer = counters::TASK_SPAWN_LATENCY
        .with_label_values(&[counters::CLIENT_EVENT_LABEL, counters::START_LABEL])
        .start_timer();
    bounded_executor
        .spawn(tasks::process_client_transaction_submission(
            smp.clone(),
            msg,
            callback,
            task_start_timer,
        ))
        .await;
}

fn handle_state_sync_request<V>(smp: &mut SharedMempool<V>, msg: CommitNotification)
where
    V: TransactionValidation,
{
    let _timer = counters::TASK_SPAWN_LATENCY
        .with_label_values(&[counters::STATE_SYNC_EVENT_LABEL, counters::SPAWN_LABEL])
        .start_timer();
    tokio::spawn(tasks::process_state_sync_request(smp.mempool.clone(), msg));
}

async fn handle_mempool_reconfig_event<V>(
    smp: &mut SharedMempool<V>,
    bounded_executor: &BoundedExecutor,
    config_update: OnChainConfigPayload,
) where
    V: TransactionValidation,
{
    info!(LogSchema::event_log(
        LogEntry::ReconfigUpdate,
        LogEvent::Received
    ));
    let _timer = counters::TASK_SPAWN_LATENCY
        .with_label_values(&[counters::RECONFIG_EVENT_LABEL, counters::SPAWN_LABEL])
        .start_timer();
    bounded_executor
        .spawn(tasks::process_config_update(
            config_update,
            smp.validator.clone(),
        ))
        .await;
}

async fn handle_event<V>(
    executor: &Handle,
    bounded_executor: &BoundedExecutor,
    scheduled_broadcasts: &mut FuturesUnordered<ScheduledBroadcast>,
    smp: &mut SharedMempool<V>,
    network_id: NodeNetworkId,
    event: Event<MempoolSyncMsg>,
) where
    V: TransactionValidation,
{
    match event {
        Event::NewPeer(metadata) => {
            counters::SHARED_MEMPOOL_EVENTS
                .with_label_values(&["new_peer"])
                .inc();
            let origin = metadata.origin;
            let peer = PeerNetworkId(network_id, metadata.remote_peer_id);
            let is_new_peer = smp.peer_manager.add_peer(peer.clone(), origin);
            let is_upstream_peer = smp.peer_manager.is_upstream_peer(&peer, Some(origin));
            debug!(LogSchema::new(LogEntry::NewPeer)
                .peer(&peer)
                .is_upstream_peer(is_upstream_peer));
            notify_subscribers(SharedMempoolNotification::PeerStateChange, &smp.subscribers);
            if is_new_peer && is_upstream_peer {
                tasks::execute_broadcast(peer, false, smp, scheduled_broadcasts, executor.clone());
            }
        }
        Event::LostPeer(metadata) => {
            counters::SHARED_MEMPOOL_EVENTS
                .with_label_values(&["lost_peer"])
                .inc();
            let peer = PeerNetworkId(network_id, metadata.remote_peer_id);
            debug!(LogSchema::new(LogEntry::LostPeer)
                .peer(&peer)
                .is_upstream_peer(
                    smp.peer_manager
                        .is_upstream_peer(&peer, Some(metadata.origin))
                ));
            smp.peer_manager.disable_peer(peer);
            notify_subscribers(SharedMempoolNotification::PeerStateChange, &smp.subscribers);
        }
        Event::Message(peer_id, msg) => {
            counters::SHARED_MEMPOOL_EVENTS
                .with_label_values(&["message"])
                .inc();
            match msg {
                MempoolSyncMsg::BroadcastTransactionsRequest {
                    request_id,
                    transactions,
                } => {
                    let smp_clone = smp.clone();
                    let peer = PeerNetworkId(network_id, peer_id);
                    let timeline_state = match smp.peer_manager.is_upstream_peer(&peer, None) {
                        true => TimelineState::NonQualified,
                        false => TimelineState::NotReady,
                    };
                    // This timer measures how long it took for the bounded executor to
                    // *schedule* the task.
                    let _timer = counters::TASK_SPAWN_LATENCY
                        .with_label_values(&[
                            counters::PEER_BROADCAST_EVENT_LABEL,
                            counters::SPAWN_LABEL,
                        ])
                        .start_timer();
                    // This timer measures how long it took for the task to go from scheduled
                    // to started.
                    let task_start_timer = counters::TASK_SPAWN_LATENCY
                        .with_label_values(&[
                            counters::PEER_BROADCAST_EVENT_LABEL,
                            counters::START_LABEL,
                        ])
                        .start_timer();
                    bounded_executor
                        .spawn(tasks::process_transaction_broadcast(
                            smp_clone,
                            transactions,
                            request_id,
                            timeline_state,
                            peer,
                            task_start_timer,
                        ))
                        .await;
                }
                MempoolSyncMsg::BroadcastTransactionsResponse {
                    request_id,
                    retry,
                    backoff,
                } => {
                    let ack_timestamp = SystemTime::now();
                    smp.peer_manager.process_broadcast_ack(
                        PeerNetworkId(network_id, peer_id),
                        request_id,
                        retry,
                        backoff,
                        ack_timestamp,
                    );
                }
            }
        }
        Event::RpcRequest(peer_id, _msg, _res_tx) => {
            counters::UNEXPECTED_NETWORK_MSG_COUNT
                .with_label_values(&[&network_id.network_id().to_string(), &peer_id.to_string()])
                .inc();
            sample!(
                SampleRate::Duration(Duration::from_secs(60)),
                warn!(LogSchema::new(LogEntry::UnexpectedNetworkMsg)
                    .peer(&PeerNetworkId(network_id, peer_id)))
            );
        }
    }
}

/// Garbage collect all expired transactions by SystemTTL.
pub(crate) async fn gc_coordinator(mempool: Arc<Mutex<CoreMempool>>, gc_interval_ms: u64) {
    info!(LogSchema::event_log(LogEntry::GCRuntime, LogEvent::Start));
    let mut interval = IntervalStream::new(interval(Duration::from_millis(gc_interval_ms)));
    while let Some(_interval) = interval.next().await {
        sample!(
            SampleRate::Duration(Duration::from_secs(60)),
            info!(LogSchema::event_log(LogEntry::GCRuntime, LogEvent::Live))
        );
        mempool.lock().gc();
    }

    error!(LogSchema::event_log(
        LogEntry::GCRuntime,
        LogEvent::Terminated
    ));
}

/// Periodically logs a snapshot of transactions in core mempool.
/// In the future we may want an interactive way to directly query mempool's internal state.
/// For now, we will rely on this periodic snapshot to observe the internal state.
pub(crate) async fn snapshot_job(mempool: Arc<Mutex<CoreMempool>>, snapshot_interval_secs: u64) {
    let mut interval = IntervalStream::new(interval(Duration::from_secs(snapshot_interval_secs)));
    while let Some(_interval) = interval.next().await {
        let snapshot = mempool.lock().gen_snapshot();
        debug!(LogSchema::new(LogEntry::MempoolSnapshot).txns(snapshot));
    }
}
<commit_msg>[mempool] Remove unnecessary clone in coordinator<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Processes that are directly spawned by shared mempool runtime initialization

use crate::{
    core_mempool::{CoreMempool, TimelineState},
    counters,
    logging::{LogEntry, LogEvent, LogSchema},
    network::{MempoolNetworkEvents, MempoolSyncMsg},
    shared_mempool::{
        tasks,
        types::{notify_subscribers, ScheduledBroadcast, SharedMempool, SharedMempoolNotification},
    },
    CommitNotification, ConsensusRequest, SubmissionStatus,
};
use ::network::protocols::network::Event;
use anyhow::Result;
use bounded_executor::BoundedExecutor;
use channel::diem_channel;
use diem_config::{config::PeerNetworkId, network_id::NodeNetworkId};
use diem_infallible::Mutex;
use diem_logger::prelude::*;
use diem_trace::prelude::*;
use diem_types::{
    mempool_status::MempoolStatus, on_chain_config::OnChainConfigPayload,
    transaction::SignedTransaction, vm_status::DiscardedVMStatus,
};
use futures::{
    channel::{mpsc, oneshot},
    stream::{select_all, FuturesUnordered},
    StreamExt,
};
use std::{
    sync::Arc,
    time::{Duration, SystemTime},
};
use tokio::{runtime::Handle, time::interval};
use tokio_stream::wrappers::IntervalStream;
use vm_validator::vm_validator::TransactionValidation;

/// Coordinator that handles inbound network events and outbound txn broadcasts.
pub(crate) async fn coordinator<V>(
    mut smp: SharedMempool<V>,
    executor: Handle,
    network_events: Vec<(NodeNetworkId, MempoolNetworkEvents)>,
    mut client_events: mpsc::Receiver<(
        SignedTransaction,
        oneshot::Sender<Result<SubmissionStatus>>,
    )>,
    mut consensus_requests: mpsc::Receiver<ConsensusRequest>,
    mut state_sync_requests: mpsc::Receiver<CommitNotification>,
    mut mempool_reconfig_events: diem_channel::Receiver<(), OnChainConfigPayload>,
) where
    V: TransactionValidation,
{
    info!(LogSchema::event_log(
        LogEntry::CoordinatorRuntime,
        LogEvent::Start
    ));
    let smp_events: Vec<_> = network_events
        .into_iter()
        .map(|(network_id, events)| events.map(move |e| (network_id.clone(), e)))
        .collect();
    let mut events = select_all(smp_events).fuse();
    let mut scheduled_broadcasts = FuturesUnordered::new();

    // Use a BoundedExecutor to restrict only `workers_available` concurrent
    // worker tasks that can process incoming transactions.
    let workers_available = smp.config.shared_mempool_max_concurrent_inbound_syncs;
    let bounded_executor = BoundedExecutor::new(workers_available, executor.clone());

    loop {
        let _timer = counters::MAIN_LOOP.start_timer();
        ::futures::select! {
            (msg, callback) = client_events.select_next_some() => {
                handle_client_event(&mut smp, &bounded_executor, msg, callback).await;
            },
            msg = consensus_requests.select_next_some() => {
                tasks::process_consensus_request(&smp.mempool, msg).await;
            }
            msg = state_sync_requests.select_next_some() => {
                handle_state_sync_request(&mut smp, msg);
            }
            config_update = mempool_reconfig_events.select_next_some() => {
                handle_mempool_reconfig_event(&mut smp, &bounded_executor, config_update).await;
            },
            (peer, backoff) = scheduled_broadcasts.select_next_some() => {
                tasks::execute_broadcast(peer, backoff, &mut smp, &mut scheduled_broadcasts, executor.clone());
            },
            (network_id, event) = events.select_next_some() => {
                handle_event(&executor, &bounded_executor, &mut scheduled_broadcasts, &mut smp, network_id, event).await;
            },
            complete => break,
        }
    }
    error!(LogSchema::event_log(
        LogEntry::CoordinatorRuntime,
        LogEvent::Terminated
    ));
}

async fn handle_client_event<V>(
    smp: &mut SharedMempool<V>,
    bounded_executor: &BoundedExecutor,
    msg: SignedTransaction,
    callback: oneshot::Sender<anyhow::Result<(MempoolStatus, Option<DiscardedVMStatus>)>>,
) where
    V: TransactionValidation,
{
    trace_event!("mempool::client_event", {"txn", msg.sender(), msg.sequence_number()});
    // This timer measures how long it took for the bounded executor to *schedule* the
    // task.
    let _timer = counters::TASK_SPAWN_LATENCY
        .with_label_values(&[counters::CLIENT_EVENT_LABEL, counters::SPAWN_LABEL])
        .start_timer();
    // This timer measures how long it took for the task to go from scheduled to started.
    let task_start_timer = counters::TASK_SPAWN_LATENCY
        .with_label_values(&[counters::CLIENT_EVENT_LABEL, counters::START_LABEL])
        .start_timer();
    bounded_executor
        .spawn(tasks::process_client_transaction_submission(
            smp.clone(),
            msg,
            callback,
            task_start_timer,
        ))
        .await;
}

fn handle_state_sync_request<V>(smp: &mut SharedMempool<V>, msg: CommitNotification)
where
    V: TransactionValidation,
{
    let _timer = counters::TASK_SPAWN_LATENCY
        .with_label_values(&[counters::STATE_SYNC_EVENT_LABEL, counters::SPAWN_LABEL])
        .start_timer();
    tokio::spawn(tasks::process_state_sync_request(smp.mempool.clone(), msg));
}

async fn handle_mempool_reconfig_event<V>(
    smp: &mut SharedMempool<V>,
    bounded_executor: &BoundedExecutor,
    config_update: OnChainConfigPayload,
) where
    V: TransactionValidation,
{
    info!(LogSchema::event_log(
        LogEntry::ReconfigUpdate,
        LogEvent::Received
    ));
    let _timer = counters::TASK_SPAWN_LATENCY
        .with_label_values(&[counters::RECONFIG_EVENT_LABEL, counters::SPAWN_LABEL])
        .start_timer();
    bounded_executor
        .spawn(tasks::process_config_update(
            config_update,
            smp.validator.clone(),
        ))
        .await;
}

async fn handle_event<V>(
    executor: &Handle,
    bounded_executor: &BoundedExecutor,
    scheduled_broadcasts: &mut FuturesUnordered<ScheduledBroadcast>,
    smp: &mut SharedMempool<V>,
    network_id: NodeNetworkId,
    event: Event<MempoolSyncMsg>,
) where
    V: TransactionValidation,
{
    match event {
        Event::NewPeer(metadata) => {
            counters::SHARED_MEMPOOL_EVENTS
                .with_label_values(&["new_peer"])
                .inc();
            let origin = metadata.origin;
            let peer = PeerNetworkId(network_id, metadata.remote_peer_id);
            let is_new_peer = smp.peer_manager.add_peer(peer.clone(), origin);
            let is_upstream_peer = smp.peer_manager.is_upstream_peer(&peer, Some(origin));
            debug!(LogSchema::new(LogEntry::NewPeer)
                .peer(&peer)
                .is_upstream_peer(is_upstream_peer));
            notify_subscribers(SharedMempoolNotification::PeerStateChange, &smp.subscribers);
            if is_new_peer && is_upstream_peer {
                tasks::execute_broadcast(peer, false, smp, scheduled_broadcasts, executor.clone());
            }
        }
        Event::LostPeer(metadata) => {
            counters::SHARED_MEMPOOL_EVENTS
                .with_label_values(&["lost_peer"])
                .inc();
            let peer = PeerNetworkId(network_id, metadata.remote_peer_id);
            debug!(LogSchema::new(LogEntry::LostPeer)
                .peer(&peer)
                .is_upstream_peer(
                    smp.peer_manager
                        .is_upstream_peer(&peer, Some(metadata.origin))
                ));
            smp.peer_manager.disable_peer(peer);
            notify_subscribers(SharedMempoolNotification::PeerStateChange, &smp.subscribers);
        }
        Event::Message(peer_id, msg) => {
            counters::SHARED_MEMPOOL_EVENTS
                .with_label_values(&["message"])
                .inc();
            match msg {
                MempoolSyncMsg::BroadcastTransactionsRequest {
                    request_id,
                    transactions,
                } => {
                    let smp_clone = smp.clone();
                    let peer = PeerNetworkId(network_id, peer_id);
                    let timeline_state = match smp.peer_manager.is_upstream_peer(&peer, None) {
                        true => TimelineState::NonQualified,
                        false => TimelineState::NotReady,
                    };
                    // This timer measures how long it took for the bounded executor to
                    // *schedule* the task.
                    let _timer = counters::TASK_SPAWN_LATENCY
                        .with_label_values(&[
                            counters::PEER_BROADCAST_EVENT_LABEL,
                            counters::SPAWN_LABEL,
                        ])
                        .start_timer();
                    // This timer measures how long it took for the task to go from scheduled
                    // to started.
                    let task_start_timer = counters::TASK_SPAWN_LATENCY
                        .with_label_values(&[
                            counters::PEER_BROADCAST_EVENT_LABEL,
                            counters::START_LABEL,
                        ])
                        .start_timer();
                    bounded_executor
                        .spawn(tasks::process_transaction_broadcast(
                            smp_clone,
                            transactions,
                            request_id,
                            timeline_state,
                            peer,
                            task_start_timer,
                        ))
                        .await;
                }
                MempoolSyncMsg::BroadcastTransactionsResponse {
                    request_id,
                    retry,
                    backoff,
                } => {
                    let ack_timestamp = SystemTime::now();
                    smp.peer_manager.process_broadcast_ack(
                        PeerNetworkId(network_id, peer_id),
                        request_id,
                        retry,
                        backoff,
                        ack_timestamp,
                    );
                }
            }
        }
        Event::RpcRequest(peer_id, _msg, _res_tx) => {
            counters::UNEXPECTED_NETWORK_MSG_COUNT
                .with_label_values(&[&network_id.network_id().to_string(), &peer_id.to_string()])
                .inc();
            sample!(
                SampleRate::Duration(Duration::from_secs(60)),
                warn!(LogSchema::new(LogEntry::UnexpectedNetworkMsg)
                    .peer(&PeerNetworkId(network_id, peer_id)))
            );
        }
    }
}

/// Garbage collect all expired transactions by SystemTTL.
pub(crate) async fn gc_coordinator(mempool: Arc<Mutex<CoreMempool>>, gc_interval_ms: u64) {
    info!(LogSchema::event_log(LogEntry::GCRuntime, LogEvent::Start));
    let mut interval = IntervalStream::new(interval(Duration::from_millis(gc_interval_ms)));
    while let Some(_interval) = interval.next().await {
        sample!(
            SampleRate::Duration(Duration::from_secs(60)),
            info!(LogSchema::event_log(LogEntry::GCRuntime, LogEvent::Live))
        );
        mempool.lock().gc();
    }

    error!(LogSchema::event_log(
        LogEntry::GCRuntime,
        LogEvent::Terminated
    ));
}

/// Periodically logs a snapshot of transactions in core mempool.
/// In the future we may want an interactive way to directly query mempool's internal state.
/// For now, we will rely on this periodic snapshot to observe the internal state.
pub(crate) async fn snapshot_job(mempool: Arc<Mutex<CoreMempool>>, snapshot_interval_secs: u64) {
    let mut interval = IntervalStream::new(interval(Duration::from_secs(snapshot_interval_secs)));
    while let Some(_interval) = interval.next().await {
        let snapshot = mempool.lock().gen_snapshot();
        debug!(LogSchema::new(LogEntry::MempoolSnapshot).txns(snapshot));
    }
}
<|endoftext|>