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

//! Element nodes.

use dom::attr::Attr;
use dom::attrlist::AttrList;
use dom::bindings::utils::{Reflectable, DOMString, ErrorResult, Fallible, Reflector};
use dom::bindings::utils::{null_str_as_empty, NamespaceError};
use dom::bindings::utils::{InvalidCharacter, QName, Name, InvalidXMLName, xml_name_type};
use dom::htmlcollection::HTMLCollection;
use dom::clientrect::ClientRect;
use dom::clientrectlist::ClientRectList;
use dom::document::AbstractDocument;
use dom::node::{AbstractNode, ElementNodeTypeId, Node};
use dom::document;
use dom::namespace;
use dom::namespace::{Namespace, Null};
use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery};
use layout_interface::{ContentBoxesResponse, ContentChangedDocumentDamage};
use layout_interface::{MatchSelectorsDocumentDamage};
use style;

use std::str::eq;
use std::ascii::StrAsciiExt;

pub struct Element {
    node: Node,
    tag_name: ~str,     // TODO: This should be an atom, not a ~str.
    namespace: Namespace,
    attrs: ~[@mut Attr],
    style_attribute: Option<style::PropertyDeclarationBlock>,
    attr_list: Option<@mut AttrList>
}

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

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.node.mut_reflector()
    }
}

#[deriving(Eq)]
pub enum ElementTypeId {
    HTMLElementTypeId,
    HTMLAnchorElementTypeId,
    HTMLAppletElementTypeId,
    HTMLAreaElementTypeId,
    HTMLAudioElementTypeId,
    HTMLBaseElementTypeId,
    HTMLBRElementTypeId,
    HTMLBodyElementTypeId,
    HTMLButtonElementTypeId,
    HTMLCanvasElementTypeId,
    HTMLDataElementTypeId,
    HTMLDataListElementTypeId,
    HTMLDirectoryElementTypeId,
    HTMLDListElementTypeId,
    HTMLDivElementTypeId,
    HTMLEmbedElementTypeId,
    HTMLFieldSetElementTypeId,
    HTMLFontElementTypeId,
    HTMLFormElementTypeId,
    HTMLFrameElementTypeId,
    HTMLFrameSetElementTypeId,
    HTMLHRElementTypeId,
    HTMLHeadElementTypeId,
    HTMLHeadingElementTypeId,
    HTMLHtmlElementTypeId,
    HTMLIframeElementTypeId,
    HTMLImageElementTypeId,
    HTMLInputElementTypeId,
    HTMLLabelElementTypeId,
    HTMLLegendElementTypeId,
    HTMLLinkElementTypeId,
    HTMLLIElementTypeId,
    HTMLMainElementTypeId,
    HTMLMapElementTypeId,
    HTMLMediaElementTypeId,
    HTMLMetaElementTypeId,
    HTMLMeterElementTypeId,
    HTMLModElementTypeId,
    HTMLObjectElementTypeId,
    HTMLOListElementTypeId,
    HTMLOptGroupElementTypeId,
    HTMLOptionElementTypeId,
    HTMLOutputElementTypeId,
    HTMLParagraphElementTypeId,
    HTMLParamElementTypeId,
    HTMLPreElementTypeId,
    HTMLProgressElementTypeId,
    HTMLQuoteElementTypeId,
    HTMLScriptElementTypeId,
    HTMLSelectElementTypeId,
    HTMLSourceElementTypeId,
    HTMLSpanElementTypeId,
    HTMLStyleElementTypeId,
    HTMLTableElementTypeId,
    HTMLTableCaptionElementTypeId,
    HTMLTableCellElementTypeId,
    HTMLTableDataCellElementTypeId,
    HTMLTableHeaderCellElementTypeId,
    HTMLTableColElementTypeId,
    HTMLTableRowElementTypeId,
    HTMLTableSectionElementTypeId,
    HTMLTemplateElementTypeId,
    HTMLTextAreaElementTypeId,
    HTMLTimeElementTypeId,
    HTMLTitleElementTypeId,
    HTMLTrackElementTypeId,
    HTMLUListElementTypeId,
    HTMLVideoElementTypeId,
    HTMLUnknownElementTypeId,
}

//
// Element methods
//

impl Element {
    pub fn new_inherited(type_id: ElementTypeId, tag_name: ~str, namespace: Namespace, document: AbstractDocument) -> Element {
        Element {
            node: Node::new_inherited(ElementNodeTypeId(type_id), document),
            tag_name: tag_name,
            namespace: namespace,
            attrs: ~[],
            attr_list: None,
            style_attribute: None,
        }
    }

    pub fn normalize_attr_name(&self, name: Option<DOMString>) -> ~str {
        //FIXME: Throw for XML-invalid names
        let owner = self.node.owner_doc();
        if owner.document().doctype == document::HTML { // && self.namespace == Namespace::HTML
            null_str_as_empty(&name).to_ascii_lower()
        } else {
            null_str_as_empty(&name)
        }
    }

    pub fn get_attribute(&self,
                         namespace: Namespace,
                         name: &str) -> Option<@mut Attr> {
        // FIXME: only case-insensitive in the HTML namespace (as opposed to SVG, etc.)
        let name = name.to_ascii_lower();
        self.attrs.iter().find(|attr| {
            name == attr.local_name && attr.namespace == namespace
        }).map(|&x| x)
    }

    // FIXME(pcwalton): This is kind of confusingly named relative to the above...
    pub fn get_attr(&self, namespace: Namespace, name: &str) -> Option<~str> {
        self.get_attribute(namespace, name).map(|attr| attr.value.clone())
    }

    pub fn set_attr(&mut self, abstract_self: AbstractNode, name: DOMString, value: DOMString)
                    -> ErrorResult {
        // FIXME: HTML-in-HTML only.
        let name = name.to_ascii_lower();
        self.set_attribute(abstract_self, namespace::Null, name, value)
    }

    pub fn set_attribute(&mut self,
                         abstract_self: AbstractNode,
                         namespace: Namespace,
                         name: DOMString,
                         value: DOMString) -> ErrorResult {
        let (prefix, local_name) = get_attribute_parts(name.clone());
        match prefix {
            Some(ref prefix_str) => {
                if (namespace == namespace::Null ||
                    (eq(prefix_str, &~"xml") && namespace != namespace::XML) ||
                    (eq(prefix_str, &~"xmlns") && namespace != namespace::XMLNS)) {
                    return Err(NamespaceError);
                }
            },
            None => {}
        }

        self.node.wait_until_safe_to_modify_dom();

        // FIXME: reduce the time of `value.clone()`.
        let mut old_raw_value: Option<DOMString> = None;
        for attr in self.attrs.iter() {
            if attr.local_name == local_name {
                old_raw_value = Some(attr.set_value(value.clone()));
                break;
            }
        }

        if old_raw_value.is_none() {
            let win = self.node.owner_doc().document().window;
            let new_attr = Attr::new_ns(win, local_name.clone(), value.clone(),
                                        name.clone(), namespace.clone(),
                                        prefix);
            self.attrs.push(new_attr);
        }

        if namespace == namespace::Null {
            self.after_set_attr(abstract_self, local_name, value, old_raw_value);
        }
        Ok(())
    }

    fn after_set_attr(&mut self,
                      abstract_self: AbstractNode,
                      local_name: DOMString,
                      value: DOMString,
                      old_value: Option<DOMString>) {

        match local_name.as_slice() {
            "style" => {
                self.style_attribute = Some(style::parse_style_attribute(value))
            }
            "id" => {
                let doc = self.node.owner_doc();
                let doc = doc.mut_document();
                doc.update_idmap(abstract_self, Some(value.clone()), old_value);
            }
            _ => ()
        }

        //XXXjdm We really need something like a vtable so we can call AfterSetAttr.
        //       This hardcoding is awful.
        match abstract_self.type_id() {
            ElementNodeTypeId(HTMLImageElementTypeId) => {
                abstract_self.with_mut_image_element(|image| {
                    image.AfterSetAttr(local_name.clone(), value.clone());
                });
            }
            ElementNodeTypeId(HTMLIframeElementTypeId) => {
                abstract_self.with_mut_iframe_element(|iframe| {
                    iframe.AfterSetAttr(local_name.clone(), value.clone());
                });
            }
            _ => ()
        }

        if abstract_self.is_in_doc() {
            let damage = match local_name.as_slice() {
                "style" | "id" | "class" => MatchSelectorsDocumentDamage,
                _ => ContentChangedDocumentDamage
            };
            let document = self.node.owner_doc();
            document.document().damage_and_reflow(damage);
        }
    }
}

// http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
impl Element {
    pub fn get_url_attribute(&self, name: &str) -> DOMString {
        // XXX Resolve URL.
        self.get_string_attribute(name)
    }
    pub fn set_url_attribute(&mut self, abstract_self: AbstractNode,
                             name: &str, value: DOMString) {
        self.set_string_attribute(abstract_self, name, value);
    }

    pub fn get_string_attribute(&self, name: &str) -> DOMString {
        match self.get_attr(Null, name) {
            Some(x) => x,
            None => ~""
        }
    }
    pub fn set_string_attribute(&mut self, abstract_self: AbstractNode,
                                name: &str, value: DOMString) {
        assert!(name == name.to_ascii_lower());
        self.set_attribute(abstract_self, Null, name.to_owned(), value);
    }
}

impl Element {
    pub fn TagName(&self) -> DOMString {
        self.tag_name.to_ascii_upper()
    }

    pub fn Id(&self, _abstract_self: AbstractNode) -> DOMString {
        self.get_string_attribute("id")
    }

    pub fn SetId(&mut self, abstract_self: AbstractNode, id: DOMString) {
        self.set_string_attribute(abstract_self, "id", id);
    }

    pub fn Attributes(&mut self, abstract_self: AbstractNode) -> @mut AttrList {
        match self.attr_list {
            None => {
                let window = self.node.owner_doc().document().window;
                let list = AttrList::new(window, abstract_self);
                self.attr_list = Some(list);
                list
            }
            Some(list) => list
        }
    }

    pub fn GetAttribute(&self, name: DOMString) -> Option<DOMString> {
        self.get_attr(Null, name).map(|s| s.to_owned())
    }

    pub fn GetAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> Option<DOMString> {
        let namespace = Namespace::from_str(namespace);
        self.get_attribute(namespace, local_name)
            .map(|attr| attr.value.clone())
    }

    pub fn SetAttribute(&mut self, abstract_self: AbstractNode, name: DOMString, value: DOMString)
                        -> ErrorResult {
        self.set_attr(abstract_self, name, value)
    }

    pub fn SetAttributeNS(&mut self,
                          abstract_self: AbstractNode,
                          namespace_url: Option<DOMString>,
                          name: DOMString,
                          value: DOMString) -> ErrorResult {
        let name_type = xml_name_type(name);
        match name_type {
            InvalidXMLName => return Err(InvalidCharacter),
            Name => return Err(NamespaceError),
            QName => {}
        }

        let namespace = Namespace::from_str(namespace_url);
        self.set_attribute(abstract_self, namespace, name, value)
    }

