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

use std::iter::FromIterator;
use std::ops::Deref;

use syntax::ast;
use syntax::ptr::P;
use syntax::ast_util::empty_generics;
use syntax::codemap::{respan, mk_sp};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build the struct for each register
pub struct BuildRegStructs<'a> {
  builder: &'a mut Builder,
  cx: &'a ExtCtxt<'a>,
}

impl<'a> node::RegVisitor for BuildRegStructs<'a> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    fields: &Vec<node::Field>) {
    let width = match reg.ty {
      node::RegType::RegPrim(ref width, _) => width.node,
      _ => panic!("visit_prim_reg called with non-primitive register"),
    };
    for field in fields.iter() {
      for item in build_field_type(self.cx, path, reg, field).into_iter() {
        self.builder.push_item(item);
      }
    }

    for item in build_reg_struct(self.cx, path, reg, &width).into_iter() {
      self.builder.push_item(item);
    }
  }
}

impl<'a> BuildRegStructs<'a> {
  pub fn new(builder: &'a mut Builder, cx: &'a ExtCtxt<'a>)
             -> BuildRegStructs<'a> {
    BuildRegStructs {builder: builder, cx: cx}
  }
}

/// Build a field type if necessary (e.g. in the case of an `EnumField`)
fn build_field_type(cx: &ExtCtxt, path: &Vec<String>,
                    reg: &node::Reg, field: &node::Field)
                    -> Vec<P<ast::Item>> {
  match field.ty.node {
    node::FieldType::EnumField { ref variants, .. } => {
      // FIXME(bgamari): We construct a path, then only take the last
      // segment, this could be more efficient
      let name: ast::Ident =
        utils::field_type_path(cx, path, reg, field)
        .segments.last().unwrap().identifier;
      let enum_def: ast::EnumDef = ast::EnumDef {
        variants: FromIterator::from_iter(
          variants.iter().map(|v| P(build_enum_variant(cx, v)))),
      };
      let mut attrs: Vec<ast::Attribute> = vec!(
        utils::list_attribute(cx, "derive",
                              vec!("PartialEq"),
                              field.name.span),
        utils::list_attribute(cx, "allow",
                              vec!("dead_code",
                                   "non_camel_case_types",
                                   "missing_docs"),
                              field.name.span));

      // setting the enum's repr to the width of the register
      // (unless there's only 1 variant; in which case we omit
      // the repr attribute due to E0083
      if variants.len() > 1 {
        let enum_repr = utils::reg_primitive_type_name(reg)
          .expect("Unexpected non-primitive reg");

        attrs.push(utils::list_attribute(cx, "repr",
                                         vec!(enum_repr),
                                         field.name.span));
      }

      let ty_item: P<ast::Item> = P(ast::Item {
        ident: name,
        id: ast::DUMMY_NODE_ID,
        node: ast::ItemEnum(enum_def, empty_generics()),
        vis: ast::Public,
        attrs: attrs,
        span: field.ty.span,
      });
      vec!(ty_item)
    },
    _ => Vec::new()
  }
}

/// Produce a register struct if necessary (for primitive typed registers).
/// In this case `None` indicates no struct is necessary, not failure.
/// For instance,
///
///     pub struct REG {_value: u32}
fn build_reg_struct(cx: &ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, _width: &node::RegWidth) -> Vec<P<ast::Item>> {
  let packed_ty =
    utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive reg");

  let reg_doc = match reg.docstring {
    Some(d) => d.node.name.to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("Register `{}`: {}",
                          reg.name.node,
                          reg_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let ty_name = utils::path_ident(cx, path);
  let item = quote_item!(cx,
    $doc_attr
    #[derive(Clone)]
    #[allow(non_camel_case_types)]
    #[repr(C)]
    pub struct $ty_name {
      value: VolatileCell<$packed_ty>,
    }
  );
  let mut item: ast::Item = item.unwrap().deref().clone();
  item.span = reg.name.span;
  let copy_impl = quote_item!(cx, impl ::core::marker::Copy for $ty_name {}).unwrap();
  vec!(P(item), copy_impl)
}

/// Build a variant of an `EnumField`
fn build_enum_variant(cx: &ExtCtxt, variant: &node::Variant)
                      -> ast::Variant {
  let doc = match variant.docstring {
    Some(d) => d.node.name.to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("`0x{:x}`. {}",
                          variant.value.node,
                          doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));
  respan(
    mk_sp(variant.name.span.lo, variant.value.span.hi),
    ast::Variant_ {
      name: cx.ident_of(variant.name.node.as_str()),
      attrs: vec!(doc_attr),
      data: P(ast::VariantData::Tuple(Vec::new(), ast::DUMMY_NODE_ID)),
      disr_expr: Some(utils::expr_int(cx, respan(variant.value.span,
                                                 variant.value.node as i64))),
    }
  )
}
<commit_msg>rustup: empty tuple -> ast::VariantData::Unit<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::iter::FromIterator;
use std::ops::Deref;

use syntax::ast;
use syntax::ptr::P;
use syntax::ast_util::empty_generics;
use syntax::codemap::{respan, mk_sp};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build the struct for each register
pub struct BuildRegStructs<'a> {
  builder: &'a mut Builder,
  cx: &'a ExtCtxt<'a>,
}

impl<'a> node::RegVisitor for BuildRegStructs<'a> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    fields: &Vec<node::Field>) {
    let width = match reg.ty {
      node::RegType::RegPrim(ref width, _) => width.node,
      _ => panic!("visit_prim_reg called with non-primitive register"),
    };
    for field in fields.iter() {
      for item in build_field_type(self.cx, path, reg, field).into_iter() {
        self.builder.push_item(item);
      }
    }

    for item in build_reg_struct(self.cx, path, reg, &width).into_iter() {
      self.builder.push_item(item);
    }
  }
}

impl<'a> BuildRegStructs<'a> {
  pub fn new(builder: &'a mut Builder, cx: &'a ExtCtxt<'a>)
             -> BuildRegStructs<'a> {
    BuildRegStructs {builder: builder, cx: cx}
  }
}

/// Build a field type if necessary (e.g. in the case of an `EnumField`)
fn build_field_type(cx: &ExtCtxt, path: &Vec<String>,
                    reg: &node::Reg, field: &node::Field)
                    -> Vec<P<ast::Item>> {
  match field.ty.node {
    node::FieldType::EnumField { ref variants, .. } => {
      // FIXME(bgamari): We construct a path, then only take the last
      // segment, this could be more efficient
      let name: ast::Ident =
        utils::field_type_path(cx, path, reg, field)
        .segments.last().unwrap().identifier;
      let enum_def: ast::EnumDef = ast::EnumDef {
        variants: FromIterator::from_iter(
          variants.iter().map(|v| P(build_enum_variant(cx, v)))),
      };
      let mut attrs: Vec<ast::Attribute> = vec!(
        utils::list_attribute(cx, "derive",
                              vec!("PartialEq"),
                              field.name.span),
        utils::list_attribute(cx, "allow",
                              vec!("dead_code",
                                   "non_camel_case_types",
                                   "missing_docs"),
                              field.name.span));

      // setting the enum's repr to the width of the register
      // (unless there's only 1 variant; in which case we omit
      // the repr attribute due to E0083
      if variants.len() > 1 {
        let enum_repr = utils::reg_primitive_type_name(reg)
          .expect("Unexpected non-primitive reg");

        attrs.push(utils::list_attribute(cx, "repr",
                                         vec!(enum_repr),
                                         field.name.span));
      }

      let ty_item: P<ast::Item> = P(ast::Item {
        ident: name,
        id: ast::DUMMY_NODE_ID,
        node: ast::ItemEnum(enum_def, empty_generics()),
        vis: ast::Public,
        attrs: attrs,
        span: field.ty.span,
      });
      vec!(ty_item)
    },
    _ => Vec::new()
  }
}

