<commit_before>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

use std::cell::RefCell;
use std::rc::Rc;

use regex::Regex;

use super::assets::MemoryCache;
use super::FileBuilder;

fn assert_build(input: &str) {
    let i_paths = Vec::new();
    let cache = MemoryCache::new();
    let mut b = FileBuilder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
    b.enable_validate_mode();
    b.eval_string(input).unwrap();
    if !b.assert_collector.success {
        assert!(false, b.assert_collector.failures);
    }
}

fn assert_build_failure(input: &str, expect: Vec<Regex>) {
    let i_paths = Vec::new();
    let cache = MemoryCache::new();
    let mut b = FileBuilder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
    b.enable_validate_mode();
    let err = b.eval_string(input);
    match err {
        Ok(_) => {
            for r in expect.iter() {
                if !b.assert_collector.success {
                    if let None = r.find(&b.assert_collector.failures) {
                        assert!(
                            false,
                            "[{}] was not found in Assertion Failures:\n{}",
                            r, b.assert_collector.failures
                        );
                    }
                } else {
                    assert!(false, "Building input Did not panic!");
                }
            }
        }
        Err(ref err) => {
            for r in expect.iter() {
                let stack_trace = format!("{}", err);
                // Look for each expect to match the string.
                if let None = r.find(&stack_trace) {
                    assert!(
                        false,
                        "[{}] was not found in stacktrace:\n{}",
                        r, stack_trace
                    );
                }
            }
        }
    }
}

#[test]
fn test_tuples() {
    assert_build(include_str!("../../integration_tests/tuple_test.ucg"));
}

#[test]
fn test_lists() {
    assert_build(include_str!("../../integration_tests/list_test.ucg"));
}

#[test]
fn test_comparisons() {
    assert_build(include_str!("../../integration_tests/comparisons_test.ucg"));
}

#[test]
fn test_funcs() {
    assert_build(include_str!("../../integration_tests/macros_test.ucg"));
}

#[test]
fn test_modules() {
    assert_build(include_str!("../../integration_tests/modules_test.ucg"));
}

#[test]
fn test_selectors() {
    assert_build(include_str!("../../integration_tests/selectors_test.ucg"));
}

#[test]
fn test_empty_value() {
    assert_build(include_str!("../../integration_tests/empty_test.ucg"));
}

#[test]
fn test_select_expressions() {
    assert_build(include_str!(
        "../../integration_tests/select_expressions_test.ucg"
    ));
}

#[test]
fn test_binary_operator_precedence() {
    assert_build(include_str!(
        "../../integration_tests/operator_precedence_test.ucg"
    ));
}

#[test]
fn test_functional_processing() {
    assert_build(include_str!(
        "../../integration_tests/functional_processing_test.ucg"
    ));
}

#[test]
fn test_concatenation() {
    assert_build(include_str!(
        "../../integration_tests/concatenation_test.ucg"
    ));
}

#[test]
fn test_format() {
    assert_build(include_str!("../../integration_tests/format_test.ucg"));
}

#[test]
fn test_type_checks() {
    assert_build(include_str!("../../integration_tests/types_test.ucg"));
}

#[test]
#[should_panic(expected = "UserDefined")]
fn test_declarative_failures_are_user_defined() {
    assert_build("fail \"I am a failure!\";");
}

#[test]
#[should_panic(expected = "Caused By:\n\tI am a failure!")]
fn test_declarative_failures_are_caused_by_msg() {
    assert_build("fail \"I am a failure!\";");
}

#[test]
#[should_panic(expected = "1 is a failure!")]
fn test_declarative_failures_can_with_format_expr() {
    assert_build("fail \"@ is a failure!\" % (1);");
}