    pub fn RemoveAttribute(&self, _name: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn RemoveAttributeNS(&self, _namespace: Option<DOMString>, _localname: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn HasAttribute(&self, name: DOMString) -> bool {
        self.GetAttribute(name).is_some()
    }

    pub fn HasAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> bool {
        self.GetAttributeNS(namespace, local_name).is_some()
    }

    pub fn GetElementsByTagName(&self, _localname: DOMString) -> @mut HTMLCollection {
        HTMLCollection::new(self.node.owner_doc().document().window, ~[])
    }

    pub fn GetElementsByTagNameNS(&self, _namespace: Option<DOMString>, _localname: DOMString) -> Fallible<@mut HTMLCollection> {
        Ok(HTMLCollection::new(self.node.owner_doc().document().window, ~[]))
    }

    pub fn GetElementsByClassName(&self, _names: DOMString) -> @mut HTMLCollection {
        HTMLCollection::new(self.node.owner_doc().document().window, ~[])
    }

    pub fn MozMatchesSelector(&self, _selector: DOMString) -> Fallible<bool> {
        Ok(false)
    }

    pub fn SetCapture(&self, _retargetToElement: bool) {
    }

    pub fn ReleaseCapture(&self) {
    }

    pub fn MozRequestFullScreen(&self) {
    }

    pub fn MozRequestPointerLock(&self) {
    }

    pub fn GetClientRects(&self, abstract_self: AbstractNode) -> @mut ClientRectList {
        let win = self.node.owner_doc().document().window;
        let node = abstract_self;
        assert!(node.is_element());
        let (port, chan) = Chan::new();
        let rects =
            match win.page.query_layout(ContentBoxesQuery(node, chan), port) {
                ContentBoxesResponse(rects) => {
                    rects.map(|r| {
                        ClientRect::new(
                            win,
                            r.origin.y,
                            r.origin.y + r.size.height,
                            r.origin.x,
                            r.origin.x + r.size.width)
                    })
                },
            };

        ClientRectList::new(win, rects)
    }

    pub fn GetBoundingClientRect(&self, abstract_self: AbstractNode) -> @mut ClientRect {
        let win = self.node.owner_doc().document().window;
        let node = abstract_self;
        assert!(node.is_element());
        let (port, chan) = Chan::new();
        match win.page.query_layout(ContentBoxQuery(node, chan), port) {
            ContentBoxResponse(rect) => {
                ClientRect::new(
                    win,
                    rect.origin.y,
                    rect.origin.y + rect.size.height,
                    rect.origin.x,
                    rect.origin.x + rect.size.width)
            }
        }
    }

    pub fn ScrollIntoView(&self, _top: bool) {
    }

    pub fn ScrollTop(&self) -> i32 {
        0
    }

    pub fn SetScrollTop(&mut self, _scroll_top: i32) {
    }

    pub fn ScrollLeft(&self) -> i32 {
        0
    }

    pub fn SetScrollLeft(&mut self, _scroll_left: i32) {
    }

    pub fn ScrollWidth(&self) -> i32 {
        0
    }

    pub fn ScrollHeight(&self) -> i32 {
        0
    }

    pub fn ClientTop(&self) -> i32 {
        0
    }

    pub fn ClientLeft(&self) -> i32 {
        0
    }

    pub fn ClientWidth(&self) -> i32 {
        0
    }

    pub fn ClientHeight(&self) -> i32 {
        0
    }

    pub fn GetInnerHTML(&self) -> Fallible<DOMString> {
        Ok(~"")
    }

    pub fn SetInnerHTML(&mut self, _value: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn GetOuterHTML(&self) -> Fallible<DOMString> {
        Ok(~"")
    }

    pub fn SetOuterHTML(&mut self, _value: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn InsertAdjacentHTML(&mut self, _position: DOMString, _text: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn QuerySelector(&self, _selectors: DOMString) -> Fallible<Option<AbstractNode>> {
        Ok(None)
    }
}

fn get_attribute_parts(name: DOMString) -> (Option<~str>, ~str) {
    //FIXME: Throw for XML-invalid names
    //FIXME: Throw for XMLNS-invalid names
    let (prefix, local_name) = if name.contains(":")  {
        let parts: ~[&str] = name.splitn(':', 1).collect();
        (Some(parts[0].to_owned()), parts[1].to_owned())
    } else {
        (None, name)
    };

    (prefix, local_name)
}
<commit_msg>Add the comment to explain the dual declaration.<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/. */

//! Element nodes.

use dom::attr::Attr;
use dom::attrlist::AttrList;
use dom::bindings::utils::{Reflectable, DOMString, ErrorResult, Fallible, Reflector};
use dom::bindings::utils::{null_str_as_empty, NamespaceError};
use dom::bindings::utils::{InvalidCharacter, QName, Name, InvalidXMLName, xml_name_type};
use dom::htmlcollection::HTMLCollection;
use dom::clientrect::ClientRect;
use dom::clientrectlist::ClientRectList;
use dom::document::AbstractDocument;
use dom::node::{AbstractNode, ElementNodeTypeId, Node};
use dom::document;
use dom::namespace;
use dom::namespace::{Namespace, Null};
use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery};
use layout_interface::{ContentBoxesResponse, ContentChangedDocumentDamage};
use layout_interface::{MatchSelectorsDocumentDamage};
use style;

use std::str::eq;
use std::ascii::StrAsciiExt;

pub struct Element {
    node: Node,
    tag_name: ~str,     // TODO: This should be an atom, not a ~str.
    namespace: Namespace,
    attrs: ~[@mut Attr],
    style_attribute: Option<style::PropertyDeclarationBlock>,
    attr_list: Option<@mut AttrList>
}

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

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.node.mut_reflector()
    }
}

#[deriving(Eq)]
pub enum ElementTypeId {
    HTMLElementTypeId,
    HTMLAnchorElementTypeId,
    HTMLAppletElementTypeId,
    HTMLAreaElementTypeId,
    HTMLAudioElementTypeId,
    HTMLBaseElementTypeId,
    HTMLBRElementTypeId,
    HTMLBodyElementTypeId,
    HTMLButtonElementTypeId,
    HTMLCanvasElementTypeId,
    HTMLDataElementTypeId,
    HTMLDataListElementTypeId,
    HTMLDirectoryElementTypeId,
    HTMLDListElementTypeId,
    HTMLDivElementTypeId,
    HTMLEmbedElementTypeId,
    HTMLFieldSetElementTypeId,
    HTMLFontElementTypeId,
    HTMLFormElementTypeId,
    HTMLFrameElementTypeId,
    HTMLFrameSetElementTypeId,
    HTMLHRElementTypeId,
    HTMLHeadElementTypeId,
    HTMLHeadingElementTypeId,
    HTMLHtmlElementTypeId,
    HTMLIframeElementTypeId,
    HTMLImageElementTypeId,
    HTMLInputElementTypeId,
    HTMLLabelElementTypeId,
    HTMLLegendElementTypeId,
    HTMLLinkElementTypeId,
    HTMLLIElementTypeId,
    HTMLMainElementTypeId,
    HTMLMapElementTypeId,
    HTMLMediaElementTypeId,
    HTMLMetaElementTypeId,
    HTMLMeterElementTypeId,
    HTMLModElementTypeId,
    HTMLObjectElementTypeId,
    HTMLOListElementTypeId,
    HTMLOptGroupElementTypeId,
    HTMLOptionElementTypeId,
    HTMLOutputElementTypeId,
    HTMLParagraphElementTypeId,
    HTMLParamElementTypeId,
    HTMLPreElementTypeId,
    HTMLProgressElementTypeId,
    HTMLQuoteElementTypeId,
    HTMLScriptElementTypeId,
    HTMLSelectElementTypeId,
    HTMLSourceElementTypeId,
    HTMLSpanElementTypeId,
    HTMLStyleElementTypeId,
    HTMLTableElementTypeId,
    HTMLTableCaptionElementTypeId,
    HTMLTableCellElementTypeId,
    HTMLTableDataCellElementTypeId,
    HTMLTableHeaderCellElementTypeId,
    HTMLTableColElementTypeId,
    HTMLTableRowElementTypeId,
    HTMLTableSectionElementTypeId,
    HTMLTemplateElementTypeId,
    HTMLTextAreaElementTypeId,
    HTMLTimeElementTypeId,
    HTMLTitleElementTypeId,
    HTMLTrackElementTypeId,
    HTMLUListElementTypeId,
    HTMLVideoElementTypeId,
    HTMLUnknownElementTypeId,
}

//
// Element methods
//

impl Element {
    pub fn new_inherited(type_id: ElementTypeId, tag_name: ~str, namespace: Namespace, document: AbstractDocument) -> Element {
        Element {
            node: Node::new_inherited(ElementNodeTypeId(type_id), document),
            tag_name: tag_name,
            namespace: namespace,
            attrs: ~[],
            attr_list: None,
            style_attribute: None,
        }
    }

    pub fn normalize_attr_name(&self, name: Option<DOMString>) -> ~str {
        //FIXME: Throw for XML-invalid names
        let owner = self.node.owner_doc();
        if owner.document().doctype == document::HTML { // && self.namespace == Namespace::HTML
            null_str_as_empty(&name).to_ascii_lower()
        } else {
            null_str_as_empty(&name)
        }
    }

    pub fn get_attribute(&self,
                         namespace: Namespace,
                         name: &str) -> Option<@mut Attr> {
        // FIXME: only case-insensitive in the HTML namespace (as opposed to SVG, etc.)
        let name = name.to_ascii_lower();
        self.attrs.iter().find(|attr| {
            name == attr.local_name && attr.namespace == namespace
        }).map(|&x| x)
    }

    // FIXME(pcwalton): This is kind of confusingly named relative to the above...
    pub fn get_attr(&self, namespace: Namespace, name: &str) -> Option<~str> {
        self.get_attribute(namespace, name).map(|attr| attr.value.clone())
    }

    pub fn set_attr(&mut self, abstract_self: AbstractNode, name: DOMString, value: DOMString)
                    -> ErrorResult {
        // FIXME: HTML-in-HTML only.
        let name = name.to_ascii_lower();
        self.set_attribute(abstract_self, namespace::Null, name, value)
    }

    pub fn set_attribute(&mut self,
                         abstract_self: AbstractNode,
                         namespace: Namespace,
                         name: DOMString,
                         value: DOMString) -> ErrorResult {
        let (prefix, local_name) = get_attribute_parts(name.clone());
        match prefix {
            Some(ref prefix_str) => {
                if (namespace == namespace::Null ||
                    (eq(prefix_str, &~"xml") && namespace != namespace::XML) ||
                    (eq(prefix_str, &~"xmlns") && namespace != namespace::XMLNS)) {
                    return Err(NamespaceError);
                }
            },
            None => {}
        }

        self.node.wait_until_safe_to_modify_dom();

        // FIXME: reduce the time of `value.clone()`.
        let mut old_raw_value: Option<DOMString> = None;
        for attr in self.attrs.iter() {
            if attr.local_name == local_name {
                old_raw_value = Some(attr.set_value(value.clone()));
                break;
            }
        }

        if old_raw_value.is_none() {
            let win = self.node.owner_doc().document().window;
            let new_attr = Attr::new_ns(win, local_name.clone(), value.clone(),
                                        name.clone(), namespace.clone(),
                                        prefix);
            self.attrs.push(new_attr);
        }

        if namespace == namespace::Null {
            self.after_set_attr(abstract_self, local_name, value, old_raw_value);
        }
        Ok(())
    }

    fn after_set_attr(&mut self,
                      abstract_self: AbstractNode,
                      local_name: DOMString,
                      value: DOMString,
                      old_value: Option<DOMString>) {

        match local_name.as_slice() {
            "style" => {
                self.style_attribute = Some(style::parse_style_attribute(value))
            }
            "id" => {
                // XXX: this dual declaration are workaround to avoid the compile error:
                // "borrowed value does not live long enough"
                let doc = self.node.owner_doc();
                let doc = doc.mut_document();
                doc.update_idmap(abstract_self, Some(value.clone()), old_value);
            }
            _ => ()
        }

        //XXXjdm We really need something like a vtable so we can call AfterSetAttr.
        //       This hardcoding is awful.
        match abstract_self.type_id() {
            ElementNodeTypeId(HTMLImageElementTypeId) => {
                abstract_self.with_mut_image_element(|image| {
                    image.AfterSetAttr(local_name.clone(), value.clone());
                });
            }
            ElementNodeTypeId(HTMLIframeElementTypeId) => {
                abstract_self.with_mut_iframe_element(|iframe| {
                    iframe.AfterSetAttr(local_name.clone(), value.clone());
                });
            }
            _ => ()
        }

        if abstract_self.is_in_doc() {
            let damage = match local_name.as_slice() {
                "style" | "id" | "class" => MatchSelectorsDocumentDamage,
                _ => ContentChangedDocumentDamage
            };
            let document = self.node.owner_doc();
            document.document().damage_and_reflow(damage);
        }
    }
}

// http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
impl Element {
    pub fn get_url_attribute(&self, name: &str) -> DOMString {
        // XXX Resolve URL.
        self.get_string_attribute(name)
    }
    pub fn set_url_attribute(&mut self, abstract_self: AbstractNode,
                             name: &str, value: DOMString) {
        self.set_string_attribute(abstract_self, name, value);
    }

    pub fn get_string_attribute(&self, name: &str) -> DOMString {
        match self.get_attr(Null, name) {
            Some(x) => x,
            None => ~""
        }
    }
    pub fn set_string_attribute(&mut self, abstract_self: AbstractNode,
                                name: &str, value: DOMString) {
        assert!(name == name.to_ascii_lower());
        self.set_attribute(abstract_self, Null, name.to_owned(), value);
    }
}

impl Element {
    pub fn TagName(&self) -> DOMString {
        self.tag_name.to_ascii_upper()
    }

    pub fn Id(&self, _abstract_self: AbstractNode) -> DOMString {
        self.get_string_attribute("id")
    }

    pub fn SetId(&mut self, abstract_self: AbstractNode, id: DOMString) {
        self.set_string_attribute(abstract_self, "id", id);
    }

    pub fn Attributes(&mut self, abstract_self: AbstractNode) -> @mut AttrList {
        match self.attr_list {
            None => {
                let window = self.node.owner_doc().document().window;
                let list = AttrList::new(window, abstract_self);
                self.attr_list = Some(list);
                list
            }
            Some(list) => list
        }
    }

    pub fn GetAttribute(&self, name: DOMString) -> Option<DOMString> {
        self.get_attr(Null, name).map(|s| s.to_owned())
    }

    pub fn GetAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> Option<DOMString> {
        let namespace = Namespace::from_str(namespace);
        self.get_attribute(namespace, local_name)
            .map(|attr| attr.value.clone())
    }

    pub fn SetAttribute(&mut self, abstract_self: AbstractNode, name: DOMString, value: DOMString)
                        -> ErrorResult {
        self.set_attr(abstract_self, name, value)
    }

    pub fn SetAttributeNS(&mut self,
                          abstract_self: AbstractNode,
                          namespace_url: Option<DOMString>,
                          name: DOMString,
                          value: DOMString) -> ErrorResult {
        let name_type = xml_name_type(name);
        match name_type {
            InvalidXMLName => return Err(InvalidCharacter),
            Name => return Err(NamespaceError),
            QName => {}
        }

        let namespace = Namespace::from_str(namespace_url);
        self.set_attribute(abstract_self, namespace, name, value)
    }

    pub fn RemoveAttribute(&self, _name: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn RemoveAttributeNS(&self, _namespace: Option<DOMString>, _localname: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn HasAttribute(&self, name: DOMString) -> bool {
        self.GetAttribute(name).is_some()
    }

    pub fn HasAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> bool {
        self.GetAttributeNS(namespace, local_name).is_some()
    }

    pub fn GetElementsByTagName(&self, _localname: DOMString) -> @mut HTMLCollection {
        HTMLCollection::new(self.node.owner_doc().document().window, ~[])
    }

    pub fn GetElementsByTagNameNS(&self, _namespace: Option<DOMString>, _localname: DOMString) -> Fallible<@mut HTMLCollection> {
        Ok(HTMLCollection::new(self.node.owner_doc().document().window, ~[]))
    }

    pub fn GetElementsByClassName(&self, _names: DOMString) -> @mut HTMLCollection {
        HTMLCollection::new(self.node.owner_doc().document().window, ~[])
    }

    pub fn MozMatchesSelector(&self, _selector: DOMString) -> Fallible<bool> {
        Ok(false)
    }

    pub fn SetCapture(&self, _retargetToElement: bool) {
    }

    pub fn ReleaseCapture(&self) {
    }

    pub fn MozRequestFullScreen(&self) {
    }

    pub fn MozRequestPointerLock(&self) {
    }

    pub fn GetClientRects(&self, abstract_self: AbstractNode) -> @mut ClientRectList {
        let win = self.node.owner_doc().document().window;
        let node = abstract_self;
        assert!(node.is_element());
        let (port, chan) = Chan::new();
        let rects =
            match win.page.query_layout(ContentBoxesQuery(node, chan), port) {
                ContentBoxesResponse(rects) => {
                    rects.map(|r| {
                        ClientRect::new(
                            win,
                            r.origin.y,
                            r.origin.y + r.size.height,
                            r.origin.x,
                            r.origin.x + r.size.width)
                    })
                },
            };

        ClientRectList::new(win, rects)
    }

    pub fn GetBoundingClientRect(&self, abstract_self: AbstractNode) -> @mut ClientRect {
        let win = self.node.owner_doc().document().window;
        let node = abstract_self;
        assert!(node.is_element());
        let (port, chan) = Chan::new();
        match win.page.query_layout(ContentBoxQuery(node, chan), port) {
            ContentBoxResponse(rect) => {
                ClientRect::new(
                    win,
                    rect.origin.y,
                    rect.origin.y + rect.size.height,
                    rect.origin.x,
                    rect.origin.x + rect.size.width)
            }
        }
    }

    pub fn ScrollIntoView(&self, _top: bool) {
    }

    pub fn ScrollTop(&self) -> i32 {
        0
    }

    pub fn SetScrollTop(&mut self, _scroll_top: i32) {
    }

    pub fn ScrollLeft(&self) -> i32 {
        0
    }

    pub fn SetScrollLeft(&mut self, _scroll_left: i32) {
    }

    pub fn ScrollWidth(&self) -> i32 {
        0
    }

    pub fn ScrollHeight(&self) -> i32 {
        0
    }

    pub fn ClientTop(&self) -> i32 {
        0
    }

    pub fn ClientLeft(&self) -> i32 {
        0
    }

    pub fn ClientWidth(&self) -> i32 {
        0
    }

    pub fn ClientHeight(&self) -> i32 {
        0
    }

    pub fn GetInnerHTML(&self) -> Fallible<DOMString> {
        Ok(~"")
    }

    pub fn SetInnerHTML(&mut self, _value: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn GetOuterHTML(&self) -> Fallible<DOMString> {
        Ok(~"")
    }

    pub fn SetOuterHTML(&mut self, _value: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn InsertAdjacentHTML(&mut self, _position: DOMString, _text: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn QuerySelector(&self, _selectors: DOMString) -> Fallible<Option<AbstractNode>> {
        Ok(None)
    }
}

fn get_attribute_parts(name: DOMString) -> (Option<~str>, ~str) {
    //FIXME: Throw for XML-invalid names
    //FIXME: Throw for XMLNS-invalid names
    let (prefix, local_name) = if name.contains(":")  {
        let parts: ~[&str] = name.splitn(':', 1).collect();
        (Some(parts[0].to_owned()), parts[1].to_owned())
    } else {
        (None, name)
    };

    (prefix, local_name)
}
<|endoftext|>


<commit_before>use crate::utils::{is_type_diagnostic_item, match_def_path, paths, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Local, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol};
use std::convert::TryInto;

declare_clippy_lint! {
    /// **What it does:** Checks for calls to `push` immediately after creating a new `Vec`.
    ///
    /// **Why is this bad?** The `vec![]` macro is both more performant and easier to read than
    /// multiple `push` calls.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// let mut v = Vec::new();
    /// v.push(0);
    /// ```
    /// Use instead:
    /// ```rust
    /// let v = vec![0];
    /// ```
    pub VEC_INIT_THEN_PUSH,
    perf,
    "`push` immediately after `Vec` creation"
}

impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]);

#[derive(Default)]
pub struct VecInitThenPush {
    searcher: Option<VecPushSearcher>,
}

#[derive(Clone, Copy)]
enum VecInitKind {
    New,
    WithCapacity(u64),
}
struct VecPushSearcher {
    init: VecInitKind,
    name: Symbol,
    lhs_is_local: bool,
    lhs_span: Span,
    err_span: Span,
    found: u64,
}
impl VecPushSearcher {
    fn display_err(&self, cx: &LateContext<'_>) {
        match self.init {
            _ if self.found == 0 => return,
            VecInitKind::WithCapacity(x) if x > self.found => return,
            _ => (),
        };

        let mut s = if self.lhs_is_local {
            String::from("let ")
        } else {
            String::new()
        };
        s.push_str(&snippet(cx, self.lhs_span, ".."));
        s.push_str(" = vec![..];");

        span_lint_and_sugg(
            cx,
            VEC_INIT_THEN_PUSH,
            self.err_span,
            "calls to `push` immediately after creation",
            "consider using the `vec![]` macro",
            s,
            Applicability::HasPlaceholders,
        );
    }
}

impl LateLintPass<'_> for VecInitThenPush {
    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
        self.searcher = None;
        if_chain! {
            if !in_external_macro(cx.sess(), local.span);
            if let Some(init) = local.init;
            if let PatKind::Binding(BindingAnnotation::Mutable, _, ident, None) = local.pat.kind;
            if let Some(init_kind) = get_vec_init_kind(cx, init);
            then {
                self.searcher = Some(VecPushSearcher {
                        init: init_kind,
                        name: ident.name,
                        lhs_is_local: true,
                        lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)),
                        err_span: local.span,
                        found: 0,
                    });
            }
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if self.searcher.is_none() {
            if_chain! {
                if !in_external_macro(cx.sess(), expr.span);
                if let ExprKind::Assign(left, right, _) = expr.kind;
                if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind;
                if let Some(name) = path.segments.get(0);
                if let Some(init_kind) = get_vec_init_kind(cx, right);
                then {
                    self.searcher = Some(VecPushSearcher {
                        init: init_kind,
                        name: name.ident.name,
                        lhs_is_local: false,
                        lhs_span: left.span,
                        err_span: expr.span,
                        found: 0,
                    });
                }
            }
        }
    }

    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
        if let Some(searcher) = self.searcher.take() {
            if_chain! {
                if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind;
                if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind;
                if path.ident.name.as_str() == "push";
                if let ExprKind::Path(QPath::Resolved(_, self_path)) = self_arg.kind;
                if let [self_name] = self_path.segments;
                if self_name.ident.name == searcher.name;
                then {
                    self.searcher = Some(VecPushSearcher {
                        found: searcher.found + 1,
                        err_span: searcher.err_span.to(stmt.span),
                        .. searcher
                    });
                } else {
                    searcher.display_err(cx);
                }
            }
        }
    }

    fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
        if let Some(searcher) = self.searcher.take() {
            searcher.display_err(cx);
        }
    }
}

fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
    if let ExprKind::Call(func, args) = expr.kind {
        match func.kind {
            ExprKind::Path(QPath::TypeRelative(ty, name))
                if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type) =>
            {
                if name.ident.name == sym::new {
                    return Some(VecInitKind::New);
                } else if name.ident.name.as_str() == "with_capacity" {
                    return args.get(0).and_then(|arg| {
                        if_chain! {
                            if let ExprKind::Lit(lit) = &arg.kind;
                            if let LitKind::Int(num, _) = lit.node;
                            then {
                                Some(VecInitKind::WithCapacity(num.try_into().ok()?))
                            } else {
                                None
                            }
                        }
                    });
                }
            }
            ExprKind::Path(QPath::Resolved(_, path))
                if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
                    && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type) =>
            {
                return Some(VecInitKind::New);
            }
            _ => (),
        }
    }
    None
}
<commit_msg>Use id instead of name<commit_after>use crate::utils::{
    is_type_diagnostic_item, match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span};
use std::convert::TryInto;

declare_clippy_lint! {
    /// **What it does:** Checks for calls to `push` immediately after creating a new `Vec`.
    ///
    /// **Why is this bad?** The `vec![]` macro is both more performant and easier to read than
    /// multiple `push` calls.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// let mut v = Vec::new();
    /// v.push(0);
    /// ```
    /// Use instead:
    /// ```rust
    /// let v = vec![0];
    /// ```
    pub VEC_INIT_THEN_PUSH,
    perf,
    "`push` immediately after `Vec` creation"
}

impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]);

#[derive(Default)]
pub struct VecInitThenPush {
    searcher: Option<VecPushSearcher>,
}

#[derive(Clone, Copy)]
enum VecInitKind {
    New,
    WithCapacity(u64),
}
struct VecPushSearcher {
    local_id: HirId,
    init: VecInitKind,
    lhs_is_local: bool,
    lhs_span: Span,
    err_span: Span,
    found: u64,
}
impl VecPushSearcher {
    fn display_err(&self, cx: &LateContext<'_>) {
        match self.init {
            _ if self.found == 0 => return,
            VecInitKind::WithCapacity(x) if x > self.found => return,
            _ => (),
        };

        let mut s = if self.lhs_is_local {
            String::from("let ")
        } else {
            String::new()
        };
        s.push_str(&snippet(cx, self.lhs_span, ".."));
        s.push_str(" = vec![..];");

        span_lint_and_sugg(
            cx,
            VEC_INIT_THEN_PUSH,
            self.err_span,
            "calls to `push` immediately after creation",
            "consider using the `vec![]` macro",
            s,
            Applicability::HasPlaceholders,
        );
    }
}

impl LateLintPass<'_> for VecInitThenPush {
    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
        self.searcher = None;
        if_chain! {
            if !in_external_macro(cx.sess(), local.span);
            if let Some(init) = local.init;
            if let PatKind::Binding(BindingAnnotation::Mutable, id, _, None) = local.pat.kind;
            if let Some(init_kind) = get_vec_init_kind(cx, init);
            then {
                self.searcher = Some(VecPushSearcher {
                        local_id: id,
                        init: init_kind,
                        lhs_is_local: true,
                        lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)),
                        err_span: local.span,
                        found: 0,
                    });
            }
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if self.searcher.is_none() {
            if_chain! {
                if !in_external_macro(cx.sess(), expr.span);
                if let ExprKind::Assign(left, right, _) = expr.kind;
                if let Some(id) = path_to_local(left);
                if let Some(init_kind) = get_vec_init_kind(cx, right);
                then {
                    self.searcher = Some(VecPushSearcher {
                        local_id: id,
                        init: init_kind,
                        lhs_is_local: false,
                        lhs_span: left.span,
                        err_span: expr.span,
                        found: 0,
                    });
                }
            }
        }
    }

    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
        if let Some(searcher) = self.searcher.take() {
            if_chain! {
                if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind;
                if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind;
                if path_to_local_id(self_arg, searcher.local_id);
                if path.ident.name.as_str() == "push";
                then {
                    self.searcher = Some(VecPushSearcher {
                        found: searcher.found + 1,
                        err_span: searcher.err_span.to(stmt.span),
                        .. searcher
                    });
                } else {
                    searcher.display_err(cx);
                }
            }
        }
    }

    fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
        if let Some(searcher) = self.searcher.take() {
            searcher.display_err(cx);
        }
    }
}

fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
    if let ExprKind::Call(func, args) = expr.kind {
        match func.kind {
            ExprKind::Path(QPath::TypeRelative(ty, name))
                if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type) =>
            {
                if name.ident.name == sym::new {
                    return Some(VecInitKind::New);
                } else if name.ident.name.as_str() == "with_capacity" {
                    return args.get(0).and_then(|arg| {
                        if_chain! {
                            if let ExprKind::Lit(lit) = &arg.kind;
                            if let LitKind::Int(num, _) = lit.node;
                            then {
                                Some(VecInitKind::WithCapacity(num.try_into().ok()?))
                            } else {
                                None
                            }
                        }
                    });
                }
            }
            ExprKind::Path(QPath::Resolved(_, path))
                if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
                    && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type) =>
            {
                return Some(VecInitKind::New);
            }
            _ => (),
        }
    }
    None
}
<|endoftext|>


<commit_before>use Column;
use Conn;
use Row;
use Stmt;

use conn::stmt::{
    InnerStmt,
    new_stmt,
};

use consts;

use either::{
    Either,
    Left,
    Right,
};

use errors::*;

use lib_futures::{
    Async,
    Future,
    Poll,
};
use lib_futures::stream::Stream;

use proto::{
    OkPacket,
    Packet,
    PacketType,
};

use super::{
    NewTextQueryResult,
    ReadPacket,
};

use std::borrow::Cow;
use std::mem;
use std::ops::Deref;
use std::sync::Arc;

use value::{
    FromRow,
    Value,
};

mod futures;

pub use self::futures::{
    BinCollect,
    BinMap,
    BinReduce,
    Collect,
    CollectAll,
    Map,
    Reduce,
};
use self::futures::{
    new_bin_collect,
    new_bin_map,
    new_bin_reduce,
    new_collect,
    new_collect_all,
    new_map,
    new_reduce,
};

/// This type stores collected result set.
pub struct ResultSet<T>(Vec<T>, TextQueryResult);

impl<T> ResultSet<T> {
    /// Affected rows value returned by a server, if any.
    pub fn affected_rows(&self) -> Option<u64> {
        self.1.affected_rows()
    }

    /// Last insert id value returned by a server, if any.
    pub fn last_insert_id(&self) -> Option<u64> {
        self.1.last_insert_id()
    }

    /// Warnings count value returned by a server, if any.
    pub fn warnings(&self) -> Option<u16> {
        self.1.warnings()
    }

    /// Bytes of info string returned by a server, if any.
    pub fn info_bytes(&self) -> Option<&[u8]> {
        self.1.info_bytes()
    }

    /// Info bytes lossy converted to UTF-8, if any.
    pub fn info(&self) -> Option<Cow<str>> {
        self.1.info()
    }

    /// Bytes of session state changed value returned by a server, if any.
    pub fn session_state_changes_bytes(&self) -> Option<&[u8]> {
        self.1.session_state_changes_bytes()
    }

    /// Session state changed value lossy converted to UTF-8, if any.
    pub fn session_state_changes(&self) -> Option<Cow<str>> {
        self.1.session_state_changes()
    }
}

impl<R> Deref for ResultSet<R> {
    type Target = [R];

    fn deref(&self) -> &Self::Target {
        self.0.deref()
    }
}

impl<R> IntoIterator for ResultSet<R> {
    type Item = R;
    type IntoIter = ::std::vec::IntoIter<R>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

pub enum MaybeRow {
    /// Row of a result set
    Row(Row),
    /// Either next result set of a multi-result set if it exists or wrapped `Conn`.
    End(Either<TextQueryResult, Conn>),
}

enum Step {
    ReadPacket(ReadPacket),
    NextResult(NewTextQueryResult),
    Done(Conn),
    Consumed,
}

/// Mysql query result set.
///
/// `TextQueryResult` may contain zero or more rows. It may also be followed by another result set in
/// case of multi-result set.
pub struct TextQueryResult {
    step: Step,
    columns: Arc<Vec<Column>>,
    ok_packet: Option<OkPacket>,
    is_bin: bool,
}

pub fn new<T: Into<Arc<Vec<Column>>>>(mut conn: Conn,
                                      columns: T,
                                      ok_packet: Option<OkPacket>,
                                      is_bin: bool) -> TextQueryResult
{
    let columns = columns.into();
    let cols_len = columns.len();
    conn.has_result = Some((columns.clone(), ok_packet.clone(), is_bin));
    TextQueryResult {
        step: if cols_len == 0 { Step::Done(conn) } else { Step::ReadPacket(conn.read_packet()) },
        columns: columns,
        ok_packet: ok_packet,
        is_bin: is_bin,
    }
}

impl TextQueryResult {
    /// Collects remaining rows of this result set and returns them as `ResultSet`.
    ///
    /// Second element of a resulting tuple is either next `TextQueryResult` if it exists in
    /// multi-result set or `Conn`.
    pub fn collect<R>(self) -> Collect<R>
        where R: FromRow,
              R: Send + 'static,
    {
        new_collect(self)
    }

    /// Collects remaining rows of this and all rows of following result sets.
    pub fn collect_all(self) -> CollectAll {
        new_collect_all(self)
    }

    pub fn map<F, U>(self, fun: F) -> Map<F, U>
    where F: FnMut(Row) -> U,
          Self: Sized,
    {
        new_map(self, fun)
    }

    pub fn reduce<A, F>(self, init: A, fun: F) -> Reduce<A, F>
    where F: FnMut(A, Row) -> A,
          A: Send + 'static,
          Self: Sized,
    {
        new_reduce(self, init, fun)
    }

    /// Affected rows value returned by a server, if any.
    pub fn affected_rows(&self) -> Option<u64> {
        self.ok_packet.as_ref().map(OkPacket::affected_rows)
    }

    /// Last insert id value returned by a server, if any.
    pub fn last_insert_id(&self) -> Option<u64> {
        self.ok_packet.as_ref().map(OkPacket::last_insert_id)
    }

    /// Warnings count value returned by a server, if any.
    pub fn warnings(&self) -> Option<u16> {
        self.ok_packet.as_ref().map(OkPacket::warnings)
    }

    /// Bytes of info string returned by a server, if any.
    pub fn info_bytes(&self) -> Option<&[u8]> {
        self.ok_packet.as_ref().map(OkPacket::info_bytes)
    }

    /// Info bytes lossy converted to UTF-8, if any.
    pub fn info(&self) -> Option<Cow<str>> {
        self.ok_packet.as_ref().map(OkPacket::info)
    }