/// Produce a register struct if necessary (for primitive typed registers).
/// In this case `None` indicates no struct is necessary, not failure.
/// For instance,
///
///     pub struct REG {_value: u32}
fn build_reg_struct(cx: &ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, _width: &node::RegWidth) -> Vec<P<ast::Item>> {
  let packed_ty =
    utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive reg");

  let reg_doc = match reg.docstring {
    Some(d) => d.node.name.to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("Register `{}`: {}",
                          reg.name.node,
                          reg_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let ty_name = utils::path_ident(cx, path);
  let item = quote_item!(cx,
    $doc_attr
    #[derive(Clone)]
    #[allow(non_camel_case_types)]
    #[repr(C)]
    pub struct $ty_name {
      value: VolatileCell<$packed_ty>,
    }
  );
  let mut item: ast::Item = item.unwrap().deref().clone();
  item.span = reg.name.span;
  let copy_impl = quote_item!(cx, impl ::core::marker::Copy for $ty_name {}).unwrap();
  vec!(P(item), copy_impl)
}

/// Build a variant of an `EnumField`
fn build_enum_variant(cx: &ExtCtxt, variant: &node::Variant)
                      -> ast::Variant {
  let doc = match variant.docstring {
    Some(d) => d.node.name.to_string(),
    None => "no documentation".to_string(),
  };
  let docstring = format!("`0x{:x}`. {}",
                          variant.value.node,
                          doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));
  respan(
    mk_sp(variant.name.span.lo, variant.value.span.hi),
    ast::Variant_ {
      name: cx.ident_of(variant.name.node.as_str()),
      attrs: vec!(doc_attr),
      data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
      disr_expr: Some(utils::expr_int(cx, respan(variant.value.span,
                                                 variant.value.node as i64))),
    }
  )
}
<|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::HTMLCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{namespace_from_domstring, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers, ElementHelpers};
use dom::node::{Node, NodeHelpers, TreeIterator};
use dom::window::Window;
use util::str::{DOMString, split_html_space_chars};

use std::ascii::AsciiExt;
use std::iter::{FilterMap, Skip};
use string_cache::{Atom, Namespace};

pub trait CollectionFilter : JSTraceable {
    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool;
}

#[derive(JSTraceable)]
#[must_root]
pub enum CollectionTypeId {
    Static(Vec<JS<Element>>),
    Live(JS<Node>, Box<CollectionFilter + 'static>)
}

#[dom_struct]
#[derive(HeapSizeOf)]
pub struct HTMLCollection {
    reflector_: Reflector,
    #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"]
    collection: CollectionTypeId,
}

impl HTMLCollection {
    fn new_inherited(collection: CollectionTypeId) -> HTMLCollection {
        HTMLCollection {
            reflector_: Reflector::new(),
            collection: collection,
        }
    }

    pub fn new(window: &Window, collection: CollectionTypeId) -> Root<HTMLCollection> {
        reflect_dom_object(box HTMLCollection::new_inherited(collection),
                           GlobalRef::Window(window), HTMLCollectionBinding::Wrap)
    }
}

impl HTMLCollection {
    pub fn create(window: &Window, root: &Node,
                  filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        HTMLCollection::new(window, CollectionTypeId::Live(JS::from_ref(root), filter))
    }

    fn all_elements(window: &Window, root: &Node,
                    namespace_filter: Option<Namespace>) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct AllElementFilter {
            namespace_filter: Option<Namespace>
        }
        impl CollectionFilter for AllElementFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                match self.namespace_filter {
                    None => true,
                    Some(ref namespace) => *elem.namespace() == *namespace
                }
            }
        }
        let filter = AllElementFilter {namespace_filter: namespace_filter};
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name(window: &Window, root: &Node, tag: DOMString)
                       -> Root<HTMLCollection> {
        if tag == "*" {
            return HTMLCollection::all_elements(window, root, None);
        }

        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameFilter {
            tag: Atom,
            ascii_lower_tag: Atom,
        }
        impl CollectionFilter for TagNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                if elem.html_element_in_html_document() {
                    *elem.local_name() == self.ascii_lower_tag
                } else {
                    *elem.local_name() == self.tag
                }
            }
        }
        let filter = TagNameFilter {
            tag: Atom::from_slice(&tag),
            ascii_lower_tag: Atom::from_slice(&tag.to_ascii_lowercase()),
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                          maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
        let namespace_filter = match maybe_ns {
            Some(ref namespace) if namespace == &"*" => None,
            ns => Some(namespace_from_domstring(ns)),
        };

        if tag == "*" {
            return HTMLCollection::all_elements(window, root, namespace_filter);
        }
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameNSFilter {
            tag: Atom,
            namespace_filter: Option<Namespace>
        }
        impl CollectionFilter for TagNameNSFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                let ns_match = match self.namespace_filter {
                    Some(ref namespace) => {
                        *elem.namespace() == *namespace
                    },
                    None => true
                };
                ns_match && *elem.local_name() == self.tag
            }
        }
        let filter = TagNameNSFilter {
            tag: Atom::from_slice(&tag),
            namespace_filter: namespace_filter
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_class_name(window: &Window, root: &Node, classes: DOMString)
                         -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ClassNameFilter {
            classes: Vec<Atom>
        }
        impl CollectionFilter for ClassNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                self.classes.iter().all(|class| elem.has_class(class))
            }
        }
        let filter = ClassNameFilter {
            classes: split_html_space_chars(&classes).map(|class| {
                         Atom::from_slice(class)
                     }).collect()
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ElementChildFilter;
        impl CollectionFilter for ElementChildFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                root.is_parent_of(NodeCast::from_ref(elem))
            }
        }
        HTMLCollection::create(window, root, box ElementChildFilter)
    }

    fn traverse(root: &Node)
                -> FilterMap<Skip<TreeIterator>,
                             fn(Root<Node>) -> Option<Root<Element>>> {
        fn to_temporary(node: Root<Node>) -> Option<Root<Element>> {
            ElementCast::to_root(node)
        }
        root.traverse_preorder()
            .skip(1)
            .filter_map(to_temporary)
    }
}

impl<'a> HTMLCollectionMethods for &'a HTMLCollection {
    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
    fn Length(self) -> u32 {
        match self.collection {
            CollectionTypeId::Static(ref elems) => elems.len() as u32,
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .count() as u32
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn Item(self, index: u32) -> Option<Root<Element>> {
        let index = index as usize;
        match self.collection {
            CollectionTypeId::Static(ref elems) => elems
                .get(index).map(|t| t.root()),
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .nth(index)
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
    fn NamedItem(self, key: DOMString) -> Option<Root<Element>> {
        // Step 1.
        if key.is_empty() {
            return None;
        }

        // Step 2.
        match self.collection {
            CollectionTypeId::Static(ref elems) => elems.iter()
                .map(|elem| elem.root())
                .find(|elem| {
                    elem.r().get_string_attribute(&atom!("name")) == key ||
                    elem.r().get_string_attribute(&atom!("id")) == key }),
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .find(|elem| {
                        elem.r().get_string_attribute(&atom!("name")) == key ||
                        elem.r().get_string_attribute(&atom!("id")) == key })
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn IndexedGetter(self, index: u32, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.Item(index);
        *found = maybe_elem.is_some();
        maybe_elem
    }

    // check-tidy: no specs after this line
    fn NamedGetter(self, name: DOMString, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.NamedItem(name);
        *found = maybe_elem.is_some();
        maybe_elem
    }
}

<commit_msg>remove unused static collection<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::HTMLCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{namespace_from_domstring, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers, ElementHelpers};
use dom::node::{Node, NodeHelpers, TreeIterator};
use dom::window::Window;
use util::str::{DOMString, split_html_space_chars};

use std::ascii::AsciiExt;
use std::iter::{FilterMap, Skip};
use string_cache::{Atom, Namespace};

pub trait CollectionFilter : JSTraceable {
    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool;
}

#[derive(JSTraceable)]
#[must_root]
pub enum CollectionTypeId {
    Live(JS<Node>, Box<CollectionFilter+'static>)
}

#[dom_struct]
#[derive(HeapSizeOf)]
pub struct HTMLCollection {
    reflector_: Reflector,
    #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"]
    collection: CollectionTypeId,
}

impl HTMLCollection {
    fn new_inherited(collection: CollectionTypeId) -> HTMLCollection {
        HTMLCollection {
            reflector_: Reflector::new(),
            collection: collection,
        }
    }

    pub fn new(window: &Window, collection: CollectionTypeId) -> Root<HTMLCollection> {
        reflect_dom_object(box HTMLCollection::new_inherited(collection),
                           GlobalRef::Window(window), HTMLCollectionBinding::Wrap)
    }
}

impl HTMLCollection {
    pub fn create(window: &Window, root: &Node,
                  filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        HTMLCollection::new(window, CollectionTypeId::Live(JS::from_ref(root), filter))
    }

    fn all_elements(window: &Window, root: &Node,
                    namespace_filter: Option<Namespace>) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct AllElementFilter {
            namespace_filter: Option<Namespace>
        }
        impl CollectionFilter for AllElementFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                match self.namespace_filter {
                    None => true,
                    Some(ref namespace) => *elem.namespace() == *namespace
                }
            }
        }
        let filter = AllElementFilter {namespace_filter: namespace_filter};
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name(window: &Window, root: &Node, tag: DOMString)
                       -> Root<HTMLCollection> {
        if tag == "*" {
            return HTMLCollection::all_elements(window, root, None);
        }

        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameFilter {
            tag: Atom,
            ascii_lower_tag: Atom,
        }
        impl CollectionFilter for TagNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                if elem.html_element_in_html_document() {
                    *elem.local_name() == self.ascii_lower_tag
                } else {
                    *elem.local_name() == self.tag
                }
            }
        }
        let filter = TagNameFilter {
            tag: Atom::from_slice(&tag),
            ascii_lower_tag: Atom::from_slice(&tag.to_ascii_lowercase()),
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                          maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
        let namespace_filter = match maybe_ns {
            Some(ref namespace) if namespace == &"*" => None,
            ns => Some(namespace_from_domstring(ns)),
        };

        if tag == "*" {
            return HTMLCollection::all_elements(window, root, namespace_filter);
        }
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameNSFilter {
            tag: Atom,
            namespace_filter: Option<Namespace>
        }
        impl CollectionFilter for TagNameNSFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                let ns_match = match self.namespace_filter {
                    Some(ref namespace) => {
                        *elem.namespace() == *namespace
                    },
                    None => true
                };
                ns_match && *elem.local_name() == self.tag
            }
        }
        let filter = TagNameNSFilter {
            tag: Atom::from_slice(&tag),
            namespace_filter: namespace_filter
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_class_name(window: &Window, root: &Node, classes: DOMString)
                         -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ClassNameFilter {
            classes: Vec<Atom>
        }
        impl CollectionFilter for ClassNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                self.classes.iter().all(|class| elem.has_class(class))
            }
        }
        let filter = ClassNameFilter {
            classes: split_html_space_chars(&classes).map(|class| {
                         Atom::from_slice(class)
                     }).collect()
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ElementChildFilter;
        impl CollectionFilter for ElementChildFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                root.is_parent_of(NodeCast::from_ref(elem))
            }
        }
        HTMLCollection::create(window, root, box ElementChildFilter)
    }

