<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use interner::StringKey;
use schema::{SDLSchema, Schema, Type};
use strsim::damerau_levenshtein;

/// Computes the lexical distance between strings A and B.
///
/// The "distance" between two strings is given by counting the minimum number
/// of edits needed to transform string A into string B. An edit can be an
/// insertion, deletion, or substitution of a single character, or a swap of two
/// adjacent characters.
///
/// Includes a custom alteration from Damerau-Levenshtein to treat case changes
/// as a single edit which helps identify mis-cased values with an edit distance
/// of 1.
///
/// This distance can be useful for detecting typos in input or sorting
struct LexicalDistance<'a> {
    input: &'a str,
    input_lower_case: String,
}

impl<'a> LexicalDistance<'a> {
    pub fn new(input: &'a str) -> Self {
        Self {
            input,
            input_lower_case: input.to_lowercase(),
        }
    }

    pub fn measure(&self, option: &str, threshold: usize) -> Option<usize> {
        if self.input == option {
            return Some(0);
        }

        let option_lower_case = option.to_lowercase();

        // Any case change counts as a single edit
        if self.input_lower_case == option_lower_case {
            return Some(1);
        }

        let distance = damerau_levenshtein(&self.input, option);
        if distance <= threshold {
            Some(distance)
        } else {
            None
        }
    }
}

/// Generate the list of the suggested values that are closest to the `input`
fn suggestion_list(input: StringKey, options: &[StringKey], limit: usize) -> Vec<StringKey> {
    let lexical_distance = LexicalDistance::new(input.lookup());

    // This is from graphql-js (https://github.com/graphql/graphql-js/commit/4c10844f6f2ab3e2993d8d5f5f3ed97dce9d3655)
    // The original idea is from TypeScript: https://github.com/microsoft/TypeScript/blob/afddaf090ad8e3a4d3501ce91490b5552d1e4a42/src/compiler/core.ts#L1752
    let threshold = (input.lookup().len() as f32 * 0.4).floor() + 1.0;

    let mut options_with_distance: Vec<(StringKey, usize)> = options
        .iter()
        .filter_map(|option| {
            let distance = lexical_distance.measure(option.lookup(), threshold as usize)?;
            Some((*option, distance))
        })
        .collect();

    options_with_distance.sort_by(|(str_a, distance_a), (str_b, distance_b)| {
        if distance_a == distance_b {
            str_a.lookup().cmp(str_b.lookup())
        } else {
            distance_a.cmp(distance_b)
        }
    });

    options_with_distance
        .iter()
        .take(limit)
        .map(|(value, _)| *value)
        .collect::<Vec<StringKey>>()
}

pub struct GraphQLSuggestions<'schema> {
    schema: &'schema SDLSchema,
}

impl<'schema> GraphQLSuggestions<'schema> {
    const MAX_SUGGESTIONS: usize = 5;

    pub fn new(schema: &'schema SDLSchema) -> Self {
        Self { schema }
    }

    pub fn input_type_suggestions(&self, input: StringKey) -> Vec<StringKey> {
        let input_types = self
            .schema
            .get_type_map()
            .filter_map(|(type_name, type_)| {
                if type_.is_input_type() {
                    Some(*type_name)
                } else {
                    None
                }
            })
            .collect::<Vec<StringKey>>();

        suggestion_list(input, &input_types, GraphQLSuggestions::MAX_SUGGESTIONS)
    }

    pub fn output_type_suggestions(&self, input: StringKey) -> Vec<StringKey> {
        let type_names = self
            .schema
            .get_type_map()
            .filter_map(|(type_name, type_)| {
                if !type_.is_input_object() {
                    Some(*type_name)
                } else {
                    None
                }
            })
            .collect::<Vec<StringKey>>();

        suggestion_list(input, &type_names, GraphQLSuggestions::MAX_SUGGESTIONS)
    }

    pub fn composite_type_suggestions(&self, input: StringKey) -> Vec<StringKey> {
        let type_names = self
            .schema
            .get_type_map()
            .filter_map(|(type_name, type_)| {
                if type_.is_composite_type() {
                    Some(*type_name)
                } else {
                    None
                }
            })
            .collect::<Vec<StringKey>>();

        suggestion_list(input, &type_names, GraphQLSuggestions::MAX_SUGGESTIONS)
    }

    pub fn field_name_suggestion(&self, type_: Option<Type>, input: StringKey) -> Vec<StringKey> {
        let field_names: Vec<StringKey> = match type_ {
            Some(Type::Object(object_id)) => self
                .schema
                .object(object_id)
                .fields
                .iter()
                .map(|field_id| self.schema.field(*field_id).name)
                .collect(),
            Some(Type::Interface(interface_id)) => self
                .schema
                .interface(interface_id)
                .fields
                .iter()
                .map(|field_id| self.schema.field(*field_id).name)
                .collect(),
            Some(Type::InputObject(input_id)) => self
                .schema
                .input_object(input_id)
                .fields
                .iter()
                .map(|arg| arg.name)
                .collect(),
            _ => vec![],
        };

        suggestion_list(input, &field_names, GraphQLSuggestions::MAX_SUGGESTIONS)
    }
}
#[cfg(test)]
mod tests {
    use super::suggestion_list;
    use interner::Intern;

    #[test]
    fn test_suggestion_list_empty_input() {
        assert_eq!(
            suggestion_list("".intern(), &["a".intern()], 5),
            vec!["a".intern()]
        );
    }

    #[test]
    fn test_suggestion_no_options() {
        assert_eq!(suggestion_list("a".intern(), &[], 5), vec![]);
    }

    #[test]
    fn test_suggestion_small_lexical_distance() {
        assert_eq!(
            suggestion_list("greenish".intern(), &["green".intern()], 5),
            vec!["green".intern()]
        );
        assert_eq!(
            suggestion_list("green".intern(), &["greenish".intern()], 5),
            vec!["greenish".intern()]
        );
    }

    #[test]
    fn test_suggestion_reject_objects_with_long_distance() {
        assert_eq!(
            suggestion_list("aaaa".intern(), &["aaab".intern()], 5),
            vec!["aaab".intern()]
        );
        assert_eq!(
            suggestion_list("aaaa".intern(), &["aabb".intern()], 5),
            vec!["aabb".intern()]
        );
        assert_eq!(
            suggestion_list("aaaa".intern(), &["abbb".intern()], 5),
            vec![]
        );
        assert_eq!(suggestion_list("ab".intern(), &["ca".intern()], 5), vec![]);
    }

    #[test]
    fn test_suggestion_different_case() {
        assert_eq!(
            suggestion_list("verylongstring".intern(), &["VERYLONGSTRING".intern()], 5),
            vec!["VERYLONGSTRING".intern()]
        );
        assert_eq!(
            suggestion_list("VERYLONGSTRING".intern(), &["verylongstring".intern()], 5),
            vec!["verylongstring".intern()]
        );
        assert_eq!(
            suggestion_list("VERYLONGSTRING".intern(), &["VeryLongString".intern()], 5),
            vec!["VeryLongString".intern()]
        );
    }

    #[test]
    fn test_suggestion_with_transpositions() {
        assert_eq!(
            suggestion_list("agr".intern(), &["arg".intern()], 5),
            vec!["arg".intern()]
        );
        assert_eq!(
            suggestion_list("214365879".intern(), &["123456789".intern()], 5),
            vec!["123456789".intern()]
        );
    }

    #[test]
    fn test_suggestion_sorted() {
        assert_eq!(
            suggestion_list(
                "abc".intern(),
                &["a".intern(), "ab".intern(), "abc".intern()],
                5
            ),
            vec!["abc".intern(), "ab".intern(), "a".intern()]
        );

        assert_eq!(
            suggestion_list(
                "a".intern(),
                &["az".intern(), "ax".intern(), "ay".intern()],
                5
            ),
            vec!["ax".intern(), "ay".intern(), "az".intern()]
        );
    }

    #[test]
    fn test_suggestions_with_user() {
        assert_eq!(
            suggestion_list(
                "Users".intern(),
                &["User".intern(), "Query".intern(), "Mutation".intern()],
                5
            ),
            vec!["User".intern(), "Query".intern()]
        );
    }
}
<commit_msg>Disable GraphQLSuggestions in flatten schema<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use interner::StringKey;
use schema::{SDLSchema, Schema, Type};
use strsim::damerau_levenshtein;

/// Computes the lexical distance between strings A and B.
///
/// The "distance" between two strings is given by counting the minimum number
/// of edits needed to transform string A into string B. An edit can be an
/// insertion, deletion, or substitution of a single character, or a swap of two
/// adjacent characters.
///
/// Includes a custom alteration from Damerau-Levenshtein to treat case changes
/// as a single edit which helps identify mis-cased values with an edit distance
/// of 1.
///
/// This distance can be useful for detecting typos in input or sorting
struct LexicalDistance<'a> {
    input: &'a str,
    input_lower_case: String,
}

impl<'a> LexicalDistance<'a> {
    pub fn new(input: &'a str) -> Self {
        Self {
            input,
            input_lower_case: input.to_lowercase(),
        }
    }

    pub fn measure(&self, option: &str, threshold: usize) -> Option<usize> {
        if self.input == option {
            return Some(0);
        }

        let option_lower_case = option.to_lowercase();

        // Any case change counts as a single edit
        if self.input_lower_case == option_lower_case {
            return Some(1);
        }

        let distance = damerau_levenshtein(&self.input, option);
        if distance <= threshold {
            Some(distance)
        } else {
            None
        }
    }
}