#[test]
fn test_assert_just_keyword_compile_failures() {
    assert_build_failure(
        "assert ",
        vec![
            Regex::new(r"line: 1, column: 1").unwrap(),
            Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
                .unwrap(),
            Regex::new(r"Expected Expression: at <eval> line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_compile_failures() {
    assert_build_failure(
        "assert {",
        vec![
            Regex::new(r"line: 1, column: 1").unwrap(),
            Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
                .unwrap(),
            Regex::new(r"Expected \(\}\) but got \(\): at <eval> line: 1, column: 9").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_missing_ok_compile_failures() {
    assert_build_failure(
        "assert {};",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_bad_ok_compile_failures() {
    assert_build_failure(
        "assert { ok = 1, };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_missing_desc_compile_failures() {
    assert_build_failure(
        "assert { ok=true, };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_bad_desc_compile_failures() {
    assert_build_failure(
        "assert { ok=true, desc = 1 };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_import_missing_path_compile_failure() {
    assert_build_failure(
        "import ;",
        vec![
            Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
            Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_import_path_not_a_string_compile_failure() {
    assert_build_failure(
        "import 1;",
        vec![
            Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
            Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_binary_operator_missing_operand_compile_failure() {
    assert_build_failure(
        "1 +",
        vec![
            Regex::new(r"Abort while parsing operator expression: at <eval> line: 1, column: 1")
                .unwrap(),
            Regex::new(r"Missing operand for binary expression: at <eval> line: 1, column: 4")
                .unwrap(),
        ],
    )
}

#[test]
fn test_binary_sum_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 + \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_minus_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 - \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_mul_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 * \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_div_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 / \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_gt_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 > \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_lt_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 < \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_lteq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 <= \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_binary_gteq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 >= \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_binary_eqeq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 == \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_compile_failure() {
    assert_build_failure(
        "{;",
        vec![
            Regex::new(r"Expected \(\}\) but got \(;\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 2").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_key_value_missing_eq_compile_failure() {
    assert_build_failure(
        "{foo",
        vec![
            Regex::new(r"Expected \(=\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_key_value_missing_value_compile_failure() {
    assert_build_failure(
        "{foo=",
        vec![
            Regex::new(r"Expected Expression").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_format_expr_missing_expr_compile_error() {
    assert_build_failure(
        "\"foo\" %",
        vec![
            Regex::new(r"Expected format arguments").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_format_expr_unclosed_parens_compile_failure() {
    assert_build_failure(
        "\"foo\" % (1",
        vec![
            Regex::new(r"Expected \(\)\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 9").unwrap(),
        ],
    )
}

#[test]
fn test_list_unclosed_bracket_compile_failure() {
    assert_build_failure(
        "[1",
        vec![
            Regex::new(r"Expected \(\]\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 3").unwrap(),
        ],
    )
}

#[test]
fn test_out_missing_type_compile_failure() {
    assert_build_failure(
        "out",
        vec![
            Regex::new(r"Expected converter name").unwrap(),
            Regex::new(r"Not a Bareword").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 4").unwrap(),
        ],
    )
}

#[test]
fn test_out_missing_out_expr_compile_failure() {
    assert_build_failure(
        "out json",
        vec![
            Regex::new(r"Expected Expression to export").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 9").unwrap(),
        ],
    )
}

#[test]
fn test_out_multiple_times_compile_failure() {
    assert_build_failure(
        "out json {};\nout json {};",
        vec![
            Regex::new(r"You can only have one output per file").unwrap(),
            Regex::new(r"at <eval> line: 2, column: 1").unwrap(),
        ],
    )
}
<commit_msg>MAINT: Unit tests for let statement comopile failures<commit_after>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

use std::cell::RefCell;
use std::rc::Rc;

use regex::Regex;

use super::assets::MemoryCache;
use super::FileBuilder;

fn assert_build(input: &str) {
    let i_paths = Vec::new();
    let cache = MemoryCache::new();
    let mut b = FileBuilder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
    b.enable_validate_mode();
    b.eval_string(input).unwrap();
    if !b.assert_collector.success {
        assert!(false, b.assert_collector.failures);
    }
}

fn assert_build_failure(input: &str, expect: Vec<Regex>) {
    let i_paths = Vec::new();
    let cache = MemoryCache::new();
    let mut b = FileBuilder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
    b.enable_validate_mode();
    let err = b.eval_string(input);
    match err {
        Ok(_) => {
            for r in expect.iter() {
                if !b.assert_collector.success {
                    if let None = r.find(&b.assert_collector.failures) {
                        assert!(
                            false,
                            "[{}] was not found in Assertion Failures:\n{}",
                            r, b.assert_collector.failures
                        );
                    }
                } else {
                    assert!(false, "Building input Did not panic!");
                }
            }
        }
        Err(ref err) => {
            for r in expect.iter() {
                let stack_trace = format!("{}", err);
                // Look for each expect to match the string.
                if let None = r.find(&stack_trace) {
                    assert!(
                        false,
                        "[{}] was not found in stacktrace:\n{}",
                        r, stack_trace
                    );
                }
            }
        }
    }
}

#[test]
fn test_tuples() {
    assert_build(include_str!("../../integration_tests/tuple_test.ucg"));
}

#[test]
fn test_lists() {
    assert_build(include_str!("../../integration_tests/list_test.ucg"));
}

#[test]
fn test_comparisons() {
    assert_build(include_str!("../../integration_tests/comparisons_test.ucg"));
}

#[test]
fn test_funcs() {
    assert_build(include_str!("../../integration_tests/macros_test.ucg"));
}

#[test]
fn test_modules() {
    assert_build(include_str!("../../integration_tests/modules_test.ucg"));
}

#[test]
fn test_selectors() {
    assert_build(include_str!("../../integration_tests/selectors_test.ucg"));
}

#[test]
fn test_empty_value() {
    assert_build(include_str!("../../integration_tests/empty_test.ucg"));
}

#[test]
fn test_select_expressions() {
    assert_build(include_str!(
        "../../integration_tests/select_expressions_test.ucg"
    ));
}

#[test]
fn test_binary_operator_precedence() {
    assert_build(include_str!(
        "../../integration_tests/operator_precedence_test.ucg"
    ));
}

#[test]
fn test_functional_processing() {
    assert_build(include_str!(
        "../../integration_tests/functional_processing_test.ucg"
    ));
}

#[test]
fn test_concatenation() {
    assert_build(include_str!(
        "../../integration_tests/concatenation_test.ucg"
    ));
}

#[test]
fn test_format() {
    assert_build(include_str!("../../integration_tests/format_test.ucg"));
}

#[test]
fn test_type_checks() {
    assert_build(include_str!("../../integration_tests/types_test.ucg"));
}

#[test]
#[should_panic(expected = "UserDefined")]
fn test_declarative_failures_are_user_defined() {
    assert_build("fail \"I am a failure!\";");
}

#[test]
#[should_panic(expected = "Caused By:\n\tI am a failure!")]
fn test_declarative_failures_are_caused_by_msg() {
    assert_build("fail \"I am a failure!\";");
}

#[test]
#[should_panic(expected = "1 is a failure!")]
fn test_declarative_failures_can_with_format_expr() {
    assert_build("fail \"@ is a failure!\" % (1);");
}

#[test]
fn test_assert_just_keyword_compile_failures() {
    assert_build_failure(
        "assert ",
        vec![
            Regex::new(r"line: 1, column: 1").unwrap(),
            Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
                .unwrap(),
            Regex::new(r"Expected Expression: at <eval> line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_compile_failures() {
    assert_build_failure(
        "assert {",
        vec![
            Regex::new(r"line: 1, column: 1").unwrap(),
            Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\}: at <eval> line: 1, column: 8")
                .unwrap(),
            Regex::new(r"Expected \(\}\) but got \(\): at <eval> line: 1, column: 9").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_missing_ok_compile_failures() {
    assert_build_failure(
        "assert {};",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_bad_ok_compile_failures() {
    assert_build_failure(
        "assert { ok = 1, };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_missing_desc_compile_failures() {
    assert_build_failure(
        "assert { ok=true, };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_assert_partial_tuple_bad_desc_compile_failures() {
    assert_build_failure(
        "assert { ok=true, desc = 1 };",
        vec![
            Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
            Regex::new(r"line: 1, column: 8").unwrap(),
        ],
    );
}

#[test]
fn test_import_missing_path_compile_failure() {
    assert_build_failure(
        "import ;",
        vec![
            Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
            Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_import_path_not_a_string_compile_failure() {
    assert_build_failure(
        "import 1;",
        vec![
            Regex::new(r"Expected import path: at <eval> line: 1, column: 8").unwrap(),
            Regex::new(r"Not a String: at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_binary_operator_missing_operand_compile_failure() {
    assert_build_failure(
        "1 +",
        vec![
            Regex::new(r"Abort while parsing operator expression: at <eval> line: 1, column: 1")
                .unwrap(),
            Regex::new(r"Missing operand for binary expression: at <eval> line: 1, column: 4")
                .unwrap(),
        ],
    )
}

#[test]
fn test_binary_sum_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 + \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_minus_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 - \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_mul_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 * \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_div_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 / \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_gt_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 > \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_lt_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 < \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_binary_lteq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 <= \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_binary_gteq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 >= \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_binary_eqeq_operator_wrong_type_on_rhs_compile_failure() {
    assert_build_failure(
        "1 == \"foo\";",
        vec![
            Regex::new(r"Expected Integer but got .foo.").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_compile_failure() {
    assert_build_failure(
        "{;",
        vec![
            Regex::new(r"Expected \(\}\) but got \(;\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 2").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_key_value_missing_eq_compile_failure() {
    assert_build_failure(
        "{foo",
        vec![
            Regex::new(r"Expected \(=\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_incomplete_tuple_key_value_missing_value_compile_failure() {
    assert_build_failure(
        "{foo=",
        vec![
            Regex::new(r"Expected Expression").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
        ],
    )
}

#[test]
fn test_format_expr_missing_expr_compile_error() {
    assert_build_failure(
        "\"foo\" %",
        vec![
            Regex::new(r"Expected format arguments").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 8").unwrap(),
        ],
    )
}

#[test]
fn test_format_expr_unclosed_parens_compile_failure() {
    assert_build_failure(
        "\"foo\" % (1",
        vec![
            Regex::new(r"Expected \(\)\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 9").unwrap(),
        ],
    )
}

#[test]
fn test_list_unclosed_bracket_compile_failure() {
    assert_build_failure(
        "[1",
        vec![
            Regex::new(r"Expected \(\]\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 3").unwrap(),
        ],
    )
}

#[test]
fn test_out_missing_type_compile_failure() {
    assert_build_failure(
        "out",
        vec![
            Regex::new(r"Expected converter name").unwrap(),
            Regex::new(r"Not a Bareword").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 4").unwrap(),
        ],
    )
}

#[test]
fn test_out_missing_out_expr_compile_failure() {
    assert_build_failure(
        "out json",
        vec![
            Regex::new(r"Expected Expression to export").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 9").unwrap(),
        ],
    )
}

#[test]
fn test_out_multiple_times_compile_failure() {
    assert_build_failure(
        "out json {};\nout json {};",
        vec![
            Regex::new(r"You can only have one output per file").unwrap(),
            Regex::new(r"at <eval> line: 2, column: 1").unwrap(),
        ],
    )
}

#[test]
fn test_let_missing_name_compile_failure() {
    assert_build_failure(
        "let ",
        vec![
            Regex::new(r"Expected name for binding").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
        ],
    )
}

#[test]
fn test_let_missing_equal_compile_failure() {
    assert_build_failure(
        "let foo ",
        vec![
            Regex::new(r"Expected \(=\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 9").unwrap(),
        ],
    )
}

#[test]
fn test_let_missing_expression_compile_failure() {
    assert_build_failure(
        "let foo =",
        vec![
            Regex::new(r"Expected Expression").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 10").unwrap(),
        ],
    )
}

#[test]
fn test_let_missing_semicolon_compile_failure() {
    assert_build_failure(
        "let foo = 1",
        vec![
            Regex::new(r"Expected \(;\) but got \(\)").unwrap(),
            Regex::new(r"at <eval> line: 1, column: 12").unwrap(),
        ],
    )
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use pretty::{Arena, DocAllocator, DocBuilder};

use ast::{Expr, is_operator_char, Literal, Pattern, SpannedExpr, SpannedPattern};
use types::{Type, pretty_print as pretty_type};

const INDENT: usize = 4;

pub fn ident<'b>(arena: &'b Arena<'b>, name: &'b str) -> DocBuilder<'b, Arena<'b>> {
    if name.starts_with(is_operator_char) {
        chain![arena; "(", name, ")"]
    } else {
        arena.text(name)
    }
}

fn forced_new_line<Id>(expr: &Expr<Id>) -> bool {
    match *expr {
        Expr::Block(..) |
        Expr::LetBindings(..) |
        Expr::Match(..) |
        Expr::TypeBindings(..) => true,
        _ => false,
    }
}

fn hang<'a, Id>(arena: &'a Arena<'a>, expr: &'a SpannedExpr<Id>) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    let line = if forced_new_line(&expr.value) {
        arena.newline()
    } else {
        arena.space()
    };
    line.append(pretty_expr(arena, &expr.value))
}

pub fn pretty_pattern<'a, Id>(
    arena: &'a Arena<'a>,
    pattern: &'a SpannedPattern<Id>,
) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    match pattern.value {
        Pattern::Constructor(ref ctor, ref args) => {
            chain![arena;
                ctor.as_ref(),
                arena.concat(args.iter().map(|arg| {
                    arena.text(" ").append(pretty_pattern(arena, arg))
                }))
            ]
        }
        Pattern::Ident(ref id) => ident(arena, id.as_ref()),
        Pattern::Record {
            ref fields,
            ref types,
            ..
        } => {
            chain![arena;
                "{",
                arena.concat(types.iter().map(|field| {
                    chain![arena;
                        arena.space(),
                        field.0.as_ref()
                    ]
                }).chain(fields.iter().map(|field| {
                    match field.1 {
                        Some(ref new_name) => {
                            chain![arena;
                                arena.space(),
                                field.0.as_ref(),
                                " = ",
                                pretty_pattern(arena, new_name)
                            ]
                        }
                        None => {
                            chain![arena;
                                arena.space(),
                                field.0.as_ref()
                            ]
                        }
                    }
                })).intersperse(arena.text(","))),
                if types.is_empty() && fields.is_empty() {
                    arena.nil()
                } else {
                    arena.space()
                },
                "}"
            ]
        }
        Pattern::Tuple { ref elems, .. } => {
            chain![arena;
                "(",
                arena.concat(elems
                    .iter()
                    .map(|elem| pretty_pattern(arena, elem))
                    .intersperse(arena.text(",").append(arena.space()))
                ),
                ")"
            ]
        }
        Pattern::Error => arena.text("<error>")
    }
}

pub fn pretty_expr<'a, Id>(arena: &'a Arena<'a>, expr: &'a Expr<Id>) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    let pretty = |expr: &'a SpannedExpr<_>| pretty_expr(arena, &expr.value);
    match *expr {
        Expr::App(ref func, ref args) => {
            pretty(func).append(
                arena.concat(args.iter().map(|arg| arena.text(" ").append(pretty(arg)))),
            )
        }
        Expr::Array(ref array) => {
            arena
                .text("[")
                .append(
                    arena.concat(
                        array
                            .exprs
                            .iter()
                            .map(|elem| pretty(elem))
                            .intersperse(arena.text(",").append(arena.space())),
                    ),
                )
                .append("]")
                .group()
        }
        Expr::Block(ref elems) => {
            arena.concat(
                elems
                    .iter()
                    .map(|elem| pretty(elem).group().append(arena.newline())),
            )
        }
        Expr::Ident(ref id) => ident(arena, id.name.as_ref()),
        Expr::IfElse(ref body, ref if_true, ref if_false) => {
            chain![arena;
                "if",
                pretty(body),
                "then",
                pretty(if_true),
                "else",
                pretty(if_false)
            ]
        }
        Expr::Infix(ref l, ref op, ref r) => {
            chain![arena;
                pretty(l),
                " ",
                op.value.name.as_ref(),
                " ",
                pretty(r)
            ]
        }
        Expr::Lambda(ref lambda) => {
            chain![arena;
                "\\",
                arena.concat(lambda.args.iter().map(|arg| arena.text(arg.name.as_ref()).append(" "))),
                "->",
                hang(arena, &lambda.body)
            ]
        }
        Expr::LetBindings(ref binds, ref body) => {
            chain![arena;
                "let ",
                arena.concat(binds.iter().map(|bind| {
                    chain![arena;
                        chain![arena;
                            pretty_pattern(arena, &bind.name),
                            " ",
                            arena.concat(bind.args.iter().map(|arg| {
                                arena.text(arg.name.as_ref()).append(" ")
                            }))
                        ].group(),
                        match *bind.typ {
                            Type::Hole => arena.nil(),
                            ref typ => arena.text(": ").append(pretty_type(arena, typ)),
                        },
                        "=",
                        hang(arena, &bind.expr).nest(INDENT)
                    ].group().nest(INDENT)
                }).intersperse(arena.newline().append("and "))),
                arena.newline(),
                pretty(body)
            ]
        }
        Expr::Literal(ref literal) => {
            match *literal {
                Literal::Byte(b) => arena.text(format!("b{}", b)),
                Literal::Char(c) => arena.text(format!("{:?}", c)),
                Literal::Float(f) => arena.text(format!("{}", f)),
                Literal::Int(i) => arena.text(format!("{}", i)),
                Literal::String(ref s) => arena.text(format!("{:?}", s)),
            }
        }
        Expr::Match(ref expr, ref alts) => {
            chain![arena;
                "match ",
                pretty(expr),
                " with",
                arena.newline(),
                arena.concat(alts.iter().map(|alt| {
                    chain![arena;
                        "| ",
                        pretty_pattern(arena, &alt.pattern),
                        " ->",
                        hang(arena, &alt.expr).group().nest(INDENT)
                    ]
                }).intersperse(arena.newline()))
            ]
        }
        Expr::Projection(ref expr, ref field, _) => {
            chain![arena;
                pretty(expr),
                ".",
                field.as_ref()
            ]
        }
        Expr::Record {
            ref types,
            ref exprs,
            ..
        } => {
            chain![arena;
                "{",
                arena.concat(types.iter().map(|field| {
                    chain![arena;
                        arena.space(),
                        field.name.as_ref()
                    ]
                }).chain(exprs.iter().map(|field| {
                    chain![arena;
                        arena.space(),
                        field.name.as_ref(),
                        match field.value {
                            Some(ref expr) => {
                                chain![arena;
                                    " =",
                                    arena.space(),
                                    pretty(&expr)
                                ]
                            },
                            None => arena.nil(),
                        }
                    ]
                })).intersperse(arena.text(","))).nest(INDENT),
                if types.is_empty() && exprs.is_empty() {
                    arena.nil()
                } else {
                    arena.space()
                },
                "}"
            ].group()
        }
        Expr::Tuple { ref elems, .. } => {
            arena
                .text("(")
                .append(
                    arena.concat(
                        elems
                            .iter()
                            .map(|elem| pretty(elem))
                            .intersperse(arena.text(",").append(arena.space())),
                    ),
                )
                .append(")")
                .group()
        }
        Expr::TypeBindings(ref binds, ref body) => {
            chain![arena;
                "type ",
                arena.concat(binds.iter().map(|bind| {
                    chain![arena;
                        bind.name.as_ref(),
                        " ",
                        arena.concat(bind.alias.args.iter().map(|arg| {
                            arena.text(arg.id.as_ref()).append(" ")
                        })),
                        "=",
                        arena.space()
                            .append(pretty_type(arena, &bind.alias.unresolved_type()))
                            .group()
                    ].group().nest(INDENT)
                }).intersperse(arena.newline().append("and "))),
                arena.newline(),
                pretty(body)
            ]
        }
        Expr::Error => arena.text("<error expr>"),
    }
}
<commit_msg>fix: Avoid double nesting in let expressions<commit_after>use itertools::Itertools;
use pretty::{Arena, DocAllocator, DocBuilder};

use ast::{Expr, is_operator_char, Literal, Pattern, SpannedExpr, SpannedPattern};
use types::{Type, pretty_print as pretty_type};

const INDENT: usize = 4;

pub fn ident<'b>(arena: &'b Arena<'b>, name: &'b str) -> DocBuilder<'b, Arena<'b>> {
    if name.starts_with(is_operator_char) {
        chain![arena; "(", name, ")"]
    } else {
        arena.text(name)
    }
}

fn forced_new_line<Id>(expr: &Expr<Id>) -> bool {
    match *expr {
        Expr::Block(..) |
        Expr::LetBindings(..) |
        Expr::Match(..) |
        Expr::TypeBindings(..) => true,
        _ => false,
    }
}

fn hang<'a, Id>(arena: &'a Arena<'a>, expr: &'a SpannedExpr<Id>) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    let line = if forced_new_line(&expr.value) {
        arena.newline()
    } else {
        arena.space()
    };
    line.append(pretty_expr(arena, &expr.value))
}

pub fn pretty_pattern<'a, Id>(
    arena: &'a Arena<'a>,
    pattern: &'a SpannedPattern<Id>,
) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    match pattern.value {
        Pattern::Constructor(ref ctor, ref args) => {
            chain![arena;
                ctor.as_ref(),
                arena.concat(args.iter().map(|arg| {
                    arena.text(" ").append(pretty_pattern(arena, arg))
                }))
            ]
        }
        Pattern::Ident(ref id) => ident(arena, id.as_ref()),
        Pattern::Record {
            ref fields,
            ref types,
            ..
        } => {
            chain![arena;
                "{",
                arena.concat(types.iter().map(|field| {
                    chain![arena;
                        arena.space(),
                        field.0.as_ref()
                    ]
                }).chain(fields.iter().map(|field| {
                    match field.1 {
                        Some(ref new_name) => {
                            chain![arena;
                                arena.space(),
                                field.0.as_ref(),
                                " = ",
                                pretty_pattern(arena, new_name)
                            ]
                        }
                        None => {
                            chain![arena;
                                arena.space(),
                                field.0.as_ref()
                            ]
                        }
                    }
                })).intersperse(arena.text(","))),
                if types.is_empty() && fields.is_empty() {
                    arena.nil()
                } else {
                    arena.space()
                },
                "}"
            ]
        }
        Pattern::Tuple { ref elems, .. } => {
            chain![arena;
                "(",
                arena.concat(elems
                    .iter()
                    .map(|elem| pretty_pattern(arena, elem))
                    .intersperse(arena.text(",").append(arena.space()))
                ),
                ")"
            ]
        }
        Pattern::Error => arena.text("<error>")
    }
}

pub fn pretty_expr<'a, Id>(arena: &'a Arena<'a>, expr: &'a Expr<Id>) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    let pretty = |expr: &'a SpannedExpr<_>| pretty_expr(arena, &expr.value);
    match *expr {
        Expr::App(ref func, ref args) => {
            pretty(func).append(
                arena.concat(args.iter().map(|arg| arena.text(" ").append(pretty(arg)))),
            )
        }
        Expr::Array(ref array) => {
            arena
                .text("[")
                .append(
                    arena.concat(
                        array
                            .exprs
                            .iter()
                            .map(|elem| pretty(elem))
                            .intersperse(arena.text(",").append(arena.space())),
                    ),
                )
                .append("]")
                .group()
        }
        Expr::Block(ref elems) => {
            arena.concat(
                elems
                    .iter()
                    .map(|elem| pretty(elem).group().append(arena.newline())),
            )
        }
        Expr::Ident(ref id) => ident(arena, id.name.as_ref()),
        Expr::IfElse(ref body, ref if_true, ref if_false) => {
            chain![arena;
                "if",
                pretty(body),
                "then",
                pretty(if_true),
                "else",
                pretty(if_false)
            ]
        }
        Expr::Infix(ref l, ref op, ref r) => {
            chain![arena;
                pretty(l),
                " ",
                op.value.name.as_ref(),
                " ",
                pretty(r)
            ]
        }
        Expr::Lambda(ref lambda) => {
            chain![arena;
                "\\",
                arena.concat(lambda.args.iter().map(|arg| arena.text(arg.name.as_ref()).append(" "))),
                "->",
                hang(arena, &lambda.body)
            ]
        }
        Expr::LetBindings(ref binds, ref body) => {
            chain![arena;
                "let ",
                arena.concat(binds.iter().map(|bind| {
                    chain![arena;
                        chain![arena;
                            pretty_pattern(arena, &bind.name),
                            " ",
                            arena.concat(bind.args.iter().map(|arg| {
                                arena.text(arg.name.as_ref()).append(" ")
                            }))
                        ].group(),
                        match *bind.typ {
                            Type::Hole => arena.nil(),
                            ref typ => arena.text(": ").append(pretty_type(arena, typ)),
                        },
                        "=",
                        hang(arena, &bind.expr)
                    ].group().nest(INDENT)
                }).intersperse(arena.newline().append("and "))),
                arena.newline(),
                pretty(body)
            ]
        }
        Expr::Literal(ref literal) => {
            match *literal {
                Literal::Byte(b) => arena.text(format!("b{}", b)),
                Literal::Char(c) => arena.text(format!("{:?}", c)),
                Literal::Float(f) => arena.text(format!("{}", f)),
                Literal::Int(i) => arena.text(format!("{}", i)),
                Literal::String(ref s) => arena.text(format!("{:?}", s)),
            }
        }
        Expr::Match(ref expr, ref alts) => {
            chain![arena;
                "match ",
                pretty(expr),
                " with",
                arena.newline(),
                arena.concat(alts.iter().map(|alt| {
                    chain![arena;
                        "| ",
                        pretty_pattern(arena, &alt.pattern),
                        " ->",
                        hang(arena, &alt.expr).group().nest(INDENT)
                    ]
                }).intersperse(arena.newline()))
            ]
        }
        Expr::Projection(ref expr, ref field, _) => {
            chain![arena;
                pretty(expr),
                ".",
                field.as_ref()
            ]
        }
        Expr::Record {
            ref types,
            ref exprs,
            ..
        } => {
            chain![arena;
                "{",
                arena.concat(types.iter().map(|field| {
                    chain![arena;
                        arena.space(),
                        field.name.as_ref()
                    ]
                }).chain(exprs.iter().map(|field| {
                    chain![arena;
                        arena.space(),
                        field.name.as_ref(),
                        match field.value {
                            Some(ref expr) => {
                                chain![arena;
                                    " =",
                                    arena.space(),
                                    pretty(&expr)
                                ]
                            },
                            None => arena.nil(),
                        }
                    ]
                })).intersperse(arena.text(","))).nest(INDENT),
                if types.is_empty() && exprs.is_empty() {
                    arena.nil()
                } else {
                    arena.space()
                },
                "}"
            ].group()
        }
        Expr::Tuple { ref elems, .. } => {
            arena
                .text("(")
                .append(
                    arena.concat(
                        elems
                            .iter()
                            .map(|elem| pretty(elem))
                            .intersperse(arena.text(",").append(arena.space())),
                    ),
                )
                .append(")")
                .group()
        }
        Expr::TypeBindings(ref binds, ref body) => {
            chain![arena;
                "type ",
                arena.concat(binds.iter().map(|bind| {
                    chain![arena;
                        bind.name.as_ref(),
                        " ",
                        arena.concat(bind.alias.args.iter().map(|arg| {
                            arena.text(arg.id.as_ref()).append(" ")
                        })),
                        "=",
                        arena.space()
                            .append(pretty_type(arena, &bind.alias.unresolved_type()))
                            .group()
                    ].group().nest(INDENT)
                }).intersperse(arena.newline().append("and "))),
                arena.newline(),
                pretty(body)
            ]
        }
        Expr::Error => arena.text("<error expr>"),
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::io;
use std::net::{Ipv4Addr, SocketAddr};
use std::rc::{Rc, Weak};
use std::time::Instant;
use log::LogLevel;
use mio::{Event, PollOpt, Ready, Token};
use mio::net::UdpSocket;

use super::binary;
use super::client::{Client, ClientChannel};
use super::connection::{Connection, ConnectionId};
use super::datagram_buffer::DatagramBuffer;
use super::ipv4_header::Ipv4Header;
use super::ipv4_packet::{Ipv4Packet, MAX_PACKET_LENGTH};
use super::packetizer::Packetizer;
use super::selector::{EventHandler, Selector};
use super::transport_header::TransportHeader;

const TAG: &'static str = "UdpConnection";

pub const IDLE_TIMEOUT_SECONDS: u64 = 2 * 60;

pub struct UdpConnection {
    id: ConnectionId,
    client: Weak<RefCell<Client>>,
    socket: UdpSocket,
    token: Token,
    client_to_network: DatagramBuffer,
    network_to_client: Packetizer,
    closed: bool,
    idle_since: Instant,
}

impl UdpConnection {
    pub fn new(
        selector: &mut Selector,
        id: ConnectionId,
        client: Weak<RefCell<Client>>,
        ipv4_header: Ipv4Header,
        transport_header: TransportHeader,
    ) -> io::Result<Rc<RefCell<Self>>> {
        cx_info!(target: TAG, id, "Open");
        let socket = Self::create_socket(&id)?;
        let packetizer = Packetizer::new(&ipv4_header, &transport_header);
        let rc = Rc::new(RefCell::new(Self {
            id: id,
            client: client,
            socket: socket,
            token: Token(0), // default value, will be set afterwards
            client_to_network: DatagramBuffer::new(4 * MAX_PACKET_LENGTH),
            network_to_client: packetizer,
            closed: false,
            idle_since: Instant::now(),
        }));

        {
            let mut self_ref = rc.borrow_mut();

            // rc is an EventHandler, register() expects a Box<EventHandler>
            let handler = Box::new(rc.clone());
            let token = selector.register(
                &self_ref.socket,
                handler,
                Ready::readable(),
                PollOpt::level(),
            )?;
            self_ref.token = token;
        }
        Ok(rc)
    }

    fn create_socket(id: &ConnectionId) -> io::Result<UdpSocket> {
        let autobind_addr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 0);
        let udp_socket = UdpSocket::bind(&autobind_addr)?;
        udp_socket.connect(id.rewritten_destination().into())?;
        Ok(udp_socket)
    }

    fn remove_from_router(&self) {
        // route is embedded in router which is embedded in client: the client necessarily exists
        let client_rc = self.client.upgrade().expect("Expected client not found");
        let mut client = client_rc.borrow_mut();
        client.router().remove(self);
    }

    // return Err(err) with err.kind() == io::ErrorKind::WouldBlock on spurious event
    fn process(&mut self, selector: &mut Selector, event: Event) -> io::Result<()> {
        if !self.closed {
            self.touch();
            let ready = event.readiness();
            if ready.is_readable() || ready.is_writable() {
                if ready.is_writable() {
                    self.process_send(selector)?;
                }
                if !self.closed && ready.is_readable() {
                    self.process_receive(selector)?;
                }
                if !self.closed {
                    self.update_interests(selector);
                }
            } else {
                // error or hup
                self.close(selector);
            }
            if self.closed {
                // on_ready is not called from the router, so the connection must remove itself
                self.remove_from_router();
            }
        }
        Ok(())
    }

    // return Err(err) with err.kind() == io::ErrorKind::WouldBlock on spurious event
    fn process_send(&mut self, selector: &mut Selector) -> io::Result<()> {
        match self.write() {
            Ok(_) => (),
            Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
                cx_debug!(target: TAG, self.id, "Spurious event, ignoring")
            }
            Err(err) => {
                if err.kind() == io::ErrorKind::WouldBlock {
                    // rethrow
                    return Err(err);
                }
                cx_error!(
                    target: TAG,
                    self.id,
                    "Cannot write: [{:?}] {}",
                    err.kind(),
                    err
                );
                self.close(selector);
            }
        }
        Ok(())
    }

    // return Err(err) with err.kind() == io::ErrorKind::WouldBlock on spurious event
    fn process_receive(&mut self, selector: &mut Selector) -> io::Result<()> {
        match self.read(selector) {
            Ok(_) => (),
            Err(err) => {
                if err.kind() == io::ErrorKind::WouldBlock {
                    // rethrow
                    return Err(err);
                }
                cx_error!(
                    target: TAG,
                    self.id,
                    "Cannot read: [{:?}] {}",
                    err.kind(),
                    err
                );
                self.close(selector);
            }
        }
        Ok(())
    }

    fn read(&mut self, selector: &mut Selector) -> io::Result<()> {
        let ipv4_packet = self.network_to_client.packetize(&mut self.socket)?;
        let client_rc = self.client.upgrade().expect("Expected client not found");
        match client_rc.borrow_mut().send_to_client(
            selector,
            &ipv4_packet,
        ) {
            Ok(_) => {
                cx_debug!(
                    target: TAG,
                    self.id,
                    "Packet ({} bytes) sent to client",
                    ipv4_packet.length()
                );
                if log_enabled!(target: TAG, LogLevel::Trace) {
                    cx_trace!(
                        target: TAG,
                        self.id,
                        "{}",
                        binary::to_string(ipv4_packet.raw())
                    );
                }
            }
            Err(_) => cx_warn!(target: TAG, self.id, "Cannot send to client, drop packet"),
        }
        Ok(())
    }

    fn write(&mut self) -> io::Result<()> {
        self.client_to_network.write_to(&mut self.socket)?;
        Ok(())
    }

    fn update_interests(&mut self, selector: &mut Selector) {
        let ready = if self.client_to_network.is_empty() {
            Ready::readable()
        } else {
            Ready::readable() | Ready::writable()
        };
        selector
            .reregister(&self.socket, self.token, ready, PollOpt::level())
            .expect("Cannot register on poll");
    }

    fn touch(&mut self) {
        self.idle_since = Instant::now();
    }
}

impl Connection for UdpConnection {
    fn id(&self) -> &ConnectionId {
        &self.id
    }

    fn send_to_network(
        &mut self,
        selector: &mut Selector,
        _: &mut ClientChannel,
        ipv4_packet: &Ipv4Packet,
    ) {
        match self.client_to_network.read_from(
            ipv4_packet.payload().expect(
                "No payload",
            ),
        ) {
            Ok(_) => {
                self.update_interests(selector);
            }
            Err(err) => {
                cx_warn!(
                    target: TAG,
                    self.id,
                    "Cannot send to network, drop packet: {}",
                    err
                )
            }
        }
    }

    fn close(&mut self, selector: &mut Selector) {
        cx_info!(target: TAG, self.id, "Close");
        self.closed = true;
        selector.deregister(&self.socket, self.token).unwrap();
        // socket will be closed by RAII
    }

    fn is_expired(&self) -> bool {
        self.idle_since.elapsed().as_secs() > IDLE_TIMEOUT_SECONDS
    }

    fn is_closed(&self) -> bool {
        self.closed
    }
}

impl EventHandler for UdpConnection {
    fn on_ready(&mut self, selector: &mut Selector, event: Event) {
        match self.process(selector, event) {
            Ok(_) => (),
            Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
                cx_debug!(target: TAG, self.id, "Spurious event, ignoring")
            }
            Err(_) => panic!("Unexpected unhandled error"),
        }
    }
}
<commit_msg>Log UDP interests<commit_after>use std::cell::RefCell;
use std::io;
use std::net::{Ipv4Addr, SocketAddr};
use std::rc::{Rc, Weak};
use std::time::Instant;
use log::LogLevel;
use mio::{Event, PollOpt, Ready, Token};
use mio::net::UdpSocket;

use super::binary;
use super::client::{Client, ClientChannel};
use super::connection::{Connection, ConnectionId};
use super::datagram_buffer::DatagramBuffer;
use super::ipv4_header::Ipv4Header;
use super::ipv4_packet::{Ipv4Packet, MAX_PACKET_LENGTH};
use super::packetizer::Packetizer;
use super::selector::{EventHandler, Selector};
use super::transport_header::TransportHeader;

const TAG: &'static str = "UdpConnection";

pub const IDLE_TIMEOUT_SECONDS: u64 = 2 * 60;

pub struct UdpConnection {
    id: ConnectionId,
    client: Weak<RefCell<Client>>,
    socket: UdpSocket,
    token: Token,
    client_to_network: DatagramBuffer,
    network_to_client: Packetizer,
    closed: bool,
    idle_since: Instant,
}

impl UdpConnection {
    pub fn new(
        selector: &mut Selector,
        id: ConnectionId,
        client: Weak<RefCell<Client>>,
        ipv4_header: Ipv4Header,
        transport_header: TransportHeader,
    ) -> io::Result<Rc<RefCell<Self>>> {
        cx_info!(target: TAG, id, "Open");
        let socket = Self::create_socket(&id)?;
        let packetizer = Packetizer::new(&ipv4_header, &transport_header);
        let rc = Rc::new(RefCell::new(Self {
            id: id,
            client: client,
            socket: socket,
            token: Token(0), // default value, will be set afterwards
            client_to_network: DatagramBuffer::new(4 * MAX_PACKET_LENGTH),
            network_to_client: packetizer,
            closed: false,
            idle_since: Instant::now(),
        }));

        {
            let mut self_ref = rc.borrow_mut();

            // rc is an EventHandler, register() expects a Box<EventHandler>
            let handler = Box::new(rc.clone());
            let token = selector.register(
                &self_ref.socket,
                handler,
                Ready::readable(),
                PollOpt::level(),
            )?;
            self_ref.token = token;
        }
        Ok(rc)
    }

    fn create_socket(id: &ConnectionId) -> io::Result<UdpSocket> {
        let autobind_addr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 0);
        let udp_socket = UdpSocket::bind(&autobind_addr)?;
        udp_socket.connect(id.rewritten_destination().into())?;
        Ok(udp_socket)
    }

    fn remove_from_router(&self) {
        // route is embedded in router which is embedded in client: the client necessarily exists
        let client_rc = self.client.upgrade().expect("Expected client not found");
        let mut client = client_rc.borrow_mut();
        client.router().remove(self);
    }

    // return Err(err) with err.kind() == io::ErrorKind::WouldBlock on spurious event
    fn process(&mut self, selector: &mut Selector, event: Event) -> io::Result<()> {
        if !self.closed {
            self.touch();
            let ready = event.readiness();
            if ready.is_readable() || ready.is_writable() {
                if ready.is_writable() {
                    self.process_send(selector)?;
                }
                if !self.closed && ready.is_readable() {
                    self.process_receive(selector)?;
                }
                if !self.closed {
                    self.update_interests(selector);
                }
            } else {
                // error or hup
                self.close(selector);
            }
            if self.closed {
                // on_ready is not called from the router, so the connection must remove itself
                self.remove_from_router();
            }
        }
        Ok(())
    }

    // return Err(err) with err.kind() == io::ErrorKind::WouldBlock on spurious event
    fn process_send(&mut self, selector: &mut Selector) -> io::Result<()> {
        match self.write() {
            Ok(_) => (),
            Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
                cx_debug!(target: TAG, self.id, "Spurious event, ignoring")
            }
            Err(err) => {
                if err.kind() == io::ErrorKind::WouldBlock {
                    // rethrow
                    return Err(err);
                }
                cx_error!(
                    target: TAG,
                    self.id,
                    "Cannot write: [{:?}] {}",
                    err.kind(),
                    err
                );
                self.close(selector);
            }
        }
        Ok(())
    }

    // return Err(err) with err.kind() == io::ErrorKind::WouldBlock on spurious event
    fn process_receive(&mut self, selector: &mut Selector) -> io::Result<()> {
        match self.read(selector) {
            Ok(_) => (),
            Err(err) => {
                if err.kind() == io::ErrorKind::WouldBlock {
                    // rethrow
                    return Err(err);
                }
                cx_error!(
                    target: TAG,
                    self.id,
                    "Cannot read: [{:?}] {}",
                    err.kind(),
                    err
                );
                self.close(selector);
            }
        }
        Ok(())
    }

    fn read(&mut self, selector: &mut Selector) -> io::Result<()> {
        let ipv4_packet = self.network_to_client.packetize(&mut self.socket)?;
        let client_rc = self.client.upgrade().expect("Expected client not found");
        match client_rc.borrow_mut().send_to_client(
            selector,
            &ipv4_packet,
        ) {
            Ok(_) => {
                cx_debug!(
                    target: TAG,
                    self.id,
                    "Packet ({} bytes) sent to client",
                    ipv4_packet.length()
                );
                if log_enabled!(target: TAG, LogLevel::Trace) {
                    cx_trace!(
                        target: TAG,
                        self.id,
                        "{}",
                        binary::to_string(ipv4_packet.raw())
                    );
                }
            }
            Err(_) => cx_warn!(target: TAG, self.id, "Cannot send to client, drop packet"),
        }
        Ok(())
    }

    fn write(&mut self) -> io::Result<()> {
        self.client_to_network.write_to(&mut self.socket)?;
        Ok(())
    }

    fn update_interests(&mut self, selector: &mut Selector) {
        let ready = if self.client_to_network.is_empty() {
            Ready::readable()
        } else {
            Ready::readable() | Ready::writable()
        };
        cx_debug!(target: TAG, self.id, "interests: {:?}", ready);
        selector
            .reregister(&self.socket, self.token, ready, PollOpt::level())
            .expect("Cannot register on poll");
    }

    fn touch(&mut self) {
        self.idle_since = Instant::now();
    }
}

impl Connection for UdpConnection {
    fn id(&self) -> &ConnectionId {
        &self.id
    }

    fn send_to_network(
        &mut self,
        selector: &mut Selector,
        _: &mut ClientChannel,
        ipv4_packet: &Ipv4Packet,
    ) {
        match self.client_to_network.read_from(
            ipv4_packet.payload().expect(
                "No payload",
            ),
        ) {
            Ok(_) => {
                self.update_interests(selector);
            }
            Err(err) => {
                cx_warn!(
                    target: TAG,
                    self.id,
                    "Cannot send to network, drop packet: {}",
                    err
                )
            }
        }
    }

    fn close(&mut self, selector: &mut Selector) {
        cx_info!(target: TAG, self.id, "Close");
        self.closed = true;
        selector.deregister(&self.socket, self.token).unwrap();
        // socket will be closed by RAII
    }

    fn is_expired(&self) -> bool {
        self.idle_since.elapsed().as_secs() > IDLE_TIMEOUT_SECONDS
    }

    fn is_closed(&self) -> bool {
        self.closed
    }
}

impl EventHandler for UdpConnection {
    fn on_ready(&mut self, selector: &mut Selector, event: Event) {
        match self.process(selector, event) {
            Ok(_) => (),
            Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
                cx_debug!(target: TAG, self.id, "Spurious event, ignoring")
            }
            Err(_) => panic!("Unexpected unhandled error"),
        }
    }
}
<|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/. */

//! Necessary types for [grid](https://drafts.csswg.org/css-grid/).

use cssparser::{Parser, Token};
use parser::{Parse, ParserContext};
use std::ascii::AsciiExt;
use std::fmt;
use style_traits::ToCss;
use values::{CSSFloat, CustomIdent, HasViewportPercentage};
use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
use values::specified::LengthOrPercentage;

#[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A `<grid-line>` type.
///
/// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line
pub struct GridLine {
    /// Flag to check whether it's a `span` keyword.
    pub is_span: bool,
    /// A custom identifier for named lines.
    ///
    /// https://drafts.csswg.org/css-grid/#grid-placement-slot
    pub ident: Option<String>,
    /// Denotes the nth grid line from grid item's placement.
    pub integer: Option<i32>,
}

impl Default for GridLine {
    fn default() -> Self {
        GridLine {
            is_span: false,
            ident: None,
            integer: None,
        }
    }
}

impl ToCss for GridLine {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        if !self.is_span && self.ident.is_none() && self.integer.is_none() {
            return dest.write_str("auto")
        }

        if self.is_span {
            try!(dest.write_str("span"));
        }

        if let Some(i) = self.integer {
            try!(write!(dest, " {}", i));
        }

        if let Some(ref s) = self.ident {
            try!(write!(dest, " {}", s));
        }

        Ok(())
    }
}

impl Parse for GridLine {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        let mut grid_line = Default::default();
        if input.try(|i| i.expect_ident_matching("auto")).is_ok() {
            return Ok(grid_line)
        }

        for _ in 0..3 {     // Maximum possible entities for <grid-line>
            if input.try(|i| i.expect_ident_matching("span")).is_ok() {
                if grid_line.is_span {
                    return Err(())
                }
                grid_line.is_span = true;
            } else if let Ok(i) = input.try(|i| i.expect_integer()) {
                if i == 0 || grid_line.integer.is_some() {
                    return Err(())
                }
                grid_line.integer = Some(i);
            } else if let Ok(name) = input.try(|i| i.expect_ident()) {
                if grid_line.ident.is_some() {
                    return Err(())
                }
                grid_line.ident = Some(name.into_owned());
            } else {
                break
            }
        }

        if grid_line.is_span {
            if let Some(i) = grid_line.integer {
                if i < 0 {      // disallow negative integers for grid spans
                    return Err(())
                }
            } else {
                grid_line.integer = Some(1);
            }
        }

        Ok(grid_line)
    }
}

impl ComputedValueAsSpecified for GridLine {}
no_viewport_percentage!(GridLine);

define_css_keyword_enum!{ TrackKeyword:
    "auto" => Auto,
    "max-content" => MaxContent,
    "min-content" => MinContent
}

#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A track breadth for explicit grid track sizing. It's generic solely to
/// avoid re-implementing it for the computed type.
///
/// https://drafts.csswg.org/css-grid/#typedef-track-breadth
pub enum TrackBreadth<L> {
    /// The generic type is almost always a non-negative `<length-percentage>`
    Breadth(L),
    /// A flex fraction specified in `fr` units.
    Flex(CSSFloat),
    /// One of the track-sizing keywords (`auto`, `min-content`, `max-content`)
    Keyword(TrackKeyword),
}

impl<L> TrackBreadth<L> {
    /// Check whether this is a `<fixed-breadth>` (i.e., it only has `<length-percentage>`)
    ///
    /// https://drafts.csswg.org/css-grid/#typedef-fixed-breadth
    #[inline]
    pub fn is_fixed(&self) -> bool {
        match *self {
            TrackBreadth::Breadth(ref _lop) => true,
            _ => false,
        }
    }
}

/// Parse a single flexible length.
pub fn parse_flex(input: &mut Parser) -> Result<CSSFloat, ()> {
    match try!(input.next()) {
        Token::Dimension(ref value, ref unit) if unit.eq_ignore_ascii_case("fr") && value.value.is_sign_positive()
            => Ok(value.value),
        _ => Err(()),
    }
}

impl Parse for TrackBreadth<LengthOrPercentage> {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
            return Ok(TrackBreadth::Breadth(lop))
        }

        if let Ok(f) = input.try(parse_flex) {
            return Ok(TrackBreadth::Flex(f))
        }

        TrackKeyword::parse(input).map(TrackBreadth::Keyword)
    }
}

impl<L: ToCss> ToCss for TrackBreadth<L> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            TrackBreadth::Breadth(ref lop) => lop.to_css(dest),
            TrackBreadth::Flex(ref value) => write!(dest, "{}fr", value),
            TrackBreadth::Keyword(ref k) => k.to_css(dest),
        }
    }
}

impl HasViewportPercentage for TrackBreadth<LengthOrPercentage> {
    #[inline]
    fn has_viewport_percentage(&self) -> bool {
        if let TrackBreadth::Breadth(ref lop) = *self {
            lop.has_viewport_percentage()
        } else {
            false
        }
    }
}

impl<L: ToComputedValue> ToComputedValue for TrackBreadth<L> {
    type ComputedValue = TrackBreadth<L::ComputedValue>;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            TrackBreadth::Breadth(ref lop) => TrackBreadth::Breadth(lop.to_computed_value(context)),
            TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr),
            TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            TrackBreadth::Breadth(ref lop) =>
                TrackBreadth::Breadth(ToComputedValue::from_computed_value(lop)),
            TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr),
            TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k),
        }
    }
}

#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A `<track-size>` type for explicit grid track sizing. Like `<track-breadth>`, this is
/// generic only to avoid code bloat. It only takes `<length-percentage>`
///
/// https://drafts.csswg.org/css-grid/#typedef-track-size
pub enum TrackSize<L> {
    /// A flexible `<track-breadth>`
    Breadth(TrackBreadth<L>),
    /// A `minmax` function for a range over an inflexible `<track-breadth>`
    /// and a flexible `<track-breadth>`
    ///
    /// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax
    MinMax(TrackBreadth<L>, TrackBreadth<L>),
    /// A `fit-content` function.
    ///
    /// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content
    FitContent(L),
}

impl<L> TrackSize<L> {
    /// Check whether this is a `<fixed-size>`
    ///
    /// https://drafts.csswg.org/css-grid/#typedef-fixed-size
    pub fn is_fixed(&self) -> bool {
        match *self {
            TrackSize::Breadth(ref breadth) => breadth.is_fixed(),
            // For minmax function, it could be either
            // minmax(<fixed-breadth>, <track-breadth>) or minmax(<inflexible-breadth>, <fixed-breadth>),
            // and since both variants are a subset of minmax(<inflexible-breadth>, <track-breadth>), we only
            // need to make sure that they're fixed. So, we don't have to modify the parsing function.
            TrackSize::MinMax(ref breadth_1, ref breadth_2) => {
                if breadth_1.is_fixed() {
                    return true     // the second value is always a <track-breadth>
                }

                match *breadth_1 {
                    TrackBreadth::Flex(_) => false,     // should be <inflexible-breadth> at this point
                    _ => breadth_2.is_fixed(),
                }
            },
            TrackSize::FitContent(_) => false,
        }
    }
}

impl<L> Default for TrackSize<L> {
    fn default() -> Self {
        TrackSize::Breadth(TrackBreadth::Keyword(TrackKeyword::Auto))
    }
}

impl Parse for TrackSize<LengthOrPercentage> {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(b) = input.try(|i| TrackBreadth::parse(context, i)) {
            return Ok(TrackSize::Breadth(b))
        }

        if input.try(|i| i.expect_function_matching("minmax")).is_ok() {
            return input.parse_nested_block(|input| {
                let inflexible_breadth =
                    match input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
                        Ok(lop) => TrackBreadth::Breadth(lop),
                        Err(..) => {
                            let keyword = try!(TrackKeyword::parse(input));
                            TrackBreadth::Keyword(keyword)
                        }
                    };

                try!(input.expect_comma());
                Ok(TrackSize::MinMax(inflexible_breadth, try!(TrackBreadth::parse(context, input))))
            });
        }

        try!(input.expect_function_matching("fit-content"));
        // FIXME(emilio): This needs a parse_nested_block, doesn't it?
        Ok(try!(LengthOrPercentage::parse(context, input).map(TrackSize::FitContent)))
    }
}

impl<L: ToCss> ToCss for TrackSize<L> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            TrackSize::Breadth(ref b) => b.to_css(dest),
            TrackSize::MinMax(ref infexible, ref flexible) => {
                try!(dest.write_str("minmax("));
                try!(infexible.to_css(dest));
                try!(dest.write_str(","));
                try!(flexible.to_css(dest));
                dest.write_str(")")
            },
            TrackSize::FitContent(ref lop) => {
                try!(dest.write_str("fit-content("));
                try!(lop.to_css(dest));
                dest.write_str(")")
            },
        }
    }
}

impl HasViewportPercentage for TrackSize<LengthOrPercentage> {
    #[inline]
    fn has_viewport_percentage(&self) -> bool {
        match *self {
            TrackSize::Breadth(ref b) => b.has_viewport_percentage(),
            TrackSize::MinMax(ref inf_b, ref b) => inf_b.has_viewport_percentage() || b.has_viewport_percentage(),
            TrackSize::FitContent(ref lop) => lop.has_viewport_percentage(),
        }
    }
}

impl<L: ToComputedValue> ToComputedValue for TrackSize<L> {
    type ComputedValue = TrackSize<L::ComputedValue>;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            TrackSize::Breadth(ref b) => TrackSize::Breadth(b.to_computed_value(context)),
            TrackSize::MinMax(ref b_1, ref b_2) =>
                TrackSize::MinMax(b_1.to_computed_value(context), b_2.to_computed_value(context)),
            TrackSize::FitContent(ref lop) => TrackSize::FitContent(lop.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            TrackSize::Breadth(ref b) =>
                TrackSize::Breadth(ToComputedValue::from_computed_value(b)),
            TrackSize::MinMax(ref b_1, ref b_2) =>
                TrackSize::MinMax(ToComputedValue::from_computed_value(b_1),
                                  ToComputedValue::from_computed_value(b_2)),
            TrackSize::FitContent(ref lop) =>
                TrackSize::FitContent(ToComputedValue::from_computed_value(lop)),
        }
    }
}

/// Parse the grid line names into a vector of owned strings.
///
/// https://drafts.csswg.org/css-grid/#typedef-line-names
pub fn parse_line_names(input: &mut Parser) -> Result<Vec<String>, ()> {
    input.expect_square_bracket_block()?;
    input.parse_nested_block(|input| {
        let mut values = vec![];
        while let Ok(ident) = input.try(|i| i.expect_ident()) {
            if CustomIdent::from_ident((&*ident).into(), &["span"]).is_err() {
                return Err(())
            }

            values.push(ident.into_owned());
        }

        Ok(values)
    })
}
<commit_msg>Add RepeatCount enum for handling the first component of repeat()<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/. */

//! Necessary types for [grid](https://drafts.csswg.org/css-grid/).

use cssparser::{Parser, Token};
use parser::{Parse, ParserContext};
use std::ascii::AsciiExt;
use std::fmt;
use style_traits::ToCss;
use values::{CSSFloat, CustomIdent, HasViewportPercentage};
use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
use values::specified::{Integer, LengthOrPercentage};

#[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A `<grid-line>` type.
///
/// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line
pub struct GridLine {
    /// Flag to check whether it's a `span` keyword.
    pub is_span: bool,
    /// A custom identifier for named lines.
    ///
    /// https://drafts.csswg.org/css-grid/#grid-placement-slot
    pub ident: Option<String>,
    /// Denotes the nth grid line from grid item's placement.
    pub integer: Option<i32>,
}

impl Default for GridLine {
    fn default() -> Self {
        GridLine {
            is_span: false,
            ident: None,
            integer: None,
        }
    }
}

impl ToCss for GridLine {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        if !self.is_span && self.ident.is_none() && self.integer.is_none() {
            return dest.write_str("auto")
        }

        if self.is_span {
            try!(dest.write_str("span"));
        }

        if let Some(i) = self.integer {
            try!(write!(dest, " {}", i));
        }

        if let Some(ref s) = self.ident {
            try!(write!(dest, " {}", s));
        }

        Ok(())
    }
}

impl Parse for GridLine {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        let mut grid_line = Default::default();
        if input.try(|i| i.expect_ident_matching("auto")).is_ok() {
            return Ok(grid_line)
        }

        for _ in 0..3 {     // Maximum possible entities for <grid-line>
            if input.try(|i| i.expect_ident_matching("span")).is_ok() {
                if grid_line.is_span {
                    return Err(())
                }
                grid_line.is_span = true;
            } else if let Ok(i) = input.try(|i| i.expect_integer()) {
                if i == 0 || grid_line.integer.is_some() {
                    return Err(())
                }
                grid_line.integer = Some(i);
            } else if let Ok(name) = input.try(|i| i.expect_ident()) {
                if grid_line.ident.is_some() {
                    return Err(())
                }
                grid_line.ident = Some(name.into_owned());
            } else {
                break
            }
        }

        if grid_line.is_span {
            if let Some(i) = grid_line.integer {
                if i < 0 {      // disallow negative integers for grid spans
                    return Err(())
                }
            } else {
                grid_line.integer = Some(1);
            }
        }

        Ok(grid_line)
    }
}

impl ComputedValueAsSpecified for GridLine {}
no_viewport_percentage!(GridLine);

define_css_keyword_enum!{ TrackKeyword:
    "auto" => Auto,
    "max-content" => MaxContent,
    "min-content" => MinContent
}

#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A track breadth for explicit grid track sizing. It's generic solely to
/// avoid re-implementing it for the computed type.
///
/// https://drafts.csswg.org/css-grid/#typedef-track-breadth
pub enum TrackBreadth<L> {
    /// The generic type is almost always a non-negative `<length-percentage>`
    Breadth(L),
    /// A flex fraction specified in `fr` units.
    Flex(CSSFloat),
    /// One of the track-sizing keywords (`auto`, `min-content`, `max-content`)
    Keyword(TrackKeyword),
}

impl<L> TrackBreadth<L> {
    /// Check whether this is a `<fixed-breadth>` (i.e., it only has `<length-percentage>`)
    ///
    /// https://drafts.csswg.org/css-grid/#typedef-fixed-breadth
    #[inline]
    pub fn is_fixed(&self) -> bool {
        match *self {
            TrackBreadth::Breadth(ref _lop) => true,
            _ => false,
        }
    }
}

/// Parse a single flexible length.
pub fn parse_flex(input: &mut Parser) -> Result<CSSFloat, ()> {
    match try!(input.next()) {
        Token::Dimension(ref value, ref unit) if unit.eq_ignore_ascii_case("fr") && value.value.is_sign_positive()
            => Ok(value.value),
        _ => Err(()),
    }
}

impl Parse for TrackBreadth<LengthOrPercentage> {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
            return Ok(TrackBreadth::Breadth(lop))
        }

        if let Ok(f) = input.try(parse_flex) {
            return Ok(TrackBreadth::Flex(f))
        }

        TrackKeyword::parse(input).map(TrackBreadth::Keyword)
    }
}

impl<L: ToCss> ToCss for TrackBreadth<L> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            TrackBreadth::Breadth(ref lop) => lop.to_css(dest),
            TrackBreadth::Flex(ref value) => write!(dest, "{}fr", value),
            TrackBreadth::Keyword(ref k) => k.to_css(dest),
        }
    }
}

impl HasViewportPercentage for TrackBreadth<LengthOrPercentage> {
    #[inline]
    fn has_viewport_percentage(&self) -> bool {
        if let TrackBreadth::Breadth(ref lop) = *self {
            lop.has_viewport_percentage()
        } else {
            false
        }
    }
}

impl<L: ToComputedValue> ToComputedValue for TrackBreadth<L> {
    type ComputedValue = TrackBreadth<L::ComputedValue>;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            TrackBreadth::Breadth(ref lop) => TrackBreadth::Breadth(lop.to_computed_value(context)),
            TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr),
            TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            TrackBreadth::Breadth(ref lop) =>
                TrackBreadth::Breadth(ToComputedValue::from_computed_value(lop)),
            TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr),
            TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k),
        }
    }
}

#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A `<track-size>` type for explicit grid track sizing. Like `<track-breadth>`, this is
/// generic only to avoid code bloat. It only takes `<length-percentage>`
///
/// https://drafts.csswg.org/css-grid/#typedef-track-size
pub enum TrackSize<L> {
    /// A flexible `<track-breadth>`
    Breadth(TrackBreadth<L>),
    /// A `minmax` function for a range over an inflexible `<track-breadth>`
    /// and a flexible `<track-breadth>`
    ///
    /// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax
    MinMax(TrackBreadth<L>, TrackBreadth<L>),
    /// A `fit-content` function.
    ///
    /// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content
    FitContent(L),
}

impl<L> TrackSize<L> {
    /// Check whether this is a `<fixed-size>`
    ///
    /// https://drafts.csswg.org/css-grid/#typedef-fixed-size
    pub fn is_fixed(&self) -> bool {
        match *self {
            TrackSize::Breadth(ref breadth) => breadth.is_fixed(),
            // For minmax function, it could be either
            // minmax(<fixed-breadth>, <track-breadth>) or minmax(<inflexible-breadth>, <fixed-breadth>),
            // and since both variants are a subset of minmax(<inflexible-breadth>, <track-breadth>), we only
            // need to make sure that they're fixed. So, we don't have to modify the parsing function.
            TrackSize::MinMax(ref breadth_1, ref breadth_2) => {
                if breadth_1.is_fixed() {
                    return true     // the second value is always a <track-breadth>
                }

                match *breadth_1 {
                    TrackBreadth::Flex(_) => false,     // should be <inflexible-breadth> at this point
                    _ => breadth_2.is_fixed(),
                }
            },
            TrackSize::FitContent(_) => false,
        }
    }
}

impl<L> Default for TrackSize<L> {
    fn default() -> Self {
        TrackSize::Breadth(TrackBreadth::Keyword(TrackKeyword::Auto))
    }
}

impl Parse for TrackSize<LengthOrPercentage> {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(b) = input.try(|i| TrackBreadth::parse(context, i)) {
            return Ok(TrackSize::Breadth(b))
        }

        if input.try(|i| i.expect_function_matching("minmax")).is_ok() {
            return input.parse_nested_block(|input| {
                let inflexible_breadth =
                    match input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
                        Ok(lop) => TrackBreadth::Breadth(lop),
                        Err(..) => {
                            let keyword = try!(TrackKeyword::parse(input));
                            TrackBreadth::Keyword(keyword)
                        }
                    };

                try!(input.expect_comma());
                Ok(TrackSize::MinMax(inflexible_breadth, try!(TrackBreadth::parse(context, input))))
            });
        }

        try!(input.expect_function_matching("fit-content"));
        // FIXME(emilio): This needs a parse_nested_block, doesn't it?
        Ok(try!(LengthOrPercentage::parse(context, input).map(TrackSize::FitContent)))
    }
}

impl<L: ToCss> ToCss for TrackSize<L> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            TrackSize::Breadth(ref b) => b.to_css(dest),
            TrackSize::MinMax(ref infexible, ref flexible) => {
                try!(dest.write_str("minmax("));
                try!(infexible.to_css(dest));
                try!(dest.write_str(","));
                try!(flexible.to_css(dest));
                dest.write_str(")")
            },
            TrackSize::FitContent(ref lop) => {
                try!(dest.write_str("fit-content("));
                try!(lop.to_css(dest));
                dest.write_str(")")
            },
        }
    }
}

impl HasViewportPercentage for TrackSize<LengthOrPercentage> {
    #[inline]
    fn has_viewport_percentage(&self) -> bool {
        match *self {
            TrackSize::Breadth(ref b) => b.has_viewport_percentage(),
            TrackSize::MinMax(ref inf_b, ref b) => inf_b.has_viewport_percentage() || b.has_viewport_percentage(),
            TrackSize::FitContent(ref lop) => lop.has_viewport_percentage(),
        }
    }
}

impl<L: ToComputedValue> ToComputedValue for TrackSize<L> {
    type ComputedValue = TrackSize<L::ComputedValue>;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            TrackSize::Breadth(ref b) => TrackSize::Breadth(b.to_computed_value(context)),
            TrackSize::MinMax(ref b_1, ref b_2) =>
                TrackSize::MinMax(b_1.to_computed_value(context), b_2.to_computed_value(context)),
            TrackSize::FitContent(ref lop) => TrackSize::FitContent(lop.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            TrackSize::Breadth(ref b) =>
                TrackSize::Breadth(ToComputedValue::from_computed_value(b)),
            TrackSize::MinMax(ref b_1, ref b_2) =>
                TrackSize::MinMax(ToComputedValue::from_computed_value(b_1),
                                  ToComputedValue::from_computed_value(b_2)),
            TrackSize::FitContent(ref lop) =>
                TrackSize::FitContent(ToComputedValue::from_computed_value(lop)),
        }
    }
}