    /// Bytes of session state changed value returned by a server, if any.
    pub fn session_state_changes_bytes(&self) -> Option<&[u8]> {
        self.ok_packet.as_ref().and_then(OkPacket::session_state_changes_bytes)
    }

    /// Session state changed value lossy converted to UTF-8, if any.
    pub fn session_state_changes(&self) -> Option<Cow<str>> {
        self.ok_packet.as_ref().and_then(OkPacket::session_state_changes)
    }

    fn either_poll(&mut self) -> Result<Async<Option<Either<(Conn, Packet),
                                                            Either<TextQueryResult, ()>>>>> {
        match self.step {
            Step::ReadPacket(ref mut fut) => {
                Ok(Async::Ready(Some(Left(try_ready!(fut.poll())))))
            },
            Step::NextResult(ref mut fut) => {
                Ok(Async::Ready(Some(Right(Left(try_ready!(fut.poll()))))))
            },
            Step::Done(_) => {
                Ok(Async::Ready(Some(Right(Right(())))))
            },
            Step::Consumed => {
                Ok(Async::Ready(None))
            },
        }
    }
}

impl Stream for TextQueryResult {
    type Item = MaybeRow;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match try_ready!(self.either_poll()) {
            Some(Left((conn, packet))) => {
                if packet.is(PacketType::Eof) {
                    if conn.status.contains(consts::SERVER_MORE_RESULTS_EXISTS) {
                        self.step = Step::NextResult(conn.handle_text_resultset());
                        self.poll()
                    } else {
                        conn.has_result = None;
                        self.step = Step::Done(conn);
                        self.poll()
                    }
                } else {
                    let values = if self.is_bin {
                        try!(Value::from_bin_payload(packet.as_ref(), &self.columns))
                    } else {
                        try!(Value::from_payload(packet.as_ref(), self.columns.len()))
                    };
                    let row = Row::new(values, self.columns.clone());
                    self.step = Step::ReadPacket(conn.read_packet());
                    Ok(Async::Ready(Some(MaybeRow::Row(row))))
                }
            },
            Some(Right(Left(query_result))) => {
                self.step = Step::Consumed;
                Ok(Async::Ready(Some(MaybeRow::End(Left(query_result)))))
            },
            Some(Right(Right(()))) => {
                if let Step::Done(conn) = mem::replace(&mut self.step, Step::Consumed) {
                    Ok(Async::Ready(Some(MaybeRow::End(Right(conn)))))
                } else {
                    unreachable!();
                }
            },
            None => {
                Ok(Async::Ready(None))
            },
        }
    }
}

pub struct BinQueryResult {
    wrapped: TextQueryResult,
    inner_stmt: InnerStmt,
}

pub fn new_bin(wrapped: TextQueryResult, inner_stmt: InnerStmt) -> BinQueryResult {
    BinQueryResult {
        wrapped: wrapped,
        inner_stmt: inner_stmt,
    }
}

pub fn unwrap_bin(bin_query_result: BinQueryResult) -> (TextQueryResult, InnerStmt) {
    let BinQueryResult {
        wrapped,
        inner_stmt,
    } = bin_query_result;
    (wrapped, inner_stmt)
}

impl BinQueryResult {
    /// Collects remaining rows of this result set and returns them as `ResultSet`.
    ///
    /// Second element of a resulting tuple is either next `TextQueryResult` if it exists in
    /// multi-result set or `Conn`.
    pub fn collect<R>(self) -> BinCollect<R>
        where R: FromRow,
              R: Send + 'static,
    {
        new_bin_collect(self)
    }

    pub fn map<F, U>(self, fun: F) -> BinMap<F, U>
        where F: FnMut(Row) -> U,
              Self: Sized,
    {
        new_bin_map(self, fun)
    }

    pub fn reduce<A, F>(self, init: A, fun: F) -> BinReduce<A, F>
        where F: FnMut(A, Row) -> A,
              A: Send + 'static,
              Self: Sized,
    {
        new_bin_reduce(self, init, fun)
    }

    /// Affected rows value returned by a server, if any.
    pub fn affected_rows(&self) -> Option<u64> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::affected_rows)
    }

    /// Last insert id value returned by a server, if any.
    pub fn last_insert_id(&self) -> Option<u64> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::last_insert_id)
    }

    /// Warnings count value returned by a server, if any.
    pub fn warnings(&self) -> Option<u16> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::warnings)
    }

    /// Bytes of info string returned by a server, if any.
    pub fn info_bytes(&self) -> Option<&[u8]> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::info_bytes)
    }

    /// Info bytes lossy converted to UTF-8, if any.
    pub fn info(&self) -> Option<Cow<str>> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::info)
    }

    /// Bytes of session state changed value returned by a server, if any.
    pub fn session_state_changes_bytes(&self) -> Option<&[u8]> {
        self.wrapped.ok_packet.as_ref().and_then(OkPacket::session_state_changes_bytes)
    }

    /// Session state changed value lossy converted to UTF-8, if any.
    pub fn session_state_changes(&self) -> Option<Cow<str>> {
        self.wrapped.ok_packet.as_ref().and_then(OkPacket::session_state_changes)
    }
}

pub enum BinMaybeRow {
    Row(Row),
    End(Stmt),
}

impl Stream for BinQueryResult {
    type Item = BinMaybeRow;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match try_ready!(self.wrapped.poll()) {
            Some(MaybeRow::Row(row)) => Ok(Async::Ready(Some(BinMaybeRow::Row(row)))),
            Some(MaybeRow::End(Right(conn))) => {
                Ok(Async::Ready(Some(BinMaybeRow::End(new_stmt(self.inner_stmt.clone(), conn)))))
            },
            _ => Ok(Async::Ready(None)),
        }
    }
}
<commit_msg>Cleanup TextQueryResult<commit_after>use Column;
use Conn;
use Row;
use Stmt;

use conn::stmt::{
    InnerStmt,
    new_stmt,
};

use consts;

use either::{
    Either,
    Left,
    Right,
};

use errors::*;

use lib_futures::{
    Async,
    Future,
    Poll,
};
use lib_futures::stream::Stream;

use proto::{
    OkPacket,
    Packet,
    PacketType,
};

use super::{
    NewTextQueryResult,
    ReadPacket,
};

use std::borrow::Cow;
use std::mem;
use std::ops::Deref;
use std::sync::Arc;

use value::{
    FromRow,
    Value,
};

mod futures;

pub use self::futures::{
    BinCollect,
    BinMap,
    BinReduce,
    Collect,
    CollectAll,
    Map,
    Reduce,
};
use self::futures::{
    new_bin_collect,
    new_bin_map,
    new_bin_reduce,
    new_collect,
    new_collect_all,
    new_map,
    new_reduce,
};

/// This type stores collected result set.
pub struct ResultSet<T>(Vec<T>, TextQueryResult);

impl<T> ResultSet<T> {
    /// Affected rows value returned by a server, if any.
    pub fn affected_rows(&self) -> Option<u64> {
        self.1.affected_rows()
    }

    /// Last insert id value returned by a server, if any.
    pub fn last_insert_id(&self) -> Option<u64> {
        self.1.last_insert_id()
    }

    /// Warnings count value returned by a server, if any.
    pub fn warnings(&self) -> Option<u16> {
        self.1.warnings()
    }

    /// Bytes of info string returned by a server, if any.
    pub fn info_bytes(&self) -> Option<&[u8]> {
        self.1.info_bytes()
    }

    /// Info bytes lossy converted to UTF-8, if any.
    pub fn info(&self) -> Option<Cow<str>> {
        self.1.info()
    }

    /// Bytes of session state changed value returned by a server, if any.
    pub fn session_state_changes_bytes(&self) -> Option<&[u8]> {
        self.1.session_state_changes_bytes()
    }

    /// Session state changed value lossy converted to UTF-8, if any.
    pub fn session_state_changes(&self) -> Option<Cow<str>> {
        self.1.session_state_changes()
    }
}

impl<R> Deref for ResultSet<R> {
    type Target = [R];

    fn deref(&self) -> &Self::Target {
        self.0.deref()
    }
}

impl<R> IntoIterator for ResultSet<R> {
    type Item = R;
    type IntoIter = ::std::vec::IntoIter<R>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

pub enum MaybeRow {
    /// Row of a result set
    Row(Row),
    /// Either next result set of a multi-result set if it exists or wrapped `Conn`.
    End(Either<TextQueryResult, Conn>),
}

enum Step {
    ReadPacket(ReadPacket),
    NextResult(NewTextQueryResult),
    Done(Conn),
    Consumed,
}

enum Out {
    ReadPacket((Conn, Packet)),
    NextResult(TextQueryResult),
    Done,
}

/// Mysql query result set.
///
/// `TextQueryResult` may contain zero or more rows. It may also be followed by another result set in
/// case of multi-result set.
pub struct TextQueryResult {
    step: Step,
    columns: Arc<Vec<Column>>,
    ok_packet: Option<OkPacket>,
    is_bin: bool,
}

pub fn new<T: Into<Arc<Vec<Column>>>>(mut conn: Conn,
                                      columns: T,
                                      ok_packet: Option<OkPacket>,
                                      is_bin: bool) -> TextQueryResult
{
    let columns = columns.into();
    let cols_len = columns.len();
    conn.has_result = Some((columns.clone(), ok_packet.clone(), is_bin));
    TextQueryResult {
        step: if cols_len == 0 { Step::Done(conn) } else { Step::ReadPacket(conn.read_packet()) },
        columns: columns,
        ok_packet: ok_packet,
        is_bin: is_bin,
    }
}

impl TextQueryResult {
    /// Collects remaining rows of this result set and returns them as `ResultSet`.
    ///
    /// Second element of a resulting tuple is either next `TextQueryResult` if it exists in
    /// multi-result set or `Conn`.
    pub fn collect<R>(self) -> Collect<R>
        where R: FromRow,
              R: Send + 'static,
    {
        new_collect(self)
    }

    /// Collects remaining rows of this and all rows of following result sets.
    pub fn collect_all(self) -> CollectAll {
        new_collect_all(self)
    }

    pub fn map<F, U>(self, fun: F) -> Map<F, U>
    where F: FnMut(Row) -> U,
          Self: Sized,
    {
        new_map(self, fun)
    }

    pub fn reduce<A, F>(self, init: A, fun: F) -> Reduce<A, F>
    where F: FnMut(A, Row) -> A,
          A: Send + 'static,
          Self: Sized,
    {
        new_reduce(self, init, fun)
    }

    /// Affected rows value returned by a server, if any.
    pub fn affected_rows(&self) -> Option<u64> {
        self.ok_packet.as_ref().map(OkPacket::affected_rows)
    }

    /// Last insert id value returned by a server, if any.
    pub fn last_insert_id(&self) -> Option<u64> {
        self.ok_packet.as_ref().map(OkPacket::last_insert_id)
    }

    /// Warnings count value returned by a server, if any.
    pub fn warnings(&self) -> Option<u16> {
        self.ok_packet.as_ref().map(OkPacket::warnings)
    }

    /// Bytes of info string returned by a server, if any.
    pub fn info_bytes(&self) -> Option<&[u8]> {
        self.ok_packet.as_ref().map(OkPacket::info_bytes)
    }

    /// Info bytes lossy converted to UTF-8, if any.
    pub fn info(&self) -> Option<Cow<str>> {
        self.ok_packet.as_ref().map(OkPacket::info)
    }