/// Generate the list of the suggested values that are closest to the `input`
fn suggestion_list(input: StringKey, options: &[StringKey], limit: usize) -> Vec<StringKey> {
    let lexical_distance = LexicalDistance::new(input.lookup());

    // This is from graphql-js (https://github.com/graphql/graphql-js/commit/4c10844f6f2ab3e2993d8d5f5f3ed97dce9d3655)
    // The original idea is from TypeScript: https://github.com/microsoft/TypeScript/blob/afddaf090ad8e3a4d3501ce91490b5552d1e4a42/src/compiler/core.ts#L1752
    let threshold = (input.lookup().len() as f32 * 0.4).floor() + 1.0;

    let mut options_with_distance: Vec<(StringKey, usize)> = options
        .iter()
        .filter_map(|option| {
            let distance = lexical_distance.measure(option.lookup(), threshold as usize)?;
            Some((*option, distance))
        })
        .collect();

    options_with_distance.sort_by(|(str_a, distance_a), (str_b, distance_b)| {
        if distance_a == distance_b {
            str_a.lookup().cmp(str_b.lookup())
        } else {
            distance_a.cmp(distance_b)
        }
    });

    options_with_distance
        .iter()
        .take(limit)
        .map(|(value, _)| *value)
        .collect::<Vec<StringKey>>()
}

pub struct GraphQLSuggestions<'schema> {
    schema: &'schema SDLSchema,
    enabled: bool,
}

impl<'schema> GraphQLSuggestions<'schema> {
    const MAX_SUGGESTIONS: usize = 5;

    pub fn new(schema: &'schema SDLSchema) -> Self {
        // If the schema is flatten schema, at this time, we need to temporary disable
        // the GraphQLSuggestions as `get_type_map` in flattenn schema is not yet implemented.
        let enabled = !matches!(schema, SDLSchema::FlatBuffer(_));

        Self { schema, enabled }
    }

    pub fn input_type_suggestions(&self, input: StringKey) -> Vec<StringKey> {
        if !self.enabled {
            return Vec::new();
        }

        let input_types = self
            .schema
            .get_type_map()
            .filter_map(|(type_name, type_)| {
                if type_.is_input_type() {
                    Some(*type_name)
                } else {
                    None
                }
            })
            .collect::<Vec<StringKey>>();

        suggestion_list(input, &input_types, GraphQLSuggestions::MAX_SUGGESTIONS)
    }

    pub fn output_type_suggestions(&self, input: StringKey) -> Vec<StringKey> {
        if !self.enabled {
            return Vec::new();
        }

        let type_names = self
            .schema
            .get_type_map()
            .filter_map(|(type_name, type_)| {
                if !type_.is_input_object() {
                    Some(*type_name)
                } else {
                    None
                }
            })
            .collect::<Vec<StringKey>>();

        suggestion_list(input, &type_names, GraphQLSuggestions::MAX_SUGGESTIONS)
    }

    pub fn composite_type_suggestions(&self, input: StringKey) -> Vec<StringKey> {
        if !self.enabled {
            return Vec::new();
        }

        let type_names = self
            .schema
            .get_type_map()
            .filter_map(|(type_name, type_)| {
                if type_.is_composite_type() {
                    Some(*type_name)
                } else {
                    None
                }
            })
            .collect::<Vec<StringKey>>();

        suggestion_list(input, &type_names, GraphQLSuggestions::MAX_SUGGESTIONS)
    }

    pub fn field_name_suggestion(&self, type_: Option<Type>, input: StringKey) -> Vec<StringKey> {
        if !self.enabled {
            return Vec::new();
        }

        let field_names: Vec<StringKey> = match type_ {
            Some(Type::Object(object_id)) => self
                .schema
                .object(object_id)
                .fields
                .iter()
                .map(|field_id| self.schema.field(*field_id).name)
                .collect(),
            Some(Type::Interface(interface_id)) => self
                .schema
                .interface(interface_id)
                .fields
                .iter()
                .map(|field_id| self.schema.field(*field_id).name)
                .collect(),
            Some(Type::InputObject(input_id)) => self
                .schema
                .input_object(input_id)
                .fields
                .iter()
                .map(|arg| arg.name)
                .collect(),
            _ => vec![],
        };

        suggestion_list(input, &field_names, GraphQLSuggestions::MAX_SUGGESTIONS)
    }
}
#[cfg(test)]
mod tests {
    use super::suggestion_list;
    use interner::Intern;

    #[test]
    fn test_suggestion_list_empty_input() {
        assert_eq!(
            suggestion_list("".intern(), &["a".intern()], 5),
            vec!["a".intern()]
        );
    }

    #[test]
    fn test_suggestion_no_options() {
        assert_eq!(suggestion_list("a".intern(), &[], 5), vec![]);
    }

    #[test]
    fn test_suggestion_small_lexical_distance() {
        assert_eq!(
            suggestion_list("greenish".intern(), &["green".intern()], 5),
            vec!["green".intern()]
        );
        assert_eq!(
            suggestion_list("green".intern(), &["greenish".intern()], 5),
            vec!["greenish".intern()]
        );
    }

    #[test]
    fn test_suggestion_reject_objects_with_long_distance() {
        assert_eq!(
            suggestion_list("aaaa".intern(), &["aaab".intern()], 5),
            vec!["aaab".intern()]
        );
        assert_eq!(
            suggestion_list("aaaa".intern(), &["aabb".intern()], 5),
            vec!["aabb".intern()]
        );
        assert_eq!(
            suggestion_list("aaaa".intern(), &["abbb".intern()], 5),
            vec![]
        );
        assert_eq!(suggestion_list("ab".intern(), &["ca".intern()], 5), vec![]);
    }

    #[test]
    fn test_suggestion_different_case() {
        assert_eq!(
            suggestion_list("verylongstring".intern(), &["VERYLONGSTRING".intern()], 5),
            vec!["VERYLONGSTRING".intern()]
        );
        assert_eq!(
            suggestion_list("VERYLONGSTRING".intern(), &["verylongstring".intern()], 5),
            vec!["verylongstring".intern()]
        );
        assert_eq!(
            suggestion_list("VERYLONGSTRING".intern(), &["VeryLongString".intern()], 5),
            vec!["VeryLongString".intern()]
        );
    }

    #[test]
    fn test_suggestion_with_transpositions() {
        assert_eq!(
            suggestion_list("agr".intern(), &["arg".intern()], 5),
            vec!["arg".intern()]
        );
        assert_eq!(
            suggestion_list("214365879".intern(), &["123456789".intern()], 5),
            vec!["123456789".intern()]
        );
    }

    #[test]
    fn test_suggestion_sorted() {
        assert_eq!(
            suggestion_list(
                "abc".intern(),
                &["a".intern(), "ab".intern(), "abc".intern()],
                5
            ),
            vec!["abc".intern(), "ab".intern(), "a".intern()]
        );

        assert_eq!(
            suggestion_list(
                "a".intern(),
                &["az".intern(), "ax".intern(), "ay".intern()],
                5
            ),
            vec!["ax".intern(), "ay".intern(), "az".intern()]
        );
    }

    #[test]
    fn test_suggestions_with_user() {
        assert_eq!(
            suggestion_list(
                "Users".intern(),
                &["User".intern(), "Query".intern(), "Mutation".intern()],
                5
            ),
            vec!["User".intern(), "Query".intern()]
        );
    }
}
<|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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

<% data.new_style_struct("Text",
                         inherited=False,
                         gecko_name="TextReset",
                         additional_methods=[Method("has_underline", "bool"),
                                             Method("has_overline", "bool"),
                                             Method("has_line_through", "bool")]) %>

<%helpers:longhand name="text-overflow" animatable="False"
                   spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow">
    use std::fmt;
    use style_traits::ToCss;
    use values::NoViewportPercentage;
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}
    impl NoViewportPercentage for SpecifiedValue {}

    #[derive(PartialEq, Eq, Clone, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub enum Side {
        Clip,
        Ellipsis,
        String(String),
    }

    #[derive(PartialEq, Eq, Clone, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub first: Side,
        pub second: Option<Side>
    }

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        SpecifiedValue {
            first: Side::Clip,
            second: None
        }
    }
    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        let first = try!(Side::parse(context, input));
        let second = Side::parse(context, input).ok();
        Ok(SpecifiedValue {
            first: first,
            second: second,
        })
    }
    impl Parse for Side {
        fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Side, ()> {
            if let Ok(ident) = input.try(|input| input.expect_ident()) {
                match_ignore_ascii_case! { ident,
                    "clip" => Ok(Side::Clip),
                    "ellipsis" => Ok(Side::Ellipsis),
                    _ => Err(())
                }
            } else {
                Ok(Side::String(try!(input.expect_string()).into_owned()))
            }
        }
    }

    impl ToCss for Side {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self {
                Side::Clip => dest.write_str("clip"),
                Side::Ellipsis => dest.write_str("ellipsis"),
                Side::String(ref s) => dest.write_str(s)
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.first.to_css(dest));
            if let Some(ref second) = self.second {
                try!(dest.write_str(" "));
                try!(second.to_css(dest));
            }
            Ok(())
        }
    }
</%helpers:longhand>