    fn traverse(root: &Node)
                -> FilterMap<Skip<TreeIterator>,
                             fn(Root<Node>) -> Option<Root<Element>>> {
        fn to_temporary(node: Root<Node>) -> Option<Root<Element>> {
            ElementCast::to_root(node)
        }
        root.traverse_preorder()
            .skip(1)
            .filter_map(to_temporary)
    }
}

impl<'a> HTMLCollectionMethods for &'a HTMLCollection {
    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
    fn Length(self) -> u32 {
        match self.collection {
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .count() as u32
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn Item(self, index: u32) -> Option<Root<Element>> {
        let index = index as usize;
        match self.collection {
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .nth(index)
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
    fn NamedItem(self, key: DOMString) -> Option<Root<Element>> {
        // Step 1.
        if key.is_empty() {
            return None;
        }

        // Step 2.
        match self.collection {
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .find(|elem| {
                        elem.r().get_string_attribute(&atom!("name")) == key ||
                        elem.r().get_string_attribute(&atom!("id")) == key })
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn IndexedGetter(self, index: u32, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.Item(index);
        *found = maybe_elem.is_some();
        maybe_elem
    }

    // check-tidy: no specs after this line
    fn NamedGetter(self, name: DOMString, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.NamedItem(name);
        *found = maybe_elem.is_some();
        maybe_elem
    }
}

<|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/. */

//! Generic types for CSS values in SVG

use cssparser::Parser;
use parser::{Parse, ParserContext};
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use values::{Either, None_};
use values::computed::NumberOrPercentage;
use values::computed::length::LengthOrPercentage;
use values::distance::{ComputeSquaredDistance, SquaredDistance};

/// An SVG paint value
///
/// <https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint>
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
#[derive(ToAnimatedValue, ToComputedValue, ToCss)]
pub struct SVGPaint<ColorType, UrlPaintServer> {
    /// The paint source
    pub kind: SVGPaintKind<ColorType, UrlPaintServer>,
    /// The fallback color. It would be empty, the `none` keyword or <color>.
    pub fallback: Option<Either<ColorType, None_>>,
}

/// An SVG paint value without the fallback
///
/// Whereas the spec only allows PaintServer
/// to have a fallback, Gecko lets the context
/// properties have a fallback as well.
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
#[derive(ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
pub enum SVGPaintKind<ColorType, UrlPaintServer> {
    /// `none`
    #[animation(error)]
    None,
    /// `<color>`
    Color(ColorType),
    /// `url(...)`
    #[animation(error)]
    PaintServer(UrlPaintServer),
    /// `context-fill`
    ContextFill,
    /// `context-stroke`
    ContextStroke,
}

impl<ColorType, UrlPaintServer> SVGPaintKind<ColorType, UrlPaintServer> {
    /// Parse a keyword value only
    fn parse_ident<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        try_match_ident_ignore_ascii_case! { input,
            "none" => Ok(SVGPaintKind::None),
            "context-fill" => Ok(SVGPaintKind::ContextFill),
            "context-stroke" => Ok(SVGPaintKind::ContextStroke),
        }
    }
}

/// Parse SVGPaint's fallback.
/// fallback is keyword(none), Color or empty.
/// <https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint>
fn parse_fallback<'i, 't, ColorType: Parse>(context: &ParserContext,
                                            input: &mut Parser<'i, 't>)
                                            -> Option<Either<ColorType, None_>> {
    if input.try(|i| i.expect_ident_matching("none")).is_ok() {
        Some(Either::Second(None_))
    } else {
        if let Ok(color) = input.try(|i| ColorType::parse(context, i)) {
            Some(Either::First(color))
        } else {
            None
        }
    }
}

impl<ColorType: Parse, UrlPaintServer: Parse> Parse for SVGPaint<ColorType, UrlPaintServer> {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        if let Ok(url) = input.try(|i| UrlPaintServer::parse(context, i)) {
            Ok(SVGPaint {
                kind: SVGPaintKind::PaintServer(url),
                fallback: parse_fallback(context, input),
            })
        } else if let Ok(kind) = input.try(SVGPaintKind::parse_ident) {
            if let SVGPaintKind::None = kind {
                Ok(SVGPaint {
                    kind: kind,
                    fallback: None,
                })
            } else {
                Ok(SVGPaint {
                    kind: kind,
                    fallback: parse_fallback(context, input),
                })
            }
        } else if let Ok(color) = input.try(|i| ColorType::parse(context, i)) {
            Ok(SVGPaint {
                kind: SVGPaintKind::Color(color),
                fallback: None,
            })
        } else {
            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        }
    }
}

/// A value of <length> | <percentage> | <number> for svg which allow unitless length.
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeProperties>
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue)]
#[derive(ToAnimatedZero, ToComputedValue, ToCss)]
pub enum SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number> {
    /// <length> | <percentage>
    LengthOrPercentage(LengthOrPercentage),
    /// <number>
    Number(Number),
}

impl<L, N> ComputeSquaredDistance for SvgLengthOrPercentageOrNumber<L, N>
    where
        L: ComputeSquaredDistance + Copy + Into<NumberOrPercentage>,
        N: ComputeSquaredDistance + Copy + Into<NumberOrPercentage>
{
    #[inline]
    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
        match (self, other) {
            (
                &SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref from),
                &SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref to)
            ) => {
                from.compute_squared_distance(to)
            },
            (
                &SvgLengthOrPercentageOrNumber::Number(ref from),
                &SvgLengthOrPercentageOrNumber::Number(ref to)
            ) => {
                from.compute_squared_distance(to)
            },
            (
                &SvgLengthOrPercentageOrNumber::LengthOrPercentage(from),
                &SvgLengthOrPercentageOrNumber::Number(to)
            ) => {
                from.into().compute_squared_distance(&to.into())
            },
            (
                &SvgLengthOrPercentageOrNumber::Number(from),
                &SvgLengthOrPercentageOrNumber::LengthOrPercentage(to)
            ) => {
                from.into().compute_squared_distance(&to.into())
            },
        }
    }
}

impl<LengthOrPercentageType, NumberType> SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType>
    where LengthOrPercentage: From<LengthOrPercentageType>,
          LengthOrPercentageType: Copy
{
    /// return true if this struct has calc value.
    pub fn has_calc(&self) -> bool {
        match self {
            &SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => {
                match LengthOrPercentage::from(lop) {
                    LengthOrPercentage::Calc(_) => true,
                    _ => false,
                }
            },
            _ => false,
        }
    }
}

/// Parsing the SvgLengthOrPercentageOrNumber. At first, we need to parse number
/// since prevent converting to the length.
impl <LengthOrPercentageType: Parse, NumberType: Parse> Parse for
    SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType> {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<Self, ParseError<'i>> {
        if let Ok(num) = input.try(|i| NumberType::parse(context, i)) {
            return Ok(SvgLengthOrPercentageOrNumber::Number(num));
        }

        if let Ok(lop) = input.try(|i| LengthOrPercentageType::parse(context, i)) {
            return Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop));
        }
        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
    }
}

/// An SVG length value supports `context-value` in addition to length.
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq)]
#[derive(ToAnimatedValue, ToAnimatedZero)]
#[derive(ToComputedValue, ToCss)]
pub enum SVGLength<LengthType> {
    /// `<length> | <percentage> | <number>`
    Length(LengthType),
    /// `context-value`
    ContextValue,
}

/// Generic value for stroke-dasharray.
#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
         ToComputedValue)]
pub enum SVGStrokeDashArray<LengthType> {
    /// `[ <length> | <percentage> | <number> ]#`
    Values(Vec<LengthType>),
    /// `context-value`
    ContextValue,
}

impl<LengthType> ToCss for SVGStrokeDashArray<LengthType> where LengthType: ToCss {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        match self {
            &SVGStrokeDashArray::Values(ref values) => {
                let mut iter = values.iter();
                if let Some(first) = iter.next() {
                    first.to_css(dest)?;
                    for item in iter {
                        dest.write_str(", ")?;
                        item.to_css(dest)?;
                    }
                    Ok(())
                } else {
                    dest.write_str("none")
                }
            }
            &SVGStrokeDashArray::ContextValue => {
                dest.write_str("context-value")
            }
        }
    }
}

/// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
/// addition to opacity value.
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
#[derive(PartialEq, ToAnimatedZero, ToComputedValue, ToCss)]
pub enum SVGOpacity<OpacityType> {
    /// `<opacity-value>`
    Opacity(OpacityType),
    /// `context-fill-opacity`
    ContextFillOpacity,
    /// `context-stroke-opacity`
    ContextStrokeOpacity,
}
<commit_msg>Derive ToCss for SVGStrokeDashArray<T><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/. */

//! Generic types for CSS values in SVG

use cssparser::Parser;
use parser::{Parse, ParserContext};
use style_traits::{ParseError, StyleParseErrorKind};
use values::{Either, None_};
use values::computed::NumberOrPercentage;
use values::computed::length::LengthOrPercentage;
use values::distance::{ComputeSquaredDistance, SquaredDistance};

/// An SVG paint value
///
/// <https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint>
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
#[derive(ToAnimatedValue, ToComputedValue, ToCss)]
pub struct SVGPaint<ColorType, UrlPaintServer> {
    /// The paint source
    pub kind: SVGPaintKind<ColorType, UrlPaintServer>,
    /// The fallback color. It would be empty, the `none` keyword or <color>.
    pub fallback: Option<Either<ColorType, None_>>,
}

