<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 common::{Location, Named, WithLocation};
use graphql_syntax::{FloatValue, OperationKind};
use interner::StringKey;
use schema::{FieldID, Type, TypeReference};
use schema::{SDLSchema, Schema};
use std::fmt;
use std::sync::Arc;
// Definitions

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ExecutableDefinition {
    Operation(OperationDefinition),
    Fragment(FragmentDefinition),
}

impl ExecutableDefinition {
    pub fn has_directive(&self, directive_name: StringKey) -> bool {
        match self {
            ExecutableDefinition::Operation(node) => node
                .directives
                .iter()
                .any(|d| d.name.item == directive_name),
            ExecutableDefinition::Fragment(node) => node
                .directives
                .iter()
                .any(|d| d.name.item == directive_name),
        }
    }

    pub fn name_with_location(&self) -> WithLocation<StringKey> {
        match self {
            ExecutableDefinition::Operation(node) => node.name,
            ExecutableDefinition::Fragment(node) => node.name,
        }
    }
}

/// A fully-typed mutation, query, or subscription definition
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct OperationDefinition {
    pub kind: OperationKind,
    pub name: WithLocation<StringKey>,
    pub type_: Type,
    pub variable_definitions: Vec<VariableDefinition>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

impl OperationDefinition {
    pub fn is_query(&self) -> bool {
        self.kind == OperationKind::Query
    }
    pub fn is_mutation(&self) -> bool {
        self.kind == OperationKind::Mutation
    }
    pub fn is_subscription(&self) -> bool {
        self.kind == OperationKind::Subscription
    }
}

/// A fully-typed fragment definition
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FragmentDefinition {
    pub name: WithLocation<StringKey>,
    pub variable_definitions: Vec<VariableDefinition>,
    pub used_global_variables: Vec<VariableDefinition>,
    pub type_condition: Type,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// A variable definition of an operation or fragment
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct VariableDefinition {
    pub name: WithLocation<StringKey>,
    pub type_: TypeReference,
    pub default_value: Option<ConstantValue>,
    pub directives: Vec<Directive>,
}

impl VariableDefinition {
    pub fn has_non_null_default_value(&self) -> bool {
        match &self.default_value {
            Some(value) => value.is_non_null(),
            _ => false,
        }
    }
}

impl Named for VariableDefinition {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

// Selections

/// A selection within an operation or fragment
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum Selection {
    FragmentSpread(Arc<FragmentSpread>),
    InlineFragment(Arc<InlineFragment>),
    LinkedField(Arc<LinkedField>),
    ScalarField(Arc<ScalarField>),
    Condition(Arc<Condition>),
}

impl Selection {
    /// Get selection directives
    /// This method will panic if called on the Selection::Condition
    pub fn directives(&self) -> &[Directive] {
        match self {
            Selection::FragmentSpread(node) => &node.directives,
            Selection::InlineFragment(node) => &node.directives,
            Selection::ScalarField(node) => &node.directives,
            Selection::LinkedField(node) => &node.directives,
            Selection::Condition(_) => unreachable!("Unexpected `Condition` selection."),
        }
    }

    /// Update Selection directives
    /// This method will panic if called on the Selection::Condition
    pub fn set_directives(&mut self, directives: Vec<Directive>) {
        match self {
            Selection::FragmentSpread(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::InlineFragment(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::ScalarField(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::LinkedField(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::Condition(_) => unreachable!("Unexpected `Condition` selection."),
        };
    }

    pub fn location(&self) -> Option<Location> {
        match self {
            Selection::Condition(_) => None,
            Selection::FragmentSpread(node) => Some(node.fragment.location),
            Selection::InlineFragment(_) => None,
            Selection::LinkedField(node) => Some(node.alias_or_name_location()),
            Selection::ScalarField(node) => Some(node.alias_or_name_location()),
        }
    }
}

impl fmt::Debug for Selection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Selection::FragmentSpread(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::InlineFragment(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::LinkedField(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::ScalarField(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::Condition(node) => f.write_fmt(format_args!("{:#?}", node)),
        }
    }
}

/// ... Name
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FragmentSpread {
    pub fragment: WithLocation<StringKey>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
}

/// ... SelectionSet
/// ... on Type SelectionSet
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct InlineFragment {
    pub type_condition: Option<Type>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// Name Arguments? SelectionSet
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct LinkedField {
    pub alias: Option<WithLocation<StringKey>>,
    pub definition: WithLocation<FieldID>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

impl LinkedField {
    pub fn alias_or_name(&self, schema: &SDLSchema) -> StringKey {
        if let Some(name) = self.alias {
            name.item
        } else {
            schema.field(self.definition.item).name
        }
    }
    pub fn alias_or_name_location(&self) -> Location {
        if let Some(name) = self.alias {
            name.location
        } else {
            self.definition.location
        }
    }
}

/// Name Arguments?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ScalarField {
    pub alias: Option<WithLocation<StringKey>>,
    pub definition: WithLocation<FieldID>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
}

impl ScalarField {
    pub fn alias_or_name(&self, schema: &SDLSchema) -> StringKey {
        if let Some(name) = self.alias {
            name.item
        } else {
            schema.field(self.definition.item).name
        }
    }
    pub fn alias_or_name_location(&self) -> Location {
        if let Some(name) = self.alias {
            name.location
        } else {
            self.definition.location
        }
    }
}

/// https://spec.graphql.org/June2018/#sec--skip
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Condition {
    pub selections: Vec<Selection>,
    pub value: ConditionValue,
    pub passing_value: bool,
}

// Associated Types

/// @ Name Arguments?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Directive {
    pub name: WithLocation<StringKey>,
    pub arguments: Vec<Argument>,
}
impl Named for Directive {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

/// Name : Value
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Argument {
    pub name: WithLocation<StringKey>,
    pub value: WithLocation<Value>,
}
impl Named for Argument {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Value {
    Constant(ConstantValue),
    Variable(Variable),
    List(Vec<Value>),
    Object(Vec<Argument>),
}
impl Value {
    /// If the value is a constant string literal, return the value, otherwise None.
    pub fn get_string_literal(&self) -> Option<StringKey> {
        if let Value::Constant(ConstantValue::String(val)) = self {
            Some(*val)
        } else {
            None
        }
    }

    /// Return the constant string literal of this value.
    /// Panics if the value is not a constant string literal.
    pub fn expect_string_literal(&self) -> StringKey {
        self.get_string_literal().unwrap_or_else(|| {
            panic!("expected a string literal, got {:?}", self);
        })
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Variable {
    pub name: WithLocation<StringKey>,
    pub type_: TypeReference,
}

/// Name : Value[Const]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ConstantArgument {
    pub name: WithLocation<StringKey>,
    pub value: WithLocation<ConstantValue>,
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ConstantValue {
    Int(i64),
    Float(FloatValue),
    String(StringKey),
    Boolean(bool),
    Null(),
    Enum(StringKey),
    List(Vec<ConstantValue>),
    Object(Vec<ConstantArgument>),
}

impl ConstantValue {
    pub fn is_null(&self) -> bool {
        matches!(self, ConstantValue::Null())
    }

    pub fn is_non_null(&self) -> bool {
        !self.is_null()
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ConditionValue {
    Constant(bool),
    Variable(Variable),
}
<commit_msg>Comment for `location()` method<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 common::{Location, Named, WithLocation};
use graphql_syntax::{FloatValue, OperationKind};
use interner::StringKey;
use schema::{FieldID, Type, TypeReference};
use schema::{SDLSchema, Schema};
use std::fmt;
use std::sync::Arc;
// Definitions

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ExecutableDefinition {
    Operation(OperationDefinition),
    Fragment(FragmentDefinition),
}

impl ExecutableDefinition {
    pub fn has_directive(&self, directive_name: StringKey) -> bool {
        match self {
            ExecutableDefinition::Operation(node) => node
                .directives
                .iter()
                .any(|d| d.name.item == directive_name),
            ExecutableDefinition::Fragment(node) => node
                .directives
                .iter()
                .any(|d| d.name.item == directive_name),
        }
    }

    pub fn name_with_location(&self) -> WithLocation<StringKey> {
        match self {
            ExecutableDefinition::Operation(node) => node.name,
            ExecutableDefinition::Fragment(node) => node.name,
        }
    }
}

/// A fully-typed mutation, query, or subscription definition
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct OperationDefinition {
    pub kind: OperationKind,
    pub name: WithLocation<StringKey>,
    pub type_: Type,
    pub variable_definitions: Vec<VariableDefinition>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

impl OperationDefinition {
    pub fn is_query(&self) -> bool {
        self.kind == OperationKind::Query
    }
    pub fn is_mutation(&self) -> bool {
        self.kind == OperationKind::Mutation
    }
    pub fn is_subscription(&self) -> bool {
        self.kind == OperationKind::Subscription
    }
}

/// A fully-typed fragment definition
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FragmentDefinition {
    pub name: WithLocation<StringKey>,
    pub variable_definitions: Vec<VariableDefinition>,
    pub used_global_variables: Vec<VariableDefinition>,
    pub type_condition: Type,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// A variable definition of an operation or fragment
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct VariableDefinition {
    pub name: WithLocation<StringKey>,
    pub type_: TypeReference,
    pub default_value: Option<ConstantValue>,
    pub directives: Vec<Directive>,
}

impl VariableDefinition {
    pub fn has_non_null_default_value(&self) -> bool {
        match &self.default_value {
            Some(value) => value.is_non_null(),
            _ => false,
        }
    }
}

impl Named for VariableDefinition {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

// Selections

/// A selection within an operation or fragment
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum Selection {
    FragmentSpread(Arc<FragmentSpread>),
    InlineFragment(Arc<InlineFragment>),
    LinkedField(Arc<LinkedField>),
    ScalarField(Arc<ScalarField>),
    Condition(Arc<Condition>),
}

impl Selection {
    /// Get selection directives
    /// This method will panic if called on the Selection::Condition
    pub fn directives(&self) -> &[Directive] {
        match self {
            Selection::FragmentSpread(node) => &node.directives,
            Selection::InlineFragment(node) => &node.directives,
            Selection::ScalarField(node) => &node.directives,
            Selection::LinkedField(node) => &node.directives,
            Selection::Condition(_) => unreachable!("Unexpected `Condition` selection."),
        }
    }

    /// Update Selection directives
    /// This method will panic if called on the Selection::Condition
    pub fn set_directives(&mut self, directives: Vec<Directive>) {
        match self {
            Selection::FragmentSpread(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::InlineFragment(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::ScalarField(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::LinkedField(node) => {
                Arc::make_mut(node).directives = directives;
            }
            Selection::Condition(_) => unreachable!("Unexpected `Condition` selection."),
        };
    }

    /// A quick method to get the location of the selection. This may
    /// be helpful for error reporting. Please note, this implementation
    /// prefers the location of the alias for scalar and linked field selections.
    /// It also returns `None` for conditional nodes and inline fragments.
    pub fn location(&self) -> Option<Location> {
        match self {
            Selection::Condition(_) => None,
            Selection::FragmentSpread(node) => Some(node.fragment.location),
            Selection::InlineFragment(_) => None,
            Selection::LinkedField(node) => Some(node.alias_or_name_location()),
            Selection::ScalarField(node) => Some(node.alias_or_name_location()),
        }
    }
}

impl fmt::Debug for Selection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Selection::FragmentSpread(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::InlineFragment(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::LinkedField(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::ScalarField(node) => f.write_fmt(format_args!("{:#?}", node)),
            Selection::Condition(node) => f.write_fmt(format_args!("{:#?}", node)),
        }
    }
}

/// ... Name
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct FragmentSpread {
    pub fragment: WithLocation<StringKey>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
}

/// ... SelectionSet
/// ... on Type SelectionSet
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct InlineFragment {
    pub type_condition: Option<Type>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

/// Name Arguments? SelectionSet
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct LinkedField {
    pub alias: Option<WithLocation<StringKey>>,
    pub definition: WithLocation<FieldID>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
    pub selections: Vec<Selection>,
}

impl LinkedField {
    pub fn alias_or_name(&self, schema: &SDLSchema) -> StringKey {
        if let Some(name) = self.alias {
            name.item
        } else {
            schema.field(self.definition.item).name
        }
    }
    pub fn alias_or_name_location(&self) -> Location {
        if let Some(name) = self.alias {
            name.location
        } else {
            self.definition.location
        }
    }
}

/// Name Arguments?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ScalarField {
    pub alias: Option<WithLocation<StringKey>>,
    pub definition: WithLocation<FieldID>,
    pub arguments: Vec<Argument>,
    pub directives: Vec<Directive>,
}

impl ScalarField {
    pub fn alias_or_name(&self, schema: &SDLSchema) -> StringKey {
        if let Some(name) = self.alias {
            name.item
        } else {
            schema.field(self.definition.item).name
        }
    }
    pub fn alias_or_name_location(&self) -> Location {
        if let Some(name) = self.alias {
            name.location
        } else {
            self.definition.location
        }
    }
}

/// https://spec.graphql.org/June2018/#sec--skip
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Condition {
    pub selections: Vec<Selection>,
    pub value: ConditionValue,
    pub passing_value: bool,
}

// Associated Types

/// @ Name Arguments?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Directive {
    pub name: WithLocation<StringKey>,
    pub arguments: Vec<Argument>,
}
impl Named for Directive {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

/// Name : Value
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Argument {
    pub name: WithLocation<StringKey>,
    pub value: WithLocation<Value>,
}
impl Named for Argument {
    fn name(&self) -> StringKey {
        self.name.item
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Value {
    Constant(ConstantValue),
    Variable(Variable),
    List(Vec<Value>),
    Object(Vec<Argument>),
}
impl Value {
    /// If the value is a constant string literal, return the value, otherwise None.
    pub fn get_string_literal(&self) -> Option<StringKey> {
        if let Value::Constant(ConstantValue::String(val)) = self {
            Some(*val)
        } else {
            None
        }
    }

    /// Return the constant string literal of this value.
    /// Panics if the value is not a constant string literal.
    pub fn expect_string_literal(&self) -> StringKey {
        self.get_string_literal().unwrap_or_else(|| {
            panic!("expected a string literal, got {:?}", self);
        })
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Variable {
    pub name: WithLocation<StringKey>,
    pub type_: TypeReference,
}

/// Name : Value[Const]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ConstantArgument {
    pub name: WithLocation<StringKey>,
    pub value: WithLocation<ConstantValue>,
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ConstantValue {
    Int(i64),
    Float(FloatValue),
    String(StringKey),
    Boolean(bool),
    Null(),
    Enum(StringKey),
    List(Vec<ConstantValue>),
    Object(Vec<ConstantArgument>),
}

impl ConstantValue {
    pub fn is_null(&self) -> bool {
        matches!(self, ConstantValue::Null())
    }

    pub fn is_non_null(&self) -> bool {
        !self.is_null()
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ConditionValue {
    Constant(bool),
    Variable(Variable),
}
<|endoftext|>


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

use dom::bindings::codegen::Bindings::TextDecoderBinding;
use dom::bindings::codegen::Bindings::TextDecoderBinding::TextDecoderMethods;
use dom::bindings::conversions::array_buffer_view_data;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::{DOMString, USVString};
use dom::globalscope::GlobalScope;
use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, EncodingRef};
use js::jsapi::{JSContext, JSObject};
use std::borrow::ToOwned;

#[dom_struct]
pub struct TextDecoder {
    reflector_: Reflector,
    #[ignore_heap_size_of = "Defined in rust-encoding"]
    encoding: EncodingRef,
    fatal: bool,
}

impl TextDecoder {
    fn new_inherited(encoding: EncodingRef, fatal: bool) -> TextDecoder {
        TextDecoder {
            reflector_: Reflector::new(),
            encoding: encoding,
            fatal: fatal,
        }
    }

    fn make_range_error() -> Fallible<Root<TextDecoder>> {
        Err(Error::Range("The given encoding is not supported.".to_owned()))
    }

    pub fn new(global: &GlobalScope, encoding: EncodingRef, fatal: bool) -> Root<TextDecoder> {
        reflect_dom_object(box TextDecoder::new_inherited(encoding, fatal),
                           global,
                           TextDecoderBinding::Wrap)
    }

    /// https://encoding.spec.whatwg.org/#dom-textdecoder
    pub fn Constructor(global: &GlobalScope,
                       label: DOMString,
                       options: &TextDecoderBinding::TextDecoderOptions)
                            -> Fallible<Root<TextDecoder>> {
        let encoding = match encoding_from_whatwg_label(&label) {
            None => return TextDecoder::make_range_error(),
            Some(enc) => enc
        };
        // The rust-encoding crate has WHATWG compatibility, so we are
        // guaranteed to have a whatwg_name because we successfully got
        // the encoding from encoding_from_whatwg_label.
        // Use match + panic! instead of unwrap for better error message
        match encoding.whatwg_name() {
            None => panic!("Label {} fits valid encoding without valid name", label),
            Some("replacement") => return TextDecoder::make_range_error(),
            _ => ()
        };
        Ok(TextDecoder::new(global, encoding, options.fatal))
    }
}

impl TextDecoderMethods for TextDecoder {
    // https://encoding.spec.whatwg.org/#dom-textdecoder-encoding
    fn Encoding(&self) -> DOMString {
        DOMString::from(self.encoding.whatwg_name().unwrap())
    }

    // https://encoding.spec.whatwg.org/#dom-textdecoder-fatal
    fn Fatal(&self) -> bool {
        self.fatal
    }

    #[allow(unsafe_code)]
    // https://encoding.spec.whatwg.org/#dom-textdecoder-decode
    unsafe fn Decode(&self, _cx: *mut JSContext, input: Option<*mut JSObject>)
              -> Fallible<USVString> {
        let input = match input {
            Some(input) => input,
            None => return Ok(USVString("".to_owned())),
        };

        let data = match array_buffer_view_data::<u8>(input) {
            Some(data) => data,
            None => {
                return Err(Error::Type("Argument to TextDecoder.decode is not an ArrayBufferView".to_owned()));
            }
        };

        let trap = if self.fatal {
            DecoderTrap::Strict
        } else {
            DecoderTrap::Replace
        };

        match self.encoding.decode(data, trap) {
            Ok(s) => Ok(USVString(s)),
            Err(_) => Err(Error::Type("Decoding failed".to_owned())),
        }
    }
}
<commit_msg>Auto merge of #14727 - zaynetro:textdecoder-use-typedarray, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::TextDecoderBinding;
use dom::bindings::codegen::Bindings::TextDecoderBinding::TextDecoderMethods;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::{DOMString, USVString};
use dom::globalscope::GlobalScope;
use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, EncodingRef};
use js::jsapi::{JSContext, JSObject};
use std::borrow::ToOwned;

#[dom_struct]
pub struct TextDecoder {
    reflector_: Reflector,
    #[ignore_heap_size_of = "Defined in rust-encoding"]
    encoding: EncodingRef,
    fatal: bool,
}

impl TextDecoder {
    fn new_inherited(encoding: EncodingRef, fatal: bool) -> TextDecoder {
        TextDecoder {
            reflector_: Reflector::new(),
            encoding: encoding,
            fatal: fatal,
        }
    }

    fn make_range_error() -> Fallible<Root<TextDecoder>> {
        Err(Error::Range("The given encoding is not supported.".to_owned()))
    }

    pub fn new(global: &GlobalScope, encoding: EncodingRef, fatal: bool) -> Root<TextDecoder> {
        reflect_dom_object(box TextDecoder::new_inherited(encoding, fatal),
                           global,
                           TextDecoderBinding::Wrap)
    }

    /// https://encoding.spec.whatwg.org/#dom-textdecoder
    pub fn Constructor(global: &GlobalScope,
                       label: DOMString,
                       options: &TextDecoderBinding::TextDecoderOptions)
                            -> Fallible<Root<TextDecoder>> {
        let encoding = match encoding_from_whatwg_label(&label) {
            None => return TextDecoder::make_range_error(),
            Some(enc) => enc
        };
        // The rust-encoding crate has WHATWG compatibility, so we are
        // guaranteed to have a whatwg_name because we successfully got
        // the encoding from encoding_from_whatwg_label.
        // Use match + panic! instead of unwrap for better error message
        match encoding.whatwg_name() {
            None => panic!("Label {} fits valid encoding without valid name", label),
            Some("replacement") => return TextDecoder::make_range_error(),
            _ => ()
        };
        Ok(TextDecoder::new(global, encoding, options.fatal))
    }
}

impl TextDecoderMethods for TextDecoder {
    // https://encoding.spec.whatwg.org/#dom-textdecoder-encoding
    fn Encoding(&self) -> DOMString {
        DOMString::from(self.encoding.whatwg_name().unwrap())
    }

    // https://encoding.spec.whatwg.org/#dom-textdecoder-fatal
    fn Fatal(&self) -> bool {
        self.fatal
    }

    #[allow(unsafe_code)]
    // https://encoding.spec.whatwg.org/#dom-textdecoder-decode
    unsafe fn Decode(&self, _cx: *mut JSContext, input: Option<*mut JSObject>)
              -> Fallible<USVString> {
        let input = match input {
            Some(input) => input,
            None => return Ok(USVString("".to_owned())),
        };

        typedarray!(in(_cx) let data_res: ArrayBufferView = input);
        let mut data = match data_res {
            Ok(data) => data,
            Err(_)   => {
                return Err(Error::Type("Argument to TextDecoder.decode is not an ArrayBufferView".to_owned()));
            }
        };

        let trap = if self.fatal {
            DecoderTrap::Strict
        } else {
            DecoderTrap::Replace
        };

        match self.encoding.decode(data.as_slice(), trap) {
            Ok(s) => Ok(USVString(s)),
            Err(_) => Err(Error::Type("Decoding failed".to_owned())),
        }
    }
}
<|endoftext|>


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

use document_loader::DocumentLoader;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::XMLDocumentBinding::{self, XMLDocumentMethods};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{Root, RootedReference};
use dom::bindings::reflector::{Reflectable, reflect_dom_object};
use dom::browsingcontext::BrowsingContext;
use dom::document::{Document, DocumentSource, IsHTMLDocument};
use dom::location::Location;
use dom::node::Node;
use dom::window::Window;
use js::jsapi::{JSContext, JSObject};
use url::Url;
use util::str::DOMString;

// https://dom.spec.whatwg.org/#xmldocument
#[dom_struct]
pub struct XMLDocument {
    document: Document,
}

impl XMLDocument {
    fn new_inherited(window: &Window,
                     browsing_context: Option<&BrowsingContext>,
                     url: Option<Url>,
                     is_html_document: IsHTMLDocument,
                     content_type: Option<DOMString>,
                     last_modified: Option<String>,
                     source: DocumentSource,
                     doc_loader: DocumentLoader) -> XMLDocument {
        XMLDocument {
            document: Document::new_inherited(window,
                                              browsing_context,
                                              url,
                                              is_html_document,
                                              content_type,
                                              last_modified,
                                              source,
                                              doc_loader),
        }
    }

    pub fn new(window: &Window,
               browsing_context: Option<&BrowsingContext>,
               url: Option<Url>,
               doctype: IsHTMLDocument,
               content_type: Option<DOMString>,
               last_modified: Option<String>,
               source: DocumentSource,
               doc_loader: DocumentLoader)
               -> Root<XMLDocument> {
        let doc = reflect_dom_object(
            box XMLDocument::new_inherited(window,
                                           browsing_context,
                                           url,
                                           doctype,
                                           content_type,
                                           last_modified,
                                           source,
                                           doc_loader),
            GlobalRef::Window(window),
            XMLDocumentBinding::Wrap);
        {
            let node = doc.upcast::<Node>();
            node.set_owner_doc(&doc.r().document);
        }
        doc
    }
}

impl XMLDocumentMethods for XMLDocument {
    // https://html.spec.whatwg.org/multipage/#dom-document-location
    fn GetLocation(&self) -> Option<Root<Location>> {
        self.document.GetLocation()
    }

    // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        self.document.SupportedPropertyNames()
    }

    // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:dom-document-nameditem-filter
    fn NamedGetter(&self, _cx: *mut JSContext, name: DOMString, found: &mut bool) -> *mut JSObject {
        self.document.NamedGetter(_cx, name, found)
    }
}
<commit_msg>Use upcast() in XMLDocument methods.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use document_loader::DocumentLoader;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::XMLDocumentBinding::{self, XMLDocumentMethods};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{Root, RootedReference};
use dom::bindings::reflector::{Reflectable, reflect_dom_object};
use dom::browsingcontext::BrowsingContext;
use dom::document::{Document, DocumentSource, IsHTMLDocument};
use dom::location::Location;
use dom::node::Node;
use dom::window::Window;
use js::jsapi::{JSContext, JSObject};
use url::Url;
use util::str::DOMString;

// https://dom.spec.whatwg.org/#xmldocument
#[dom_struct]
pub struct XMLDocument {
    document: Document,
}

impl XMLDocument {
    fn new_inherited(window: &Window,
                     browsing_context: Option<&BrowsingContext>,
                     url: Option<Url>,
                     is_html_document: IsHTMLDocument,
                     content_type: Option<DOMString>,
                     last_modified: Option<String>,
                     source: DocumentSource,
                     doc_loader: DocumentLoader) -> XMLDocument {
        XMLDocument {
            document: Document::new_inherited(window,
                                              browsing_context,
                                              url,
                                              is_html_document,
                                              content_type,
                                              last_modified,
                                              source,
                                              doc_loader),
        }
    }

    pub fn new(window: &Window,
               browsing_context: Option<&BrowsingContext>,
               url: Option<Url>,
               doctype: IsHTMLDocument,
               content_type: Option<DOMString>,
               last_modified: Option<String>,
               source: DocumentSource,
               doc_loader: DocumentLoader)
               -> Root<XMLDocument> {
        let doc = reflect_dom_object(
            box XMLDocument::new_inherited(window,
                                           browsing_context,
                                           url,
                                           doctype,
                                           content_type,
                                           last_modified,
                                           source,
                                           doc_loader),
            GlobalRef::Window(window),
            XMLDocumentBinding::Wrap);
        {
            let node = doc.upcast::<Node>();
            node.set_owner_doc(&doc.r().document);
        }
        doc
    }
}

impl XMLDocumentMethods for XMLDocument {
    // https://html.spec.whatwg.org/multipage/#dom-document-location
    fn GetLocation(&self) -> Option<Root<Location>> {
        self.upcast::<Document>().GetLocation()
    }

    // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        self.upcast::<Document>().SupportedPropertyNames()
    }

    // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:dom-document-nameditem-filter
    fn NamedGetter(&self, _cx: *mut JSContext, name: DOMString, found: &mut bool) -> *mut JSObject {
        self.upcast::<Document>().NamedGetter(_cx, name, found)
    }
}
<|endoftext|>


<commit_before>
    pub fn add(num_l: &str, num_r: &str) -> String {
        let sum = String::from(num_l) + num_r;

        if sum == "IIIII" {
            String::from("V")
        } else {
            sum
        }
    }

    #[cfg(test)]
    mod tests {
        use super::add;

        #[test]
        fn add_i_i() {
            assert_eq!("II", add("I", "I"));
        }

        #[test]
        fn add_i_ii() {
            assert_eq!("III", add("I", "II"));
        }

        #[test]
        fn add_ii_iii() {
            assert_eq!("V", add("II", "III"));

        }
    }
<commit_msg>Fix indent<commit_after>pub fn add(num_l: &str, num_r: &str) -> String {
    let sum = String::from(num_l) + num_r;

    if sum == "IIIII" {
        String::from("V")
    } else {
        sum
    }
}

#[cfg(test)]
mod tests {
    use super::add;

    #[test]
    fn add_i_i() {
        assert_eq!("II", add("I", "I"));
    }

    #[test]
    fn add_i_ii() {
        assert_eq!("III", add("I", "II"));
    }

    #[test]
    fn add_ii_iii() {
        assert_eq!("V", add("II", "III"));

    }
}
<|endoftext|>


<commit_before>//! Serializes and deserializes Ardite Schema Definitions from different data
//! formats such as JSON and YAML.

use std::collections::BTreeMap;
use std::io::BufReader;
use std::path::PathBuf;
use std::fs::File;
use linear_map::LinearMap;
use regex::Regex;
use serde_json;
use error::{Error, ErrorCode};
use schema::{Definition, Schema};
use value::Value;

/// Gets an Ardite Schema Definition from a file. Aims to support mainly the
/// JSON and YAML formats.
pub fn from_file(path: PathBuf) -> Result<Definition, Error> {
  let extension = path.extension().map_or("", |s| s.to_str().unwrap());
  let file = try!(File::open(&path));
  let reader = BufReader::new(file);
  match extension {
    "json" => {
      let data: SerdeDefinition = try!(serde_json::from_reader(reader));
      Ok(try!(data.to_definition()))
    },
    "yml" => Err(Error::unimplemented("YAML file parsing has not yet been implemented.")),
    _ => Err(Error {
      code: ErrorCode::NotAcceptable,
      message: String::from(format!("File extension '{}' cannot be deserialized in '{}'.", extension, path.display())),
      hint: Some(String::from(format!("Use a recognizable file extension like '.json' or '.yml'.")))
    })
  }
}

/// Type used to deserialize data files into a usable definition type.
#[derive(Deserialize)]
struct SerdeDefinition {
  data: SerdeSchema
}

impl SerdeDefinition {
  /// Transforms the intermediary type into the useful type.
  fn to_definition(self) -> Result<Definition, Error> {
    Ok(Definition {
      data: try!(self.data.to_schema())
    })
  }
}

/// Intermediary type used to deserialized data files into a usable schema
/// enum.
#[derive(Deserialize)]
struct SerdeSchema {
  #[serde(rename="type")]
  type_: Option<String>,
  #[serde(rename="multipleOf")]
  multiple_of: Option<f32>,
  minimum: Option<f64>,
  #[serde(rename="exclusiveMinimum")]
  exclusive_minimum: Option<bool>,
  maximum: Option<f64>,
  #[serde(rename="exclusiveMaximum")]
  exclusive_maximum: Option<bool>,
  #[serde(rename="minLength")]
  min_length: Option<u64>,
  #[serde(rename="maxLength")]
  max_length: Option<u64>,
  pattern: Option<String>,
  items: Option<Box<SerdeSchema>>,
  properties: Option<BTreeMap<String, SerdeSchema>>,
  required: Option<Vec<String>>,
  #[serde(rename="additionalProperties")]
  additional_properties: Option<bool>,
  // TODO: `enum` should not just accept strings.
  #[serde(rename="enum")]
  enum_: Option<Vec<String>>
}

impl SerdeSchema {
  /// Transforms the intermediary type into the useful type.
  fn to_schema(self) -> Result<Schema, Error> {
    match self.type_ {
      Some(type_) => match type_.as_ref() {
        "null" => Ok(Schema::Null),
        "boolean" => Ok(Schema::Boolean),
        "number" | "integer" => Ok(Schema::Number {
          multiple_of: self.multiple_of,
          minimum: self.minimum,
          exclusive_minimum: self.exclusive_minimum.unwrap_or(false),
          maximum: self.maximum,
          exclusive_maximum: self.exclusive_maximum.unwrap_or(false)
        }),
        "string" => Ok(Schema::String {
          min_length: self.min_length,
          max_length: self.max_length,
          pattern: self.pattern.map_or(None, |pattern| Regex::new(&pattern).ok())
        }),
        "array" => {
          if let Some(items) = self.items {
            Ok(Schema::Array {
              items: Box::new(try!(items.to_schema()))
            })
          } else {
            Err(Error::validation("Missing `items` property for type 'array'.", "Add a schema at `items`."))
          }
        },
        "object" => Ok(Schema::Object {
          required: self.required.unwrap_or(vec![]),
          additional_properties: self.additional_properties.unwrap_or(false),
          properties: {
            let mut map = LinearMap::new();
            for (key, definition) in self.properties.unwrap_or(BTreeMap::new()) {
              map.insert(key, try!(definition.to_schema()));
            }
            map
          }
        }),
        _ => Err(Error::validation(format!("Invalid type '{}'.", type_), format!("Use a permitted type like 'string' and not '{}'.", type_)))
      },
      None => {
        if let Some(enum_) = self.enum_ {
          Ok(Schema::Enum(enum_.into_iter().map(|s| Value::String(s)).collect()))
        } else {
          Err(Error::validation("No schema type specified.", "Set a `type` property or an `enum` property."))
        }
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use std::path::PathBuf;
  use regex::Regex;
  use schema::{Definition, Schema, from_file};

  lazy_static! {
    static ref BASIC_DEFINITION: Definition = Definition {
      data: Schema::Object {
        required: vec![],
        additional_properties: false,
        properties: linear_map! {
          String::from("people") => Schema::Array {
            items: Box::new(Schema::Object {
              required: vec![String::from("email")],
              additional_properties: false,
              properties: linear_map! {
                String::from("email") => Schema::String {
                  min_length: Some(4),
                  max_length: Some(256),
                  pattern: Some(Regex::new(r".+@.+\..+").unwrap())
                },
                String::from("name") => Schema::String {
                  min_length: Some(2),
                  max_length: Some(64),
                  pattern: None
                }
              }
            })
          },
          String::from("posts") => Schema::Array {
            items: Box::new(Schema::Object {
              required: vec![String::from("headline")],
              additional_properties: false,
              properties: linear_map! {
                String::from("headline") => Schema::String {
                  min_length: Some(4),
                  max_length: Some(1024),
                  pattern: None
                },
                String::from("text") => Schema::String {
                  min_length: None,
                  max_length: Some(65536),
                  pattern: None
                },
                String::from("topic") => Schema::Enum(vec![vstring!("showcase"), vstring!("help"), vstring!("ama")])
              }
            })
          }
        }
      }
    };
  }

  #[test]
  fn test_basic_json() {
    assert_eq!(from_file(PathBuf::from("tests/fixtures/definitions/basic.json")).unwrap(), *BASIC_DEFINITION);
  }

  #[test]
  #[ignore]
  fn test_basic_yaml() {
    assert_eq!(from_file(PathBuf::from("tests/fixtures/definitions/basic.yml")).unwrap(), *BASIC_DEFINITION);
  }
}
<commit_msg>doc: remove todo comment (there is no strong need)<commit_after>//! Serializes and deserializes Ardite Schema Definitions from different data
//! formats such as JSON and YAML.

use std::collections::BTreeMap;
use std::io::BufReader;
use std::path::PathBuf;
use std::fs::File;
use linear_map::LinearMap;
use regex::Regex;
use serde_json;
use error::{Error, ErrorCode};
use schema::{Definition, Schema};
use value::Value;

/// Gets an Ardite Schema Definition from a file. Aims to support mainly the
/// JSON and YAML formats.
pub fn from_file(path: PathBuf) -> Result<Definition, Error> {
  let extension = path.extension().map_or("", |s| s.to_str().unwrap());
  let file = try!(File::open(&path));
  let reader = BufReader::new(file);
  match extension {
    "json" => {
      let data: SerdeDefinition = try!(serde_json::from_reader(reader));
      Ok(try!(data.to_definition()))
    },
    "yml" => Err(Error::unimplemented("YAML file parsing has not yet been implemented.")),
    _ => Err(Error {
      code: ErrorCode::NotAcceptable,
      message: String::from(format!("File extension '{}' cannot be deserialized in '{}'.", extension, path.display())),
      hint: Some(String::from(format!("Use a recognizable file extension like '.json' or '.yml'.")))
    })
  }
}

/// Type used to deserialize data files into a usable definition type.
#[derive(Deserialize)]
struct SerdeDefinition {
  data: SerdeSchema
}

impl SerdeDefinition {
  /// Transforms the intermediary type into the useful type.
  fn to_definition(self) -> Result<Definition, Error> {
    Ok(Definition {
      data: try!(self.data.to_schema())
    })
  }
}

/// Intermediary type used to deserialized data files into a usable schema
/// enum.
#[derive(Deserialize)]
struct SerdeSchema {
  #[serde(rename="type")]
  type_: Option<String>,
  #[serde(rename="multipleOf")]
  multiple_of: Option<f32>,
  minimum: Option<f64>,
  #[serde(rename="exclusiveMinimum")]
  exclusive_minimum: Option<bool>,
  maximum: Option<f64>,
  #[serde(rename="exclusiveMaximum")]
  exclusive_maximum: Option<bool>,
  #[serde(rename="minLength")]
  min_length: Option<u64>,
  #[serde(rename="maxLength")]
  max_length: Option<u64>,
  pattern: Option<String>,
  items: Option<Box<SerdeSchema>>,
  properties: Option<BTreeMap<String, SerdeSchema>>,
  required: Option<Vec<String>>,
  #[serde(rename="additionalProperties")]
  additional_properties: Option<bool>,
  #[serde(rename="enum")]
  enum_: Option<Vec<String>>
}

impl SerdeSchema {
  /// Transforms the intermediary type into the useful type.
  fn to_schema(self) -> Result<Schema, Error> {
    match self.type_ {
      Some(type_) => match type_.as_ref() {
        "null" => Ok(Schema::Null),
        "boolean" => Ok(Schema::Boolean),
        "number" | "integer" => Ok(Schema::Number {
          multiple_of: self.multiple_of,
          minimum: self.minimum,
          exclusive_minimum: self.exclusive_minimum.unwrap_or(false),
          maximum: self.maximum,
          exclusive_maximum: self.exclusive_maximum.unwrap_or(false)
        }),
        "string" => Ok(Schema::String {
          min_length: self.min_length,
          max_length: self.max_length,
          pattern: self.pattern.map_or(None, |pattern| Regex::new(&pattern).ok())
        }),
        "array" => {
          if let Some(items) = self.items {
            Ok(Schema::Array {
              items: Box::new(try!(items.to_schema()))
            })
          } else {
            Err(Error::validation("Missing `items` property for type 'array'.", "Add a schema at `items`."))
          }
        },
        "object" => Ok(Schema::Object {
          required: self.required.unwrap_or(vec![]),
          additional_properties: self.additional_properties.unwrap_or(false),
          properties: {
            let mut map = LinearMap::new();
            for (key, definition) in self.properties.unwrap_or(BTreeMap::new()) {
              map.insert(key, try!(definition.to_schema()));
            }
            map
          }
        }),
        _ => Err(Error::validation(format!("Invalid type '{}'.", type_), format!("Use a permitted type like 'string' and not '{}'.", type_)))
      },
      None => {
        if let Some(enum_) = self.enum_ {
          Ok(Schema::Enum(enum_.into_iter().map(|s| Value::String(s)).collect()))
        } else {
          Err(Error::validation("No schema type specified.", "Set a `type` property or an `enum` property."))
        }
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use std::path::PathBuf;
  use regex::Regex;
  use schema::{Definition, Schema, from_file};

  lazy_static! {
    static ref BASIC_DEFINITION: Definition = Definition {
      data: Schema::Object {
        required: vec![],
        additional_properties: false,
        properties: linear_map! {
          String::from("people") => Schema::Array {
            items: Box::new(Schema::Object {
              required: vec![String::from("email")],
              additional_properties: false,
              properties: linear_map! {
                String::from("email") => Schema::String {
                  min_length: Some(4),
                  max_length: Some(256),
                  pattern: Some(Regex::new(r".+@.+\..+").unwrap())
                },
                String::from("name") => Schema::String {
                  min_length: Some(2),
                  max_length: Some(64),
                  pattern: None
                }
              }
            })
          },
          String::from("posts") => Schema::Array {
            items: Box::new(Schema::Object {
              required: vec![String::from("headline")],
              additional_properties: false,
              properties: linear_map! {
                String::from("headline") => Schema::String {
                  min_length: Some(4),
                  max_length: Some(1024),
                  pattern: None
                },
                String::from("text") => Schema::String {
                  min_length: None,
                  max_length: Some(65536),
                  pattern: None
                },
                String::from("topic") => Schema::Enum(vec![vstring!("showcase"), vstring!("help"), vstring!("ama")])
              }
            })
          }
        }
      }
    };
  }

  #[test]
  fn test_basic_json() {
    assert_eq!(from_file(PathBuf::from("tests/fixtures/definitions/basic.json")).unwrap(), *BASIC_DEFINITION);
  }

  #[test]
  #[ignore]
  fn test_basic_yaml() {
    assert_eq!(from_file(PathBuf::from("tests/fixtures/definitions/basic.yml")).unwrap(), *BASIC_DEFINITION);
  }
}
<|endoftext|>


<commit_before>use latch::{CountLatch, Latch};
use futures::{Async, Future, Poll};
use futures::future::CatchUnwind;
use futures::sync::oneshot::{channel, Sender, Receiver};
use futures::task::{self, Spawn, Unpark};
use job::{Job, JobRef};
use std::panic::{self, AssertUnwindSafe};
use std::mem;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::*;
use std::sync::Mutex;
use std::thread;
use thread_pool::{Registry, WorkerThread};

const STATE_PARKED: usize = 0;
const STATE_UNPARKED: usize = 1;
const STATE_EXECUTING: usize = 2;
const STATE_EXECUTING_UNPARKED: usize = 3;
const STATE_COMPLETE: usize = 4;

// Warning: Public end-user API.
pub struct RayonFuture<T, E> {
    receiver: Receiver<thread::Result<Result<T, E>>>,
}

/// This is a free fn so that we can expose `RayonFuture` as public API.
pub unsafe fn new_rayon_future<F>(future: F,
                                  counter: *const CountLatch)
                                  -> RayonFuture<F::Item, F::Error>
    where F: Future + Send
{
    let (tx, rx) = channel();
    ScopeFuture::spawn(future, tx, counter);
    RayonFuture { receiver: rx }
}

impl<T, E> Future for RayonFuture<T, E> {
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<T, E> {
        match self.receiver.poll().expect("shouldn't be canceled") {
            Async::Ready(Ok(Ok(e))) => Ok(e.into()),
            Async::Ready(Ok(Err(e))) => Err(e),
            Async::Ready(Err(e)) => panic::resume_unwind(e),
            Async::NotReady => Ok(Async::NotReady),
        }
    }
}

/// ////////////////////////////////////////////////////////////////////////

struct ScopeFuture<F: Future + Send> {
    state: AtomicUsize,
    registry: Arc<Registry>,
    contents: Mutex<ScopeFutureContents<F>>,
}

type CU<F> = CatchUnwind<AssertUnwindSafe<F>>;

struct ScopeFutureContents<F: Future + Send> {
    spawn: Option<Spawn<CU<F>>>,
    sender: Option<Sender<thread::Result<Result<F::Item, F::Error>>>>,
    unpark: Option<Arc<Unpark>>,

    // Pointer to ourselves. We `None` this out when we are finished
    // executing, but it's convenient to keep around normally.
    this: Option<Arc<ScopeFuture<F>>>,

    // the counter in the scope; since the scope doesn't terminate until
    // counter reaches zero, and we hold a ref in this counter, we are
    // assured that this pointer remains valid
    counter: *const CountLatch,
}

trait Ping: Send + Sync {
    fn ping(&self);
}

// Assert that the `*const` is safe to transmit between threads:
unsafe impl<F: Future + Send> Send for ScopeFuture<F> {}
unsafe impl<F: Future + Send> Sync for ScopeFuture<F> {}

impl<F: Future + Send> ScopeFuture<F> {
    // Unsafe: Caller asserts that `future` and `counter` will remain
    // valid until we invoke `counter.set()`.
    unsafe fn spawn(future: F,
                    sender: Sender<thread::Result<Result<F::Item, F::Error>>>,
                    counter: *const CountLatch) {
        let worker_thread = WorkerThread::current();
        debug_assert!(!worker_thread.is_null());

        // Using `AssertUnwindSafe` is valid here because (a) the data
        // is `Send + Sync`, which is our usual boundary and (b)
        // panics will be propagated when the `RayonFuture` is polled.
        let spawn = task::spawn(AssertUnwindSafe(future).catch_unwind());

        let future: Arc<Self> = Arc::new(ScopeFuture::<F> {
            state: AtomicUsize::new(STATE_PARKED),
            registry: (*worker_thread).registry().clone(),
            contents: Mutex::new(ScopeFutureContents {
                spawn: None,
                unpark: None,
                this: None,
                sender: Some(sender),
                counter: counter,
            }),
        });

        // Make the two self-cycles. Note that these imply the future
        // cannot be freed until these fields are set to `None` (which
        // occurs when it is finished executing).
        {
            let mut contents = future.contents.try_lock().unwrap();
            contents.spawn = Some(spawn);
            contents.unpark = Some(Self::make_unpark(&future));
            contents.this = Some(future.clone());
        }

        future.ping();
    }

    /// Creates a `JobRef` from this job -- note that this hides all
    /// lifetimes, so it is up to you to ensure that this JobRef
    /// doesn't outlive any data that it closes over.
    unsafe fn into_job_ref(this: Arc<Self>) -> JobRef {
        let this: *const Self = mem::transmute(this);
        JobRef::new(this)
    }

    fn make_unpark(this: &Arc<Self>) -> Arc<Unpark> {
        // Hide any lifetimes in `self`. This is safe because, until
        // `self` is dropped, the counter is not decremented, and so
        // the `'scope` lifetimes cannot end.
        //
        // Unfortunately, as `Unpark` currently requires `'static`, we
        // have to do an indirection and this ultimately requires a
        // fresh allocation.
        unsafe {
            let ping: PingUnpark = PingUnpark::new(this.clone());
            let ping: PingUnpark<'static> = mem::transmute(ping);
            Arc::new(ping)
        }
    }

    fn unpark_inherent(&self) {
        loop {
            let state = self.state.load(Acquire);
            if {
                state == STATE_PARKED &&
                self.state
                    .compare_exchange_weak(state, STATE_UNPARKED, Release, Relaxed)
                    .is_ok()
            } {
                // Contention here is unlikely but possible: a
                // previous execution might have moved us to the
                // PARKED state but not yet released the lock.
                let contents = self.contents.lock().unwrap();
                unsafe {
                    let job_ref = Self::into_job_ref(contents.this.clone().unwrap());
                    self.registry.inject(&[job_ref]);
                }
                return;
            } else if {
                state == STATE_EXECUTING &&
                self.state
                    .compare_exchange_weak(state, STATE_EXECUTING_UNPARKED, Release, Relaxed)
                    .is_ok()
            } {
                return;
            } else {
                debug_assert!(state == STATE_UNPARKED || state == STATE_EXECUTING_UNPARKED ||
                              state == STATE_COMPLETE);
                return;
            }
        }
    }

    fn begin_execute_state(&self) {
        // When we are put into the unparked state, we are enqueued in
        // a worker thread. We should then be executed exactly once,
        // at which point we transiition to STATE_EXECUTING. Nobody
        // should be contending with us to change the state here.
        let state = self.state.load(Acquire);
        debug_assert_eq!(state, STATE_UNPARKED);
        let result = self.state.compare_exchange(state, STATE_EXECUTING, Release, Relaxed);
        debug_assert_eq!(result, Ok(STATE_UNPARKED));
    }

    fn end_execute_state(&self) -> bool {
        loop {
            let state = self.state.load(Acquire);
            if state == STATE_EXECUTING {
                if {
                    self.state
                        .compare_exchange_weak(state, STATE_PARKED, Release, Relaxed)
                        .is_ok()
                } {
                    // We put ourselves into parked state, no need to
                    // re-execute.  We'll just wait for the Unpark.
                    return true;
                }
            } else {
                debug_assert_eq!(state, STATE_EXECUTING_UNPARKED);
                if {
                    self.state
                        .compare_exchange_weak(state, STATE_EXECUTING, Release, Relaxed)
                        .is_ok()
                } {
                    // We finished executing, but an unpark request
                    // came in the meantime.  We need to execute
                    // again. Return false as we failed to end the
                    // execution phase.
                    return false;
                }
            }
        }
    }
}

impl<F: Future + Send> Ping for ScopeFuture<F> {
    fn ping(&self) {
        self.unpark_inherent();
    }
}

impl<F: Future + Send> Job for ScopeFuture<F> {
    unsafe fn execute(this: *const Self) {
        let this: Arc<Self> = mem::transmute(this);

        // *generally speaking* there should be no contention for the
        // lock, but it is possible -- we can end execution, get re-enqeueud,
        // and re-executed, before we have time to return from this fn
        let mut contents = this.contents.lock().unwrap();

        this.begin_execute_state();
        loop {
            match contents.poll() {
                Ok(Async::Ready(v)) => {
                    return contents.complete(Ok(v));
                }
                Ok(Async::NotReady) => {
                    if this.end_execute_state() {
                        return;
                    }
                }
                Err(err) => {
                    return contents.complete(Err(err));
                }
            }
        }
    }
}

impl<F: Future + Send> ScopeFutureContents<F> {
    fn poll(&mut self) -> Poll<<CU<F> as Future>::Item, <CU<F> as Future>::Error> {
        let unpark = self.unpark.clone().unwrap();
        self.spawn.as_mut().unwrap().poll_future(unpark)
    }

    fn complete(&mut self, value: thread::Result<Result<F::Item, F::Error>>) {
        self.unpark = None;
        self.sender.take().unwrap().complete(value);
        let this = self.this.take().unwrap();
        let state = this.state.load(Acquire);
        debug_assert!(state == STATE_EXECUTING || state == STATE_EXECUTING_UNPARKED,
                      "cannot complete when not executing (state = {})",
                      state);
        this.state.store(STATE_COMPLETE, Release);
    }
}

impl<F: Future + Send> Drop for ScopeFuture<F> {
    fn drop(&mut self) {
        unsafe {
            // can't be any contention for this lock in drop
            let mut contents = self.contents.try_lock().unwrap();

            // So, this is subtle. We know that the type `F` may have
            // some data which is only valid until the end of the
            // scope, and we also know that the scope doesn't end
            // until `self.counter` is decremented below. So we want
            // to be sure to drop `self.future` first.
            mem::drop(contents.spawn.take());

            // Set the latch. By the struct invariant, we know that
            // the counter pointer will remain valid until this ref is
            // dropped.
            (*contents.counter).set();
        }
    }
}

struct PingUnpark<'l> {
    ping: Arc<Ping + 'l>,
}

impl<'l> PingUnpark<'l> {
    fn new(ping: Arc<Ping + 'l>) -> PingUnpark {
        PingUnpark { ping: ping }
    }
}

impl Unpark for PingUnpark<'static> {
    fn unpark(&self) {
        self.ping.ping()
    }
}
<commit_msg>weaken the load that is only used for debug assertions<commit_after>use latch::{CountLatch, Latch};
use futures::{Async, Future, Poll};
use futures::future::CatchUnwind;
use futures::sync::oneshot::{channel, Sender, Receiver};
use futures::task::{self, Spawn, Unpark};
use job::{Job, JobRef};
use std::panic::{self, AssertUnwindSafe};
use std::mem;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::*;
use std::sync::Mutex;
use std::thread;
use thread_pool::{Registry, WorkerThread};

const STATE_PARKED: usize = 0;
const STATE_UNPARKED: usize = 1;
const STATE_EXECUTING: usize = 2;
const STATE_EXECUTING_UNPARKED: usize = 3;
const STATE_COMPLETE: usize = 4;

// Warning: Public end-user API.
pub struct RayonFuture<T, E> {
    receiver: Receiver<thread::Result<Result<T, E>>>,
}

/// This is a free fn so that we can expose `RayonFuture` as public API.
pub unsafe fn new_rayon_future<F>(future: F,
                                  counter: *const CountLatch)
                                  -> RayonFuture<F::Item, F::Error>
    where F: Future + Send
{
    let (tx, rx) = channel();
    ScopeFuture::spawn(future, tx, counter);
    RayonFuture { receiver: rx }
}

impl<T, E> Future for RayonFuture<T, E> {
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<T, E> {
        match self.receiver.poll().expect("shouldn't be canceled") {
            Async::Ready(Ok(Ok(e))) => Ok(e.into()),
            Async::Ready(Ok(Err(e))) => Err(e),
            Async::Ready(Err(e)) => panic::resume_unwind(e),
            Async::NotReady => Ok(Async::NotReady),
        }
    }
}

/// ////////////////////////////////////////////////////////////////////////

struct ScopeFuture<F: Future + Send> {
    state: AtomicUsize,
    registry: Arc<Registry>,
    contents: Mutex<ScopeFutureContents<F>>,
}

type CU<F> = CatchUnwind<AssertUnwindSafe<F>>;

struct ScopeFutureContents<F: Future + Send> {
    spawn: Option<Spawn<CU<F>>>,
    sender: Option<Sender<thread::Result<Result<F::Item, F::Error>>>>,
    unpark: Option<Arc<Unpark>>,

    // Pointer to ourselves. We `None` this out when we are finished
    // executing, but it's convenient to keep around normally.
    this: Option<Arc<ScopeFuture<F>>>,

    // the counter in the scope; since the scope doesn't terminate until
    // counter reaches zero, and we hold a ref in this counter, we are
    // assured that this pointer remains valid
    counter: *const CountLatch,
}

trait Ping: Send + Sync {
    fn ping(&self);
}

// Assert that the `*const` is safe to transmit between threads:
unsafe impl<F: Future + Send> Send for ScopeFuture<F> {}
unsafe impl<F: Future + Send> Sync for ScopeFuture<F> {}

impl<F: Future + Send> ScopeFuture<F> {
    // Unsafe: Caller asserts that `future` and `counter` will remain
    // valid until we invoke `counter.set()`.
    unsafe fn spawn(future: F,
                    sender: Sender<thread::Result<Result<F::Item, F::Error>>>,
                    counter: *const CountLatch) {
        let worker_thread = WorkerThread::current();
        debug_assert!(!worker_thread.is_null());

        // Using `AssertUnwindSafe` is valid here because (a) the data
        // is `Send + Sync`, which is our usual boundary and (b)
        // panics will be propagated when the `RayonFuture` is polled.
        let spawn = task::spawn(AssertUnwindSafe(future).catch_unwind());

        let future: Arc<Self> = Arc::new(ScopeFuture::<F> {
            state: AtomicUsize::new(STATE_PARKED),
            registry: (*worker_thread).registry().clone(),
            contents: Mutex::new(ScopeFutureContents {
                spawn: None,
                unpark: None,
                this: None,
                sender: Some(sender),
                counter: counter,
            }),
        });

        // Make the two self-cycles. Note that these imply the future
        // cannot be freed until these fields are set to `None` (which
        // occurs when it is finished executing).
        {
            let mut contents = future.contents.try_lock().unwrap();
            contents.spawn = Some(spawn);
            contents.unpark = Some(Self::make_unpark(&future));
            contents.this = Some(future.clone());
        }

        future.ping();
    }

    /// Creates a `JobRef` from this job -- note that this hides all
    /// lifetimes, so it is up to you to ensure that this JobRef
    /// doesn't outlive any data that it closes over.
    unsafe fn into_job_ref(this: Arc<Self>) -> JobRef {
        let this: *const Self = mem::transmute(this);
        JobRef::new(this)
    }

    fn make_unpark(this: &Arc<Self>) -> Arc<Unpark> {
        // Hide any lifetimes in `self`. This is safe because, until
        // `self` is dropped, the counter is not decremented, and so
        // the `'scope` lifetimes cannot end.
        //
        // Unfortunately, as `Unpark` currently requires `'static`, we
        // have to do an indirection and this ultimately requires a
        // fresh allocation.
        unsafe {
            let ping: PingUnpark = PingUnpark::new(this.clone());
            let ping: PingUnpark<'static> = mem::transmute(ping);
            Arc::new(ping)
        }
    }

    fn unpark_inherent(&self) {
        loop {
            let state = self.state.load(Acquire);
            if {
                state == STATE_PARKED &&
                self.state
                    .compare_exchange_weak(state, STATE_UNPARKED, Release, Relaxed)
                    .is_ok()
            } {
                // Contention here is unlikely but possible: a
                // previous execution might have moved us to the
                // PARKED state but not yet released the lock.
                let contents = self.contents.lock().unwrap();
                unsafe {
                    let job_ref = Self::into_job_ref(contents.this.clone().unwrap());
                    self.registry.inject(&[job_ref]);
                }
                return;
            } else if {
                state == STATE_EXECUTING &&
                self.state
                    .compare_exchange_weak(state, STATE_EXECUTING_UNPARKED, Release, Relaxed)
                    .is_ok()
            } {
                return;
            } else {
                debug_assert!(state == STATE_UNPARKED || state == STATE_EXECUTING_UNPARKED ||
                              state == STATE_COMPLETE);
                return;
            }
        }
    }

    fn begin_execute_state(&self) {
        // When we are put into the unparked state, we are enqueued in
        // a worker thread. We should then be executed exactly once,
        // at which point we transiition to STATE_EXECUTING. Nobody
        // should be contending with us to change the state here.
        let state = self.state.load(Acquire);
        debug_assert_eq!(state, STATE_UNPARKED);
        let result = self.state.compare_exchange(state, STATE_EXECUTING, Release, Relaxed);
        debug_assert_eq!(result, Ok(STATE_UNPARKED));
    }

    fn end_execute_state(&self) -> bool {
        loop {
            let state = self.state.load(Acquire);
            if state == STATE_EXECUTING {
                if {
                    self.state
                        .compare_exchange_weak(state, STATE_PARKED, Release, Relaxed)
                        .is_ok()
                } {
                    // We put ourselves into parked state, no need to
                    // re-execute.  We'll just wait for the Unpark.
                    return true;
                }
            } else {
                debug_assert_eq!(state, STATE_EXECUTING_UNPARKED);
                if {
                    self.state
                        .compare_exchange_weak(state, STATE_EXECUTING, Release, Relaxed)
                        .is_ok()
                } {
                    // We finished executing, but an unpark request
                    // came in the meantime.  We need to execute
                    // again. Return false as we failed to end the
                    // execution phase.
                    return false;
                }
            }
        }
    }
}

impl<F: Future + Send> Ping for ScopeFuture<F> {
    fn ping(&self) {
        self.unpark_inherent();
    }
}

impl<F: Future + Send> Job for ScopeFuture<F> {
    unsafe fn execute(this: *const Self) {
        let this: Arc<Self> = mem::transmute(this);

        // *generally speaking* there should be no contention for the
        // lock, but it is possible -- we can end execution, get re-enqeueud,
        // and re-executed, before we have time to return from this fn
        let mut contents = this.contents.lock().unwrap();

        this.begin_execute_state();
        loop {
            match contents.poll() {
                Ok(Async::Ready(v)) => {
                    return contents.complete(Ok(v));
                }
                Ok(Async::NotReady) => {
                    if this.end_execute_state() {
                        return;
                    }
                }
                Err(err) => {
                    return contents.complete(Err(err));
                }
            }
        }
    }
}

impl<F: Future + Send> ScopeFutureContents<F> {
    fn poll(&mut self) -> Poll<<CU<F> as Future>::Item, <CU<F> as Future>::Error> {
        let unpark = self.unpark.clone().unwrap();
        self.spawn.as_mut().unwrap().poll_future(unpark)
    }

    fn complete(&mut self, value: thread::Result<Result<F::Item, F::Error>>) {
        self.unpark = None;
        self.sender.take().unwrap().complete(value);
        let this = self.this.take().unwrap();
        if cfg!(debug_assertions) {
            let state = this.state.load(Relaxed);
            debug_assert!(state == STATE_EXECUTING || state == STATE_EXECUTING_UNPARKED,
                          "cannot complete when not executing (state = {})",
                          state);
        }
        this.state.store(STATE_COMPLETE, Release);
    }
}

impl<F: Future + Send> Drop for ScopeFuture<F> {
    fn drop(&mut self) {
        unsafe {
            // can't be any contention for this lock in drop
            let mut contents = self.contents.try_lock().unwrap();

            // So, this is subtle. We know that the type `F` may have
            // some data which is only valid until the end of the
            // scope, and we also know that the scope doesn't end
            // until `self.counter` is decremented below. So we want
            // to be sure to drop `self.future` first.
            mem::drop(contents.spawn.take());

            // Set the latch. By the struct invariant, we know that
            // the counter pointer will remain valid until this ref is
            // dropped.
            (*contents.counter).set();
        }
    }
}

struct PingUnpark<'l> {
    ping: Arc<Ping + 'l>,
}

impl<'l> PingUnpark<'l> {
    fn new(ping: Arc<Ping + 'l>) -> PingUnpark {
        PingUnpark { ping: ping }
    }
}

impl Unpark for PingUnpark<'static> {
    fn unpark(&self) {
        self.ping.ping()
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
//! and margins in CSS.

use super::UnknownUnit;
use length::Length;
use num::Zero;
use core::fmt;
use core::ops::Add;
use core::marker::PhantomData;

/// A group of 2D side offsets, which correspond to top/left/bottom/right for borders, padding,
/// and margins in CSS, optionally tagged with a unit.
#[derive(EuclidMatrix)]
#[repr(C)]
pub struct TypedSideOffsets2D<T, U> {
    pub top: T,
    pub right: T,
    pub bottom: T,
    pub left: T,
    #[doc(hidden)]
    pub _unit: PhantomData<U>,
}

impl<T: fmt::Debug, U> fmt::Debug for TypedSideOffsets2D<T, U> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "({:?},{:?},{:?},{:?})",
            self.top, self.right, self.bottom, self.left
        )
    }
}

/// The default 2D side offset type with no unit.
pub type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;

impl<T: Copy, U> TypedSideOffsets2D<T, U> {
    /// Constructor taking a scalar for each side.
    pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
        TypedSideOffsets2D {
            top,
            right,
            bottom,
            left,
            _unit: PhantomData,
        }
    }

    /// Constructor taking a typed Length for each side.
    pub fn from_lengths(
        top: Length<T, U>,
        right: Length<T, U>,
        bottom: Length<T, U>,
        left: Length<T, U>,
    ) -> Self {
        TypedSideOffsets2D::new(top.0, right.0, bottom.0, left.0)
    }

    /// Access self.top as a typed Length instead of a scalar value.
    pub fn top_typed(&self) -> Length<T, U> {
        Length::new(self.top)
    }

    /// Access self.right as a typed Length instead of a scalar value.
    pub fn right_typed(&self) -> Length<T, U> {
        Length::new(self.right)
    }

    /// Access self.bottom as a typed Length instead of a scalar value.
    pub fn bottom_typed(&self) -> Length<T, U> {
        Length::new(self.bottom)
    }

    /// Access self.left as a typed Length instead of a scalar value.
    pub fn left_typed(&self) -> Length<T, U> {
        Length::new(self.left)
    }

    /// Constructor setting the same value to all sides, taking a scalar value directly.
    pub fn new_all_same(all: T) -> Self {
        TypedSideOffsets2D::new(all, all, all, all)
    }

    /// Constructor setting the same value to all sides, taking a typed Length.
    pub fn from_length_all_same(all: Length<T, U>) -> Self {
        TypedSideOffsets2D::new_all_same(all.0)
    }
}

impl<T, U> TypedSideOffsets2D<T, U>
where
    T: Add<T, Output = T> + Copy,
{
    pub fn horizontal(&self) -> T {
        self.left + self.right
    }

    pub fn vertical(&self) -> T {
        self.top + self.bottom
    }

    pub fn horizontal_typed(&self) -> Length<T, U> {
        Length::new(self.horizontal())
    }

    pub fn vertical_typed(&self) -> Length<T, U> {
        Length::new(self.vertical())
    }
}

impl<T, U> Add for TypedSideOffsets2D<T, U>
where
    T: Copy + Add<T, Output = T>,
{
    type Output = Self;
    fn add(self, other: Self) -> Self {
        TypedSideOffsets2D::new(
            self.top + other.top,
            self.right + other.right,
            self.bottom + other.bottom,
            self.left + other.left,
        )
    }
}

impl<T: Copy + Zero, U> TypedSideOffsets2D<T, U> {
    /// Constructor, setting all sides to zero.
    pub fn zero() -> Self {
        TypedSideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
    }
}
<commit_msg>Implement Default for side offsets.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
//! and margins in CSS.

use super::UnknownUnit;
use length::Length;
use num::Zero;
use core::fmt;
use core::ops::Add;
use core::marker::PhantomData;

/// A group of 2D side offsets, which correspond to top/left/bottom/right for borders, padding,
/// and margins in CSS, optionally tagged with a unit.
#[derive(EuclidMatrix)]
#[repr(C)]
pub struct TypedSideOffsets2D<T, U> {
    pub top: T,
    pub right: T,
    pub bottom: T,
    pub left: T,
    #[doc(hidden)]
    pub _unit: PhantomData<U>,
}

impl<T: fmt::Debug, U> fmt::Debug for TypedSideOffsets2D<T, U> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "({:?},{:?},{:?},{:?})",
            self.top, self.right, self.bottom, self.left
        )
    }
}

impl<T: Default, U> Default for TypedSideOffsets2D<T, U> {
    fn default() -> Self {
        TypedSideOffsets2D {
            top: Default::default(),
            right: Default::default(),
            bottom: Default::default(),
            left: Default::default(),
            _unit: PhantomData,
        }
    }
}

/// The default 2D side offset type with no unit.
pub type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;

impl<T: Copy, U> TypedSideOffsets2D<T, U> {
    /// Constructor taking a scalar for each side.
    pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
        TypedSideOffsets2D {
            top,
            right,
            bottom,
            left,
            _unit: PhantomData,
        }
    }

    /// Constructor taking a typed Length for each side.
    pub fn from_lengths(
        top: Length<T, U>,
        right: Length<T, U>,
        bottom: Length<T, U>,
        left: Length<T, U>,
    ) -> Self {
        TypedSideOffsets2D::new(top.0, right.0, bottom.0, left.0)
    }

    /// Access self.top as a typed Length instead of a scalar value.
    pub fn top_typed(&self) -> Length<T, U> {
        Length::new(self.top)
    }

    /// Access self.right as a typed Length instead of a scalar value.
    pub fn right_typed(&self) -> Length<T, U> {
        Length::new(self.right)
    }

    /// Access self.bottom as a typed Length instead of a scalar value.
    pub fn bottom_typed(&self) -> Length<T, U> {
        Length::new(self.bottom)
    }

    /// Access self.left as a typed Length instead of a scalar value.
    pub fn left_typed(&self) -> Length<T, U> {
        Length::new(self.left)
    }

    /// Constructor setting the same value to all sides, taking a scalar value directly.
    pub fn new_all_same(all: T) -> Self {
        TypedSideOffsets2D::new(all, all, all, all)
    }

    /// Constructor setting the same value to all sides, taking a typed Length.
    pub fn from_length_all_same(all: Length<T, U>) -> Self {
        TypedSideOffsets2D::new_all_same(all.0)
    }
}

impl<T, U> TypedSideOffsets2D<T, U>
where
    T: Add<T, Output = T> + Copy,
{
    pub fn horizontal(&self) -> T {
        self.left + self.right
    }

    pub fn vertical(&self) -> T {
        self.top + self.bottom
    }

    pub fn horizontal_typed(&self) -> Length<T, U> {
        Length::new(self.horizontal())
    }

    pub fn vertical_typed(&self) -> Length<T, U> {
        Length::new(self.vertical())
    }
}

impl<T, U> Add for TypedSideOffsets2D<T, U>
where
    T: Copy + Add<T, Output = T>,
{
    type Output = Self;
    fn add(self, other: Self) -> Self {
        TypedSideOffsets2D::new(
            self.top + other.top,
            self.right + other.right,
            self.bottom + other.bottom,
            self.left + other.left,
        )
    }
}

impl<T: Copy + Zero, U> TypedSideOffsets2D<T, U> {
    /// Constructor, setting all sides to zero.
    pub fn zero() -> Self {
        TypedSideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
    }
}
<|endoftext|>