/// Parse the grid line names into a vector of owned strings.
///
/// https://drafts.csswg.org/css-grid/#typedef-line-names
pub fn parse_line_names(input: &mut Parser) -> Result<Vec<String>, ()> {
    input.expect_square_bracket_block()?;
    input.parse_nested_block(|input| {
        let mut values = vec![];
        while let Ok(ident) = input.try(|i| i.expect_ident()) {
            if CustomIdent::from_ident((&*ident).into(), &["span"]).is_err() {
                return Err(())
            }

            values.push(ident.into_owned());
        }

        Ok(values)
    })
}

/// The initial argument of the `repeat` function.
///
/// https://drafts.csswg.org/css-grid/#typedef-track-repeat
#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum RepeatCount {
    /// A positive integer. This is allowed only for `<track-repeat>` and `<fixed-repeat>`
    Number(Integer),
    /// An `<auto-fill>` keyword allowed only for `<auto-repeat>`
    AutoFill,
    /// An `<auto-fit>` keyword allowed only for `<auto-repeat>`
    AutoFit,
}

impl Parse for RepeatCount {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if let Ok(i) = input.try(|i| Integer::parse(context, i)) {
            if i.value > 0 {
                Ok(RepeatCount::Number(i))
            } else {
                Err(())
            }
        } else {
            match_ignore_ascii_case! { &input.expect_ident()?,
                "auto-fill" => Ok(RepeatCount::AutoFill),
                "auto-fit" => Ok(RepeatCount::AutoFit),
                _ => Err(()),
            }
        }
    }
}