/// An SVG paint value without the fallback
///
/// Whereas the spec only allows PaintServer
/// to have a fallback, Gecko lets the context
/// properties have a fallback as well.
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
#[derive(ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
pub enum SVGPaintKind<ColorType, UrlPaintServer> {
    /// `none`
    #[animation(error)]
    None,
    /// `<color>`
    Color(ColorType),
    /// `url(...)`
    #[animation(error)]
    PaintServer(UrlPaintServer),
    /// `context-fill`
    ContextFill,
    /// `context-stroke`
    ContextStroke,
}

impl<ColorType, UrlPaintServer> SVGPaintKind<ColorType, UrlPaintServer> {
    /// Parse a keyword value only
    fn parse_ident<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        try_match_ident_ignore_ascii_case! { input,
            "none" => Ok(SVGPaintKind::None),
            "context-fill" => Ok(SVGPaintKind::ContextFill),
            "context-stroke" => Ok(SVGPaintKind::ContextStroke),
        }
    }
}

/// Parse SVGPaint's fallback.
/// fallback is keyword(none), Color or empty.
/// <https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint>
fn parse_fallback<'i, 't, ColorType: Parse>(context: &ParserContext,
                                            input: &mut Parser<'i, 't>)
                                            -> Option<Either<ColorType, None_>> {
    if input.try(|i| i.expect_ident_matching("none")).is_ok() {
        Some(Either::Second(None_))
    } else {
        if let Ok(color) = input.try(|i| ColorType::parse(context, i)) {
            Some(Either::First(color))
        } else {
            None
        }
    }
}

impl<ColorType: Parse, UrlPaintServer: Parse> Parse for SVGPaint<ColorType, UrlPaintServer> {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        if let Ok(url) = input.try(|i| UrlPaintServer::parse(context, i)) {
            Ok(SVGPaint {
                kind: SVGPaintKind::PaintServer(url),
                fallback: parse_fallback(context, input),
            })
        } else if let Ok(kind) = input.try(SVGPaintKind::parse_ident) {
            if let SVGPaintKind::None = kind {
                Ok(SVGPaint {
                    kind: kind,
                    fallback: None,
                })
            } else {
                Ok(SVGPaint {
                    kind: kind,
                    fallback: parse_fallback(context, input),
                })
            }
        } else if let Ok(color) = input.try(|i| ColorType::parse(context, i)) {
            Ok(SVGPaint {
                kind: SVGPaintKind::Color(color),
                fallback: None,
            })
        } else {
            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        }
    }
}

/// A value of <length> | <percentage> | <number> for svg which allow unitless length.
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeProperties>
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue)]
#[derive(ToAnimatedZero, ToComputedValue, ToCss)]
pub enum SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number> {
    /// <length> | <percentage>
    LengthOrPercentage(LengthOrPercentage),
    /// <number>
    Number(Number),
}

impl<L, N> ComputeSquaredDistance for SvgLengthOrPercentageOrNumber<L, N>
    where
        L: ComputeSquaredDistance + Copy + Into<NumberOrPercentage>,
        N: ComputeSquaredDistance + Copy + Into<NumberOrPercentage>
{
    #[inline]
    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
        match (self, other) {
            (
                &SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref from),
                &SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref to)
            ) => {
                from.compute_squared_distance(to)
            },
            (
                &SvgLengthOrPercentageOrNumber::Number(ref from),
                &SvgLengthOrPercentageOrNumber::Number(ref to)
            ) => {
                from.compute_squared_distance(to)
            },
            (
                &SvgLengthOrPercentageOrNumber::LengthOrPercentage(from),
                &SvgLengthOrPercentageOrNumber::Number(to)
            ) => {
                from.into().compute_squared_distance(&to.into())
            },
            (
                &SvgLengthOrPercentageOrNumber::Number(from),
                &SvgLengthOrPercentageOrNumber::LengthOrPercentage(to)
            ) => {
                from.into().compute_squared_distance(&to.into())
            },
        }
    }
}

impl<LengthOrPercentageType, NumberType> SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType>
    where LengthOrPercentage: From<LengthOrPercentageType>,
          LengthOrPercentageType: Copy
{
    /// return true if this struct has calc value.
    pub fn has_calc(&self) -> bool {
        match self {
            &SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => {
                match LengthOrPercentage::from(lop) {
                    LengthOrPercentage::Calc(_) => true,
                    _ => false,
                }
            },
            _ => false,
        }
    }
}

/// Parsing the SvgLengthOrPercentageOrNumber. At first, we need to parse number
/// since prevent converting to the length.
impl <LengthOrPercentageType: Parse, NumberType: Parse> Parse for
    SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType> {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<Self, ParseError<'i>> {
        if let Ok(num) = input.try(|i| NumberType::parse(context, i)) {
            return Ok(SvgLengthOrPercentageOrNumber::Number(num));
        }

        if let Ok(lop) = input.try(|i| LengthOrPercentageType::parse(context, i)) {
            return Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop));
        }
        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
    }
}

/// An SVG length value supports `context-value` in addition to length.
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq)]
#[derive(ToAnimatedValue, ToAnimatedZero)]
#[derive(ToComputedValue, ToCss)]
pub enum SVGLength<LengthType> {
    /// `<length> | <percentage> | <number>`
    Length(LengthType),
    /// `context-value`
    ContextValue,
}

/// Generic value for stroke-dasharray.
#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
#[derive(ToAnimatedValue, ToComputedValue, ToCss)]
pub enum SVGStrokeDashArray<LengthType> {
    /// `[ <length> | <percentage> | <number> ]#`
    #[css(comma)]
    Values(#[css(if_empty = "none", iterable)] Vec<LengthType>),
    /// `context-value`
    ContextValue,
}

/// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
/// addition to opacity value.
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
#[derive(PartialEq, ToAnimatedZero, ToComputedValue, ToCss)]
pub enum SVGOpacity<OpacityType> {
    /// `<opacity-value>`
    Opacity(OpacityType),
    /// `context-fill-opacity`
    ContextFillOpacity,
    /// `context-stroke-opacity`
    ContextStrokeOpacity,
}
<|endoftext|>


<commit_before>// Copyright 2016 Bruno Medeiros
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>.
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::convert;
use std::io;

use std::fmt;
use std::result;

pub fn new<T>(x: T) -> Box<T> {
	Box::new(x)
}

pub type GError = Box<GErrorT>;
pub type GResult<T> = result::Result<T, GError>;
pub type Void = GResult<()>;

pub trait GErrorT {

	fn write_message(&self, writer: &mut CommonCharOutput) -> Void;

	fn write_message_to_string(&self) -> String {
		let mut str = String::new();
		self.write_message(&mut str).ok(); // Should not error because it's writting to a string
		str
	}

}

impl fmt::Display for GErrorT {

	fn fmt(&self, fmt : &mut fmt::Formatter) -> fmt::Result {
		let to_string = self.write_message_to_string();
		fmt.write_str(&to_string)
	}

}

impl fmt::Debug for GErrorT {

	fn fmt(&self, fmt : &mut fmt::Formatter) -> fmt::Result {
		<Self as fmt::Display>::fmt(self, fmt)
	}

}

pub struct ErrorMessage(String);

impl ErrorMessage {

	pub fn create(string : String) -> Box<ErrorMessage> {
		Box::new(ErrorMessage(string))
	}

}

impl GErrorT for ErrorMessage {
	fn write_message(&self, out: &mut CommonCharOutput) -> Void {
		out.write_str(&self.0)
	}
}

struct FmtDisplayError<T : fmt::Display>(T);

impl<T : fmt::Display> GErrorT for FmtDisplayError<T> {
	fn write_message(& self, out: &mut CommonCharOutput) -> Void {
		write_display_to_char_out(& self.0, out)
	}
}

fn write_display_to_char_out(display: & fmt::Display, out: &mut CommonCharOutput) -> Void {
	let string = format!("{}", display);
	out.write_str(&string)
}

/* ----------------- convert to GError ----------------- */

impl convert::From<io::Error> for GError {
	fn from(obj: io::Error) -> Self {
		Box::new(FmtDisplayError(obj))
	}
}

impl convert::From<fmt::Error> for GError {
	fn from(obj: fmt::Error) -> Self {
		Box::new(FmtDisplayError(obj))
	}
}

impl convert::From<String> for GError {
	fn from(obj: String) -> Self {
		Box::new(ErrorMessage(obj))
	}
}

use std::num;

impl convert::From<num::ParseIntError> for GError {
	fn from(obj: num::ParseIntError) -> Self {
		Box::new(FmtDisplayError(obj))
	}
}

#[test]
fn test_convert() {

	fn test() -> Void {
		try!(Err(String::from("ERROR")));
		Ok(())
	}

	test().unwrap_err();
}

/* ----------------- CommonCharOutput ----------------- */

pub type CommonCharOutput = CharOutput<GError>;

pub trait CharOutput<ERR> {

	fn write_str(&mut self, s: &str) -> result::Result<(), ERR>;

	fn write_char(&mut self, c: char) -> result::Result<(), ERR>;

}

impl<ERR> CharOutput<ERR> for String {

	fn write_str(&mut self, s: &str) -> result::Result<(), ERR> {
		self.put_str(s);
		Ok(())
	}

	fn write_char(&mut self, c: char) -> result::Result<(), ERR> {
		self.put_char(c);
		Ok(())
	}
}

pub trait BasicCharOutput {