${helpers.single_keyword("unicode-bidi",
                         "normal embed isolate bidi-override isolate-override plaintext",
                         animatable=False,
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi")}

// FIXME: This prop should be animatable.
<%helpers:longhand name="${'text-decoration' if product == 'servo' else 'text-decoration-line'}"
                   custom_cascade="${product == 'servo'}"
                   animatable="False"
                   disable_when_testing="True",
                   spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
    use std::fmt;
    use style_traits::ToCss;
    use values::NoViewportPercentage;
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}
    impl NoViewportPercentage for SpecifiedValue {}

    #[derive(PartialEq, Eq, Copy, Clone, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub underline: bool,
        pub overline: bool,
        pub line_through: bool,
        pub blink: bool,
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let mut space = false;
            if self.underline {
                try!(dest.write_str("underline"));
                space = true;
            }
            if self.overline {
                if space {
                    try!(dest.write_str(" "));
                }
                try!(dest.write_str("overline"));
                space = true;
            }
            if self.line_through {
                if space {
                    try!(dest.write_str(" "));
                }
                try!(dest.write_str("line-through"));
                space = true;
            }
            if self.blink {
                if space {
                    try!(dest.write_str(" "));
                }
                try!(dest.write_str("blink"));
            }
            Ok(())
        }
    }
    pub mod computed_value {
        pub type T = super::SpecifiedValue;
        #[allow(non_upper_case_globals)]
        pub const none: T = super::SpecifiedValue {
            underline: false, overline: false, line_through: false, blink: false
        };
    }
    #[inline] pub fn get_initial_value() -> computed_value::T {
        computed_value::none
    }
    /// none | [ underline || overline || line-through || blink ]
    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        let mut result = SpecifiedValue {
            underline: false, overline: false, line_through: false, blink: false
        };
        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(result)
        }
        let mut empty = true;

        while input.try(|input| {
                if let Ok(ident) = input.expect_ident() {
                    match_ignore_ascii_case! { ident,
                        "underline" => if result.underline { return Err(()) }
                                       else { empty = false; result.underline = true },
                        "overline" => if result.overline { return Err(()) }
                                      else { empty = false; result.overline = true },
                        "line-through" => if result.line_through { return Err(()) }
                                          else { empty = false; result.line_through = true },
                        "blink" => if result.blink { return Err(()) }
                                   else { empty = false; result.blink = true },
                        _ => return Err(())
                    }
                } else {
                    return Err(());
                }
                Ok(())
            }).is_ok() {
        }

        if !empty { Ok(result) } else { Err(()) }
    }

    % if product == "servo":
        fn cascade_property_custom(_declaration: &PropertyDeclaration,
                                   _inherited_style: &ComputedValues,
                                   context: &mut computed::Context,
                                   _seen: &mut PropertyBitField,
                                   _cacheable: &mut bool,
                                   _error_reporter: &mut StdBox<ParseErrorReporter + Send>) {
                longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context);
        }
    % endif
</%helpers:longhand>

${helpers.single_keyword("text-decoration-style",
                         "solid double dotted dashed wavy -moz-none",
                         products="gecko",
                         animatable=False,
                         spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}

${helpers.predefined_type(
    "text-decoration-color", "CSSColor",
    "CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
    complex_color=True,
    products="gecko",
    animatable=True,
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
<commit_msg>Auto merge of #15210 - upsuper:text-decoration-line, r=Manishearth<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

<% data.new_style_struct("Text",
                         inherited=False,
                         gecko_name="TextReset",
                         additional_methods=[Method("has_underline", "bool"),
                                             Method("has_overline", "bool"),
                                             Method("has_line_through", "bool")]) %>

<%helpers:longhand name="text-overflow" animatable="False"
                   spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow">
    use std::fmt;
    use style_traits::ToCss;
    use values::NoViewportPercentage;
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}
    impl NoViewportPercentage for SpecifiedValue {}

    #[derive(PartialEq, Eq, Clone, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub enum Side {
        Clip,
        Ellipsis,
        String(String),
    }

    #[derive(PartialEq, Eq, Clone, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub first: Side,
        pub second: Option<Side>
    }

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        SpecifiedValue {
            first: Side::Clip,
            second: None
        }
    }
    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        let first = try!(Side::parse(context, input));
        let second = Side::parse(context, input).ok();
        Ok(SpecifiedValue {
            first: first,
            second: second,
        })
    }
    impl Parse for Side {
        fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Side, ()> {
            if let Ok(ident) = input.try(|input| input.expect_ident()) {
                match_ignore_ascii_case! { ident,
                    "clip" => Ok(Side::Clip),
                    "ellipsis" => Ok(Side::Ellipsis),
                    _ => Err(())
                }
            } else {
                Ok(Side::String(try!(input.expect_string()).into_owned()))
            }
        }
    }

    impl ToCss for Side {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self {
                Side::Clip => dest.write_str("clip"),
                Side::Ellipsis => dest.write_str("ellipsis"),
                Side::String(ref s) => dest.write_str(s)
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.first.to_css(dest));
            if let Some(ref second) = self.second {
                try!(dest.write_str(" "));
                try!(second.to_css(dest));
            }
            Ok(())
        }
    }
</%helpers:longhand>

${helpers.single_keyword("unicode-bidi",
                         "normal embed isolate bidi-override isolate-override plaintext",
                         animatable=False,
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi")}

// FIXME: This prop should be animatable.
<%helpers:longhand name="${'text-decoration' if product == 'servo' else 'text-decoration-line'}"
                   custom_cascade="${product == 'servo'}"
                   animatable="False"
                   disable_when_testing="True",
                   spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
    use std::fmt;
    use style_traits::ToCss;
    use values::NoViewportPercentage;
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}
    impl NoViewportPercentage for SpecifiedValue {}

    #[derive(PartialEq, Eq, Copy, Clone, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub underline: bool,
        pub overline: bool,
        pub line_through: bool,
        pub blink: bool,
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let mut has_any = false;
            macro_rules! write_value {
                ($line:ident => $css:expr) => {
                    if self.$line {
                        if has_any {
                            dest.write_str(" ")?;
                        }
                        dest.write_str($css)?;
                        has_any = true;
                    }
                }
            }
            write_value!(underline => "underline");
            write_value!(overline => "overline");
            write_value!(line_through => "line-through");
            write_value!(blink => "blink");
            if !has_any {
                dest.write_str("none")?;
            }
            Ok(())
        }
    }
    pub mod computed_value {
        pub type T = super::SpecifiedValue;
        #[allow(non_upper_case_globals)]
        pub const none: T = super::SpecifiedValue {
            underline: false, overline: false, line_through: false, blink: false
        };
    }
    #[inline] pub fn get_initial_value() -> computed_value::T {
        computed_value::none
    }
    /// none | [ underline || overline || line-through || blink ]
    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        let mut result = SpecifiedValue {
            underline: false, overline: false, line_through: false, blink: false
        };
        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(result)
        }
        let mut empty = true;

        while input.try(|input| {
                if let Ok(ident) = input.expect_ident() {
                    match_ignore_ascii_case! { ident,
                        "underline" => if result.underline { return Err(()) }
                                       else { empty = false; result.underline = true },
                        "overline" => if result.overline { return Err(()) }
                                      else { empty = false; result.overline = true },
                        "line-through" => if result.line_through { return Err(()) }
                                          else { empty = false; result.line_through = true },
                        "blink" => if result.blink { return Err(()) }
                                   else { empty = false; result.blink = true },
                        _ => return Err(())
                    }
                } else {
                    return Err(());
                }
                Ok(())
            }).is_ok() {
        }

        if !empty { Ok(result) } else { Err(()) }
    }

    % if product == "servo":
        fn cascade_property_custom(_declaration: &PropertyDeclaration,
                                   _inherited_style: &ComputedValues,
                                   context: &mut computed::Context,
                                   _seen: &mut PropertyBitField,
                                   _cacheable: &mut bool,
                                   _error_reporter: &mut StdBox<ParseErrorReporter + Send>) {
                longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context);
        }
    % endif
</%helpers:longhand>