impl ToCss for RepeatCount {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            RepeatCount::Number(ref c) => c.to_css(dest),
            RepeatCount::AutoFill => dest.write_str("auto-fill"),
            RepeatCount::AutoFit => dest.write_str("auto-fit"),
        }
    }
}

impl ComputedValueAsSpecified for RepeatCount {}
no_viewport_percentage!(RepeatCount);
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The `Clone` trait for types that cannot be 'implicitly copied'
//!
//! In Rust, some simple types are "implicitly copyable" and when you
//! assign them or pass them as arguments, the receiver will get a copy,
//! leaving the original value in place. These types do not require
//! allocation to copy and do not have finalizers (i.e. they do not
//! contain owned boxes or implement `Drop`), so the compiler considers
//! them cheap and safe to copy. For other types copies must be made
//! explicitly, by convention implementing the `Clone` trait and calling
//! the `clone` method.

#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;

/// A common trait for cloning an object.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Clone : Sized {
    /// Returns a copy of the value.
    ///
    /// # Examples
    ///
    /// ```
    /// let hello = "Hello"; // &str implements Clone
    ///
    /// assert_eq!("Hello", hello.clone());
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    fn clone(&self) -> Self;

    /// Perform copy-assignment from `source`.
    ///
    /// `a.clone_from(&b)` is equivalent to `a = b.clone()` in functionality,
    /// but can be overridden to reuse the resources of `a` to avoid unnecessary
    /// allocations.
    #[inline(always)]
    #[unstable(feature = "core",
               reason = "this function is rarely used")]
    fn clone_from(&mut self, source: &Self) {
        *self = source.clone()
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized> Clone for &'a T {
    /// Return a shallow copy of the reference.
    #[inline]
    fn clone(&self) -> &'a T { *self }
}

