<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::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods};
use dom::bindings::conversions::Castable;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::element::{Element, StylePriority};
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
use dom::window::Window;
use selectors::parser::PseudoElement;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Ref;
use string_cache::Atom;
use style::properties::PropertyDeclaration;
use style::properties::{is_supported_property, longhands_from_shorthand, parse_one_declaration};
use util::str::{DOMString, str_join};

// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
pub struct CSSStyleDeclaration {
    reflector_: Reflector,
    owner: JS<Element>,
    readonly: bool,
    pseudo: Option<PseudoElement>,
}

#[derive(PartialEq, HeapSizeOf)]
pub enum CSSModificationAccess {
    ReadWrite,
    Readonly
}

macro_rules! css_properties(
    ( $([$getter:ident, $setter:ident, $cssprop:expr]),* ) => (
        $(
            fn $getter(&self) -> DOMString {
                self.GetPropertyValue($cssprop.to_owned())
            }
            fn $setter(&self, value: DOMString) -> ErrorResult {
                self.SetPropertyValue($cssprop.to_owned(), value)
            }
        )*
    );
);

fn serialize_list(list: &[Ref<PropertyDeclaration>]) -> DOMString {
    let str_iter = list.iter().map(|d| d.value());
    str_join(str_iter, " ")
}

impl CSSStyleDeclaration {
    pub fn new_inherited(owner: &Element,
                         pseudo: Option<PseudoElement>,
                         modification_access: CSSModificationAccess) -> CSSStyleDeclaration {
        CSSStyleDeclaration {
            reflector_: Reflector::new(),
            owner: JS::from_ref(owner),
            pseudo: pseudo,
            readonly: modification_access == CSSModificationAccess::Readonly,
        }
    }

    pub fn new(global: &Window, owner: &Element,
               pseudo: Option<PseudoElement>,
               modification_access: CSSModificationAccess) -> Root<CSSStyleDeclaration> {
        reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner, pseudo, modification_access),
                           GlobalRef::Window(global),
                           CSSStyleDeclarationBinding::Wrap)
    }

    fn get_computed_style(&self, property: &Atom) -> Option<DOMString> {
        let owner = self.owner.root();
        let node = owner.upcast::<Node>();
        if !node.is_in_doc() {
            // TODO: Node should be matched against the style rules of this window.
            // Firefox is currently the only browser to implement this.
            return None;
        }
        let addr = node.to_trusted_node_address();
        window_from_node(owner.r()).resolved_style_query(addr, self.pseudo.clone(), property)
    }
}

impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
    fn Length(&self) -> u32 {
        let owner = self.owner.root();
        let elem = owner.upcast::<Element>();
        let len = match *elem.style_attribute().borrow() {
            Some(ref declarations) => declarations.normal.len() + declarations.important.len(),
            None => 0
        };
        len as u32
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item
    fn Item(&self, index: u32) -> DOMString {
        let index = index as usize;
        let owner = self.owner.root();
        let elem = owner.upcast::<Element>();
        let style_attribute = elem.style_attribute().borrow();
        let result = style_attribute.as_ref().and_then(|declarations| {
            if index > declarations.normal.len() {
                declarations.important
                            .get(index - declarations.normal.len())
                            .map(|decl| format!("{:?} !important", decl))
            } else {
                declarations.normal
                            .get(index)
                            .map(|decl| format!("{:?}", decl))
            }
        });

        result.unwrap_or("".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
    fn GetPropertyValue(&self, mut property: DOMString) -> DOMString {
        let owner = self.owner.root();

        // Step 1
        property.make_ascii_lowercase();
        let property = Atom::from_slice(&property);

        if self.readonly {
            // Readonly style declarations are used for getComputedStyle.
            return self.get_computed_style(&property).unwrap_or("".to_owned());
        }

        // Step 2
        let longhand_properties = longhands_from_shorthand(&property);
        if let Some(longhand_properties) = longhand_properties {
            // Step 2.1
            let mut list = vec!();

            // Step 2.2
            for longhand in &*longhand_properties {
                // Step 2.2.1
                let declaration = owner.get_inline_style_declaration(&Atom::from_slice(&longhand));

                // Step 2.2.2 & 2.2.3
                match declaration {
                    Some(declaration) => list.push(declaration),
                    None => return "".to_owned(),
                }
            }

            // Step 2.3
            return serialize_list(&list);
        }

        // Step 3 & 4
        // FIXME: redundant let binding https://github.com/rust-lang/rust/issues/22252
        let result = match owner.get_inline_style_declaration(&property) {
            Some(declaration) => declaration.value(),
            None => "".to_owned(),
        };
        result
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
    fn GetPropertyPriority(&self, mut property: DOMString) -> DOMString {
        // Step 1
        property.make_ascii_lowercase();
        let property = Atom::from_slice(&property);

        // Step 2
        let longhand_properties = longhands_from_shorthand(&property);
        if let Some(longhand_properties) = longhand_properties {
            // Step 2.1 & 2.2 & 2.3
            if longhand_properties.iter()
                                  .map(|&longhand| self.GetPropertyPriority(longhand.to_owned()))
                                  .all(|priority| priority == "important") {

                return "important".to_owned();
            }
        // Step 3
        } else {
            // FIXME: extra let binding https://github.com/rust-lang/rust/issues/22323
            let owner = self.owner.root();
            if owner.get_important_inline_style_declaration(&property).is_some() {
                return "important".to_owned();
            }
        }

        // Step 4
        "".to_owned()
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
    fn SetProperty(&self, mut property: DOMString, value: DOMString,
                   priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2
        property.make_ascii_lowercase();

        // Step 3
        if !is_supported_property(&property) {
            return Ok(());
        }

        // Step 4
        if value.is_empty() {
            return self.RemoveProperty(property).map(|_| ());
        }

        // Step 5
        let priority = match &*priority {
            "" => StylePriority::Normal,
            p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
            _ => return Ok(()),
        };

        // Step 6
        let owner = self.owner.root();
        let window = window_from_node(owner.r());
        let declarations = parse_one_declaration(&property, &value, &window.r().get_url());

        // Step 7
        let declarations = if let Ok(declarations) = declarations {
            declarations
        } else {
            return Ok(());
        };

        let owner = self.owner.root();
        let element = owner.upcast::<Element>();

        // Step 8
        for decl in declarations {
            // Step 9
            element.update_inline_style(decl, priority);
        }

        let document = document_from_node(element);
        let node = element.upcast();
        document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
    fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2 & 3
        if !is_supported_property(&property) {
            return Ok(());
        }

        // Step 4
        let priority = match &*priority {
            "" => StylePriority::Normal,
            p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
            _ => return Ok(()),
        };

        let owner = self.owner.root();
        let element = owner.upcast::<Element>();

        // Step 5 & 6
        match longhands_from_shorthand(&property) {
            Some(properties) => element.set_inline_style_property_priority(properties, priority),
            None => element.set_inline_style_property_priority(&[&*property], priority)
        }

        let document = document_from_node(element);
        let node = element.upcast();
        document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
    fn SetPropertyValue(&self, property: DOMString, value: DOMString) -> ErrorResult {
        self.SetProperty(property, value, "".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
    fn RemoveProperty(&self, mut property: DOMString) -> Fallible<DOMString> {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2
        property.make_ascii_lowercase();

        // Step 3
        let value = self.GetPropertyValue(property.clone());

        let owner = self.owner.root();
        let elem = owner.upcast::<Element>();

        match longhands_from_shorthand(&property) {
            // Step 4
            Some(longhands) => {
                for longhand in &*longhands {
                    elem.remove_inline_style_property(longhand)
                }
            }
            // Step 5
            None => elem.remove_inline_style_property(&property)
        }

        // Step 6
        Ok(value)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn CssFloat(&self) -> DOMString {
        self.GetPropertyValue("float".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn SetCssFloat(&self, value: DOMString) -> ErrorResult {
        self.SetPropertyValue("float".to_owned(), value)
    }

    // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
    fn IndexedGetter(&self, index: u32, found: &mut bool) -> DOMString {
        let rval = self.Item(index);
        *found = index < self.Length();
        rval
    }

    // https://drafts.csswg.org/cssom/#cssstyledeclaration
    css_properties_accessors!(css_properties);
}
<commit_msg>Auto merge of #8137 - romankl:gh/8126, r=nox<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::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods};
use dom::bindings::conversions::Castable;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::element::{Element, StylePriority};
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
use dom::window::Window;
use selectors::parser::PseudoElement;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Ref;
use string_cache::Atom;
use style::properties::PropertyDeclaration;
use style::properties::{is_supported_property, longhands_from_shorthand, parse_one_declaration};
use util::str::{DOMString, str_join};

// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
pub struct CSSStyleDeclaration {
    reflector_: Reflector,
    owner: JS<Element>,
    readonly: bool,
    pseudo: Option<PseudoElement>,
}

#[derive(PartialEq, HeapSizeOf)]
pub enum CSSModificationAccess {
    ReadWrite,
    Readonly
}

macro_rules! css_properties(
    ( $([$getter:ident, $setter:ident, $cssprop:expr]),* ) => (
        $(
            fn $getter(&self) -> DOMString {
                self.GetPropertyValue($cssprop.to_owned())
            }
            fn $setter(&self, value: DOMString) -> ErrorResult {
                self.SetPropertyValue($cssprop.to_owned(), value)
            }
        )*
    );
);

fn serialize_list(list: &[Ref<PropertyDeclaration>]) -> DOMString {
    let str_iter = list.iter().map(|d| d.value());
    str_join(str_iter, " ")
}

impl CSSStyleDeclaration {
    pub fn new_inherited(owner: &Element,
                         pseudo: Option<PseudoElement>,
                         modification_access: CSSModificationAccess) -> CSSStyleDeclaration {
        CSSStyleDeclaration {
            reflector_: Reflector::new(),
            owner: JS::from_ref(owner),
            pseudo: pseudo,
            readonly: modification_access == CSSModificationAccess::Readonly,
        }
    }

    pub fn new(global: &Window, owner: &Element,
               pseudo: Option<PseudoElement>,
               modification_access: CSSModificationAccess) -> Root<CSSStyleDeclaration> {
        reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner, pseudo, modification_access),
                           GlobalRef::Window(global),
                           CSSStyleDeclarationBinding::Wrap)
    }

    fn get_computed_style(&self, property: &Atom) -> Option<DOMString> {
        let node = self.owner.upcast::<Node>();
        if !node.is_in_doc() {
            // TODO: Node should be matched against the style rules of this window.
            // Firefox is currently the only browser to implement this.
            return None;
        }
        let addr = node.to_trusted_node_address();
        window_from_node(&*self.owner).resolved_style_query(addr, self.pseudo.clone(), property)
    }
}

impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
    fn Length(&self) -> u32 {
        let elem = self.owner.upcast::<Element>();
        let len = match *elem.style_attribute().borrow() {
            Some(ref declarations) => declarations.normal.len() + declarations.important.len(),
            None => 0
        };
        len as u32
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item
    fn Item(&self, index: u32) -> DOMString {
        let index = index as usize;
        let elem = self.owner.upcast::<Element>();
        let style_attribute = elem.style_attribute().borrow();
        let result = style_attribute.as_ref().and_then(|declarations| {
            if index > declarations.normal.len() {
                declarations.important
                            .get(index - declarations.normal.len())
                            .map(|decl| format!("{:?} !important", decl))
            } else {
                declarations.normal
                            .get(index)
                            .map(|decl| format!("{:?}", decl))
            }
        });

        result.unwrap_or("".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
    fn GetPropertyValue(&self, mut property: DOMString) -> DOMString {
        let owner = &self.owner;

        // Step 1
        property.make_ascii_lowercase();
        let property = Atom::from_slice(&property);

        if self.readonly {
            // Readonly style declarations are used for getComputedStyle.
            return self.get_computed_style(&property).unwrap_or("".to_owned());
        }

        // Step 2
        let longhand_properties = longhands_from_shorthand(&property);
        if let Some(longhand_properties) = longhand_properties {
            // Step 2.1
            let mut list = vec!();

            // Step 2.2
            for longhand in &*longhand_properties {
                // Step 2.2.1
                let declaration = owner.get_inline_style_declaration(&Atom::from_slice(&longhand));

                // Step 2.2.2 & 2.2.3
                match declaration {
                    Some(declaration) => list.push(declaration),
                    None => return "".to_owned(),
                }
            }

            // Step 2.3
            return serialize_list(&list);
        }

        // Step 3 & 4
        let result = match owner.get_inline_style_declaration(&property) {
            Some(declaration) => declaration.value(),
            None => "".to_owned(),
        };
        result
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
    fn GetPropertyPriority(&self, mut property: DOMString) -> DOMString {
        // Step 1
        property.make_ascii_lowercase();
        let property = Atom::from_slice(&property);

        // Step 2
        let longhand_properties = longhands_from_shorthand(&property);
        if let Some(longhand_properties) = longhand_properties {
            // Step 2.1 & 2.2 & 2.3
            if longhand_properties.iter()
                                  .map(|&longhand| self.GetPropertyPriority(longhand.to_owned()))
                                  .all(|priority| priority == "important") {

                return "important".to_owned();
            }
        // Step 3
        } else {
            if self.owner.get_important_inline_style_declaration(&property).is_some() {
                return "important".to_owned();
            }
        }

        // Step 4
        "".to_owned()
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
    fn SetProperty(&self, mut property: DOMString, value: DOMString,
                   priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2
        property.make_ascii_lowercase();

        // Step 3
        if !is_supported_property(&property) {
            return Ok(());
        }

        // Step 4
        if value.is_empty() {
            return self.RemoveProperty(property).map(|_| ());
        }

        // Step 5
        let priority = match &*priority {
            "" => StylePriority::Normal,
            p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
            _ => return Ok(()),
        };

        // Step 6
        let window = window_from_node(&*self.owner);
        let declarations = parse_one_declaration(&property, &value, &window.r().get_url());

        // Step 7
        let declarations = if let Ok(declarations) = declarations {
            declarations
        } else {
            return Ok(());
        };

        let element = self.owner.upcast::<Element>();

        // Step 8
        for decl in declarations {
            // Step 9
            element.update_inline_style(decl, priority);
        }

        let document = document_from_node(element);
        let node = element.upcast();
        document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
    fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2 & 3
        if !is_supported_property(&property) {
            return Ok(());
        }

        // Step 4
        let priority = match &*priority {
            "" => StylePriority::Normal,
            p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
            _ => return Ok(()),
        };

        let element = self.owner.upcast::<Element>();

        // Step 5 & 6
        match longhands_from_shorthand(&property) {
            Some(properties) => element.set_inline_style_property_priority(properties, priority),
            None => element.set_inline_style_property_priority(&[&*property], priority)
        }

        let document = document_from_node(element);
        let node = element.upcast();
        document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
    fn SetPropertyValue(&self, property: DOMString, value: DOMString) -> ErrorResult {
        self.SetProperty(property, value, "".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
    fn RemoveProperty(&self, mut property: DOMString) -> Fallible<DOMString> {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2
        property.make_ascii_lowercase();

        // Step 3
        let value = self.GetPropertyValue(property.clone());

        let elem = self.owner.upcast::<Element>();

        match longhands_from_shorthand(&property) {
            // Step 4
            Some(longhands) => {
                for longhand in &*longhands {
                    elem.remove_inline_style_property(longhand)
                }
            }
            // Step 5
            None => elem.remove_inline_style_property(&property)
        }

        // Step 6
        Ok(value)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn CssFloat(&self) -> DOMString {
        self.GetPropertyValue("float".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn SetCssFloat(&self, value: DOMString) -> ErrorResult {
        self.SetPropertyValue("float".to_owned(), value)
    }

    // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
    fn IndexedGetter(&self, index: u32, found: &mut bool) -> DOMString {
        let rval = self.Item(index);
        *found = index < self.Length();
        rval
    }

    // https://drafts.csswg.org/cssom/#cssstyledeclaration
    css_properties_accessors!(css_properties);
}
<|endoftext|>


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

use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{NodeBase, NodeCast, TextCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLScriptElementCast};
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, Root};
use dom::bindings::utils::Reflectable;
use dom::document::{Document, DocumentHelpers};
use dom::element::AttributeHandlers;
use dom::htmlelement::HTMLElement;
use dom::htmlheadingelement::{Heading1, Heading2, Heading3, Heading4, Heading5, Heading6};
use dom::htmlformelement::HTMLFormElement;
use dom::htmlscriptelement::HTMLScriptElementHelpers;
use dom::node::NodeHelpers;
use dom::types::*;
use page::Page;

use encoding::all::UTF_8;
use encoding::types::{Encoding, DecodeReplace};

use hubbub::hubbub;
use hubbub::hubbub::{NullNs, HtmlNs, MathMlNs, SvgNs, XLinkNs, XmlNs, XmlNsNs};
use servo_net::resource_task::{Load, LoadData, Payload, Done, ResourceTask, load_whole_resource};
use servo_msg::constellation_msg::LoadData as MsgLoadData;
use servo_util::str::DOMString;
use servo_util::task::spawn_named;
use std::ascii::StrAsciiExt;
use std::mem;
use std::cell::RefCell;
use std::comm::{channel, Sender, Receiver};
use url::{Url, UrlParser};
use http::headers::HeaderEnum;
use time;
use string_cache::{Atom, Namespace};

macro_rules! handle_element(
    ($document: expr,
     $localName: expr,
     $prefix: expr,
     $string: expr,
     $ctor: ident
     $(, $arg:expr )*) => (
        if $string == $localName.as_slice() {
            return ElementCast::from_temporary($ctor::new($localName, $prefix, $document $(, $arg)*));
        }
    )
)

pub struct JSFile {
    pub data: String,
    pub url: Option<Url>,
}

pub type JSResult = Vec<JSFile>;

pub enum HTMLInput {
    InputString(String),
    InputUrl(Url),
}

enum JSMessage {
    JSTaskNewFile(Url),
    JSTaskNewInlineScript(String, Option<Url>),
    JSTaskExit
}

/// Messages generated by the HTML parser upon discovery of additional resources
pub enum HtmlDiscoveryMessage {
    HtmlDiscoveredScript(JSResult)
}

pub struct HtmlParserResult {
    pub discovery_port: Receiver<HtmlDiscoveryMessage>,
}

trait NodeWrapping<T> {
    unsafe fn to_hubbub_node(&self) -> hubbub::NodeDataPtr;
}

impl<'a, T: NodeBase+Reflectable> NodeWrapping<T> for JSRef<'a, T> {
    unsafe fn to_hubbub_node(&self) -> hubbub::NodeDataPtr {
        mem::transmute(self.deref())
    }
}

unsafe fn from_hubbub_node<T: Reflectable>(n: hubbub::NodeDataPtr) -> Temporary<T> {
    Temporary::new(JS::from_raw(mem::transmute(n)))
}

fn js_script_listener(to_parent: Sender<HtmlDiscoveryMessage>,
                      from_parent: Receiver<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = vec!();

    loop {
        match from_parent.recv_opt() {
            Ok(JSTaskNewFile(url)) => {
                match load_whole_resource(&resource_task, url.clone()) {
                    Err(_) => {
                        error!("error loading script {:s}", url.serialize());
                    }
                    Ok((metadata, bytes)) => {
                        let decoded = UTF_8.decode(bytes.as_slice(), DecodeReplace).unwrap();
                        result_vec.push(JSFile {
                            data: decoded.to_string(),
                            url: Some(metadata.final_url),
                        });
                    }
                }
            }
            Ok(JSTaskNewInlineScript(data, url)) => {
                result_vec.push(JSFile { data: data, url: url });
            }
            Ok(JSTaskExit) | Err(()) => {
                break;
            }
        }
    }

    assert!(to_parent.send_opt(HtmlDiscoveredScript(result_vec)).is_ok());
}

// Parses an RFC 2616 compliant date/time string, and returns a localized
// date/time string in a format suitable for document.lastModified.
fn parse_last_modified(timestamp: &str) -> String {
    let format = "%m/%d/%Y %H:%M:%S";

    // RFC 822, updated by RFC 1123
    match time::strptime(timestamp, "%a, %d %b %Y %T %Z") {
        Ok(t) => return t.to_local().strftime(format),
        Err(_) => ()
    }

    // RFC 850, obsoleted by RFC 1036
    match time::strptime(timestamp, "%A, %d-%b-%y %T %Z") {
        Ok(t) => return t.to_local().strftime(format),
        Err(_) => ()
    }

    // ANSI C's asctime() format
    match time::strptime(timestamp, "%c") {
        Ok(t) => t.to_local().strftime(format),
        Err(_) => String::from_str("")
    }
}

// Silly macros to handle constructing      DOM nodes. This produces bad code and should be optimized
// via atomization (issue #85).

pub fn build_element_from_tag(tag: DOMString, ns: Namespace, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<Element> {
    if ns != ns!(HTML) {
        return Element::new(tag, ns, prefix, document);
    }

    // TODO (Issue #85): use atoms
    handle_element!(document, tag, prefix, "a",         HTMLAnchorElement);
    handle_element!(document, tag, prefix, "abbr",      HTMLElement);
    handle_element!(document, tag, prefix, "acronym",   HTMLElement);
    handle_element!(document, tag, prefix, "address",   HTMLElement);
    handle_element!(document, tag, prefix, "applet",    HTMLAppletElement);
    handle_element!(document, tag, prefix, "area",      HTMLAreaElement);
    handle_element!(document, tag, prefix, "article",   HTMLElement);
    handle_element!(document, tag, prefix, "aside",     HTMLElement);
    handle_element!(document, tag, prefix, "audio",     HTMLAudioElement);
    handle_element!(document, tag, prefix, "b",         HTMLElement);
    handle_element!(document, tag, prefix, "base",      HTMLBaseElement);
    handle_element!(document, tag, prefix, "bdi",       HTMLElement);
    handle_element!(document, tag, prefix, "bdo",       HTMLElement);
    handle_element!(document, tag, prefix, "bgsound",   HTMLElement);
    handle_element!(document, tag, prefix, "big",       HTMLElement);
    handle_element!(document, tag, prefix, "blockquote",HTMLElement);
    handle_element!(document, tag, prefix, "body",      HTMLBodyElement);
    handle_element!(document, tag, prefix, "br",        HTMLBRElement);
    handle_element!(document, tag, prefix, "button",    HTMLButtonElement);
    handle_element!(document, tag, prefix, "canvas",    HTMLCanvasElement);
    handle_element!(document, tag, prefix, "caption",   HTMLTableCaptionElement);
    handle_element!(document, tag, prefix, "center",    HTMLElement);
    handle_element!(document, tag, prefix, "cite",      HTMLElement);
    handle_element!(document, tag, prefix, "code",      HTMLElement);
    handle_element!(document, tag, prefix, "col",       HTMLTableColElement);
    handle_element!(document, tag, prefix, "colgroup",  HTMLTableColElement);
    handle_element!(document, tag, prefix, "data",      HTMLDataElement);
    handle_element!(document, tag, prefix, "datalist",  HTMLDataListElement);
    handle_element!(document, tag, prefix, "dd",        HTMLElement);
    handle_element!(document, tag, prefix, "del",       HTMLModElement);
    handle_element!(document, tag, prefix, "details",   HTMLElement);
    handle_element!(document, tag, prefix, "dfn",       HTMLElement);
    handle_element!(document, tag, prefix, "dir",       HTMLDirectoryElement);
    handle_element!(document, tag, prefix, "div",       HTMLDivElement);
    handle_element!(document, tag, prefix, "dl",        HTMLDListElement);
    handle_element!(document, tag, prefix, "dt",        HTMLElement);
    handle_element!(document, tag, prefix, "em",        HTMLElement);
    handle_element!(document, tag, prefix, "embed",     HTMLEmbedElement);
    handle_element!(document, tag, prefix, "fieldset",  HTMLFieldSetElement);
    handle_element!(document, tag, prefix, "figcaption",HTMLElement);
    handle_element!(document, tag, prefix, "figure",    HTMLElement);
    handle_element!(document, tag, prefix, "font",      HTMLFontElement);
    handle_element!(document, tag, prefix, "footer",    HTMLElement);
    handle_element!(document, tag, prefix, "form",      HTMLFormElement);
    handle_element!(document, tag, prefix, "frame",     HTMLFrameElement);
    handle_element!(document, tag, prefix, "frameset",  HTMLFrameSetElement);
    handle_element!(document, tag, prefix, "h1",        HTMLHeadingElement, Heading1);
    handle_element!(document, tag, prefix, "h2",        HTMLHeadingElement, Heading2);
    handle_element!(document, tag, prefix, "h3",        HTMLHeadingElement, Heading3);
    handle_element!(document, tag, prefix, "h4",        HTMLHeadingElement, Heading4);
    handle_element!(document, tag, prefix, "h5",        HTMLHeadingElement, Heading5);
    handle_element!(document, tag, prefix, "h6",        HTMLHeadingElement, Heading6);
    handle_element!(document, tag, prefix, "head",      HTMLHeadElement);
    handle_element!(document, tag, prefix, "header",    HTMLElement);
    handle_element!(document, tag, prefix, "hgroup",    HTMLElement);
    handle_element!(document, tag, prefix, "hr",        HTMLHRElement);
    handle_element!(document, tag, prefix, "html",      HTMLHtmlElement);
    handle_element!(document, tag, prefix, "i",         HTMLElement);
    handle_element!(document, tag, prefix, "iframe",    HTMLIFrameElement);
    handle_element!(document, tag, prefix, "img",       HTMLImageElement);
    handle_element!(document, tag, prefix, "input",     HTMLInputElement);
    handle_element!(document, tag, prefix, "ins",       HTMLModElement);
    handle_element!(document, tag, prefix, "isindex",   HTMLElement);
    handle_element!(document, tag, prefix, "kbd",       HTMLElement);
    handle_element!(document, tag, prefix, "label",     HTMLLabelElement);
    handle_element!(document, tag, prefix, "legend",    HTMLLegendElement);
    handle_element!(document, tag, prefix, "li",        HTMLLIElement);
    handle_element!(document, tag, prefix, "link",      HTMLLinkElement);
    handle_element!(document, tag, prefix, "main",      HTMLElement);
    handle_element!(document, tag, prefix, "map",       HTMLMapElement);
    handle_element!(document, tag, prefix, "mark",      HTMLElement);
    handle_element!(document, tag, prefix, "marquee",   HTMLElement);
    handle_element!(document, tag, prefix, "meta",      HTMLMetaElement);
    handle_element!(document, tag, prefix, "meter",     HTMLMeterElement);
    handle_element!(document, tag, prefix, "nav",       HTMLElement);
    handle_element!(document, tag, prefix, "nobr",      HTMLElement);
    handle_element!(document, tag, prefix, "noframes",  HTMLElement);
    handle_element!(document, tag, prefix, "noscript",  HTMLElement);
    handle_element!(document, tag, prefix, "object",    HTMLObjectElement);
    handle_element!(document, tag, prefix, "ol",        HTMLOListElement);
    handle_element!(document, tag, prefix, "optgroup",  HTMLOptGroupElement);
    handle_element!(document, tag, prefix, "option",    HTMLOptionElement);
    handle_element!(document, tag, prefix, "output",    HTMLOutputElement);
    handle_element!(document, tag, prefix, "p",         HTMLParagraphElement);
    handle_element!(document, tag, prefix, "param",     HTMLParamElement);
    handle_element!(document, tag, prefix, "pre",       HTMLPreElement);
    handle_element!(document, tag, prefix, "progress",  HTMLProgressElement);
    handle_element!(document, tag, prefix, "q",         HTMLQuoteElement);
    handle_element!(document, tag, prefix, "rp",        HTMLElement);
    handle_element!(document, tag, prefix, "rt",        HTMLElement);
    handle_element!(document, tag, prefix, "ruby",      HTMLElement);
    handle_element!(document, tag, prefix, "s",         HTMLElement);
    handle_element!(document, tag, prefix, "samp",      HTMLElement);
    handle_element!(document, tag, prefix, "script",    HTMLScriptElement);
    handle_element!(document, tag, prefix, "section",   HTMLElement);
    handle_element!(document, tag, prefix, "select",    HTMLSelectElement);
    handle_element!(document, tag, prefix, "small",     HTMLElement);
    handle_element!(document, tag, prefix, "source",    HTMLSourceElement);
    handle_element!(document, tag, prefix, "spacer",    HTMLElement);
    handle_element!(document, tag, prefix, "span",      HTMLSpanElement);
    handle_element!(document, tag, prefix, "strike",    HTMLElement);
    handle_element!(document, tag, prefix, "strong",    HTMLElement);
    handle_element!(document, tag, prefix, "style",     HTMLStyleElement);
    handle_element!(document, tag, prefix, "sub",       HTMLElement);
    handle_element!(document, tag, prefix, "summary",   HTMLElement);
    handle_element!(document, tag, prefix, "sup",       HTMLElement);
    handle_element!(document, tag, prefix, "table",     HTMLTableElement);
    handle_element!(document, tag, prefix, "tbody",     HTMLTableSectionElement);
    handle_element!(document, tag, prefix, "td",        HTMLTableDataCellElement);
    handle_element!(document, tag, prefix, "template",  HTMLTemplateElement);
    handle_element!(document, tag, prefix, "textarea",  HTMLTextAreaElement);
    handle_element!(document, tag, prefix, "th",        HTMLTableHeaderCellElement);
    handle_element!(document, tag, prefix, "time",      HTMLTimeElement);
    handle_element!(document, tag, prefix, "title",     HTMLTitleElement);
    handle_element!(document, tag, prefix, "tr",        HTMLTableRowElement);
    handle_element!(document, tag, prefix, "tt",        HTMLElement);
    handle_element!(document, tag, prefix, "track",     HTMLTrackElement);
    handle_element!(document, tag, prefix, "u",         HTMLElement);
    handle_element!(document, tag, prefix, "ul",        HTMLUListElement);
    handle_element!(document, tag, prefix, "var",       HTMLElement);
    handle_element!(document, tag, prefix, "video",     HTMLVideoElement);
    handle_element!(document, tag, prefix, "wbr",       HTMLElement);

    return ElementCast::from_temporary(HTMLUnknownElement::new(tag, prefix, document));
}

// The url from msg_load_data is ignored here
pub fn parse_html(page: &Page,
                  document: JSRef<Document>,
                  input: HTMLInput,
                  resource_task: ResourceTask,
                  msg_load_data: Option<MsgLoadData>)
                  -> HtmlParserResult {
    debug!("Hubbub: parsing {:?}", input);

    // Spawn a JS parser to receive JavaScript.
    let (discovery_chan, discovery_port) = channel();
    let resource_task2 = resource_task.clone();
    let js_result_chan = discovery_chan.clone();
    let (js_chan, js_msg_port) = channel();
    spawn_named("parse_html:js", proc() {
        js_script_listener(js_result_chan, js_msg_port, resource_task2.clone());
    });

    let (base_url, load_response) = match input {
        InputUrl(ref url) => {
            // Wait for the LoadResponse so that the parser knows the final URL.
            let (input_chan, input_port) = channel();
            let mut load_data = LoadData::new(url.clone());
            msg_load_data.map(|m| {
                load_data.headers = m.headers;
                load_data.method = m.method;
                load_data.data = m.data;
            });
            resource_task.send(Load(load_data, input_chan));

            let load_response = input_port.recv();

            debug!("Fetched page; metadata is {:?}", load_response.metadata);

            load_response.metadata.headers.as_ref().map(|headers| {
                let header = headers.iter().find(|h|
                    h.header_name().as_slice().to_ascii_lower() == "last-modified".to_string()
                );

                match header {
                    Some(h) => document.set_last_modified(
                        parse_last_modified(h.header_value().as_slice())),
                    None => {},
                };
            });

            let base_url = load_response.metadata.final_url.clone();

            {
                // Store the final URL before we start parsing, so that DOM routines
                // (e.g. HTMLImageElement::update_image) can resolve relative URLs
                // correctly.
                *page.mut_url() = Some((base_url.clone(), true));
            }

            (Some(base_url), Some(load_response))
        },
        InputString(_) => {
            match *page.url() {
                Some((ref page_url, _)) => (Some(page_url.clone()), None),
                None => (None, None),
            }
        },
    };

    let mut parser = build_parser(unsafe { document.to_hubbub_node() });
    debug!("created parser");

    let js_chan2 = js_chan.clone();

    let doc_cell = RefCell::new(document);

    let mut tree_handler = hubbub::TreeHandler {
        create_comment: |data: String| {
            debug!("create comment");
            // NOTE: tmp vars are workaround for lifetime issues. Both required.
            let tmp_borrow = doc_cell.borrow();
            let tmp = &*tmp_borrow;
            let comment = Comment::new(data, *tmp).root();
            let comment: JSRef<Node> = NodeCast::from_ref(*comment);
            unsafe { comment.to_hubbub_node() }
        },
        create_doctype: |box hubbub::Doctype { name: name, public_id: public_id, system_id: system_id, ..}: Box<hubbub::Doctype>| {
            debug!("create doctype");
            // NOTE: tmp vars are workaround for lifetime issues. Both required.
            let tmp_borrow = doc_cell.borrow();
            let tmp = &*tmp_borrow;
            let doctype_node = DocumentType::new(name, public_id, system_id, *tmp).root();
            unsafe {
                doctype_node.to_hubbub_node()
            }
        },
        create_element: |tag: Box<hubbub::Tag>| {
            debug!("create element {}", tag.name);
            // NOTE: tmp vars are workaround for lifetime issues. Both required.
            let tmp_borrow = doc_cell.borrow();
            let tmp = &*tmp_borrow;
            let namespace = match tag.ns {
                HtmlNs => ns!(HTML),
                MathMlNs => ns!(MathML),
                SvgNs => ns!(SVG),
                ns => fail!("Not expecting namespace {:?}", ns),
            };
            let element: Root<Element> = build_element_from_tag(tag.name.clone(), namespace, None, *tmp).root();

            debug!("-- attach attrs");
            for attr in tag.attributes.iter() {
                let (namespace, prefix) = match attr.ns {
                    NullNs => (ns!(""), None),
                    XLinkNs => (ns!(XLink), Some("xlink")),
                    XmlNs => (ns!(XML), Some("xml")),
                    XmlNsNs => (ns!(XMLNS), Some("xmlns")),
                    ns => fail!("Not expecting namespace {:?}", ns),
                };
                element.set_attribute_from_parser(Atom::from_slice(attr.name.as_slice()),
                                                  attr.value.clone(),
                                                  namespace,
                                                  prefix.map(|p| p.to_string()));
            }

            unsafe { element.to_hubbub_node() }
        },
        create_text: |data: String| {
            debug!("create text");
            // NOTE: tmp vars are workaround for lifetime issues. Both required.
            let tmp_borrow = doc_cell.borrow();
            let tmp = &*tmp_borrow;
            let text = Text::new(data, *tmp).root();
            unsafe { text.to_hubbub_node() }
        },
        ref_node: |_| {},
        unref_node: |_| {},
        append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| {
            unsafe {
                debug!("append child {:x} {:x}", parent, child);
                let child: Root<Node> = from_hubbub_node(child).root();
                let parent: Root<Node> = from_hubbub_node(parent).root();
                assert!(parent.AppendChild(*child).is_ok());
            }
            child
        },
        insert_before: |_parent, _child| {
            debug!("insert before");
            0u
        },
        remove_child: |_parent, _child| {
            debug!("remove child");
            0u
        },
        clone_node: |_node, deep| {
            debug!("clone node");
            if deep { error!("-- deep clone unimplemented"); }
            fail!("clone node unimplemented")
        },
        reparent_children: |_node, _new_parent| {
            debug!("reparent children");
            0u
        },
        get_parent: |_node, _element_only| {
            debug!("get parent");
            0u
        },
        has_children: |_node| {
            debug!("has children");
            false
        },
        form_associate: |_form, _node| {
            debug!("form associate");
        },
        add_attributes: |_node, _attributes| {
            debug!("add attributes");
        },
        set_quirks_mode: |mode| {
            debug!("set quirks mode");
            // NOTE: tmp vars are workaround for lifetime issues. Both required.
            let tmp_borrow = doc_cell.borrow_mut();
            let tmp = &*tmp_borrow;
            tmp.set_quirks_mode(mode);
        },
        encoding_change: |encname| {
            debug!("encoding change");
            // NOTE: tmp vars are workaround for lifetime issues. Both required.
            let tmp_borrow = doc_cell.borrow_mut();
            let tmp = &*tmp_borrow;
            tmp.set_encoding_name(encname);
        },
        complete_script: |script| {
            unsafe {
                let script = from_hubbub_node::<Node>(script).root();
                let script: Option<JSRef<HTMLScriptElement>> =
                    HTMLScriptElementCast::to_ref(*script);
                let script = match script {
                    Some(script) if script.is_javascript() => script,
                    _ => return,
                };

                let script_element: JSRef<Element> = ElementCast::from_ref(script);
                match script_element.get_attribute(ns!(""), &atom!("src")).root() {
                    Some(src) => {
                        debug!("found script: {:s}", src.Value());
                        let mut url_parser = UrlParser::new();
                        match base_url {
                            None => (),
                            Some(ref base_url) => {
                                url_parser.base_url(base_url);
                            }
                        };
                        match url_parser.parse(src.value().as_slice()) {
                            Ok(new_url) => js_chan2.send(JSTaskNewFile(new_url)),
                            Err(e) => debug!("Parsing url {:s} failed: {:?}", src.Value(), e)
                        };
                    }
                    None => {
                        let mut data = String::new();
                        let scriptnode: JSRef<Node> = NodeCast::from_ref(script);
                        debug!("iterating over children {:?}", scriptnode.first_child());
                        for child in scriptnode.children() {
                            debug!("child = {:?}", child);
                            let text: JSRef<Text> = TextCast::to_ref(child).unwrap();
                            data.push_str(text.characterdata().data().as_slice());
                        }

                        debug!("script data = {:?}", data);
                        js_chan2.send(JSTaskNewInlineScript(data, base_url.clone()));
                    }
                }
            }
            debug!("complete script");
        },
        complete_style: |_| {
            // style parsing is handled in element::notify_child_list_changed.
        },
    };
    parser.set_tree_handler(&mut tree_handler);
    debug!("set tree handler");
    debug!("loaded page");
    match input {
        InputString(s) => {
            parser.parse_chunk(s.into_bytes().as_slice());
        },
        InputUrl(url) => {
            let load_response = load_response.unwrap();
            match load_response.metadata.content_type {
                Some((ref t, _)) if t.as_slice().eq_ignore_ascii_case("image") => {
                    let page = format!("<html><body><img src='{:s}' /></body></html>", base_url.as_ref().unwrap().serialize());
                    parser.parse_chunk(page.into_bytes().as_slice());
                },
                _ => loop {
                    match load_response.progress_port.recv() {
                        Payload(data) => {
                            debug!("received data");
                            parser.parse_chunk(data.as_slice());
                        }
                        Done(Err(err)) => {
                            fail!("Failed to load page URL {:s}, error: {:s}", url.serialize(), err);
                        }
                        Done(..) => {
                            break;
                        }
                    }
                }
            }
        },
    }

    debug!("finished parsing");
    js_chan.send(JSTaskExit);

    HtmlParserResult {
        discovery_port: discovery_port,
    }
}

fn build_parser<'a>(node: hubbub::NodeDataPtr) -> hubbub::Parser<'a> {
    let mut parser = hubbub::Parser::new("UTF-8", false);
    parser.set_document_node(node);
    parser.enable_scripting(true);
    parser.enable_styling(true);
    parser
}

<commit_msg>Remove old hubbub HTML parsing code<commit_after><|endoftext|>


<commit_before>#![feature(globs)]

extern crate piston;
extern crate event;

use piston::*;
use event::{
    AddKeyboard,
    AddLasting,
    AddPress,
    AddPressing,

    Event,

    Map,

    BackEnd,
    Observer,
};

pub struct App<'a> {
    e: Event<'a>,
    back_end: TestBackEnd<'a>,
}

impl<'a> App<'a> {
    pub fn new() -> App {
        App {
            e: Event::new(),
            back_end: TestBackEnd::new(),
        }
    }
}

impl<'a> Game for App<'a> {
    fn load(&mut self, _asset_store: &mut AssetStore) {
        self.e.keyboard().press(keyboard::Up).map(&mut self.back_end, || {
            println!("Oops! You pressed keyboard::Up");
        });

        let e = self.e.keyboard().pressing(keyboard::Up);
        e.map(&mut self.back_end, || {
            println!("Wow! You are pressing keyboard::Up");
        });

        e.lasting(1.0).map(&mut self.back_end, || {
            println!("Wooooooow! You are pressing keybaord::Up at least 1.0 second!!");
        });
    }

    fn update(&mut self, dt: f64, _asset_store: &mut AssetStore) {
        self.back_end.update(dt);
    }

    // re-wrap those events to Event, a good way to do this is in the GameWindow
    // implementions.
    fn key_press(
        &mut self,
        key: keyboard::Key,
        _asset_store: &mut AssetStore
    ) {
        self.back_end.on_event(event::KeyPressed(key));
    }

    fn key_release(
        &mut self,
        key: keyboard::Key,
        _asset_store: &mut AssetStore
    ) {
        self.back_end.on_event(event::KeyReleased(key));
    }

    fn mouse_press(
        &mut self,
        button: mouse::Button,
        _asset_store: &mut AssetStore
    ) {
        self.back_end.on_event(event::MouseButtonPressed(button));
    }

    fn mouse_release(
        &mut self,
        button: mouse::Button,
        _asset_store: &mut AssetStore
    ) {
        self.back_end.on_event(event::MouseButtonReleased(button));
    }
}

struct TestBackEnd<'a> {
    observers: Vec<Box<Observer>>,
}

impl<'a> TestBackEnd<'a> {
    pub fn new() -> TestBackEnd {
        TestBackEnd {
            observers: Vec::<Box<Observer>>::new(),
        }
    }
}

impl<'a> BackEnd for TestBackEnd<'a> {
    fn add_observer(&mut self, ob: Box<Observer>) -> uint {
        self.observers.push(ob);
        self.observers.len() - 1
    }

    fn update(&mut self, dt: f64) {
        for i in range(0, self.observers.len()) {
            let ob = self.observers.get_mut(i);

            ob.update(dt);

            if ob.can_trigger() {
                ob.trigger();
            }
        }
    }

    fn on_event(&mut self, e: event::Event) {
        for ob in self.observers.mut_iter() {
            ob.on_event(e);
        }
    }
}

type GameWindowBackEnd = GameWindowSDL2;

fn main() {
    let mut game_window: GameWindowBackEnd = GameWindow::new(
        GameWindowSettings::new (
            "Piston-Lab".to_owned(),
            [300, 300],
            false,
            true,
            [1.0, 1.0, 1.0, 1.0]
        )
    );

    let mut asset_store = AssetStore::from_folder("assets");
    let mut app = App::new();

    app.run(&mut game_window, &mut asset_store);
}

<commit_msg>Added `remove_observer` to back-end<commit_after>#![feature(globs)]

extern crate collections;

extern crate piston;
extern crate event;

use collections::treemap::TreeMap;
use piston::*;
use event::{
    AddKeyboard,
    AddLasting,
    AddPress,
    AddPressing,

    Event,

    Map,

    BackEnd,
    Observer,
};

pub struct App<'a> {
    e: Event<'a>,
    back_end: TestBackEnd<'a>,
}

impl<'a> App<'a> {
    pub fn new() -> App {
        App {
            e: Event::new(),
            back_end: TestBackEnd::new(),
        }
    }
}

impl<'a> Game for App<'a> {
    fn load(&mut self, _asset_store: &mut AssetStore) {
        self.e.keyboard().press(keyboard::Up).map(&mut self.back_end, || {
            println!("Oops! You pressed keyboard::Up");
        });

        let e = self.e.keyboard().pressing(keyboard::Up);
        let i =e.map(&mut self.back_end, || {
            println!("Wow! You are pressing keyboard::Up");
        });

        e.lasting(1.0).map(&mut self.back_end, || {
            println!("Wooooooow! You are pressing keybaord::Up at least 1.0 second!!");
        });
        self.back_end.remove_observer(i);
    }

    fn update(&mut self, dt: f64, _asset_store: &mut AssetStore) {
        self.back_end.update(dt);
    }

    // re-wrap those events to Event, a good way to do this is in the GameWindow
    // implementions.
    fn key_press(
        &mut self,
        key: keyboard::Key,
        _asset_store: &mut AssetStore
    ) {
        self.back_end.on_event(event::KeyPressed(key));
    }

    fn key_release(
        &mut self,
        key: keyboard::Key,
        _asset_store: &mut AssetStore
    ) {
        self.back_end.on_event(event::KeyReleased(key));
    }

    fn mouse_press(
        &mut self,
        button: mouse::Button,
        _asset_store: &mut AssetStore
    ) {
        self.back_end.on_event(event::MouseButtonPressed(button));
    }

    fn mouse_release(
        &mut self,
        button: mouse::Button,
        _asset_store: &mut AssetStore
    ) {
        self.back_end.on_event(event::MouseButtonReleased(button));
    }
}

struct TestBackEnd<'a> {
    observers: TreeMap<uint, Box<Observer>>,
    count: uint,
}

impl<'a> TestBackEnd<'a> {
    pub fn new() -> TestBackEnd {
        TestBackEnd {
            observers: TreeMap::<uint, Box<Observer>>::new(),
            count: 0,
        }
    }
}

impl<'a> BackEnd for TestBackEnd<'a> {
    fn add_observer(&mut self, ob: Box<Observer>) -> uint {
        let i = self.count;
        self.count += 1;
        self.observers.insert(i, ob);
        i
    }

    fn remove_observer(&mut self, i: uint) {
        self.observers.remove(&i);
    }

    fn update(&mut self, dt: f64) {
        for (_, ob) in self.observers.mut_iter() {
            ob.update(dt);

            if ob.can_trigger() {
                ob.trigger();
            }
        }
    }

    fn on_event(&mut self, e: event::Event) {
        for (_, ob) in self.observers.mut_iter() {
            ob.on_event(e);
        }
    }
}

type GameWindowBackEnd = GameWindowSDL2;

fn main() {
    let mut game_window: GameWindowBackEnd = GameWindow::new(
        GameWindowSettings::new (
            "Piston-Lab".to_owned(),
            [300, 300],
            false,
            true,
            [1.0, 1.0, 1.0, 1.0]
        )
    );

    let mut asset_store = AssetStore::from_folder("assets");
    let mut app = App::new();

    app.run(&mut game_window, &mut asset_store);
}

<|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::ClientRectListBinding;
use dom::bindings::js::JS;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::clientrect::ClientRect;
use dom::window::Window;

#[deriving(Encodable)]
pub struct ClientRectList {
    pub reflector_: Reflector,
    pub rects: ~[JS<ClientRect>],
    pub window: JS<Window>,
}

impl ClientRectList {
    pub fn new_inherited(window: JS<Window>,
                         rects: ~[JS<ClientRect>]) -> ClientRectList {
        ClientRectList {
            reflector_: Reflector::new(),
            rects: rects,
            window: window,
        }
    }

    pub fn new(window: &JS<Window>,
               rects: ~[JS<ClientRect>]) -> JS<ClientRectList> {
        reflect_dom_object(~ClientRectList::new_inherited(window.clone(), rects),
                           window, ClientRectListBinding::Wrap)
    }

    pub fn Length(&self) -> u32 {
        self.rects.len() as u32
    }

    pub fn Item(&self, index: u32) -> Option<JS<ClientRect>> {
        if index < self.rects.len() as u32 {
            Some(self.rects[index as uint].clone())
        } else {
            None
        }
    }

    pub fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<JS<ClientRect>> {
        *found = index < self.rects.len() as u32;
        self.Item(index)
    }
}

impl Reflectable for ClientRectList {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        &mut self.reflector_
    }
}
<commit_msg>Make ClientRectList::rects a Vec.<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::ClientRectListBinding;
use dom::bindings::js::JS;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::clientrect::ClientRect;
use dom::window::Window;

#[deriving(Encodable)]
pub struct ClientRectList {
    pub reflector_: Reflector,
    pub rects: Vec<JS<ClientRect>>,
    pub window: JS<Window>,
}

impl ClientRectList {
    pub fn new_inherited(window: JS<Window>,
                         rects: Vec<JS<ClientRect>>) -> ClientRectList {
        ClientRectList {
            reflector_: Reflector::new(),
            rects: rects,
            window: window,
        }
    }

    pub fn new(window: &JS<Window>,
               rects: Vec<JS<ClientRect>>) -> JS<ClientRectList> {
        reflect_dom_object(~ClientRectList::new_inherited(window.clone(), rects),
                           window, ClientRectListBinding::Wrap)
    }

    pub fn Length(&self) -> u32 {
        self.rects.len() as u32
    }

    pub fn Item(&self, index: u32) -> Option<JS<ClientRect>> {
        if index < self.rects.len() as u32 {
            Some(self.rects.get(index as uint).clone())
        } else {
            None
        }
    }

    pub fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<JS<ClientRect>> {
        *found = index < self.rects.len() as u32;
        self.Item(index)
    }
}

impl Reflectable for ClientRectList {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        &mut self.reflector_
    }
}
<|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 servo_util::namespace;
use dom::attr::Attr;
use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, CommentCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, CharacterDataCast};
use dom::bindings::codegen::InheritTypes::ProcessingInstructionCast;
use dom::bindings::js::JSRef;
use dom::characterdata::CharacterData;
use dom::comment::Comment;
use dom::documenttype::DocumentType;
use dom::element::Element;
use dom::node::{Node, NodeIterator};
use dom::node::{DoctypeNodeTypeId, DocumentFragmentNodeTypeId, CommentNodeTypeId};
use dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
use dom::node::{TextNodeTypeId, NodeHelpers};
use dom::processinginstruction::ProcessingInstruction;
use dom::text::Text;

pub fn serialize(iterator: &mut NodeIterator) -> ~str {
    let mut html = StrBuf::new();
    let mut open_elements: Vec<~str> = vec!();

    for node in *iterator {
        while open_elements.len() > iterator.depth {
            html.push_str("</".to_owned() + open_elements.pop().unwrap().as_slice() + ">");
        }
        html.push_str(
            match node.type_id() {
                ElementNodeTypeId(..) => {
                    let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap();
                    serialize_elem(elem, &mut open_elements)
                }
                CommentNodeTypeId => {
                    let comment: &JSRef<Comment> = CommentCast::to_ref(&node).unwrap();
                    serialize_comment(comment)
                }
                TextNodeTypeId => {
                    let text: &JSRef<Text> = TextCast::to_ref(&node).unwrap();
                    serialize_text(text)
                }
                DoctypeNodeTypeId => {
                    let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(&node).unwrap();
                    serialize_doctype(doctype)
                }
                ProcessingInstructionNodeTypeId => {
                    let processing_instruction: &JSRef<ProcessingInstruction> =
                        ProcessingInstructionCast::to_ref(&node).unwrap();
                    serialize_processing_instruction(processing_instruction)
                }
                DocumentFragmentNodeTypeId => {
                    "".to_owned()
                }
                DocumentNodeTypeId => {
                    fail!("It shouldn't be possible to serialize a document node")
                }
            }
        );
    }
    while open_elements.len() > 0 {
        html.push_str("</".to_owned() + open_elements.pop().unwrap().as_slice() + ">");
    }
    html.into_owned()
}

fn serialize_comment(comment: &JSRef<Comment>) -> ~str {
    "<!--".to_owned() + comment.deref().characterdata.data + "-->"
}

fn serialize_text(text: &JSRef<Text>) -> ~str {
    let text_node: &JSRef<Node> = NodeCast::from_ref(text);
    match text_node.parent_node().map(|node| node.root()) {
        Some(ref parent) if parent.is_element() => {
            let elem: &JSRef<Element> = ElementCast::to_ref(&**parent).unwrap();
            match elem.deref().local_name.as_slice() {
                "style" | "script" | "xmp" | "iframe" |
                "noembed" | "noframes" | "plaintext" |
                "noscript" if elem.deref().namespace == namespace::HTML => {
                    text.deref().characterdata.data.clone()
                },
                _ => escape(text.deref().characterdata.data, false)
            }
        }
        _ => escape(text.deref().characterdata.data, false)
    }
}

fn serialize_processing_instruction(processing_instruction: &JSRef<ProcessingInstruction>) -> ~str {
    "<?".to_owned() + processing_instruction.deref().target + " " + processing_instruction.deref().characterdata.data + "?>"
}

fn serialize_doctype(doctype: &JSRef<DocumentType>) -> ~str {
    "<!DOCTYPE".to_owned() + doctype.deref().name + ">"
}

fn serialize_elem(elem: &JSRef<Element>, open_elements: &mut Vec<~str>) -> ~str {
    let mut rv = StrBuf::new();
    rv.push_str("<");
    rv.push_str(elem.deref().local_name);
    for attr in elem.deref().attrs.iter() {
        let attr = attr.root();
        rv.push_str(serialize_attr(&*attr));
    };
    rv.push_str(">");
    match elem.deref().local_name.as_slice() {
        "pre" | "listing" | "textarea" if elem.deref().namespace == namespace::HTML => {
            let node: &JSRef<Node> = NodeCast::from_ref(elem);
            match node.first_child().map(|child| child.root()) {
                Some(ref child) if child.is_text() => {
                    let text: &JSRef<CharacterData> = CharacterDataCast::to_ref(&**child).unwrap();
                    if text.deref().data.len() > 0 && text.deref().data[0] == 0x0A as u8 {
                        rv.push_str("\x0A");
                    }
                },
                _ => {}
            }
        },
        _ => {}
    }
    if !elem.deref().is_void() {
        open_elements.push(elem.deref().local_name.clone());
    }
    rv.into_owned()
}

fn serialize_attr(attr: &JSRef<Attr>) -> ~str {
    let attr_name = if attr.deref().namespace == namespace::XML {
        "xml:".to_owned() + attr.deref().local_name.clone()
    } else if attr.deref().namespace == namespace::XMLNS &&
        attr.deref().local_name.as_slice() == "xmlns" {
        "xmlns".to_owned()
    } else if attr.deref().namespace == namespace::XMLNS {
        "xmlns:".to_owned() + attr.deref().local_name.clone()
    } else if attr.deref().namespace == namespace::XLink {
        "xlink:".to_owned() + attr.deref().local_name.clone()
    } else {
        attr.deref().name.clone()
    };
    " ".to_owned() + attr_name + "=\"" + escape(attr.deref().value, true) + "\""
}

fn escape(string: &str, attr_mode: bool) -> ~str {
    let replaced = string.replace("&", "&").replace("\xA0", " ");
    match attr_mode {
        true => {
            replaced.replace("\"", """)
        },
        false => {
            replaced.replace("<", "<").replace(">", ">")
        }
    }
}
<commit_msg>auto merge of #2484 : Ms2ger/servo/htmlserializer-strbuf, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use servo_util::namespace;
use dom::attr::Attr;
use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, CommentCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, CharacterDataCast};
use dom::bindings::codegen::InheritTypes::ProcessingInstructionCast;
use dom::bindings::js::JSRef;
use dom::characterdata::CharacterData;
use dom::comment::Comment;
use dom::documenttype::DocumentType;
use dom::element::Element;
use dom::node::{Node, NodeIterator};
use dom::node::{DoctypeNodeTypeId, DocumentFragmentNodeTypeId, CommentNodeTypeId};
use dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
use dom::node::{TextNodeTypeId, NodeHelpers};
use dom::processinginstruction::ProcessingInstruction;
use dom::text::Text;

pub fn serialize(iterator: &mut NodeIterator) -> ~str {
    let mut html = StrBuf::new();
    let mut open_elements: Vec<~str> = vec!();

    for node in *iterator {
        while open_elements.len() > iterator.depth {
            html.push_str("</");
            html.push_str(open_elements.pop().unwrap().as_slice());
            html.push_str(">");
        }
        match node.type_id() {
            ElementNodeTypeId(..) => {
                let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap();
                serialize_elem(elem, &mut open_elements, &mut html)
            }
            CommentNodeTypeId => {
                let comment: &JSRef<Comment> = CommentCast::to_ref(&node).unwrap();
                serialize_comment(comment, &mut html)
            }
            TextNodeTypeId => {
                let text: &JSRef<Text> = TextCast::to_ref(&node).unwrap();
                serialize_text(text, &mut html)
            }
            DoctypeNodeTypeId => {
                let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(&node).unwrap();
                serialize_doctype(doctype, &mut html)
            }
            ProcessingInstructionNodeTypeId => {
                let processing_instruction: &JSRef<ProcessingInstruction> =
                    ProcessingInstructionCast::to_ref(&node).unwrap();
                serialize_processing_instruction(processing_instruction, &mut html)
            }
            DocumentFragmentNodeTypeId => {}
            DocumentNodeTypeId => {
                fail!("It shouldn't be possible to serialize a document node")
            }
        }
    }
    while open_elements.len() > 0 {
        html.push_str("</");
        html.push_str(open_elements.pop().unwrap().as_slice());
        html.push_str(">");
    }
    html.into_owned()
}

fn serialize_comment(comment: &JSRef<Comment>, html: &mut StrBuf) {
    html.push_str("<!--");
    html.push_str(comment.deref().characterdata.data);
    html.push_str("-->");
}

fn serialize_text(text: &JSRef<Text>, html: &mut StrBuf) {
    let text_node: &JSRef<Node> = NodeCast::from_ref(text);
    match text_node.parent_node().map(|node| node.root()) {
        Some(ref parent) if parent.is_element() => {
            let elem: &JSRef<Element> = ElementCast::to_ref(&**parent).unwrap();
            match elem.deref().local_name.as_slice() {
                "style" | "script" | "xmp" | "iframe" |
                "noembed" | "noframes" | "plaintext" |
                "noscript" if elem.deref().namespace == namespace::HTML
                => html.push_str(text.deref().characterdata.data),
                _ => escape(text.deref().characterdata.data, false, html)
            }
        }
        _ => escape(text.deref().characterdata.data, false, html)
    }
}

fn serialize_processing_instruction(processing_instruction: &JSRef<ProcessingInstruction>,
                                    html: &mut StrBuf) {
    html.push_str("<?");
    html.push_str(processing_instruction.deref().target);
    html.push_char(' ');
    html.push_str(processing_instruction.deref().characterdata.data);
    html.push_str("?>");
}

fn serialize_doctype(doctype: &JSRef<DocumentType>, html: &mut StrBuf) {
    html.push_str("<!DOCTYPE");
    html.push_str(doctype.deref().name);
    html.push_char('>');
}

fn serialize_elem(elem: &JSRef<Element>, open_elements: &mut Vec<~str>, html: &mut StrBuf) {
    html.push_char('<');
    html.push_str(elem.deref().local_name);
    for attr in elem.deref().attrs.iter() {
        let attr = attr.root();
        serialize_attr(&*attr, html);
    };
    html.push_char('>');

    match elem.deref().local_name.as_slice() {
        "pre" | "listing" | "textarea" if elem.deref().namespace == namespace::HTML => {
            let node: &JSRef<Node> = NodeCast::from_ref(elem);
            match node.first_child().map(|child| child.root()) {
                Some(ref child) if child.is_text() => {
                    let text: &JSRef<CharacterData> = CharacterDataCast::to_ref(&**child).unwrap();
                    if text.deref().data.len() > 0 && text.deref().data[0] == 0x0A as u8 {
                        html.push_char('\x0A');
                    }
                },
                _ => {}
            }
        },
        _ => {}
    }

    if !elem.deref().is_void() {
        open_elements.push(elem.deref().local_name.clone());
    }
}

fn serialize_attr(attr: &JSRef<Attr>, html: &mut StrBuf) {
    html.push_char(' ');
    if attr.deref().namespace == namespace::XML {
        html.push_str("xml:");
        html.push_str(attr.deref().local_name);
    } else if attr.deref().namespace == namespace::XMLNS &&
        attr.deref().local_name.as_slice() == "xmlns" {
        html.push_str("xmlns");
    } else if attr.deref().namespace == namespace::XMLNS {
        html.push_str("xmlns:");
        html.push_str(attr.deref().local_name);
    } else if attr.deref().namespace == namespace::XLink {
        html.push_str("xlink:");
        html.push_str(attr.deref().local_name);
    } else {
        html.push_str(attr.deref().name);
    };
    html.push_str("=\"");
    escape(attr.deref().value, true, html);
    html.push_char('"');
}

fn escape(string: &str, attr_mode: bool, html: &mut StrBuf) {
    for c in string.chars() {
        match c {
            '&' => html.push_str("&"),
            '\xA0' => html.push_str(" "),
            '"' if attr_mode => html.push_str("""),
            '<' if !attr_mode => html.push_str("<"),
            '>' if !attr_mode => html.push_str(">"),
            c => html.push_char(c),
        }
    }
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::{env, fs, io};
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurement {
    command: String,
    mean: f64,
    stddev: f64,
    median: f64,
    user: f64,
    system: f64,
    min: f64,
    max: f64,
    times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurements {
    results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
struct Regression {
    threshold: f64,
    difference: f64,
    baseline: Measurement,
    latest: Measurement,
}

#[derive(Debug, Clone, PartialEq)]
struct MeasurementGroup {
    version: String,
    run: String,
    measurement: Measurement,
}

#[derive(Error, Debug)]
enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
}

fn regression(baseline: &Measurement, latest: &Measurement) -> Option<Regression> {
    let threshold = 1.05; // 5% regression threshold
    let difference = latest.median / baseline.median;
    if difference > threshold {
        Some(Regression {
            threshold,
            difference,
            baseline: baseline.clone(),
            latest: latest.clone(),
        })
    } else {
        None
    }
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            println!("{:?}", filename);
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn detect_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Regression>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join(""),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let x: Vec<Regression> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| (x.run.clone(), x.version.clone()))
        .into_iter()
        .map(|(_, group)| {
            let g: Vec<MeasurementGroup> = group.collect();
            regression(&g[0].measurement, &g[1].measurement)
        })
        .flatten()
        .collect();

    Ok(x)
}

fn main() {
    // TODO args lib
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        println!("please provide the results directory");
        exit(1);
    }
    let results_directory = &args[1];

    let regressions = measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        detect_regressions(&v_next)
    });

    match regressions {
        Err(e) => panic!("{}", e),
        Ok(rs) => match rs[..] {
            [] => println!("congrats! no regressions"),
            _ => {
                for r in rs {
                    println!("{:?}", r);
                }
                println!("the above regressions were found.");
                exit(1)
            }
        },
    }
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<commit_msg>add some output to comparitor<commit_after>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::{env, fs, io};
use std::borrow::Cow;
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurement {
    command: String,
    mean: f64,
    stddev: f64,
    median: f64,
    user: f64,
    system: f64,
    min: f64,
    max: f64,
    times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurements {
    results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
struct Regression {
    threshold: f64,
    difference: f64,
    baseline: Measurement,
    latest: Measurement,
}

#[derive(Debug, Clone, PartialEq)]
struct MeasurementGroup {
    version: String,
    run: String,
    measurement: Measurement,
}

#[derive(Error, Debug)]
enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
}

fn regression(baseline: &Measurement, latest: &Measurement) -> Option<Regression> {
    let threshold = 1.05; // 5% regression threshold
    let difference = latest.median / baseline.median;
    if difference > threshold {
        Some(Regression {
            threshold,
            difference,
            baseline: baseline.clone(),
            latest: latest.clone(),
        })
    } else {
        None
    }
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            println!("{:?}", filename);
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn detect_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Regression>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join(""),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let x: Vec<Regression> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| (x.run.clone(), x.version.clone()))
        .into_iter()
        .map(|(_, group)| {
            let g: Vec<MeasurementGroup> = group.collect();
            regression(&g[0].measurement, &g[1].measurement)
        })
        .flatten()
        .collect();

    Ok(x)
}

fn main() {
    // TODO args lib
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        println!("please provide the results directory");
        exit(1);
    }
    let results_directory = &args[1];

    let regressions = measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        println!("checking regressions with the following measurements:");
        for (path, _) in &v_next {
            println!("{}", path.file_name().map(|x| x.to_string_lossy()).unwrap_or(Cow::from("unknown file")))
        }

        detect_regressions(&v_next)
    });

    match regressions {
        Err(e) => panic!("{}", e),
        Ok(rs) => match rs[..] {
            [] => println!("congrats! no regressions"),
            _ => {
                for r in rs {
                    println!("{:?}", r);
                }
                println!("the above regressions were found.");
                exit(1)
            }
        },
    }
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<|endoftext|>


<commit_before>use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::os::unix;
use std::path::Path;

// A simple implementation of `% cat path`
fn cat(path: &Path) -> io::Result<String> {
    let mut f = try!(File::open(path));
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// A simple implementation of `% echo s > path`
fn echo(s: &str, path: &Path) -> io::Result<()> {
    let mut f = try!(File::create(path));

    f.write_all(s.as_bytes())
}

// A simple implementation of `% touch path` (ignores existing files)
fn touch(path: &Path) -> io::Result<()> {
    match OpenOptions::new().create(true).open(path) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

fn main() {
    println!("`mkdir a`");
    // Create a directory, returns `io::Result<()>`
    match fs::create_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(_) => {},
    }

    println!("`echo hello > a/b.txt`");
    // The previous match can be simplified using the `unwrap_or_else` method
    echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`mkdir -p a/c/d`");
    // Recursively create a directory, returns `io::Result<()>`
    fs::create_dir_all("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`touch a/c/e.txt`");
    touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`ln -s ../b.txt a/c/b.txt`");
    // Create a symbolic link, returns `io::Result<()>`
    if cfg!(target_family = "unix") {
        unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
        });
    }

    println!("`cat a/c/b.txt`");
    match cat(&Path::new("a/c/b.txt")) {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(s) => println!("> {}", s),
    }

    println!("`ls a`");
    // Read the contents of a directory, returns `io::Result<Vec<Path>>`
    match fs::read_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(paths) => for path in paths {
            println!("> {:?}", path.unwrap().path());
        },
    }

    println!("`rm a/c/e.txt`");
    // Remove a file, returns `io::Result<()>`
    fs::remove_file("a/c/e.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`rmdir a/c/d`");
    // Remove an empty directory, returns `io::Result<()>`
    fs::remove_dir("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });
}
<commit_msg>Added missing write(true) option. Fixes issue #765.<commit_after>use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::os::unix;
use std::path::Path;

// A simple implementation of `% cat path`
fn cat(path: &Path) -> io::Result<String> {
    let mut f = try!(File::open(path));
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// A simple implementation of `% echo s > path`
fn echo(s: &str, path: &Path) -> io::Result<()> {
    let mut f = try!(File::create(path));

    f.write_all(s.as_bytes())
}

// A simple implementation of `% touch path` (ignores existing files)
fn touch(path: &Path) -> io::Result<()> {
    match OpenOptions::new().create(true).write(true).open(path) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

fn main() {
    println!("`mkdir a`");
    // Create a directory, returns `io::Result<()>`
    match fs::create_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(_) => {},
    }

    println!("`echo hello > a/b.txt`");
    // The previous match can be simplified using the `unwrap_or_else` method
    echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`mkdir -p a/c/d`");
    // Recursively create a directory, returns `io::Result<()>`
    fs::create_dir_all("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`touch a/c/e.txt`");
    touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`ln -s ../b.txt a/c/b.txt`");
    // Create a symbolic link, returns `io::Result<()>`
    if cfg!(target_family = "unix") {
        unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
        });
    }

    println!("`cat a/c/b.txt`");
    match cat(&Path::new("a/c/b.txt")) {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(s) => println!("> {}", s),
    }

    println!("`ls a`");
    // Read the contents of a directory, returns `io::Result<Vec<Path>>`
    match fs::read_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(paths) => for path in paths {
            println!("> {:?}", path.unwrap().path());
        },
    }

    println!("`rm a/c/e.txt`");
    // Remove a file, returns `io::Result<()>`
    fs::remove_file("a/c/e.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`rmdir a/c/d`");
    // Remove an empty directory, returns `io::Result<()>`
    fs::remove_dir("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });
}
<|endoftext|>


<commit_before>pub fn is_positive_int(val: &str) -> Result<(), String> {
    match val.parse::<u64>() {
        Ok(val) if val > 0 => Ok(()),
        // If the the value is <= 0 or can't be parsed
        _ => Err(format!("{} isn't a positive integer.", val)),
    }
}

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

    // #[test]
    // fn is_positive_int_should_parse_a_positive_integer_from_input_string() {
    //     assert_eq!(is_positive_int("1"), Ok(()))
    // }

    // #[test]
    // fn is_positive_int_should_throw_an_error_with_zero() {
    //     assert_eq!(
    //         is_positive_int("0"),
    //         Err("0 isn't a positive integer.".to_string())
    //     )
    // }

    // #[test]
    // fn is_positive_int_should_throw_an_error_with_negative_integer() {
    //     assert_eq!(
    //         is_positive_int("-5"),
    //         Err("-5 isn't a positive integer.".to_string())
    //     )
    // }

    // #[test]
    // fn is_positive_int_should_throw_an_error_with_float() {
    //     assert_eq!(
    //         is_positive_int("5.2"),
    //         Err("5.2 isn't a positive integer.".to_string())
    //     )
    // }
}
<commit_msg>restore commented positive_int validator tests<commit_after>pub fn is_positive_int(val: &str) -> Result<(), String> {
    match val.parse::<u64>() {
        Ok(val) if val > 0 => Ok(()),
        // If the the value is <= 0 or can't be parsed
        _ => Err(format!("{} isn't a positive integer.", val)),
    }
}

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

    #[test]
    fn is_positive_int_should_parse_a_positive_integer_from_input_string() {
        assert_eq!(is_positive_int("1"), Ok(()))
    }

    #[test]
    fn is_positive_int_should_throw_an_error_with_zero() {
        assert_eq!(
            is_positive_int("0"),
            Err("0 isn't a positive integer.".to_string())
        )
    }

    #[test]
    fn is_positive_int_should_throw_an_error_with_negative_integer() {
        assert_eq!(
            is_positive_int("-5"),
            Err("-5 isn't a positive integer.".to_string())
        )
    }

    #[test]
    fn is_positive_int_should_throw_an_error_with_float() {
        assert_eq!(
            is_positive_int("5.2"),
            Err("5.2 isn't a positive integer.".to_string())
        )
    }
}
<|endoftext|>