${helpers.single_keyword("text-decoration-style",
                         "solid double dotted dashed wavy -moz-none",
                         products="gecko",
                         animatable=False,
                         spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}

${helpers.predefined_type(
    "text-decoration-color", "CSSColor",
    "CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
    complex_color=True,
    products="gecko",
    animatable=True,
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
<|endoftext|>


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

fn change_directory(path: path::PathBuf) {
    match env::set_current_dir(&path) {
        Err(err) => println!("Error : {}", err),
        _ => {}
    }
}

pub fn cd (command_vector: &Vec<&str> ) {
    if command_vector.len() == 2 {
        let mut new_path = path::PathBuf::new();
        new_path.push(command_vector[1]);
        change_directory(new_path);
    } else if command_vector.len() == 1 {
        match env::home_dir() {
            Some(n) => change_directory(n),
            None => println!("No known home directory")
        }
    } else {
        println!("Error : Invalid path");
    }
}
<commit_msg>nique<commit_after>use std::env;
use std::path;

fn change_directory(path: &path::PathBuf) {
    match env::set_current_dir(path) {
        Err(err) => println!("Error : {}, {}", err, path.to_str().unwrap()),
        Ok(_) => println!("Success : {}", path.to_str().unwrap())
    }
}

fn build_new_path(command_path: &str) -> Option<path::PathBuf> {
    let mut desired_path = path::PathBuf::new();
    let user_path = path::PathBuf::from(command_path);
    if !user_path.is_absolute() {
        let current_path = env::current_dir();
        match current_path {
            Ok(n) => {
                desired_path.push(n);
                desired_path.push(user_path);
            },
            Err(e) => {
                println!("Error : {}", e);
                return None;
            }
        }
    } else {
        desired_path.push(user_path);
    }
    return Some(desired_path)
}

pub fn cd (command_vector: &Vec<&str> ) {
    if command_vector.len() == 2 {
        match build_new_path(&command_vector[1]) {
            Some(n) => change_directory(&n),
            None => return
        }
    } else if command_vector.len() == 1 {
        match env::home_dir() {
            Some(n) => change_directory(&n),
            None => println!("No known home directory")
        }
    } else {
        println!("Error : Invalid path");
    }
}
<|endoftext|>


<commit_before><commit_msg>include tabstops<commit_after><|endoftext|>


<commit_before>use std::collections::{ HashSet, HashMap };
use std::sync::RwLock;
use super::blake2::blake2b::blake2b;
use super::{
    ClientDB,
    DBValue,
    DBError
};

type DBHashMap = HashMap<Vec<u8>, Vec<u8>>;
type ColumnHashSet = HashSet<String>;

/// An in-memory database implementing the ClientDB trait.
///
/// It is not particularily optimized, it exists for ease and speed of testing. It's not expected
/// this DB would be used outside of tests.
pub struct MemoryDB {
    db: RwLock<DBHashMap>,
    known_columns: RwLock<ColumnHashSet>
}

impl MemoryDB {
    /// Open the in-memory database.
    ///
    /// All columns must be supplied initially, you will get an error if you try to access a column
    /// that was not declared here. This condition is enforced artificially to simulate RocksDB.
    pub fn open(columns: Option<&[&str]>) -> Self {
        let db: DBHashMap = HashMap::new();
        let mut known_columns: ColumnHashSet = HashSet::new();
        if let Some(columns) = columns {
            for col in columns {
                known_columns.insert(col.to_string());
            }
        }
        Self {
            db: RwLock::new(db),
            known_columns: RwLock::new(known_columns),
        }
    }

    /// Hashes a key and a column name in order to get a unique key for the supplied column.
    fn get_key_for_col(col: &str, key: &[u8]) -> Vec<u8> {
        blake2b(32, col.as_bytes(), key).as_bytes().to_vec()
    }
}

impl ClientDB for MemoryDB {
    /// Get the value of some key from the database. Returns `None` if the key does not exist.
    fn get(&self, col: &str, key: &[u8])
        -> Result<Option<DBValue>, DBError>
    {
        // Panic if the DB locks are poisoned.
        let db = self.db.read().unwrap();
        let known_columns = self.known_columns.read().unwrap();

        if known_columns.contains(&col.to_string()) {
            let column_key = MemoryDB::get_key_for_col(col, key);
            Ok(db.get(&column_key).and_then(|val| Some(val.clone())))
        } else {
            Err(DBError{ message: "Unknown column".to_string() })
        }
    }

    /// Puts a key in the database.
    fn put(&self, col: &str, key: &[u8], val: &[u8])
        -> Result<(), DBError>
    {
        // Panic if the DB locks are poisoned.
        let mut db = self.db.write().unwrap();
        let known_columns = self.known_columns.read().unwrap();

        if known_columns.contains(&col.to_string()) {
            let column_key = MemoryDB::get_key_for_col(col, key);
            db.insert(column_key, val.to_vec());
            Ok(())
        } else {
            Err(DBError{ message: "Unknown column".to_string() })
        }
    }

    /// Return true if some key exists in some column.
    fn exists(&self, col: &str, key: &[u8])
        -> Result<bool, DBError>
    {
        // Panic if the DB locks are poisoned.
        let db = self.db.read().unwrap();
        let known_columns = self.known_columns.read().unwrap();

        match known_columns.contains(&col.to_string()) {
            false => Err(DBError{ message: "Unknown column".to_string() }),
            true => {
                let column_key = MemoryDB::get_key_for_col(col, key);
                Ok(db.contains_key(&column_key))
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::ClientDB;
    use std::thread;
    use std::sync::Arc;

    #[test]
    fn test_memorydb_column_access() {
        let col_a: &str = "ColumnA";
        let col_b: &str = "ColumnB";

        let column_families = vec![
            col_a,
            col_b,
        ];

        let db = MemoryDB::open(Some(&column_families));

        /*
         * Testing that if we write to the same key in different columns that
         * there is not an overlap.
         */
        db.put(col_a, "same".as_bytes(), "cat".as_bytes()).unwrap();
        db.put(col_b, "same".as_bytes(), "dog".as_bytes()).unwrap();

        assert_eq!(db.get(col_a, "same".as_bytes()).unwrap().unwrap(), "cat".as_bytes());
        assert_eq!(db.get(col_b, "same".as_bytes()).unwrap().unwrap(), "dog".as_bytes());

    }

    #[test]
    fn test_memorydb_unknown_column_access() {
        let col_a: &str = "ColumnA";
        let col_x: &str = "ColumnX";

        let column_families = vec![
            col_a,
            // col_x is excluded on purpose
        ];

        let db = MemoryDB::open(Some(&column_families));

        /*
         * Test that we get errors when using undeclared columns
         */
        assert!(db.put(col_a, "cats".as_bytes(), "lol".as_bytes()).is_ok());
        assert!(db.put(col_x, "cats".as_bytes(), "lol".as_bytes()).is_err());

        assert!(db.get(col_a, "cats".as_bytes()).is_ok());
        assert!(db.get(col_x, "cats".as_bytes()).is_err());
    }

    #[test]
    fn test_memorydb_exists() {
        let col_a: &str = "ColumnA";
        let col_b: &str = "ColumnB";

        let column_families = vec![
            col_a,
            col_b,
        ];

        let db = MemoryDB::open(Some(&column_families));

        /*
         * Testing that if we write to the same key in different columns that
         * there is not an overlap.
         */
        db.put(col_a, "cats".as_bytes(), "lol".as_bytes()).unwrap();

        assert_eq!(true, db.exists(col_a, "cats".as_bytes()).unwrap());
        assert_eq!(false, db.exists(col_b, "cats".as_bytes()).unwrap());

        assert_eq!(false, db.exists(col_a, "dogs".as_bytes()).unwrap());
        assert_eq!(false, db.exists(col_b, "dogs".as_bytes()).unwrap());
    }

    #[test]
    fn test_memorydb_threading() {
        let col_name: &str = "TestColumn";
        let column_families = vec![col_name];

        let db = Arc::new(MemoryDB::open(Some(&column_families)));

        let thread_count = 10;
        let write_count = 10;

        // We're execting the product of these numbers to fit in one byte.
        assert!(thread_count * write_count <= 255);

        let mut handles = vec![];
        for t in 0..thread_count {
            let wc = write_count;
            let db = db.clone();
            let col = col_name.clone();
            let handle = thread::spawn(move || {
                for w in 0..wc {
                    let key = (t * w) as u8;
                    let val = 42;
                    db.put(&col, &vec![key], &vec![val]).unwrap();
                }
            });
            handles.push(handle);
        }

        for handle in handles {
            handle.join().unwrap();
        }

        for t in 0..thread_count {
            for w in 0..write_count {
                let key = (t * w) as u8;
                let val = db.get(&col_name, &vec![key]).unwrap().unwrap();
                assert_eq!(vec![42], val);
            }
        }
    }
}
<commit_msg>Fix clippy lint in MemoryDB<commit_after>use std::collections::{ HashSet, HashMap };
use std::sync::RwLock;
use super::blake2::blake2b::blake2b;
use super::{
    ClientDB,
    DBValue,
    DBError
};

type DBHashMap = HashMap<Vec<u8>, Vec<u8>>;
type ColumnHashSet = HashSet<String>;

/// An in-memory database implementing the ClientDB trait.
///
/// It is not particularily optimized, it exists for ease and speed of testing. It's not expected
/// this DB would be used outside of tests.
pub struct MemoryDB {
    db: RwLock<DBHashMap>,
    known_columns: RwLock<ColumnHashSet>
}

impl MemoryDB {
    /// Open the in-memory database.
    ///
    /// All columns must be supplied initially, you will get an error if you try to access a column
    /// that was not declared here. This condition is enforced artificially to simulate RocksDB.
    pub fn open(columns: Option<&[&str]>) -> Self {
        let db: DBHashMap = HashMap::new();
        let mut known_columns: ColumnHashSet = HashSet::new();
        if let Some(columns) = columns {
            for col in columns {
                known_columns.insert(col.to_string());
            }
        }
        Self {
            db: RwLock::new(db),
            known_columns: RwLock::new(known_columns),
        }
    }

    /// Hashes a key and a column name in order to get a unique key for the supplied column.
    fn get_key_for_col(col: &str, key: &[u8]) -> Vec<u8> {
        blake2b(32, col.as_bytes(), key).as_bytes().to_vec()
    }
}

impl ClientDB for MemoryDB {
    /// Get the value of some key from the database. Returns `None` if the key does not exist.
    fn get(&self, col: &str, key: &[u8])
        -> Result<Option<DBValue>, DBError>
    {
        // Panic if the DB locks are poisoned.
        let db = self.db.read().unwrap();
        let known_columns = self.known_columns.read().unwrap();

        if known_columns.contains(&col.to_string()) {
            let column_key = MemoryDB::get_key_for_col(col, key);
            Ok(db.get(&column_key).and_then(|val| Some(val.clone())))
        } else {
            Err(DBError{ message: "Unknown column".to_string() })
        }
    }

    /// Puts a key in the database.
    fn put(&self, col: &str, key: &[u8], val: &[u8])
        -> Result<(), DBError>
    {
        // Panic if the DB locks are poisoned.
        let mut db = self.db.write().unwrap();
        let known_columns = self.known_columns.read().unwrap();

        if known_columns.contains(&col.to_string()) {
            let column_key = MemoryDB::get_key_for_col(col, key);
            db.insert(column_key, val.to_vec());
            Ok(())
        } else {
            Err(DBError{ message: "Unknown column".to_string() })
        }
    }

    /// Return true if some key exists in some column.
    fn exists(&self, col: &str, key: &[u8])
        -> Result<bool, DBError>
    {
        // Panic if the DB locks are poisoned.
        let db = self.db.read().unwrap();
        let known_columns = self.known_columns.read().unwrap();

        if known_columns.contains(&col.to_string()) {
            let column_key = MemoryDB::get_key_for_col(col, key);
            Ok(db.contains_key(&column_key))
        } else {
            Err(DBError{ message: "Unknown column".to_string() })

        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::ClientDB;
    use std::thread;
    use std::sync::Arc;

    #[test]
    fn test_memorydb_column_access() {
        let col_a: &str = "ColumnA";
        let col_b: &str = "ColumnB";

        let column_families = vec![
            col_a,
            col_b,
        ];

        let db = MemoryDB::open(Some(&column_families));

        /*
         * Testing that if we write to the same key in different columns that
         * there is not an overlap.
         */
        db.put(col_a, "same".as_bytes(), "cat".as_bytes()).unwrap();
        db.put(col_b, "same".as_bytes(), "dog".as_bytes()).unwrap();

        assert_eq!(db.get(col_a, "same".as_bytes()).unwrap().unwrap(), "cat".as_bytes());
        assert_eq!(db.get(col_b, "same".as_bytes()).unwrap().unwrap(), "dog".as_bytes());

    }

    #[test]
    fn test_memorydb_unknown_column_access() {
        let col_a: &str = "ColumnA";
        let col_x: &str = "ColumnX";

        let column_families = vec![
            col_a,
            // col_x is excluded on purpose
        ];

        let db = MemoryDB::open(Some(&column_families));

        /*
         * Test that we get errors when using undeclared columns
         */
        assert!(db.put(col_a, "cats".as_bytes(), "lol".as_bytes()).is_ok());
        assert!(db.put(col_x, "cats".as_bytes(), "lol".as_bytes()).is_err());

        assert!(db.get(col_a, "cats".as_bytes()).is_ok());
        assert!(db.get(col_x, "cats".as_bytes()).is_err());
    }

    #[test]
    fn test_memorydb_exists() {
        let col_a: &str = "ColumnA";
        let col_b: &str = "ColumnB";

        let column_families = vec![
            col_a,
            col_b,
        ];

        let db = MemoryDB::open(Some(&column_families));

        /*
         * Testing that if we write to the same key in different columns that
         * there is not an overlap.
         */
        db.put(col_a, "cats".as_bytes(), "lol".as_bytes()).unwrap();

        assert_eq!(true, db.exists(col_a, "cats".as_bytes()).unwrap());
        assert_eq!(false, db.exists(col_b, "cats".as_bytes()).unwrap());

        assert_eq!(false, db.exists(col_a, "dogs".as_bytes()).unwrap());
        assert_eq!(false, db.exists(col_b, "dogs".as_bytes()).unwrap());
    }

    #[test]
    fn test_memorydb_threading() {
        let col_name: &str = "TestColumn";
        let column_families = vec![col_name];

        let db = Arc::new(MemoryDB::open(Some(&column_families)));

        let thread_count = 10;
        let write_count = 10;

        // We're execting the product of these numbers to fit in one byte.
        assert!(thread_count * write_count <= 255);

        let mut handles = vec![];
        for t in 0..thread_count {
            let wc = write_count;
            let db = db.clone();
            let col = col_name.clone();
            let handle = thread::spawn(move || {
                for w in 0..wc {
                    let key = (t * w) as u8;
                    let val = 42;
                    db.put(&col, &vec![key], &vec![val]).unwrap();
                }
            });
            handles.push(handle);
        }

        for handle in handles {
            handle.join().unwrap();
        }

        for t in 0..thread_count {
            for w in 0..write_count {
                let key = (t * w) as u8;
                let val = db.get(&col_name, &vec![key]).unwrap().unwrap();
                assert_eq!(vec![42], val);
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::{read_uint, write_uint, ByMode, BySpeed, GameId, Mode, Record, Speed};
use byteorder::{ReadBytesExt as _, WriteBytesExt as _};
use std::cmp::min;
use std::io::{self, Read, Write};
use std::ops::AddAssign;

const MAX_GAMES: usize = 15; // 4 bits

#[derive(Debug, Eq, PartialEq)]
enum Header {
    Group {
        mode: Mode,
        speed: Speed,
        num_games: usize,
    },
    End,
}

impl Record for Header {
    fn read<R: Read>(reader: &mut R) -> io::Result<Header> {
        let n = reader.read_u8()?;
        Ok(Header::Group {
            speed: match n & 7 {
                0 => return Ok(Header::End),
                1 => Speed::Ultrabullet,
                2 => Speed::Bullet,
                3 => Speed::Blitz,
                4 => Speed::Rapid,
                5 => Speed::Classical,
                6 => Speed::Correspondence,
                _ => return Err(io::ErrorKind::InvalidData.into()),
            },
            mode: Mode::from_rated((n >> 3) & 1 == 1),
            num_games: usize::from(n >> 4),
        })
    }

    fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
        writer.write_u8(match *self {
            Header::End => 0,
            Header::Group {
                mode,
                speed,
                num_games,
            } => {
                (match speed {
                    Speed::Ultrabullet => 1,
                    Speed::Bullet => 2,
                    Speed::Blitz => 3,
                    Speed::Rapid => 4,
                    Speed::Classical => 5,
                    Speed::Correspondence => 6,
                }) | ((mode.is_rated() as u8) << 3)
                    | ((num_games as u8) << 4)
            }
        })
    }
}

#[derive(Debug, Default)]
struct Stats {
    white: u64,
    draw: u64,
    black: u64,
}

impl AddAssign for Stats {
    fn add_assign(&mut self, rhs: Stats) {
        self.white += rhs.white;
        self.draw += rhs.draw;
        self.black += rhs.black;
    }
}

impl Record for Stats {
    fn read<R: Read>(reader: &mut R) -> io::Result<Stats> {
        Ok(Stats {
            white: read_uint(reader)?,
            draw: read_uint(reader)?,
            black: read_uint(reader)?,
        })
    }

    fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
        write_uint(writer, self.white)?;
        write_uint(writer, self.draw)?;
        write_uint(writer, self.black)
    }
}

struct Group {
    stats: Stats,
    games: Vec<GameId>,
}

type Inner = BySpeed<ByMode<Group>>;

struct PersonalRecord {
    inner: Inner,
}

impl Record for PersonalRecord {
    fn read<R: Read>(reader: &mut R) -> io::Result<PersonalRecord> {
        let mut inner = Inner::default();
        loop {
            match Header::read(reader)? {
                Header::Group {
                    speed,
                    mode,
                    num_games,
                } => {
                    let stats = Stats::read(reader)?;
                    let mut games = Vec::with_capacity(num_games);
                    for _ in 0..num_games {
                        games.push(GameId::read(reader)?);
                    }
                    let group = inner.by_speed_mut(speed).by_mode_mut(mode);
                    *group = (stats, games);
                }
                Header::End => break,
            }
        }
        Ok(PersonalRecord { inner })
    }

    fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
        self.inner.as_ref().try_map(|speed, by_mode| {
            by_mode.as_ref().try_map(|mode, (stats, games)| {
                let num_games = min(games.len(), MAX_GAMES);

                Header::Group {
                    speed,
                    mode,
                    num_games,
                }
                .write(writer)?;

                stats.write(writer)?;

                for game in games.iter().take(num_games) {
                    game.write(writer)?;
                }

                Ok::<_, io::Error>(())
            })
        })?;

        Header::End.write(writer)
    }
}

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

    #[test]
    fn test_header_roundtrip() {
        let headers = [
            Header::Group {
                mode: Mode::Rated,
                speed: Speed::Correspondence,
                num_games: 15,
            },
            Header::End,
        ];

        let mut writer = Cursor::new(Vec::new());
        for header in &headers {
            header.write(&mut writer).unwrap();
        }

        let mut reader = Cursor::new(writer.into_inner());
        for header in headers {
            assert_eq!(Header::read(&mut reader).unwrap(), header);
        }
    }
}
<commit_msg>mergable groups<commit_after>use super::{read_uint, write_uint, ByMode, BySpeed, GameId, Mode, Record, Speed};
use byteorder::{ReadBytesExt as _, WriteBytesExt as _};
use std::cmp::min;
use std::io::{self, Read, Write};
use std::ops::AddAssign;

const MAX_GAMES: usize = 15; // 4 bits

#[derive(Debug, Eq, PartialEq)]
enum Header {
    Group {
        mode: Mode,
        speed: Speed,
        num_games: usize,
    },
    End,
}

impl Record for Header {
    fn read<R: Read>(reader: &mut R) -> io::Result<Header> {
        let n = reader.read_u8()?;
        Ok(Header::Group {
            speed: match n & 7 {
                0 => return Ok(Header::End),
                1 => Speed::Ultrabullet,
                2 => Speed::Bullet,
                3 => Speed::Blitz,
                4 => Speed::Rapid,
                5 => Speed::Classical,
                6 => Speed::Correspondence,
                _ => return Err(io::ErrorKind::InvalidData.into()),
            },
            mode: Mode::from_rated((n >> 3) & 1 == 1),
            num_games: usize::from(n >> 4),
        })
    }

    fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
        writer.write_u8(match *self {
            Header::End => 0,
            Header::Group {
                mode,
                speed,
                num_games,
            } => {
                (match speed {
                    Speed::Ultrabullet => 1,
                    Speed::Bullet => 2,
                    Speed::Blitz => 3,
                    Speed::Rapid => 4,
                    Speed::Classical => 5,
                    Speed::Correspondence => 6,
                }) | ((mode.is_rated() as u8) << 3)
                    | ((num_games as u8) << 4)
            }
        })
    }
}

#[derive(Debug, Default)]
struct Stats {
    white: u64,
    draw: u64,
    black: u64,
}

impl AddAssign for Stats {
    fn add_assign(&mut self, rhs: Stats) {
        self.white += rhs.white;
        self.draw += rhs.draw;
        self.black += rhs.black;
    }
}

impl Record for Stats {
    fn read<R: Read>(reader: &mut R) -> io::Result<Stats> {
        Ok(Stats {
            white: read_uint(reader)?,
            draw: read_uint(reader)?,
            black: read_uint(reader)?,
        })
    }

    fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
        write_uint(writer, self.white)?;
        write_uint(writer, self.draw)?;
        write_uint(writer, self.black)
    }
}

#[derive(Default)]
struct Group {
    stats: Stats,
    games: Vec<GameId>,
}

impl AddAssign for Group {
    fn add_assign(&mut self, rhs: Group) {
        self.stats += rhs.stats;
        self.games.extend(rhs.games);
    }
}

type Inner = BySpeed<ByMode<Group>>;

struct PersonalRecord {
    inner: Inner,
}

impl Record for PersonalRecord {
    fn read<R: Read>(reader: &mut R) -> io::Result<PersonalRecord> {
        let mut inner = Inner::default();
        loop {
            match Header::read(reader)? {
                Header::Group {
                    speed,
                    mode,
                    num_games,
                } => {
                    let stats = Stats::read(reader)?;
                    let mut games = Vec::with_capacity(num_games);
                    for _ in 0..num_games {
                        games.push(GameId::read(reader)?);
                    }
                    let group = inner.by_speed_mut(speed).by_mode_mut(mode);
                    *group = Group { stats, games };
                }
                Header::End => break,
            }
        }
        Ok(PersonalRecord { inner })
    }

    fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
        self.inner.as_ref().try_map(|speed, by_mode| {
            by_mode.as_ref().try_map(|mode, group| {
                let num_games = min(group.games.len(), MAX_GAMES);

                Header::Group {
                    speed,
                    mode,
                    num_games,
                }
                .write(writer)?;

                group.stats.write(writer)?;

                for game in group.games.iter().take(num_games) {
                    game.write(writer)?;
                }

                Ok::<_, io::Error>(())
            })
        })?;

        Header::End.write(writer)
    }
}

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

    #[test]
    fn test_header_roundtrip() {
        let headers = [
            Header::Group {
                mode: Mode::Rated,
                speed: Speed::Correspondence,
                num_games: 15,
            },
            Header::End,
        ];

        let mut writer = Cursor::new(Vec::new());
        for header in &headers {
            header.write(&mut writer).unwrap();
        }

        let mut reader = Cursor::new(writer.into_inner());
        for header in headers {
            assert_eq!(Header::read(&mut reader).unwrap(), header);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 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.

// Test that we can resolve nested projection types. Issue #20666.

use std::slice;

trait Bound {}

impl<'a> Bound for &'a int {}

trait IntoIterator {
    type Iter: Iterator;

    fn into_iter(self) -> Self::Iter;
}

impl<'a, T> IntoIterator for &'a [T; 3] {
    type Iter = slice::Iter<'a, T>;

    fn into_iter(self) -> slice::Iter<'a, T> {
        self.iter()
    }
}

fn foo<X>(x: X) where
    X: IntoIterator,
    <<X as IntoIterator>::Iter as Iterator>::Item: Bound,
{
}

fn bar<T, I, X>(x: X) where
    T: Bound,
    I: Iterator<Item=T>,
    X: IntoIterator<Iter=I>,
{

}

fn main() {
    foo(&[0i, 1, 2]);
    //~^ error: the trait `Bound` is not implemented for the type `<<&[int; 3] as IntoIterator>::Iter as core::iter::Iterator>::Item`
    bar(&[0i, 1, 2]);
}
<commit_msg>Remove silly long line.<commit_after>// Copyright 2015 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.

// Test that we can resolve nested projection types. Issue #20666.

use std::slice;

trait Bound {}

impl<'a> Bound for &'a int {}

trait IntoIterator {
    type Iter: Iterator;

    fn into_iter(self) -> Self::Iter;
}

impl<'a, T> IntoIterator for &'a [T; 3] {
    type Iter = slice::Iter<'a, T>;

    fn into_iter(self) -> slice::Iter<'a, T> {
        self.iter()
    }
}

fn foo<X>(x: X) where
    X: IntoIterator,
    <<X as IntoIterator>::Iter as Iterator>::Item: Bound,
{
}

fn bar<T, I, X>(x: X) where
    T: Bound,
    I: Iterator<Item=T>,
    X: IntoIterator<Iter=I>,
{

}

fn main() {
    foo(&[0i, 1, 2]);
    bar(&[0i, 1, 2]);
}
<|endoftext|>


<commit_before>// Copyright 2018 TiKV Project Authors. Licensed under Apache-2.0.

use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;

use crossbeam::channel::{
    self, RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError,
};
use futures::task::{self, Task};
use futures::{Async, Poll, Stream};

struct State {
    // If the receiver can't get any messages temporarily in `poll` context, it will put its
    // current task here.
    recv_task: AtomicPtr<Task>,
    notify_size: usize,
    // How many messages are sent without notify.
    pending: AtomicUsize,
    notifier_registered: AtomicBool,
}

impl State {
    fn new(notify_size: usize) -> State {
        State {
            // Any pointer that is put into `recv_task` must be a valid and owned
            // pointer (it must not be dropped). When a pointer is retrieved from
            // `recv_task`, the user is responsible for its proper destruction.
            recv_task: AtomicPtr::new(null_mut()),
            notify_size,
            pending: AtomicUsize::new(0),
            notifier_registered: AtomicBool::new(false),
        }
    }

    #[inline]
    fn try_notify_post_send(&self) {
        let old_pending = self.pending.fetch_add(1, Ordering::AcqRel);
        if old_pending >= self.notify_size - 1 {
            self.notify();
        }
    }

    #[inline]
    fn notify(&self) {
        let t = self.recv_task.swap(null_mut(), Ordering::AcqRel);
        if !t.is_null() {
            self.pending.store(0, Ordering::Release);
            // Safety: see comment on `recv_task`.
            let t = unsafe { Box::from_raw(t) };
            t.notify();
        }
    }

    /// When the `Receiver` that holds the `State` is running on an `Executor`,
    /// the `Receiver` calls this to yield from the current `poll` context,
    /// and puts the current task handle to `recv_task`, so that the `Sender`
    /// respectively can notify it after sending some messages into the channel.
    #[inline]
    fn yield_poll(&self) -> bool {
        let t = Box::into_raw(Box::new(task::current()));
        let origin = self.recv_task.swap(t, Ordering::AcqRel);
        if !origin.is_null() {
            // Safety: see comment on `recv_task`.
            unsafe { drop(Box::from_raw(origin)) };
            return true;
        }
        false
    }
}

impl Drop for State {
    fn drop(&mut self) {
        let t = self.recv_task.swap(null_mut(), Ordering::AcqRel);
        if !t.is_null() {
            // Safety: see comment on `recv_task`.
            unsafe { drop(Box::from_raw(t)) };
        }
    }
}

/// `Notifier` is used to notify receiver whenever you want.
pub struct Notifier(Arc<State>);
impl Notifier {
    #[inline]
    pub fn notify(self) {
        drop(self);
    }
}

impl Drop for Notifier {
    #[inline]
    fn drop(&mut self) {
        let notifier_registered = &self.0.notifier_registered;
        if !notifier_registered.compare_and_swap(true, false, Ordering::AcqRel) {
            unreachable!("notifier_registered must be true");
        }
        self.0.notify();
    }
}

pub struct Sender<T> {
    sender: channel::Sender<T>,
    state: Arc<State>,
}

impl<T> Clone for Sender<T> {
    #[inline]
    fn clone(&self) -> Sender<T> {
        Sender {
            sender: self.sender.clone(),
            state: Arc::clone(&self.state),
        }
    }
}

impl<T> Drop for Sender<T> {
    #[inline]
    fn drop(&mut self) {
        self.state.notify();
    }
}

pub struct Receiver<T> {
    receiver: channel::Receiver<T>,
    state: Arc<State>,
}

impl<T> Sender<T> {
    #[inline]
    pub fn send(&self, t: T) -> Result<(), SendError<T>> {
        self.sender.send(t)?;
        self.state.try_notify_post_send();
        Ok(())
    }

    #[inline]
    pub fn send_and_notify(&self, t: T) -> Result<(), SendError<T>> {
        self.sender.send(t)?;
        self.state.notify();
        Ok(())
    }

    #[inline]
    pub fn try_send(&self, t: T) -> Result<(), TrySendError<T>> {
        self.sender.try_send(t)?;
        self.state.try_notify_post_send();
        Ok(())
    }

    #[inline]
    pub fn get_notifier(&self) -> Option<Notifier> {
        let notifier_registered = &self.state.notifier_registered;
        if !notifier_registered.compare_and_swap(false, true, Ordering::AcqRel) {
            return Some(Notifier(Arc::clone(&self.state)));
        }
        None
    }
}

impl<T> Receiver<T> {
    #[inline]
    pub fn recv(&self) -> Result<T, RecvError> {
        self.receiver.recv()
    }

    #[inline]
    pub fn try_recv(&self) -> Result<T, TryRecvError> {
        self.receiver.try_recv()
    }

    #[inline]
    pub fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
        self.receiver.recv_timeout(timeout)
    }
}

/// Creates a unbounded channel with a given `notify_size`, which means if there are more pending
/// messages in the channel than `notify_size`, the `Sender` will auto notify the `Receiver`.
///
/// # Panics
/// if `notify_size` equals to 0.
#[inline]
pub fn unbounded<T>(notify_size: usize) -> (Sender<T>, Receiver<T>) {
    assert!(notify_size > 0);
    let state = Arc::new(State::new(notify_size));
    let (sender, receiver) = channel::unbounded();
    (
        Sender {
            sender,
            state: state.clone(),
        },
        Receiver { receiver, state },
    )
}

/// Creates a bounded channel with a given `notify_size`, which means if there are more pending
/// messages in the channel than `notify_size`, the `Sender` will auto notify the `Receiver`.
///
/// # Panics
/// if `notify_size` equals to 0.
#[inline]
pub fn bounded<T>(cap: usize, notify_size: usize) -> (Sender<T>, Receiver<T>) {
    assert!(notify_size > 0);
    let state = Arc::new(State::new(notify_size));
    let (sender, receiver) = channel::bounded(cap);
    (
        Sender {
            sender,
            state: state.clone(),
        },
        Receiver { receiver, state },
    )
}

impl<T> Stream for Receiver<T> {
    type Error = ();
    type Item = T;

    fn poll(&mut self) -> Poll<Option<T>, ()> {
        match self.try_recv() {
            Ok(m) => Ok(Async::Ready(Some(m))),
            Err(TryRecvError::Empty) => {
                if self.state.yield_poll() {
                    Ok(Async::NotReady)
                } else {
                    // For the case that all senders are dropped before the current task is saved.
                    self.poll()
                }
            }
            Err(TryRecvError::Disconnected) => Ok(Async::Ready(None)),
        }
    }
}

/// `BatchReceiver` is a `futures::Stream`, which returns a batched type.
pub struct BatchReceiver<T, E, I: Fn() -> E, C: Fn(&mut E, T)> {
    rx: Receiver<T>,
    max_batch_size: usize,
    elem: Option<E>,
    initializer: I,
    collector: C,
}

impl<T, E, I: Fn() -> E, C: Fn(&mut E, T)> BatchReceiver<T, E, I, C> {
    /// Creates a new `BatchReceiver` with given `initializer` and `collector`. `initializer` is
    /// used to generate a initial value, and `collector` will collect every (at most
    /// `max_batch_size`) raw items into the batched value.
    pub fn new(rx: Receiver<T>, max_batch_size: usize, initializer: I, collector: C) -> Self {
        BatchReceiver {
            rx,
            max_batch_size,
            elem: None,
            initializer,
            collector,
        }
    }
}

impl<T, E, I: Fn() -> E, C: Fn(&mut E, T)> Stream for BatchReceiver<T, E, I, C> {
    type Error = ();
    type Item = E;

    fn poll(&mut self) -> Poll<Option<Self::Item>, ()> {
        let mut count = 0;
        let finished = loop {
            match self.rx.try_recv() {
                Ok(m) => {
                    (self.collector)(self.elem.get_or_insert_with(&self.initializer), m);
                    count += 1;
                    if count >= self.max_batch_size {
                        break false;
                    }
                }
                Err(TryRecvError::Disconnected) => break true,
                Err(TryRecvError::Empty) => {
                    if self.rx.state.yield_poll() {
                        break false;
                    }
                }
            }
        };

        if self.elem.is_none() && finished {
            return Ok(Async::Ready(None));
        } else if self.elem.is_none() {
            return Ok(Async::NotReady);
        }
        Ok(Async::Ready(self.elem.take()))
    }
}

#[cfg(test)]
mod tests {
    use std::{thread, time};

    use futures_cpupool::CpuPool;

    use super::*;

    #[test]
    fn test_receiver() {
        let (tx, rx) = unbounded::<u64>(4);

        let msg_counter = Arc::new(AtomicUsize::new(0));
        let msg_counter1 = Arc::clone(&msg_counter);
        let pool = CpuPool::new(1);
        pool.spawn(rx.for_each(move |_| {
            msg_counter1.fetch_add(1, Ordering::AcqRel);
            Ok(())
        }))
        .forget();

        // Wait until the receiver is suspended.
        loop {
            thread::sleep(time::Duration::from_millis(10));
            if !tx.state.recv_task.load(Ordering::SeqCst).is_null() {
                break;
            }
        }

        // Send without notify, the receiver can't get batched messages.
        assert!(tx.send(0).is_ok());
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 0);

        // Send with notify.
        let notifier = tx.get_notifier().unwrap();
        assert!(tx.get_notifier().is_none());
        notifier.notify();
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 1);

        // Auto notify with more sendings.
        for _ in 0..4 {
            assert!(tx.send(0).is_ok());
        }
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 5);
    }

    #[test]
    fn test_batch_receiver() {
        let (tx, rx) = unbounded::<u64>(4);
        let rx = BatchReceiver::new(rx, 8, || Vec::with_capacity(4), |v, e| v.push(e));

        let msg_counter = Arc::new(AtomicUsize::new(0));
        let msg_counter1 = Arc::clone(&msg_counter);
        let pool = CpuPool::new(1);
        pool.spawn(rx.for_each(move |v| {
            let len = v.len();
            assert!(len <= 8);
            msg_counter1.fetch_add(len, Ordering::AcqRel);
            Ok(())
        }))
        .forget();
        thread::sleep(time::Duration::from_millis(10));

        // Send without notify, the receiver can't get batched messages.
        assert!(tx.send(0).is_ok());
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 0);

        // Send with notify.
        let notifier = tx.get_notifier().unwrap();
        assert!(tx.get_notifier().is_none());
        notifier.notify();
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 1);

        // Auto notify with more sendings.
        for _ in 0..16 {
            assert!(tx.send(0).is_ok());
        }
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 17);
    }
}
<commit_msg>util: fix a resource leak bug about mpsc::batch (#5566)<commit_after>// Copyright 2018 TiKV Project Authors. Licensed under Apache-2.0.

use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;

use crossbeam::channel::{
    self, RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError,
};
use futures::task::{self, Task};
use futures::{Async, Poll, Stream};

struct State {
    // If the receiver can't get any messages temporarily in `poll` context, it will put its
    // current task here.
    recv_task: AtomicPtr<Task>,
    notify_size: usize,
    // How many messages are sent without notify.
    pending: AtomicUsize,
    notifier_registered: AtomicBool,
}

impl State {
    fn new(notify_size: usize) -> State {
        State {
            // Any pointer that is put into `recv_task` must be a valid and owned
            // pointer (it must not be dropped). When a pointer is retrieved from
            // `recv_task`, the user is responsible for its proper destruction.
            recv_task: AtomicPtr::new(null_mut()),
            notify_size,
            pending: AtomicUsize::new(0),
            notifier_registered: AtomicBool::new(false),
        }
    }

    #[inline]
    fn try_notify_post_send(&self) {
        let old_pending = self.pending.fetch_add(1, Ordering::AcqRel);
        if old_pending >= self.notify_size - 1 {
            self.notify();
        }
    }

    #[inline]
    fn notify(&self) {
        let t = self.recv_task.swap(null_mut(), Ordering::AcqRel);
        if !t.is_null() {
            self.pending.store(0, Ordering::Release);
            // Safety: see comment on `recv_task`.
            let t = unsafe { Box::from_raw(t) };
            t.notify();
        }
    }

    /// When the `Receiver` that holds the `State` is running on an `Executor`,
    /// the `Receiver` calls this to yield from the current `poll` context,
    /// and puts the current task handle to `recv_task`, so that the `Sender`
    /// respectively can notify it after sending some messages into the channel.
    #[inline]
    fn yield_poll(&self) -> bool {
        let t = Box::into_raw(Box::new(task::current()));
        let origin = self.recv_task.swap(t, Ordering::AcqRel);
        if !origin.is_null() {
            // Safety: see comment on `recv_task`.
            unsafe { drop(Box::from_raw(origin)) };
            return true;
        }
        false
    }
}

impl Drop for State {
    fn drop(&mut self) {
        let t = self.recv_task.swap(null_mut(), Ordering::AcqRel);
        if !t.is_null() {
            // Safety: see comment on `recv_task`.
            unsafe { drop(Box::from_raw(t)) };
        }
    }
}

/// `Notifier` is used to notify receiver whenever you want.
pub struct Notifier(Arc<State>);
impl Notifier {
    #[inline]
    pub fn notify(self) {
        drop(self);
    }
}

impl Drop for Notifier {
    #[inline]
    fn drop(&mut self) {
        let notifier_registered = &self.0.notifier_registered;
        if !notifier_registered.compare_and_swap(true, false, Ordering::AcqRel) {
            unreachable!("notifier_registered must be true");
        }
        self.0.notify();
    }
}

pub struct Sender<T> {
    sender: Option<channel::Sender<T>>,
    state: Arc<State>,
}

impl<T> Clone for Sender<T> {
    #[inline]
    fn clone(&self) -> Sender<T> {
        Sender {
            sender: self.sender.clone(),
            state: Arc::clone(&self.state),
        }
    }
}

impl<T> Drop for Sender<T> {
    #[inline]
    fn drop(&mut self) {
        drop(self.sender.take());
        self.state.notify();
    }
}

pub struct Receiver<T> {
    receiver: channel::Receiver<T>,
    state: Arc<State>,
}

impl<T> Sender<T> {
    #[inline]
    pub fn send(&self, t: T) -> Result<(), SendError<T>> {
        self.sender.as_ref().unwrap().send(t)?;
        self.state.try_notify_post_send();
        Ok(())
    }

    #[inline]
    pub fn send_and_notify(&self, t: T) -> Result<(), SendError<T>> {
        self.sender.as_ref().unwrap().send(t)?;
        self.state.notify();
        Ok(())
    }

    #[inline]
    pub fn try_send(&self, t: T) -> Result<(), TrySendError<T>> {
        self.sender.as_ref().unwrap().try_send(t)?;
        self.state.try_notify_post_send();
        Ok(())
    }

    #[inline]
    pub fn get_notifier(&self) -> Option<Notifier> {
        let notifier_registered = &self.state.notifier_registered;
        if !notifier_registered.compare_and_swap(false, true, Ordering::AcqRel) {
            return Some(Notifier(Arc::clone(&self.state)));
        }
        None
    }
}

impl<T> Receiver<T> {
    #[inline]
    pub fn recv(&self) -> Result<T, RecvError> {
        self.receiver.recv()
    }

    #[inline]
    pub fn try_recv(&self) -> Result<T, TryRecvError> {
        self.receiver.try_recv()
    }

    #[inline]
    pub fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
        self.receiver.recv_timeout(timeout)
    }
}