macro_rules! clone_impl {
    ($t:ty) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl Clone for $t {
            /// Return a deep copy of the value.
            #[inline]
            fn clone(&self) -> $t { *self }
        }
    }
}

clone_impl! { isize }
clone_impl! { i8 }
clone_impl! { i16 }
clone_impl! { i32 }
clone_impl! { i64 }

clone_impl! { usize }
clone_impl! { u8 }
clone_impl! { u16 }
clone_impl! { u32 }
clone_impl! { u64 }

clone_impl! { f32 }
clone_impl! { f64 }

clone_impl! { () }
clone_impl! { bool }
clone_impl! { char }

macro_rules! extern_fn_clone {
    ($($A:ident),*) => (
        #[unstable(feature = "core",
                   reason = "this may not be sufficient for fns with region parameters")]
        impl<$($A,)* ReturnType> Clone for extern "Rust" fn($($A),*) -> ReturnType {
            /// Return a copy of a function pointer
            #[inline]
            fn clone(&self) -> extern "Rust" fn($($A),*) -> ReturnType { *self }
        }
    )
}

extern_fn_clone! {}
extern_fn_clone! { A }
extern_fn_clone! { A, B }
extern_fn_clone! { A, B, C }
extern_fn_clone! { A, B, C, D }
extern_fn_clone! { A, B, C, D, E }
extern_fn_clone! { A, B, C, D, E, F }
extern_fn_clone! { A, B, C, D, E, F, G }
extern_fn_clone! { A, B, C, D, E, F, G, H }
<commit_msg>std: Stabilize Clone::clone_from<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The `Clone` trait for types that cannot be 'implicitly copied'
//!
//! In Rust, some simple types are "implicitly copyable" and when you
//! assign them or pass them as arguments, the receiver will get a copy,
//! leaving the original value in place. These types do not require
//! allocation to copy and do not have finalizers (i.e. they do not
//! contain owned boxes or implement `Drop`), so the compiler considers
//! them cheap and safe to copy. For other types copies must be made
//! explicitly, by convention implementing the `Clone` trait and calling
//! the `clone` method.