    /// Bytes of session state changed value returned by a server, if any.
    pub fn session_state_changes_bytes(&self) -> Option<&[u8]> {
        self.ok_packet.as_ref().and_then(OkPacket::session_state_changes_bytes)
    }

    /// Session state changed value lossy converted to UTF-8, if any.
    pub fn session_state_changes(&self) -> Option<Cow<str>> {
        self.ok_packet.as_ref().and_then(OkPacket::session_state_changes)
    }

    fn either_poll(&mut self) -> Result<Async<Option<Out>>> {
        match self.step {
            Step::ReadPacket(ref mut fut) => {
                let val = try_ready!(fut.poll());
                Ok(Async::Ready(Some(Out::ReadPacket(val))))
            },
            Step::NextResult(ref mut fut) => {
                let val = try_ready!(fut.poll());
                Ok(Async::Ready(Some(Out::NextResult(val))))
            },
            Step::Done(_) => {
                Ok(Async::Ready(Some(Out::Done)))
            },
            Step::Consumed => {
                Ok(Async::Ready(None))
            },
        }
    }
}

impl Stream for TextQueryResult {
    type Item = MaybeRow;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match try_ready!(self.either_poll()) {
            Some(Out::ReadPacket((mut conn, packet))) => {
                if packet.is(PacketType::Eof) {
                    if conn.status.contains(consts::SERVER_MORE_RESULTS_EXISTS) {
                        self.step = Step::NextResult(conn.handle_text_resultset());
                        self.poll()
                    } else {
                        conn.has_result = None;
                        self.step = Step::Done(conn);
                        self.poll()
                    }
                } else {
                    let values = if self.is_bin {
                        try!(Value::from_bin_payload(packet.as_ref(), &self.columns))
                    } else {
                        try!(Value::from_payload(packet.as_ref(), self.columns.len()))
                    };
                    let row = Row::new(values, self.columns.clone());
                    self.step = Step::ReadPacket(conn.read_packet());
                    Ok(Async::Ready(Some(MaybeRow::Row(row))))
                }
            },
            Some(Out::NextResult(query_result)) => {
                self.step = Step::Consumed;
                Ok(Async::Ready(Some(MaybeRow::End(Left(query_result)))))
            },
            Some(Out::Done) => {
                if let Step::Done(conn) = mem::replace(&mut self.step, Step::Consumed) {
                    Ok(Async::Ready(Some(MaybeRow::End(Right(conn)))))
                } else {
                    unreachable!();
                }
            },
            None => {
                Ok(Async::Ready(None))
            },
        }
    }
}

pub struct BinQueryResult {
    wrapped: TextQueryResult,
    inner_stmt: InnerStmt,
}

pub fn new_bin(wrapped: TextQueryResult, inner_stmt: InnerStmt) -> BinQueryResult {
    BinQueryResult {
        wrapped: wrapped,
        inner_stmt: inner_stmt,
    }
}

pub fn unwrap_bin(bin_query_result: BinQueryResult) -> (TextQueryResult, InnerStmt) {
    let BinQueryResult {
        wrapped,
        inner_stmt,
    } = bin_query_result;
    (wrapped, inner_stmt)
}

impl BinQueryResult {
    /// Collects remaining rows of this result set and returns them as `ResultSet`.
    ///
    /// Second element of a resulting tuple is either next `TextQueryResult` if it exists in
    /// multi-result set or `Conn`.
    pub fn collect<R>(self) -> BinCollect<R>
        where R: FromRow,
              R: Send + 'static,
    {
        new_bin_collect(self)
    }

    pub fn map<F, U>(self, fun: F) -> BinMap<F, U>
        where F: FnMut(Row) -> U,
              Self: Sized,
    {
        new_bin_map(self, fun)
    }

    pub fn reduce<A, F>(self, init: A, fun: F) -> BinReduce<A, F>
        where F: FnMut(A, Row) -> A,
              A: Send + 'static,
              Self: Sized,
    {
        new_bin_reduce(self, init, fun)
    }

    /// Affected rows value returned by a server, if any.
    pub fn affected_rows(&self) -> Option<u64> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::affected_rows)
    }

    /// Last insert id value returned by a server, if any.
    pub fn last_insert_id(&self) -> Option<u64> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::last_insert_id)
    }

    /// Warnings count value returned by a server, if any.
    pub fn warnings(&self) -> Option<u16> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::warnings)
    }

    /// Bytes of info string returned by a server, if any.
    pub fn info_bytes(&self) -> Option<&[u8]> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::info_bytes)
    }

    /// Info bytes lossy converted to UTF-8, if any.
    pub fn info(&self) -> Option<Cow<str>> {
        self.wrapped.ok_packet.as_ref().map(OkPacket::info)
    }

    /// Bytes of session state changed value returned by a server, if any.
    pub fn session_state_changes_bytes(&self) -> Option<&[u8]> {
        self.wrapped.ok_packet.as_ref().and_then(OkPacket::session_state_changes_bytes)
    }

    /// Session state changed value lossy converted to UTF-8, if any.
    pub fn session_state_changes(&self) -> Option<Cow<str>> {
        self.wrapped.ok_packet.as_ref().and_then(OkPacket::session_state_changes)
    }
}

pub enum BinMaybeRow {
    Row(Row),
    End(Stmt),
}

impl Stream for BinQueryResult {
    type Item = BinMaybeRow;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match try_ready!(self.wrapped.poll()) {
            Some(MaybeRow::Row(row)) => Ok(Async::Ready(Some(BinMaybeRow::Row(row)))),
            Some(MaybeRow::End(Right(conn))) => {
                Ok(Async::Ready(Some(BinMaybeRow::End(new_stmt(self.inner_stmt.clone(), conn)))))
            },
            _ => Ok(Async::Ready(None)),
        }
    }
}
<|endoftext|>


<commit_before>use futures::executor::block_on;
use reql::r;

#[test]
fn queries_are_thread_safe() {
    env_logger::init();
    let conn = block_on(r.connect(())).unwrap();
    crossbeam::scope(|thread| {
        for i in 0..10_000 {
            let conn = &conn;
            thread.spawn(move |_| {
                let msg = format!("iteration {}", i);
                let resp = block_on(r.expr(&msg).run(conn)).unwrap();
                let first: &String = resp.first().unwrap();
                if &msg != first {
                    log::error!("{} != {}", msg, first);
                    std::process::exit(1);
                } else {
                    log::info!("result => {}", first);
                }
            });
        }
    })
    .unwrap();
}
<commit_msg>reduce number of threads until Stream is optimised<commit_after>use futures::executor::block_on;
use reql::r;