/// Creates a unbounded channel with a given `notify_size`, which means if there are more pending
/// messages in the channel than `notify_size`, the `Sender` will auto notify the `Receiver`.
///
/// # Panics
/// if `notify_size` equals to 0.
#[inline]
pub fn unbounded<T>(notify_size: usize) -> (Sender<T>, Receiver<T>) {
    assert!(notify_size > 0);
    let state = Arc::new(State::new(notify_size));
    let (sender, receiver) = channel::unbounded();
    (
        Sender {
            sender: Some(sender),
            state: state.clone(),
        },
        Receiver { receiver, state },
    )
}

/// Creates a bounded channel with a given `notify_size`, which means if there are more pending
/// messages in the channel than `notify_size`, the `Sender` will auto notify the `Receiver`.
///
/// # Panics
/// if `notify_size` equals to 0.
#[inline]
pub fn bounded<T>(cap: usize, notify_size: usize) -> (Sender<T>, Receiver<T>) {
    assert!(notify_size > 0);
    let state = Arc::new(State::new(notify_size));
    let (sender, receiver) = channel::bounded(cap);
    (
        Sender {
            sender: Some(sender),
            state: state.clone(),
        },
        Receiver { receiver, state },
    )
}

impl<T> Stream for Receiver<T> {
    type Error = ();
    type Item = T;