#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;

/// A common trait for cloning an object.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Clone : Sized {
    /// Returns a copy of the value.
    ///
    /// # Examples
    ///
    /// ```
    /// let hello = "Hello"; // &str implements Clone
    ///
    /// assert_eq!("Hello", hello.clone());
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    fn clone(&self) -> Self;

    /// Perform copy-assignment from `source`.
    ///
    /// `a.clone_from(&b)` is equivalent to `a = b.clone()` in functionality,
    /// but can be overridden to reuse the resources of `a` to avoid unnecessary
    /// allocations.
    #[inline(always)]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn clone_from(&mut self, source: &Self) {
        *self = source.clone()
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized> Clone for &'a T {
    /// Return a shallow copy of the reference.
    #[inline]
    fn clone(&self) -> &'a T { *self }
}

macro_rules! clone_impl {
    ($t:ty) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl Clone for $t {
            /// Return a deep copy of the value.
            #[inline]
            fn clone(&self) -> $t { *self }
        }
    }
}

clone_impl! { isize }
clone_impl! { i8 }
clone_impl! { i16 }
clone_impl! { i32 }
clone_impl! { i64 }

clone_impl! { usize }
clone_impl! { u8 }
clone_impl! { u16 }
clone_impl! { u32 }
clone_impl! { u64 }

clone_impl! { f32 }
clone_impl! { f64 }

clone_impl! { () }
clone_impl! { bool }
clone_impl! { char }

macro_rules! extern_fn_clone {
    ($($A:ident),*) => (
        #[unstable(feature = "core",
                   reason = "this may not be sufficient for fns with region parameters")]
        impl<$($A,)* ReturnType> Clone for extern "Rust" fn($($A),*) -> ReturnType {
            /// Return a copy of a function pointer
            #[inline]
            fn clone(&self) -> extern "Rust" fn($($A),*) -> ReturnType { *self }
        }
    )
}

extern_fn_clone! {}
extern_fn_clone! { A }
extern_fn_clone! { A, B }
extern_fn_clone! { A, B, C }
extern_fn_clone! { A, B, C, D }
extern_fn_clone! { A, B, C, D, E }
extern_fn_clone! { A, B, C, D, E, F }
extern_fn_clone! { A, B, C, D, E, F, G }
extern_fn_clone! { A, B, C, D, E, F, G, H }
<|endoftext|>