	fn put_str(&mut self, s: &str) ;

	fn put_char(&mut self, c: char) ;

}

//impl fmt::Write for BasicCharOutput {
//	fn write_str(&mut self, str: &str) -> fmt::Result {
//    	self.put_str(str);
//    	Ok(())
//    }
//
//    fn write_char(&mut self, ch: char) -> fmt::Result {
//    	self.put_char(ch);
//    	Ok(())
//    }
//}

impl<ERR> CharOutput<ERR> for BasicCharOutput {

	fn write_str(&mut self, s: &str) -> result::Result<(), ERR> {
		BasicCharOutput::put_str(self, s);
		Ok(())
	}

	fn write_char(&mut self, c: char) -> result::Result<(), ERR> {
		BasicCharOutput::put_char(self, c);
		Ok(())
	}

}

//// TODO: might have to remove this, if it's polluting namespace?
impl BasicCharOutput for String {

	fn put_str(&mut self, str: &str) {
		self.push_str(str);
	}

	fn put_char(&mut self, ch: char) {
		self.push(ch);
	}

}

/*
fn write_display_to_BasicCharOut(display : &fmt::Display, out: &mut BasicCharOutput) {

	struct _BasicWrite<'a>(&'a mut BasicCharOutput);

	impl<'a> fmt::Write for _BasicWrite<'a> {
		fn write_str(&mut self, str: &str) -> fmt::Result {
			self.0.put_str(str);
			Ok(())
		}

		fn write_char(&mut self, ch: char) -> fmt::Result {
			self.0.put_char(ch);
			Ok(())
		}

	}

	fmt::write(&mut _BasicWrite(out), format_args!("{}", display))
		.expect("displayObj object should not result an error.");

}

#[test]
fn test_write_display_to_BasicCharOut() {

	struct _Display(());
	impl fmt::Display for _Display {
		fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
			write!(formatter, "Blah {}", "XXX")
		}
	}

	let mut result = String::new();
	write_display_to_BasicCharOut(&_Display(()), &mut result);
	assert_eq!(result, "Blah XXX");
}
*/

/* -----------------  lifecycle / dispose  ----------------- */

pub struct FinishedFlag(pub bool);

impl FinishedFlag {

	pub fn is_finished(&self) -> bool {
		return self.0
	}

	/// Set this flag as finished. Can only be invoked once.
	pub fn finish(&mut self) {
		assert!(!self.is_finished());
		self.set_finished();
	}

	pub fn set_finished(&mut self) {
		self.0 = true;
	}

}

impl Drop for FinishedFlag {

	fn drop(&mut self) {
		assert!(self.is_finished());
	}
}

/* -----------------  Sync and Rc util ----------------- */

use std::sync::Arc;
use std::sync::Mutex;

pub fn newArcMutex<T>(x: T) -> Arc<Mutex<T>> {
	Arc::new(Mutex::new(x))
}

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

pub fn unwrap_Rc_RefCell<T>(this: Rc<RefCell<T>>) -> T {
	let ures : result::Result<RefCell<_>, _> = Rc::try_unwrap(this);
	match ures {
		Ok(refCell) => return refCell.into_inner(),
		Err(_) => panic!("std::Rc unwrap failed")
	}
}<commit_msg>simplify: remove BasicCharOutput<commit_after>// Copyright 2016 Bruno Medeiros
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>.
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::convert;
use std::io;

use std::fmt;
use std::result;

pub fn new<T>(x: T) -> Box<T> {
	Box::new(x)
}

pub type GError = Box<GErrorT>;
pub type GResult<T> = result::Result<T, GError>;
pub type Void = GResult<()>;

pub trait GErrorT {

	fn write_message(&self, writer: &mut CommonCharOutput) -> Void;

	fn write_message_to_string(&self) -> String {
		let mut str = String::new();
		self.write_message(&mut str).ok(); // Should not error because it's writting to a string
		str
	}

}

impl fmt::Display for GErrorT {

	fn fmt(&self, fmt : &mut fmt::Formatter) -> fmt::Result {
		let to_string = self.write_message_to_string();
		fmt.write_str(&to_string)
	}

}

impl fmt::Debug for GErrorT {

	fn fmt(&self, fmt : &mut fmt::Formatter) -> fmt::Result {
		<Self as fmt::Display>::fmt(self, fmt)
	}

}

pub struct ErrorMessage(String);

impl ErrorMessage {

	pub fn create(string : String) -> Box<ErrorMessage> {
		Box::new(ErrorMessage(string))
	}

}

impl GErrorT for ErrorMessage {
	fn write_message(&self, out: &mut CommonCharOutput) -> Void {
		out.write_str(&self.0)
	}
}

struct FmtDisplayError<T : fmt::Display>(T);

impl<T : fmt::Display> GErrorT for FmtDisplayError<T> {
	fn write_message(& self, out: &mut CommonCharOutput) -> Void {
		write_display_to_char_out(& self.0, out)
	}
}

fn write_display_to_char_out(display: & fmt::Display, out: &mut CommonCharOutput) -> Void {
	let string = format!("{}", display);
	out.write_str(&string)
}

/* ----------------- convert to GError ----------------- */

impl convert::From<io::Error> for GError {
	fn from(obj: io::Error) -> Self {
		Box::new(FmtDisplayError(obj))
	}
}

impl convert::From<fmt::Error> for GError {
	fn from(obj: fmt::Error) -> Self {
		Box::new(FmtDisplayError(obj))
	}
}

impl convert::From<String> for GError {
	fn from(obj: String) -> Self {
		Box::new(ErrorMessage(obj))
	}
}

use std::num;

impl convert::From<num::ParseIntError> for GError {
	fn from(obj: num::ParseIntError) -> Self {
		Box::new(FmtDisplayError(obj))
	}
}

#[test]
fn test_convert() {

	fn test() -> Void {
		try!(Err(String::from("ERROR")));
		Ok(())
	}

	test().unwrap_err();
}

/* ----------------- CommonCharOutput ----------------- */

pub type CommonCharOutput = CharOutput<GError>;

pub trait CharOutput<ERR> {

	fn write_str(&mut self, st: &str) -> result::Result<(), ERR>;

	fn write_char(&mut self, ch: char) -> result::Result<(), ERR>;

}

impl<ERR> CharOutput<ERR> for String {

	fn write_str(&mut self, st: &str) -> result::Result<(), ERR> {
		self.push_str(st);
		Ok(())
	}

	fn write_char(&mut self, ch: char) -> result::Result<(), ERR> {
		self.push(ch);
		Ok(())
	}
}

/* -----------------  lifecycle / dispose  ----------------- */

pub struct FinishedFlag(pub bool);

impl FinishedFlag {

	pub fn is_finished(&self) -> bool {
		return self.0
	}

	/// Set this flag as finished. Can only be invoked once.
	pub fn finish(&mut self) {
		assert!(!self.is_finished());
		self.set_finished();
	}

	pub fn set_finished(&mut self) {
		self.0 = true;
	}

}

impl Drop for FinishedFlag {

	fn drop(&mut self) {
		assert!(self.is_finished());
	}
}

/* -----------------  Sync and Rc util ----------------- */

use std::sync::Arc;
use std::sync::Mutex;

pub fn newArcMutex<T>(x: T) -> Arc<Mutex<T>> {
	Arc::new(Mutex::new(x))
}

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

pub fn unwrap_Rc_RefCell<T>(this: Rc<RefCell<T>>) -> T {
	let ures : result::Result<RefCell<_>, _> = Rc::try_unwrap(this);
	match ures {
		Ok(refCell) => return refCell.into_inner(),
		Err(_) => panic!("std::Rc unwrap failed")
	}
}<|endoftext|>


<commit_before><commit_msg>Add a self-parse of `.debug_info` test using `EndianRcSlice`<commit_after><|endoftext|>


<commit_before>#[macro_use] extern crate conrod;
extern crate find_folder;
extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use conrod::{
    Background,
    Color,
    Colorable,
    Frameable,
    Labelable,
    Positionable,
    Sizeable,
    Theme,
    Toggle,
    Ui,
    Widget,
};
use conrod::color::{black, grayscale, green, red};
use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use opengl_graphics::glyph_cache::GlyphCache;
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;
type RcGraphics = Rc<RefCell<GlGraphics>>;
type RcUi = Rc<RefCell<Ui<GlyphCache<'static>>>>;

struct App {
    window: RcWindow,
    graphics: RcGraphics,
    ui: RcUi,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    cursor: VirtualCursor,
    bg_color: Color
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.cursor.position = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        // we need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(self.window.borrow().size());

        // update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        // This demo translates and rebinds in a single screen, forcing a clone of the translator. This
        // limitation is a result of the necessity of providing a different interface for rebinding. One
        // way you could avoid a clone every update/render is to structure your application to pass the
        // translator between separate options and game screens.
        let mut rebind = self.translator.clone().into_rebind();

        let mut gl_graphics = self.graphics.borrow_mut();
        let ui = &mut *self.ui.borrow_mut();

        // declare widget ids
        widget_ids! {
            X_INVERT_TOGGLE,
            Y_INVERT_TOGGLE
        }