    fn poll(&mut self) -> Poll<Option<T>, ()> {
        match self.try_recv() {
            Ok(m) => Ok(Async::Ready(Some(m))),
            Err(TryRecvError::Empty) => {
                if self.state.yield_poll() {
                    Ok(Async::NotReady)
                } else {
                    // For the case that all senders are dropped before the current task is saved.
                    self.poll()
                }
            }
            Err(TryRecvError::Disconnected) => Ok(Async::Ready(None)),
        }
    }
}

/// `BatchReceiver` is a `futures::Stream`, which returns a batched type.
pub struct BatchReceiver<T, E, I: Fn() -> E, C: Fn(&mut E, T)> {
    rx: Receiver<T>,
    max_batch_size: usize,
    elem: Option<E>,
    initializer: I,
    collector: C,
}

impl<T, E, I: Fn() -> E, C: Fn(&mut E, T)> BatchReceiver<T, E, I, C> {
    /// Creates a new `BatchReceiver` with given `initializer` and `collector`. `initializer` is
    /// used to generate a initial value, and `collector` will collect every (at most
    /// `max_batch_size`) raw items into the batched value.
    pub fn new(rx: Receiver<T>, max_batch_size: usize, initializer: I, collector: C) -> Self {
        BatchReceiver {
            rx,
            max_batch_size,
            elem: None,
            initializer,
            collector,
        }
    }
}