#[test]
fn queries_are_thread_safe() {
    env_logger::init();
    let conn = block_on(r.connect(())).unwrap();
    crossbeam::scope(|thread| {
        for i in 0..10 {
            let conn = &conn;
            thread.spawn(move |_| {
                let msg = format!("iteration {}", i);
                let resp = block_on(r.expr(&msg).run(conn)).unwrap();
                let first: &String = resp.first().unwrap();
                if &msg != first {
                    log::error!("{} != {}", msg, first);
                    std::process::exit(1);
                } else {
                    log::info!("result => {}", first);
                }
            });
        }
    })
    .unwrap();
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::hash::Hash;
use std::collections::HashMap;

mod graph;
use graph::Graph;

#[derive(Debug, Copy, Clone, PartialEq)]
enum PortalType {
  Inner,
  Outer
}

#[derive(Debug, Copy, Clone)]
enum Tile<P> {
  Wall,
  Passage,
  Portal(P, PortalType)
}

type Position = (usize, usize);

#[derive(Debug)]
struct Labyrinth<N: Hash + Eq, P: Hash + Eq> {
  tiles: HashMap<N, Tile<P>>,
  portals: HashMap<P, (N, N)>,
  entrance: Option<N>,
  goal: Option<N>,
}

impl<P: Hash + Eq + Clone> Graph<Position> for Labyrinth<Position, P> {
  fn get_neighbors(&self, (x, y): Position) -> Vec<Position> {
    let portal = match self.tiles.get(&(x, y)) {
      Some(Tile::Portal(p, _)) => Some(p.clone()),
      _ => None
    };

    let mut result = vec![(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
      .filter(|&(x, y)| x > 0 && y > 0)
      .filter(|pos| match self.tiles.get(pos) {
        Some(Tile::Passage) | Some(Tile::Portal(_, _)) => true,
        _ => false
      })
      .collect::<Vec<_>>();

    if let Some(p) = portal {
      let (pos1, pos2) = self.portals.get(&p).cloned().unwrap();

      if pos1 == (x, y) {
        result.push(pos2);
      } else {
        result.push(pos1);
      }
    }

    result
  }
}

impl<P: Hash + Eq + Clone> Graph<(Position, usize)> for Labyrinth<Position, P> {
  fn get_neighbors(&self, ((x, y), level): (Position, usize)) -> Vec<(Position, usize)> {
    let (portal, portal_type) = match self.tiles.get(&(x, y)) {
      Some(Tile::Portal(p, t)) => (Some(p.clone()), Some(t.clone())),
      _ => (None, None)
    };

    let mut result = vec![(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
      .filter(|&(x, y)| x > 0 && y > 0)
      .filter(|pos| match self.tiles.get(pos) {
        Some(Tile::Passage) | Some(Tile::Portal(_, _)) => true,
        _ => false
      })
      .map(|pos| (pos, level))
      .collect::<Vec<_>>();

    if let (Some(p), Some(portal_type)) = (portal, portal_type) {
      if level > 0 && portal_type == PortalType::Outer || portal_type == PortalType::Inner {
        let (pos1, pos2) = self.portals.get(&p).cloned().unwrap();

        result.push((
          if pos1 == (x, y) { pos2 } else { pos1 },
          if portal_type == PortalType::Outer { level - 1 } else { level + 1 }
        ));
      }
    }

    result
  }
}

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("20.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn parse_labyrinth(input: &str) -> Labyrinth<Position, String> {
  let height = input.lines().count();
  let width = input.lines().next().map(|row| row.len()).unwrap_or(0);
  let mut character_map = HashMap::new();
  let mut tiles = HashMap::new();
  let mut portals = HashMap::<String, Vec<Position>>::new();
  let mut entrance = None;
  let mut goal = None;

  for (y, line) in input.lines().enumerate() {
    for (x, c) in line.chars().enumerate() {
      character_map.insert((x, y), c);
    }
  }

  for (&(x, y), &c) in character_map.iter() {
    tiles.insert((x, y), match c {
      '#' => Tile::Wall,
      '.' => {
        let get_label_positions = |(x, y), d| (
          (x - 3 + d, y), // Left
          (x + d, y),     // Right
          (x, y - 3 + d), // Top
          (x, y + d)      // Bottom
        );

        let mut label_char_iter = [1, 2].into_iter().cloned()
          .map(|d| get_label_positions((x as isize, y as isize), d));

        let (portal_type, mut label) = match (label_char_iter.next(), label_char_iter.next()) {
          (Some((l11, l21, l31, l41)), Some((l12, l22, l32, l42))) => {
            [(l11, l12), (l21, l22), (l31, l32), (l41, l42)]
            .into_iter()
            .cloned()
            .map(|((a, b), (c, d))| ((a as usize, b as usize), (c as usize, d as usize)))
            .enumerate()
            .filter_map(|(i, (pos1, pos2))| match (character_map.get(&pos1), character_map.get(&pos2)) {
              (Some(&c1), Some(&c2)) => Some((i, (c1, c2))),
              _ => None
            })
            .filter(|&(_, (c1, c2))| c1.is_ascii_alphabetic() && c2.is_ascii_alphabetic())
            .map(|(i, (c1, c2))| {
              let mut label = String::new();
              label.push(c1);
              label.push(c2);

              let portal_type = match i {
                0 => {
                  // Left
                  if x < width / 2 {
                    PortalType::Outer
                  } else {
                    PortalType::Inner
                  }
                },
                1 => {
                  // Right
                  if x < width / 2 {
                    PortalType::Inner
                  } else {
                    PortalType::Outer
                  }
                },
                2 => {
                  // Top
                  if y < height / 2 {
                    PortalType::Outer
                  } else {
                    PortalType::Inner
                  }
                },
                3 => {
                  // Bottom
                  if y < height / 2 {
                    PortalType::Inner
                  } else {
                    PortalType::Outer
                  }
                },
                _ => panic!()
              };

              (Some(portal_type), Some(label))
            })
            .next()
            .unwrap_or((None, None))
          },
          _ => (None, None)
        };

        match label.as_ref().map(|s| &s[..]) {
          Some("ZZ") => {
            label = None;
            goal = Some((x, y));
          },
          Some("AA") => {
            label = None;
            entrance = Some((x, y));
          },
          _ => {}
        }

        if let (Some(portal_type), Some(label)) = (portal_type, label) {
          match portals.get_mut(&label) {
            Some(positions) => {
              positions.push((x, y));
            },
            None => {
              portals.insert(label.clone(), vec![(x, y)]);
            }
          }

          Tile::Portal(label, portal_type)
        } else {
          Tile::Passage
        }
      }
      _ => continue
    });
  }

  let portals = portals.into_iter()
    .filter(|(_, positions)| positions.len() == 2)
    .map(|(key, positions)| (key, (positions[0], positions[1])))
    .collect();

  Labyrinth {
    tiles,
    portals,
    entrance,
    goal
  }
}

fn main() {
  let input = get_input().unwrap();
  let labyrinth = parse_labyrinth(&input);
  let entrance = labyrinth.entrance.unwrap();
  let goal = labyrinth.goal.unwrap();

  let path = labyrinth.bfs(entrance).construct_path(goal).unwrap();
  println!("Part 1: {}", path.len() - 1);

  let path = labyrinth.bfs((entrance, 0)).construct_path((goal, 0)).unwrap();
  println!("Part 2: {}", path.len() - 1);
}
<commit_msg>Dry refactor of parse_labyrinth()<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::hash::Hash;
use std::collections::HashMap;

mod graph;
use graph::Graph;

#[derive(Debug, Copy, Clone, PartialEq)]
enum PortalType {
  Inner,
  Outer
}

#[derive(Debug, Copy, Clone)]
enum Tile<P> {
  Wall,
  Passage,
  Portal(P, PortalType)
}

type Position = (usize, usize);

#[derive(Debug)]
struct Labyrinth<N: Hash + Eq, P: Hash + Eq> {
  tiles: HashMap<N, Tile<P>>,
  portals: HashMap<P, (N, N)>,
  entrance: Option<N>,
  goal: Option<N>,
}

impl<P: Hash + Eq + Clone> Graph<Position> for Labyrinth<Position, P> {
  fn get_neighbors(&self, (x, y): Position) -> Vec<Position> {
    let portal = match self.tiles.get(&(x, y)) {
      Some(Tile::Portal(p, _)) => Some(p.clone()),
      _ => None
    };

    let mut result = vec![(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
      .filter(|&(x, y)| x > 0 && y > 0)
      .filter(|pos| match self.tiles.get(pos) {
        Some(Tile::Passage) | Some(Tile::Portal(_, _)) => true,
        _ => false
      })
      .collect::<Vec<_>>();

    if let Some(p) = portal {
      let (pos1, pos2) = self.portals.get(&p).cloned().unwrap();

      if pos1 == (x, y) {
        result.push(pos2);
      } else {
        result.push(pos1);
      }
    }

    result
  }
}

impl<P: Hash + Eq + Clone> Graph<(Position, usize)> for Labyrinth<Position, P> {
  fn get_neighbors(&self, ((x, y), level): (Position, usize)) -> Vec<(Position, usize)> {
    let (portal, portal_type) = match self.tiles.get(&(x, y)) {
      Some(Tile::Portal(p, t)) => (Some(p.clone()), Some(t.clone())),
      _ => (None, None)
    };

    let mut result = vec![(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
      .filter(|&(x, y)| x > 0 && y > 0)
      .filter(|pos| match self.tiles.get(pos) {
        Some(Tile::Passage) | Some(Tile::Portal(_, _)) => true,
        _ => false
      })
      .map(|pos| (pos, level))
      .collect::<Vec<_>>();

    if let (Some(p), Some(portal_type)) = (portal, portal_type) {
      if level > 0 && portal_type == PortalType::Outer || portal_type == PortalType::Inner {
        let (pos1, pos2) = self.portals.get(&p).cloned().unwrap();

        result.push((
          if pos1 == (x, y) { pos2 } else { pos1 },
          if portal_type == PortalType::Outer { level - 1 } else { level + 1 }
        ));
      }
    }

    result
  }
}

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("20.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn parse_labyrinth(input: &str) -> Labyrinth<Position, String> {
  let height = input.lines().count();
  let width = input.lines().next().map(|row| row.len()).unwrap_or(0);
  let mut character_map = HashMap::new();
  let mut tiles = HashMap::new();
  let mut portals = HashMap::<String, Vec<Position>>::new();
  let mut entrance = None;
  let mut goal = None;

  for (y, line) in input.lines().enumerate() {
    for (x, c) in line.chars().enumerate() {
      character_map.insert((x, y), c);
    }
  }

  for (&(x, y), &c) in character_map.iter() {
    tiles.insert((x, y), match c {
      '#' => Tile::Wall,
      '.' => {
        let get_label_positions = |(x, y), d| (
          (x - 3 + d, y), // Left
          (x + d, y),     // Right
          (x, y - 3 + d), // Top
          (x, y + d)      // Bottom
        );

        let mut label_char_iter = [1, 2].into_iter().cloned()
          .map(|d| get_label_positions((x as isize, y as isize), d));

        let (portal_type, mut label) = match (label_char_iter.next(), label_char_iter.next()) {
          (Some((l11, l21, l31, l41)), Some((l12, l22, l32, l42))) => {
            [(l11, l12), (l21, l22), (l31, l32), (l41, l42)]
            .into_iter()
            .cloned()
            .map(|((a, b), (c, d))| ((a as usize, b as usize), (c as usize, d as usize)))
            .enumerate()
            .filter_map(|(i, (pos1, pos2))| match (character_map.get(&pos1), character_map.get(&pos2)) {
              (Some(&c1), Some(&c2)) => Some((i, (c1, c2))),
              _ => None
            })
            .filter(|&(_, (c1, c2))| c1.is_ascii_alphabetic() && c2.is_ascii_alphabetic())
            .map(|(i, (c1, c2))| {
              let mut label = String::new();
              label.push(c1);
              label.push(c2);

              let portal_type = match [
                (x, width, PortalType::Outer, PortalType::Inner),  // Left
                (x, width, PortalType::Inner, PortalType::Outer),  // Right
                (y, height, PortalType::Outer, PortalType::Inner), // Top
                (y, height, PortalType::Inner, PortalType::Outer)  // Bottom
              ][i] {
                (a, b, x, y) => if a < b / 2 { x } else { y }
              };

              (Some(portal_type), Some(label))
            })
            .next()
            .unwrap_or((None, None))
          },
          _ => (None, None)
        };

        match label.as_ref().map(|s| &s[..]) {
          Some("ZZ") => {
            label = None;
            goal = Some((x, y));
          },
          Some("AA") => {
            label = None;
            entrance = Some((x, y));
          },
          _ => {}
        }

        if let (Some(portal_type), Some(label)) = (portal_type, label) {
          match portals.get_mut(&label) {
            Some(positions) => {
              positions.push((x, y));
            },
            None => {
              portals.insert(label.clone(), vec![(x, y)]);
            }
          }

          Tile::Portal(label, portal_type)
        } else {
          Tile::Passage
        }
      }
      _ => continue
    });
  }

  let portals = portals.into_iter()
    .filter(|(_, positions)| positions.len() == 2)
    .map(|(key, positions)| (key, (positions[0], positions[1])))
    .collect();

  Labyrinth {
    tiles,
    portals,
    entrance,
    goal
  }
}

fn main() {
  let input = get_input().unwrap();
  let labyrinth = parse_labyrinth(&input);
  let entrance = labyrinth.entrance.unwrap();
  let goal = labyrinth.goal.unwrap();

  let path = labyrinth.bfs(entrance).construct_path(goal).unwrap();
  println!("Part 1: {}", path.len() - 1);

  let path = labyrinth.bfs((entrance, 0)).construct_path((goal, 0)).unwrap();
  println!("Part 2: {}", path.len() - 1);
}
<|endoftext|>


<commit_before>//! Module defining all sensor related structures.

use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson};
use utils::Optional;

//--- Templates ---//

/// A trait for all possible sensor templates.
/// the `SensorTemplate`s are capable of registering themselves in a `Sensors` struct
pub trait SensorTemplate : Send+Sync {
    fn to_sensor(&self, value_str: &str, sensors: &mut Sensors);
}

#[derive(Debug, Clone)]
pub struct PeopleNowPresentSensorTemplate {
    pub location: Optional<String>,
    pub name: Optional<String>,
    pub names: Optional<Vec<String>>,
    pub description: Optional<String>,
}

impl SensorTemplate for PeopleNowPresentSensorTemplate {
    fn to_sensor(&self, value_str: &str, sensors: &mut Sensors) {
        if value_str.parse::<i64>().map(|value|{
            let sensor = PeopleNowPresentSensor {
                location: self.location.clone(),
                name: self.name.clone(),
                names: self.names.clone(),
                description: self.description.clone(),
                value: value,
            };

            match sensors.people_now_present {
                Optional::Value(ref mut vec_sensors) => vec_sensors.push(sensor),
                Optional::Absent => sensors.people_now_present = Optional::Value(vec![sensor]),
            }
        }).is_err() {
            warn!("Could not parse '{}': omiting the sensor", value_str);
        }
    }
}

#[derive(Debug, Clone)]
pub struct TemperatureSensorTemplate {
    pub unit: String,
    pub location: String,
    pub name: Optional<String>,
    pub description: Optional<String>,
}

impl SensorTemplate for TemperatureSensorTemplate {
    fn to_sensor(&self, value_str: &str, sensors: &mut Sensors) {
        if value_str.parse::<f64>().map(|value|{
            let sensor = TemperatureSensor {
                unit: self.unit.clone(),
                location: self.location.clone(),
                name: self.name.clone(),
                description: self.description.clone(),
                value: value,
            };
            match sensors.temperature {
                Optional::Value(ref mut vec_sensors) => vec_sensors.push(sensor),
                Optional::Absent => sensors.temperature = Optional::Value(vec![sensor]),
            }
        }).is_err() {
            warn!("Could not parse '{}': omiting the sensor", value_str);
        }
    }
}

//--- Structures ---//

#[derive(Debug, Clone)]
pub struct PeopleNowPresentSensor {
    pub location: Optional<String>,
    pub name: Optional<String>,
    pub names: Optional<Vec<String>>,
    pub description: Optional<String>,
    pub value: i64,
}

#[derive(Debug, Clone)]
pub struct TemperatureSensor {
    pub unit: String,
    pub location: String,
    pub name: Optional<String>,
    pub description: Optional<String>,
    pub value: f64,
}

#[derive(Debug, Clone)]
pub struct Sensors {
    pub people_now_present: Optional<Vec<PeopleNowPresentSensor>>,
    pub temperature: Optional<Vec<TemperatureSensor>>,
}

//--- Serialization ---//

impl ToJson for Sensors {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        self.people_now_present.as_ref().map_or((), |v| { d.insert("people_now_present".to_string(), v.to_json()); });
        self.temperature.as_ref().map_or((), |v| { d.insert("temperature".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for PeopleNowPresentSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        self.location.as_ref().map_or((), |v| { d.insert("location".to_string(), v.to_json()); });
        self.name.as_ref().map_or((), |v| { d.insert("name".to_string(), v.to_json()); });
        self.names.as_ref().map_or((), |v| { d.insert("names".to_string(), v.to_json()); });
        self.description.as_ref().map_or((), |v| { d.insert("description".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for TemperatureSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        d.insert("unit".to_string(), self.unit.to_json());
        d.insert("location".to_string(), self.location.to_json());
        self.name.as_ref().map_or((), |v| { d.insert("name".to_string(), v.to_json()); });
        self.description.as_ref().map_or((), |v| { d.insert("description".to_string(), v.to_json()); });
        Json::Object(d)
    }
}
<commit_msg>spaceapi: Improve readability in SensorTemplate<commit_after>//! Module defining all sensor related structures.

use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson};
use utils::Optional;

//--- Templates ---//

/// A trait for all possible sensor templates.
/// the `SensorTemplate`s are capable of registering themselves in a `Sensors` struct
pub trait SensorTemplate : Send+Sync {
    fn to_sensor(&self, value_str: &str, sensors: &mut Sensors);
}

#[derive(Debug, Clone)]
pub struct PeopleNowPresentSensorTemplate {
    pub location: Optional<String>,
    pub name: Optional<String>,
    pub names: Optional<Vec<String>>,
    pub description: Optional<String>,
}

impl SensorTemplate for PeopleNowPresentSensorTemplate {
    fn to_sensor(&self, value_str: &str, sensors: &mut Sensors) {
        if value_str.parse::<i64>().map(|value|{
            let sensor = PeopleNowPresentSensor {
                location: self.location.clone(),
                name: self.name.clone(),
                names: self.names.clone(),
                description: self.description.clone(),
                value: value,
            };

            match sensors.people_now_present {
                Optional::Value(ref mut vec_sensors) => {
                    vec_sensors.push(sensor)
                }
                Optional::Absent => {
                    sensors.people_now_present = Optional::Value(vec![sensor])
                }
            }
        }).is_err() {
            warn!("Could not parse '{}': omiting the sensor", value_str);
        }
    }
}

#[derive(Debug, Clone)]
pub struct TemperatureSensorTemplate {
    pub unit: String,
    pub location: String,
    pub name: Optional<String>,
    pub description: Optional<String>,
}

impl SensorTemplate for TemperatureSensorTemplate {
    fn to_sensor(&self, value_str: &str, sensors: &mut Sensors) {
        if value_str.parse::<f64>().map(|value|{
            let sensor = TemperatureSensor {
                unit: self.unit.clone(),
                location: self.location.clone(),
                name: self.name.clone(),
                description: self.description.clone(),
                value: value,
            };
            match sensors.temperature {
                Optional::Value(ref mut vec_sensors) => {
                    vec_sensors.push(sensor)
                }
                Optional::Absent => {
                    sensors.temperature = Optional::Value(vec![sensor])
                }
            }
        }).is_err() {
            warn!("Could not parse '{}': omiting the sensor", value_str);
        }
    }
}

//--- Structures ---//

#[derive(Debug, Clone)]
pub struct PeopleNowPresentSensor {
    pub location: Optional<String>,
    pub name: Optional<String>,
    pub names: Optional<Vec<String>>,
    pub description: Optional<String>,
    pub value: i64,
}

#[derive(Debug, Clone)]
pub struct TemperatureSensor {
    pub unit: String,
    pub location: String,
    pub name: Optional<String>,
    pub description: Optional<String>,
    pub value: f64,
}

#[derive(Debug, Clone)]
pub struct Sensors {
    pub people_now_present: Optional<Vec<PeopleNowPresentSensor>>,
    pub temperature: Optional<Vec<TemperatureSensor>>,
}

//--- Serialization ---//

impl ToJson for Sensors {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        self.people_now_present.as_ref().map_or((), |v| { d.insert("people_now_present".to_string(), v.to_json()); });
        self.temperature.as_ref().map_or((), |v| { d.insert("temperature".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for PeopleNowPresentSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        self.location.as_ref().map_or((), |v| { d.insert("location".to_string(), v.to_json()); });
        self.name.as_ref().map_or((), |v| { d.insert("name".to_string(), v.to_json()); });
        self.names.as_ref().map_or((), |v| { d.insert("names".to_string(), v.to_json()); });
        self.description.as_ref().map_or((), |v| { d.insert("description".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for TemperatureSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        d.insert("unit".to_string(), self.unit.to_json());
        d.insert("location".to_string(), self.location.to_json());
        self.name.as_ref().map_or((), |v| { d.insert("name".to_string(), v.to_json()); });
        self.description.as_ref().map_or((), |v| { d.insert("description".to_string(), v.to_json()); });
        Json::Object(d)
    }
}
<|endoftext|>


<commit_before>extern crate twitter;

use std::io::{Write, Read};

fn main() {
    //IMPORTANT: make an app for yourself at apps.twitter.com and get your
    //key/secret into these files; this example won't work without them
    let consumer_key = include_str!("consumer_key").trim();
    let consumer_secret = include_str!("consumer_secret").trim();

    let token = twitter::Token::new(consumer_key, consumer_secret);

    let mut config = String::new();
    let user_id: i64;
    let username: String;
    let access_token: twitter::Token;

    //look at all this unwrapping! who told you it was my birthday?
    if let Ok(mut f) = std::fs::File::open("twitter_settings") {
        f.read_to_string(&mut config).unwrap();

        let mut iter = config.split('\n');

        username = iter.next().unwrap().to_string();
        user_id = i64::from_str_radix(&iter.next().unwrap(), 10).unwrap();
        access_token = twitter::Token::new(iter.next().unwrap(),
                                                 iter.next().unwrap());

        println!("Welcome back, {}!", username);
    }
    else {
        let request_token = match twitter::request_token(&token, "oob") {
            Ok(token) => token,
            Err(e) => {
                println!("Error: {}", e);
                return;
            },
        };

        println!("Go to the following URL, sign in, and give me the PIN that comes back:");
        println!("{}", twitter::authorize_url(&request_token));

        let mut pin = String::new();
        std::io::stdin().read_line(&mut pin).unwrap();
        println!("");

        let tok_result = twitter::access_token(&token, &request_token, pin).unwrap();

        access_token = tok_result.0;
        user_id = tok_result.1;
        username = tok_result.2;

        config.push_str(&username);
        config.push('\n');
        config.push_str(&format!("{}", user_id));
        config.push('\n');
        config.push_str(&access_token.key);
        config.push('\n');
        config.push_str(&access_token.secret);

        let mut f = std::fs::File::create("twitter_settings").unwrap();
        f.write_all(config.as_bytes()).unwrap();

        println!("Welcome, {}, let's get this show on the road!", username);
    }

    let resp = twitter::user::TwitterUser::lookup_ids(&[user_id], &token, &access_token);
    let ref user = resp.unwrap().response[0];

    println!("{} (@{})", user.name, user.screen_name);
    println!("Created at {}", user.created_at);
    println!("Follows {}, followed by {}", user.friends_count, user.followers_count);
    if let Some(ref desc) = user.description {
        println!("{}", desc);
    }
    else {
        println!("(no description provided)");
    }
    match (&user.location, &user.url) {
        (&Some(ref loc), &Some(ref link)) => println!("{} | {}", loc, link),
        (&None, &Some(ref link)) => println!("{}", link),
        (&Some(ref loc), &None) => println!("{}", loc),
        (&None, &None) => (),
    }
}
<commit_msg>extend basic with another lookup<commit_after>extern crate twitter;

use std::io::{Write, Read};

fn main() {
    //IMPORTANT: make an app for yourself at apps.twitter.com and get your
    //key/secret into these files; this example won't work without them
    let consumer_key = include_str!("consumer_key").trim();
    let consumer_secret = include_str!("consumer_secret").trim();

    let token = twitter::Token::new(consumer_key, consumer_secret);

    let mut config = String::new();
    let user_id: i64;
    let username: String;
    let access_token: twitter::Token;

    //look at all this unwrapping! who told you it was my birthday?
    if let Ok(mut f) = std::fs::File::open("twitter_settings") {
        f.read_to_string(&mut config).unwrap();

        let mut iter = config.split('\n');

        username = iter.next().unwrap().to_string();
        user_id = i64::from_str_radix(&iter.next().unwrap(), 10).unwrap();
        access_token = twitter::Token::new(iter.next().unwrap(),
                                                 iter.next().unwrap());

        println!("Welcome back, {}!", username);
    }
    else {
        let request_token = match twitter::request_token(&token, "oob") {
            Ok(token) => token,
            Err(e) => {
                println!("Error: {}", e);
                return;
            },
        };

        println!("Go to the following URL, sign in, and give me the PIN that comes back:");
        println!("{}", twitter::authorize_url(&request_token));

        let mut pin = String::new();
        std::io::stdin().read_line(&mut pin).unwrap();
        println!("");

        let tok_result = twitter::access_token(&token, &request_token, pin).unwrap();

        access_token = tok_result.0;
        user_id = tok_result.1;
        username = tok_result.2;

        config.push_str(&username);
        config.push('\n');
        config.push_str(&format!("{}", user_id));
        config.push('\n');
        config.push_str(&access_token.key);
        config.push('\n');
        config.push_str(&access_token.secret);

        let mut f = std::fs::File::create("twitter_settings").unwrap();
        f.write_all(config.as_bytes()).unwrap();

        println!("Welcome, {}, let's get this show on the road!", username);
    }

    let resp = twitter::user::TwitterUser::show_id(user_id, &token, &access_token);
    print_user(&resp.unwrap().response);

    let resp = twitter::user::TwitterUser::show_name("SwiftOnSecurity", &token, &access_token);
    print_user(&resp.unwrap().response);
}

fn print_user(user: &twitter::user::TwitterUser) {
    println!("{} (@{})", user.name, user.screen_name);
    println!("Created at {}", user.created_at);
    println!("Follows {}, followed by {}", user.friends_count, user.followers_count);
    if let Some(ref desc) = user.description {
        println!("{}", desc);
    }
    else {
        println!("(no description provided)");
    }
    match (&user.location, &user.url) {
        (&Some(ref loc), &Some(ref link)) => println!("{} | {}", loc, link),
        (&None, &Some(ref link)) => println!("{}", link),
        (&Some(ref loc), &None) => println!("{}", loc),
        (&None, &None) => (),
    }
}
<|endoftext|>