        // draw the ui
        gl_graphics.draw(args.viewport(), |c, gl| {
            Background::new().color(self.bg_color).set(ui);

            Toggle::new(rebind.get_x_motion_inverted())
                .xy(-350.0, 260.0)
                .dimensions(80.0, 40.0)
                .color(grayscale(0.4))
                .frame(1.0)
                .label(&"Invert X")
                .label_color(self.bg_color.plain_contrast())
                .label_font_size(16)
                .react(|b| rebind.set_x_motion_inverted(b))
                .set(X_INVERT_TOGGLE, ui);

            Toggle::new(rebind.get_y_motion_inverted())
                .xy(-350.0, 200.0)
                .dimensions(80.0, 40.0)
                .color(grayscale(0.4))
                .frame(1.0)
                .label(&"Invert Y")
                .label_color(self.bg_color.plain_contrast())
                .label_font_size(16)
                .react(|b| rebind.set_y_motion_inverted(b))
                .set(Y_INVERT_TOGGLE, ui);

            ui.draw(c, gl);
        });

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color.to_fsa(),
                                                                square,
                                                                c.transform,
                                                                gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.cursor.position[0],
                                      self.cursor.position[1],
                                      self.cursor.size);

            gl_graphics.draw(args.viewport(), |c, gl| ellipse(self.cursor.color.to_fsa(),
                                                              dot,
                                                              c.transform,
                                                              gl));
        }

        self.translator = rebind.into();
    }
}

struct Character {
    color: Color,
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: Color, tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

struct VirtualCursor {
    position: [f64; 2],
    color: Color,
    size: f64
}

impl VirtualCursor {
    fn new() -> Self {
        VirtualCursor {
            position: [0.0, 0.0],
            color: green(),
            size: 5.0
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("rebind-example", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .ok()
        .expect("Could not create main window");

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = {
        use piston::input::keyboard::Key;
        use piston::input::Button::Keyboard;
        RebindBuilder::new(WINDOW_SIZE)
            .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
            .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
            .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
            .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
            .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
            .build_translator()
    };

    let character = {
        const INITIAL_CHARACTER_POS: [f64; 2] = [WINDOW_SIZE.0 as f64 / 20.0,
                                                 WINDOW_SIZE.1 as f64 * 0.85];
        Character::new(red(), INITIAL_CHARACTER_POS, 50.0)
    };

    let ui = {
        let glyph_cache = {
            let font_path = find_folder::Search::ParentsThenKids(3, 3)
                .for_folder("assets")
                .ok()
                .expect("Could not find assets folder")
                .join("fonts/NotoSans/NotoSans-Regular.ttf");

            GlyphCache::new(&font_path)
                .ok()
                .expect("Could not find font file within assets folder")
        };

        Ui::new(glyph_cache, Theme::default())
    };

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        ui: Rc::new(RefCell::new(ui)),
        translator: translator,
        character: character,
        cursor: VirtualCursor::new(),
        bg_color: black()
    };

    for e in app.window.clone().events() {
        app.ui.borrow_mut().handle_event(&e);
        match e {
            Event::Input(i) => { app.input(&i); },
            Event::Update(u) => { app.update(&u); },
            Event::Render(r) => { app.render(&r); },
            _ => { }
        }
    }
}<commit_msg>Improve comments<commit_after>#[macro_use] extern crate conrod;
extern crate find_folder;
extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use conrod::{
    Background,
    Color,
    Colorable,
    Frameable,
    Labelable,
    Positionable,
    Sizeable,
    Theme,
    Toggle,
    Ui,
    Widget,
};
use conrod::color::{black, grayscale, green, red};
use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use opengl_graphics::glyph_cache::GlyphCache;
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;
type RcGraphics = Rc<RefCell<GlGraphics>>;
type RcUi = Rc<RefCell<Ui<GlyphCache<'static>>>>;

struct App {
    window: RcWindow,
    graphics: RcGraphics,
    ui: RcUi,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    cursor: VirtualCursor,
    bg_color: Color
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.cursor.position = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        // We need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(self.window.borrow().size());

        // Update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        // This demo translates and rebinds in a single screen, forcing a clone of the translator. This
        // limitation is a result of the necessity of providing a different interface for rebinding. One
        // way you could avoid a clone every update/render is to structure your application to pass the
        // translator between separate options and game screens.
        let mut rebind = self.translator.clone().into_rebind();

        let mut gl_graphics = self.graphics.borrow_mut();
        let ui = &mut *self.ui.borrow_mut();

        widget_ids! {
            X_INVERT_TOGGLE,
            Y_INVERT_TOGGLE
        }

        // Draw the ui
        gl_graphics.draw(args.viewport(), |c, gl| {
            Background::new().color(self.bg_color).set(ui);

            Toggle::new(rebind.get_x_motion_inverted())
                .xy(-350.0, 260.0)
                .dimensions(80.0, 40.0)
                .color(grayscale(0.4))
                .frame(1.0)
                .label(&"Invert X")
                .label_color(self.bg_color.plain_contrast())
                .label_font_size(16)
                .react(|b| rebind.set_x_motion_inverted(b))
                .set(X_INVERT_TOGGLE, ui);

            Toggle::new(rebind.get_y_motion_inverted())
                .xy(-350.0, 200.0)
                .dimensions(80.0, 40.0)
                .color(grayscale(0.4))
                .frame(1.0)
                .label(&"Invert Y")
                .label_color(self.bg_color.plain_contrast())
                .label_font_size(16)
                .react(|b| rebind.set_y_motion_inverted(b))
                .set(Y_INVERT_TOGGLE, ui);

            ui.draw(c, gl);
        });

        // Draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color.to_fsa(),
                                                                square,
                                                                c.transform,
                                                                gl));
        }

        // Draw the cursor dot
        {
            let dot = ellipse::circle(self.cursor.position[0],
                                      self.cursor.position[1],
                                      self.cursor.size);

            gl_graphics.draw(args.viewport(), |c, gl| ellipse(self.cursor.color.to_fsa(),
                                                              dot,
                                                              c.transform,
                                                              gl));
        }

        self.translator = rebind.into();
    }
}