impl<T, E, I: Fn() -> E, C: Fn(&mut E, T)> Stream for BatchReceiver<T, E, I, C> {
    type Error = ();
    type Item = E;

    fn poll(&mut self) -> Poll<Option<Self::Item>, ()> {
        let mut count = 0;
        let finished = loop {
            match self.rx.try_recv() {
                Ok(m) => {
                    (self.collector)(self.elem.get_or_insert_with(&self.initializer), m);
                    count += 1;
                    if count >= self.max_batch_size {
                        break false;
                    }
                }
                Err(TryRecvError::Disconnected) => break true,
                Err(TryRecvError::Empty) => {
                    if self.rx.state.yield_poll() {
                        break false;
                    }
                }
            }
        };

        if self.elem.is_none() && finished {
            return Ok(Async::Ready(None));
        } else if self.elem.is_none() {
            return Ok(Async::NotReady);
        }
        Ok(Async::Ready(self.elem.take()))
    }
}

#[cfg(test)]
mod tests {
    use std::sync::Mutex;
    use std::{thread, time};

    use futures::executor::{self, Notify, Spawn};
    use futures::Future;
    use futures_cpupool::CpuPool;

    use super::*;

    #[test]
    fn test_receiver() {
        let (tx, rx) = unbounded::<u64>(4);

        let msg_counter = Arc::new(AtomicUsize::new(0));
        let msg_counter1 = Arc::clone(&msg_counter);
        let pool = CpuPool::new(1);
        pool.spawn(rx.for_each(move |_| {
            msg_counter1.fetch_add(1, Ordering::AcqRel);
            Ok(())
        }))
        .forget();

        // Wait until the receiver is suspended.
        loop {
            thread::sleep(time::Duration::from_millis(10));
            if !tx.state.recv_task.load(Ordering::SeqCst).is_null() {
                break;
            }
        }

        // Send without notify, the receiver can't get batched messages.
        assert!(tx.send(0).is_ok());
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 0);

        // Send with notify.
        let notifier = tx.get_notifier().unwrap();
        assert!(tx.get_notifier().is_none());
        notifier.notify();
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 1);

        // Auto notify with more sendings.
        for _ in 0..4 {
            assert!(tx.send(0).is_ok());
        }
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 5);
    }

    #[test]
    fn test_batch_receiver() {
        let (tx, rx) = unbounded::<u64>(4);
        let rx = BatchReceiver::new(rx, 8, || Vec::with_capacity(4), |v, e| v.push(e));

        let msg_counter = Arc::new(AtomicUsize::new(0));
        let msg_counter1 = Arc::clone(&msg_counter);
        let pool = CpuPool::new(1);
        pool.spawn(rx.for_each(move |v| {
            let len = v.len();
            assert!(len <= 8);
            msg_counter1.fetch_add(len, Ordering::AcqRel);
            Ok(())
        }))
        .forget();
        thread::sleep(time::Duration::from_millis(10));

        // Send without notify, the receiver can't get batched messages.
        assert!(tx.send(0).is_ok());
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 0);

        // Send with notify.
        let notifier = tx.get_notifier().unwrap();
        assert!(tx.get_notifier().is_none());
        notifier.notify();
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 1);

        // Auto notify with more sendings.
        for _ in 0..16 {
            assert!(tx.send(0).is_ok());
        }
        thread::sleep(time::Duration::from_millis(10));
        assert_eq!(msg_counter.load(Ordering::Acquire), 17);
    }

    #[test]
    fn test_switch_between_sender_and_receiver() {
        struct Notifier<F>(Arc<Mutex<Option<Spawn<F>>>>);
        impl<F> Clone for Notifier<F> {
            fn clone(&self) -> Notifier<F> {
                Notifier(Arc::clone(&self.0))
            }
        }
        impl<F> Notify for Notifier<F>
        where
            F: Future<Item = (), Error = ()> + Send + 'static,
        {
            fn notify(&self, id: usize) {
                let n = Arc::new(self.clone());
                let mut s = self.0.lock().unwrap();
                match s.as_mut().map(|spawn| spawn.poll_future_notify(&n, id)) {
                    Some(Ok(Async::NotReady)) | None => {}
                    _ => *s = None,
                }
            }
        }

        let (tx, rx) = unbounded::<u64>(4);
        let f = rx.for_each(|_| Ok(())).map_err(|_| ());
        let spawn = Arc::new(Mutex::new(Some(executor::spawn(f))));
        Notifier(Arc::clone(&spawn)).notify(0);

        // Switch to `receiver` in one thread in where `sender` is dropped.
        drop(tx);
        assert!(spawn.lock().unwrap().is_none());
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_autoscaling::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The name of the Amazon EC2 Auto Scaling group.
    #[structopt(short, long)]
    autoscaling_name: String,

    /// Whether to force the deletion.
    #[structopt(short, long)]
    force: bool,

    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Deletes a group.
// snippet-start:[autoscaling.rust.delete-autoscaling-group]
async fn delete_group(client: &Client, name: &str, force: bool) -> Result<(), Error> {
    client
        .delete_auto_scaling_group()
        .auto_scaling_group_name(name)
        .set_force_delete(force.then_some(true))
        .send()
        .await?;

    println!("Deleted Auto Scaling group");

    Ok(())
}
// snippet-end:[autoscaling.rust.delete-autoscaling-group]

/// Deletes an Auto Scaling group in the Region.
/// # Arguments
///
/// * `-a AUTOSCALING-NAME` - The name of the Auto Scaling group.
/// * - [-f] - Whether to force the deletion.
/// * `[-r REGION]` - The Region in which the client is created.
///    If not supplied, uses the value of the **AWS_REGION** environment variable.
///    If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display additional information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();

    let Opt {
        autoscaling_name,
        force,
        region,
        verbose,
    } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    println!();

    if verbose {
        println!("Auto Scaling client version: {}", PKG_VERSION);
        println!(
            "Region:                      {}",
            region_provider.region().await.unwrap().as_ref()
        );
        println!("Auto Scaling group name:     {}", &autoscaling_name);
        println!("Force deletion?:             {}", &force);
        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    delete_group(&client, &autoscaling_name, force).await
}
<commit_msg>Fix Rust `delete-autoscaling-group` test<commit_after>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_autoscaling::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The name of the Amazon EC2 Auto Scaling group.
    #[structopt(short, long)]
    autoscaling_name: String,

    /// Whether to force the deletion.
    #[structopt(short, long)]
    force: bool,

    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Deletes a group.
// snippet-start:[autoscaling.rust.delete-autoscaling-group]
async fn delete_group(client: &Client, name: &str, force: bool) -> Result<(), Error> {
    client
        .delete_auto_scaling_group()
        .auto_scaling_group_name(name)
        .set_force_delete(if force { Some(true) } else { None })
        .send()
        .await?;

    println!("Deleted Auto Scaling group");

    Ok(())
}
// snippet-end:[autoscaling.rust.delete-autoscaling-group]

/// Deletes an Auto Scaling group in the Region.
/// # Arguments
///
/// * `-a AUTOSCALING-NAME` - The name of the Auto Scaling group.
/// * - [-f] - Whether to force the deletion.
/// * `[-r REGION]` - The Region in which the client is created.
///    If not supplied, uses the value of the **AWS_REGION** environment variable.
///    If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display additional information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();

    let Opt {
        autoscaling_name,
        force,
        region,
        verbose,
    } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    println!();

    if verbose {
        println!("Auto Scaling client version: {}", PKG_VERSION);
        println!(
            "Region:                      {}",
            region_provider.region().await.unwrap().as_ref()
        );
        println!("Auto Scaling group name:     {}", &autoscaling_name);
        println!("Force deletion?:             {}", &force);
        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    delete_group(&client, &autoscaling_name, force).await
}
<|endoftext|>