struct Character {
    color: Color,
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: Color, tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

struct VirtualCursor {
    position: [f64; 2],
    color: Color,
    size: f64
}

impl VirtualCursor {
    fn new() -> Self {
        VirtualCursor {
            position: [0.0, 0.0],
            color: green(),
            size: 5.0
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("rebind-example", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .ok()
        .expect("Could not create main window");

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = {
        use piston::input::keyboard::Key;
        use piston::input::Button::Keyboard;
        RebindBuilder::new(WINDOW_SIZE)
            .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
            .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
            .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
            .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
            .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
            .build_translator()
    };

    let character = {
        const INITIAL_CHARACTER_POS: [f64; 2] = [WINDOW_SIZE.0 as f64 / 20.0,
                                                 WINDOW_SIZE.1 as f64 * 0.85];
        Character::new(red(), INITIAL_CHARACTER_POS, 50.0)
    };

    let ui = {
        let glyph_cache = {
            let font_path = find_folder::Search::ParentsThenKids(3, 3)
                .for_folder("assets")
                .ok()
                .expect("Could not find assets folder")
                .join("fonts/NotoSans/NotoSans-Regular.ttf");

            GlyphCache::new(&font_path)
                .ok()
                .expect("Could not find font file within assets folder")
        };

        Ui::new(glyph_cache, Theme::default())
    };

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        ui: Rc::new(RefCell::new(ui)),
        translator: translator,
        character: character,
        cursor: VirtualCursor::new(),
        bg_color: black()
    };

    for e in app.window.clone().events() {
        app.ui.borrow_mut().handle_event(&e);
        match e {
            Event::Input(i) => { app.input(&i); },
            Event::Update(u) => { app.update(&u); },
            Event::Render(r) => { app.render(&r); },
            _ => { }
        }
    }
}<|endoftext|>


<commit_before>//! 非同期タスクを管理する。
use crate::sysmod::SystemModules;
use anyhow::Result;
use chrono::prelude::*;
use log::{error, info, trace};
use std::future::Future;
use std::sync::Arc;
use tokio::signal::unix::{signal, SignalKind};
use tokio::sync::mpsc;

type ShutdownTx = mpsc::UnboundedSender<()>;
type ShutdownRx = mpsc::UnboundedReceiver<()>;
/// タスク完了通知側 (複数)
type CompleteSyncChild = mpsc::Sender<()>;
/// タスク完了待ち側 (単数)
type CompleteSyncParent = mpsc::Receiver<()>;

/// [Arc] により [TaskServer] と各非同期タスク間で共有されるデータ。
struct InternalControl {
    rt: tokio::runtime::Runtime,
    sysmods: SystemModules,
    shutdown_tx: ShutdownTx,
    shutdown_rx: ShutdownRx,
}

/// 各非同期タスクに clone され渡されるコントロールハンドル。
///
/// 各フィールドは private で、メソッドで機能を提供する。
///
/// [Clone::clone] 可能で、複製すると [Arc] がインクリメントされる。
/// また、全タスク完了待ちのための channel も clone される。
#[derive(Clone)]
pub struct Control {
    /// private で [InternalControl] への [Arc] を持つ。
    internal: Arc<InternalControl>,
    /// サーバ側は clone されたこれがすべて drop されるまで待機する。
    complete_sync: Option<CompleteSyncChild>,
}

pub enum RunResult {
    Shutdown,
    Reboot,
}

/// タスクサーバ本体。
pub struct TaskServer {
    /// 各タスクに clone して渡すオリジナルの [Control]。
    ctrl: Control,
    /// [Control::complete_sync] の対向。
    ///
    /// clone されたすべての [Control::complete_sync] が drop するまで待機する。
    /// <https://tokio.rs/tokio/topics/shutdown>
    complete_sync_wait: CompleteSyncParent,
}

impl Control {
    /// 1回限りのタスクを生成して実行開始する。
    ///
    /// F: [Control] を引数に、T を返す関数。
    /// T: Future<Output = anyhow::Result<()> かつスレッド間移動可能。
    ///
    /// つまり、F は [Control] を引数に、anyhow::Result<()> を返す async function。
    pub fn spawn_oneshot_task<F, T>(&self, name: &str, f: F)
    where
        F: FnOnce(Control) -> T,
        T: Future<Output = Result<()>> + Send + 'static,
    {
        // move するデータを準備する
        let name = name.to_string();
        let ctrl = self.clone();
        let future = f(ctrl);

        self.internal.rt.spawn(async move {
            info!("[{}] start (one-shot)", name);

            let result = future.await;

            if let Err(e) = result {
                error!("[{}] finish (error): {:?}", name, e);
            } else {
                info!("[{}] finish (success)", name);
            }
        });
    }

    /// time_list
    pub fn spawn_periodic_task<F, T>(&self, name: &str, wakeup_list: &[NaiveTime], f: F)
    where
        F: Fn(Control) -> T + Send + 'static,
        T: Future<Output = Result<()>> + Send + 'static,
    {
        // move するデータを準備する
        let name = name.to_string();
        let ctrl = self.clone();

        // 空でなくソート済み、秒以下がゼロなのを確認後
        // 今日のその時刻からなる Local DateTime に変換する
        // TODO: is_sorted() がまだ unstable
        assert!(!wakeup_list.is_empty(), "time list is empty");
        let sorted = wakeup_list.windows(2).all(|t| t[0] <= t[1]);
        assert!(sorted, "time list is not sorted");
        let today = Local::today();
        let mut dt_list: Vec<_> = wakeup_list
            .iter()
            .map(|time| {
                assert_eq!(time.second(), 0);
                assert_eq!(time.nanosecond(), 0);
                today.and_time(*time).unwrap()
            })
            .collect();

        // wakeup time list を最初の LOG_LIMIT 個までログに出力する
        const LOG_LIMIT: usize = 5;
        let log_iter = wakeup_list.iter().take(LOG_LIMIT);
        let mut str = log_iter.enumerate().fold(String::new(), |sum, (i, v)| {
            let str = if i == 0 {
                format!("{}", v)
            } else {
                format!(", {}", v)
            };
            sum + &str
        });
        if wakeup_list.len() > LOG_LIMIT {
            str += &format!(", ... ({} items)", wakeup_list.len());
        }
        info!("[{}] registered as a periodic task", name);
        info!("[{}] wakeup time: {}", name, str);

        self.internal.rt.spawn(async move {
            type CDuration = chrono::Duration;
            type TDuration = tokio::time::Duration;

            loop {
                // 現在時刻を取得して分までに切り捨てる
                let now = Local::now();
                let now_hmd = now.date().and_hms(now.hour(), now.minute(), 0);
                let next_min = now_hmd + CDuration::minutes(1);
                trace!("[{}] periodic task check: {}", name, now_hmd);

                // 起動時刻リスト内で二分探索
                match dt_list.binary_search(&now_hmd) {
                    Ok(ind) => {
                        // 一致するものを発見したので続行
                        trace!("[{}] hit in time list: {}", name, now_hmd);
                    }
                    Err(ind) => {
                        // ind = insertion point
                        trace!("[{}] not found in time list: {}", name, now_hmd);
                        // 起きるべき時刻は dt_list[ind]
                        if ind < dt_list.len() {
                            let target_dt = dt_list[ind] + CDuration::seconds(1);
                            let sleep_duration = target_dt - Local::now();
                            let sleep_sec = sleep_duration.num_seconds().clamp(0, i64::MAX) as u64;
                            trace!("[{}] target: {}, sleep_sec: {}", name, target_dt, sleep_sec);
                            tokio::time::sleep(TDuration::from_secs(sleep_sec)).await;
                            trace!("[{}] wake up", name);
                        } else {
                            // 一番後ろよりも現在時刻が後
                            // 起動時刻リストをすべて1日ずつ後ろにずらす
                            for dt in dt_list.iter_mut() {
                                let tomorrow = dt.date() + CDuration::days(1);
                                let time = dt.time();
                                *dt = tomorrow.and_time(time).unwrap();
                                trace!("[{}] advance time list by 1 day", name);
                            }
                        }
                        continue;
                    }
                }

                let future = f(ctrl.clone());
                info!("[{}] start (periodic)", name);
                let result = future.await;
                if let Err(e) = result {
                    error!("[{}] finish (error): {:?}", name, e);
                } else {
                    info!("[{}] finish (success)", name);
                }

                // 次の "分" を狙って sleep する
                // 目標は安全のため hh:mm:05 を狙う
                let target_dt = next_min + CDuration::seconds(5);
                // タスクの実行に1分以上かかると負になるが、
                // chrono::Duration は負数を許している
                // その場合は 0 に補正する
                let sleep_duration = target_dt - Local::now();
                let sleep_sec = sleep_duration.num_seconds().clamp(0, i64::MAX) as u64;
                trace!("[{}] target: {}, sleep_sec: {}", name, target_dt, sleep_sec);
                tokio::time::sleep(tokio::time::Duration::from_secs(sleep_sec)).await;
                trace!("[{}] wake up", name);
            }
        });
    }

    /// [crate::sysmod::SystemModule] リストを取得する。
    pub fn sysmods(&self) -> &SystemModules {
        &self.internal.sysmods
    }
}

impl TaskServer {
    /// タスクサーバを初期化して開始する。
    pub fn new(sysmods: SystemModules) -> Self {
        let rt = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap();

        let (shutdown_tx, shutdown_rx) = mpsc::unbounded_channel();
        let (complete_sync, complete_sync_wait) = mpsc::channel(1);

        let internal = InternalControl {
            rt,
            sysmods,
            shutdown_tx,
            shutdown_rx,
        };
        let ctrl = Control {
            internal: Arc::new(internal),
            complete_sync: Some(complete_sync),
        };

        TaskServer {
            ctrl,
            complete_sync_wait,
        }
    }

    pub fn spawn_oneshot_task<F, T>(&self, name: &str, f: F)
    where
        F: FnOnce(Control) -> T,
        T: Future<Output = Result<()>> + Send + 'static,
    {
        self.ctrl.spawn_oneshot_task(name, f);
    }

    /// 実行を開始し、完了するまでブロックする。
    ///
    /// self の所有権は consume する。一度しか実行できない。
    pub fn run(mut self) -> RunResult {
        // async block へ move するためのコピーを作る
        let ctrl = self.ctrl.clone();
        // オリジナルの complete_sync を self から奪って drop しておく
        drop(self.ctrl.complete_sync.take().unwrap());

        self.ctrl.internal.rt.block_on(async move {
            // SystemModule 全体に on_start イベントを配送
            ctrl.internal.sysmods.on_start(&ctrl).await;

            // この async block をシグナル処理に使う
            let mut sigint = signal(SignalKind::interrupt()).unwrap();
            let mut sigterm = signal(SignalKind::terminate()).unwrap();
            let mut sighup = signal(SignalKind::hangup()).unwrap();

            let run_result;
            tokio::select! {
                _ = sigint.recv() => {
                    info!("[signal] SIGINT");
                    run_result = RunResult::Shutdown;
                },
                _ = sigterm.recv() => {
                    info!("[signal] SIGTERM");
                    run_result = RunResult::Shutdown;
                },
                _ = sighup.recv() => {
                    info!("[signal] SIGHUP");
                    run_result = RunResult::Reboot;
                },
            }

            // 全タスク完了待ち
            // この async block で持っている分の complete_sync を
            // ctrl ごと drop する
            // オリジナルの self.ctrl.complete_sync も drop 済みなので
            // 残りは各 async task に clone された Control
            drop(ctrl);
            // 全 complete_sync (Sender) が drop されるまで待つ
            // その時 None を返す
            // それ以外の用法はしないので、データは送信されてこない
            info!("waiting for all tasks to be completed....");
            let sync_result = self.complete_sync_wait.recv().await;
            assert!(sync_result.is_none());
            info!("OK: all tasks are completed");

            run_result
        })
    }
}
<commit_msg>ドキュメントのリファイン<commit_after>//! 非同期タスクを管理する。

use crate::sysmod::SystemModules;
use anyhow::Result;
use chrono::prelude::*;
use log::{error, info, trace};
use std::future::Future;
use std::sync::Arc;
use tokio::signal::unix::{signal, SignalKind};
use tokio::sync::mpsc;

type ShutdownTx = mpsc::UnboundedSender<()>;
type ShutdownRx = mpsc::UnboundedReceiver<()>;
/// タスク完了通知側 (複数)
type CompleteSyncChild = mpsc::Sender<()>;
/// タスク完了待ち側 (単数)
type CompleteSyncParent = mpsc::Receiver<()>;

/// [TaskServer] と各非同期タスク間で共有されるデータ。
///
/// インスタンスは1つだけ生成され、[Arc] により共有される。
struct InternalControl {
    /// Tokio ランタイム。
    rt: tokio::runtime::Runtime,
    /// 全システムモジュールのリスト。
    sysmods: SystemModules,
    shutdown_tx: ShutdownTx,
    shutdown_rx: ShutdownRx,
}

/// 各非同期タスクに clone され渡されるコントロールハンドル。
///
/// 各フィールドは private で、メソッドで機能を提供する。
/// [Clone::clone] 可能で、各タスクごとに複製したものが渡される。
#[derive(Clone)]
pub struct Control {
    /// private で [InternalControl] への [Arc] を持つ。
    internal: Arc<InternalControl>,
    /// システムシャットダウン時、全タスクの完了待ちのため、
    /// サーバ側は clone されたこれがすべて drop されるまで待機する。
    complete_sync: Option<CompleteSyncChild>,
}

/// [TaskServer::run] の返す実行終了種別。
pub enum RunResult {
    Shutdown,
    Reboot,
}

/// タスクサーバ本体。
pub struct TaskServer {
    /// 各タスクに clone して渡すオリジナルの [Control]。
    ctrl: Control,
    /// [Control::complete_sync] (Sender) の対向 (Receiver)。
    ///
    /// clone されたすべての [Control::complete_sync] が drop するまで待機する。
    /// <https://tokio.rs/tokio/topics/shutdown>
    complete_sync_wait: CompleteSyncParent,
}

impl Control {
    /// 1回限りのタスクを生成して実行開始する。
    ///
    /// F: [Control] を引数に、T を返す関数。
    /// T: Future<Output = anyhow::Result<()> かつスレッド間移動可能。
    ///
    /// つまり、F は [Control] を引数に、anyhow::Result<()> を返す async function。
    pub fn spawn_oneshot_task<F, T>(&self, name: &str, f: F)
    where
        F: FnOnce(Control) -> T,
        T: Future<Output = Result<()>> + Send + 'static,
    {
        // move するデータを準備する
        let name = name.to_string();
        let ctrl = self.clone();
        let future = f(ctrl);

        self.internal.rt.spawn(async move {
            info!("[{}] start (one-shot)", name);

            let result = future.await;

            if let Err(e) = result {
                error!("[{}] finish (error): {:?}", name, e);
            } else {
                info!("[{}] finish (success)", name);
            }
        });
    }

    /// 周期タスクを生成する。
    ///
    /// wakeup_list: 起動時刻。以下を満たさないと panic する。
    /// * second 以下が 0 である。(分単位)
    /// * 昇順ソート済みである。
    ///
    /// F: [Control] を引数に、T を返す関数。
    /// T: Future<Output = anyhow::Result<()> かつスレッド間移動可能。
    ///
    /// つまり、F は [Control] を引数に、anyhow::Result<()> を返す async function。
    pub fn spawn_periodic_task<F, T>(&self, name: &str, wakeup_list: &[NaiveTime], f: F)
    where
        F: Fn(Control) -> T + Send + 'static,
        T: Future<Output = Result<()>> + Send + 'static,
    {
        // move するデータを準備する
        let name = name.to_string();
        let ctrl = self.clone();

        // 空でなくソート済み、秒以下がゼロなのを確認後
        // 今日のその時刻からなる Local DateTime に変換する
        // TODO: is_sorted() がまだ unstable
        assert!(!wakeup_list.is_empty(), "time list is empty");
        let sorted = wakeup_list.windows(2).all(|t| t[0] <= t[1]);
        assert!(sorted, "time list is not sorted");
        let today = Local::today();
        let mut dt_list: Vec<_> = wakeup_list
            .iter()
            .map(|time| {
                assert_eq!(time.second(), 0);
                assert_eq!(time.nanosecond(), 0);
                today.and_time(*time).unwrap()
            })
            .collect();

        // wakeup time list を最初の LOG_LIMIT 個までログに出力する
        const LOG_LIMIT: usize = 5;
        let log_iter = wakeup_list.iter().take(LOG_LIMIT);
        let mut str = log_iter.enumerate().fold(String::new(), |sum, (i, v)| {
            let str = if i == 0 {
                format!("{}", v)
            } else {
                format!(", {}", v)
            };
            sum + &str
        });
        if wakeup_list.len() > LOG_LIMIT {
            str += &format!(", ... ({} items)", wakeup_list.len());
        }
        info!("[{}] registered as a periodic task", name);
        info!("[{}] wakeup time: {}", name, str);

        self.internal.rt.spawn(async move {
            type CDuration = chrono::Duration;
            type TDuration = tokio::time::Duration;

            loop {
                // 現在時刻を取得して分までに切り捨てる
                let now = Local::now();
                let now_hmd = now.date().and_hms(now.hour(), now.minute(), 0);
                let next_min = now_hmd + CDuration::minutes(1);
                trace!("[{}] periodic task check: {}", name, now_hmd);

                // 起動時刻リスト内で二分探索
                match dt_list.binary_search(&now_hmd) {
                    Ok(ind) => {
                        // 一致するものを発見したので続行
                        trace!("[{}] hit in time list: {}", name, now_hmd);
                    }
                    Err(ind) => {
                        // ind = insertion point
                        trace!("[{}] not found in time list: {}", name, now_hmd);
                        // 起きるべき時刻は dt_list[ind]
                        if ind < dt_list.len() {
                            let target_dt = dt_list[ind] + CDuration::seconds(1);
                            let sleep_duration = target_dt - Local::now();
                            let sleep_sec = sleep_duration.num_seconds().clamp(0, i64::MAX) as u64;
                            trace!("[{}] target: {}, sleep_sec: {}", name, target_dt, sleep_sec);
                            tokio::time::sleep(TDuration::from_secs(sleep_sec)).await;
                            trace!("[{}] wake up", name);
                        } else {
                            // 一番後ろよりも現在時刻が後
                            // 起動時刻リストをすべて1日ずつ後ろにずらす
                            for dt in dt_list.iter_mut() {
                                let tomorrow = dt.date() + CDuration::days(1);
                                let time = dt.time();
                                *dt = tomorrow.and_time(time).unwrap();
                                trace!("[{}] advance time list by 1 day", name);
                            }
                        }
                        continue;
                    }
                }

                let future = f(ctrl.clone());
                info!("[{}] start (periodic)", name);
                let result = future.await;
                if let Err(e) = result {
                    error!("[{}] finish (error): {:?}", name, e);
                } else {
                    info!("[{}] finish (success)", name);
                }

                // 次の "分" を狙って sleep する
                // 目標は安全のため hh:mm:05 を狙う
                let target_dt = next_min + CDuration::seconds(5);
                // タスクの実行に1分以上かかると負になるが、
                // chrono::Duration は負数を許している
                // その場合は 0 に補正する
                let sleep_duration = target_dt - Local::now();
                let sleep_sec = sleep_duration.num_seconds().clamp(0, i64::MAX) as u64;
                trace!("[{}] target: {}, sleep_sec: {}", name, target_dt, sleep_sec);
                tokio::time::sleep(tokio::time::Duration::from_secs(sleep_sec)).await;
                trace!("[{}] wake up", name);
            }
        });
    }

    /// [crate::sysmod::SystemModule] リストへの参照を取得する。
    pub fn sysmods(&self) -> &SystemModules {
        &self.internal.sysmods
    }
}

impl TaskServer {
    /// タスクサーバを生成して初期化する。
    pub fn new(sysmods: SystemModules) -> Self {
        let rt = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap();

        let (shutdown_tx, shutdown_rx) = mpsc::unbounded_channel();
        let (complete_sync, complete_sync_wait) = mpsc::channel(1);

        let internal = InternalControl {
            rt,
            sysmods,
            shutdown_tx,
            shutdown_rx,
        };
        let ctrl = Control {
            internal: Arc::new(internal),
            complete_sync: Some(complete_sync),
        };

        TaskServer {
            ctrl,
            complete_sync_wait,
        }
    }

    /// [Control::spawn_oneshot_task] と同じ。
    pub fn spawn_oneshot_task<F, T>(&self, name: &str, f: F)
    where
        F: FnOnce(Control) -> T,
        T: Future<Output = Result<()>> + Send + 'static,
    {
        self.ctrl.spawn_oneshot_task(name, f);
    }

    /// 実行を開始し、完了するまでブロックする。
    ///
    /// self の所有権はを consume するため、一度しか実行できない。
    pub fn run(mut self) -> RunResult {
        // async block へ move するためのコピーを作る
        let ctrl = self.ctrl.clone();
        // オリジナルの complete_sync を self から奪って drop しておく
        drop(self.ctrl.complete_sync.take().unwrap());

        self.ctrl.internal.rt.block_on(async move {
            // SystemModule 全体に on_start イベントを配送
            ctrl.internal.sysmods.on_start(&ctrl).await;

            // この async block をシグナル処理に使う
            let mut sigint = signal(SignalKind::interrupt()).unwrap();
            let mut sigterm = signal(SignalKind::terminate()).unwrap();
            let mut sighup = signal(SignalKind::hangup()).unwrap();

            let run_result;
            tokio::select! {
                _ = sigint.recv() => {
                    info!("[signal] SIGINT");
                    run_result = RunResult::Shutdown;
                },
                _ = sigterm.recv() => {
                    info!("[signal] SIGTERM");
                    run_result = RunResult::Shutdown;
                },
                _ = sighup.recv() => {
                    info!("[signal] SIGHUP");
                    run_result = RunResult::Reboot;
                },
            }

            // 全タスク完了待ち
            // この async block で持っている分の complete_sync を
            // ctrl ごと drop する
            // オリジナルの self.ctrl.complete_sync も drop 済みなので
            // 残りは各 async task に clone された Control
            drop(ctrl);
            // 全 complete_sync (Sender) が drop されるまで待つ
            // その時 None を返す
            // それ以外の用法はしないので、データは送信されてこない
            info!("waiting for all tasks to be completed....");
            let sync_result = self.complete_sync_wait.recv().await;
            assert!(sync_result.is_none());
            info!("OK: all tasks are completed");

            run_result
        })
    }
}
<|endoftext|>