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

use dom::comment::Comment;
use dom::bindings::codegen::DocumentBinding;
use dom::bindings::utils::{Reflectable, Reflector, Traceable, reflect_dom_object};
use dom::bindings::utils::{ErrorResult, Fallible, NotSupported, InvalidCharacter, HierarchyRequest};
use dom::bindings::utils::DOMString;
use dom::bindings::utils::{xml_name_type, InvalidXMLName};
use dom::documentfragment::DocumentFragment;
use dom::domimplementation::DOMImplementation;
use dom::element::{Element};
use dom::element::{HTMLHtmlElementTypeId, HTMLHeadElementTypeId, HTMLTitleElementTypeId, HTMLBodyElementTypeId, HTMLFrameSetElementTypeId};
use dom::event::{AbstractEvent, Event};
use dom::htmlcollection::HTMLCollection;
use dom::htmldocument::HTMLDocument;
use dom::mouseevent::MouseEvent;
use dom::namespace::Null;
use dom::node::{AbstractNode, Node, ElementNodeTypeId, DocumentNodeTypeId};
use dom::text::Text;
use dom::uievent::UIEvent;
use dom::window::Window;
use dom::htmltitleelement::HTMLTitleElement;
use html::hubbub_html_parser::build_element_from_tag;
use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage};

use js::jsapi::{JSObject, JSContext, JSTracer};
use std::ascii::StrAsciiExt;
use std::cast;
use std::hashmap::HashMap;
use std::str::eq_slice;
use std::unstable::raw::Box;

#[deriving(Eq)]
pub enum DocumentTypeId {
    PlainDocumentTypeId,
    HTMLDocumentTypeId
}

#[deriving(Eq)]
pub struct AbstractDocument {
    document: *mut Box<Document>
}

impl AbstractDocument {
    pub fn document<'a>(&'a self) -> &'a Document {
        unsafe {
            &(*self.document).data
        }
    }

    pub fn mut_document<'a>(&'a self) -> &'a mut Document {
        unsafe {
            &mut (*self.document).data
        }
    }

    unsafe fn transmute<T, R>(&self, f: |&T| -> R) -> R {
        let box_: *Box<T> = cast::transmute(self.document);
        f(&(*box_).data)
    }

    pub fn with_html<R>(&self, callback: |&HTMLDocument| -> R) -> R {
        match self.document().doctype {
            HTML => unsafe { self.transmute(callback) },
            _ => fail!("attempt to downcast a non-HTMLDocument to HTMLDocument")
        }
    }

    pub fn from_box<T>(ptr: *mut Box<T>) -> AbstractDocument {
        AbstractDocument {
            document: ptr as *mut Box<Document>
        }
    }
}

#[deriving(Eq)]
pub enum DocumentType {
    HTML,
    SVG,
    XML
}

pub struct Document {
    node: Node,
    reflector_: Reflector,
    window: @mut Window,
    doctype: DocumentType,
    title: ~str,
    idmap: HashMap<DOMString, AbstractNode>,
    implementation: Option<@mut DOMImplementation>
}

impl Document {
    pub fn reflect_document<D: Reflectable>
            (document:  @mut D,
             window:    @mut Window,
             wrap_fn:   extern "Rust" fn(*JSContext, *JSObject, @mut D) -> *JSObject)
             -> AbstractDocument {
        assert!(document.reflector().get_jsobject().is_null());
        let document = reflect_dom_object(document, window, wrap_fn);
        assert!(document.reflector().get_jsobject().is_not_null());

        // JS object now owns the Document, so transmute_copy is needed
        let abstract = AbstractDocument {
            document: unsafe { cast::transmute_copy(&document) }
        };
        abstract.mut_document().node.set_owner_doc(abstract);
        abstract
    }

    pub fn new_inherited(window: @mut Window, doctype: DocumentType) -> Document {
        let node_type = match doctype {
            HTML => HTMLDocumentTypeId,
            SVG | XML => PlainDocumentTypeId
        };
        Document {
            node: Node::new_without_doc(DocumentNodeTypeId(node_type)),
            reflector_: Reflector::new(),
            window: window,
            doctype: doctype,
            title: ~"",
            idmap: HashMap::new(),
            implementation: None
        }
    }

    pub fn new(window: @mut Window, doctype: DocumentType) -> AbstractDocument {
        let document = Document::new_inherited(window, doctype);
        Document::reflect_document(@mut document, window, DocumentBinding::Wrap)
    }
}

impl Document {
    pub fn Constructor(owner: @mut Window) -> Fallible<AbstractDocument> {
        Ok(Document::new(owner, XML))
    }
}

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

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

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

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

impl Document {
    pub fn Implementation(&mut self) -> @mut DOMImplementation {
        if self.implementation.is_none() {
            self.implementation = Some(DOMImplementation::new(self.window));
        }
        self.implementation.unwrap()
    }

    pub fn GetDoctype(&self) -> Option<AbstractNode> {
        self.node.children().find(|child| child.is_doctype())
    }

    pub fn GetDocumentElement(&self) -> Option<AbstractNode> {
        self.node.child_elements().next()
    }

    pub fn GetElementsByTagName(&self, tag: DOMString) -> @mut HTMLCollection {
        self.createHTMLCollection(|elem| eq_slice(elem.tag_name, tag))
    }

    pub fn GetElementsByTagNameNS(&self, _ns: Option<DOMString>, _tag: DOMString) -> @mut HTMLCollection {
        HTMLCollection::new(self.window, ~[])
    }

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

    pub fn GetElementById(&self, id: DOMString) -> Option<AbstractNode> {
        // TODO: "in tree order, within the context object's tree"
        // http://dom.spec.whatwg.org/#dom-document-getelementbyid.
        match self.idmap.find_equiv(&id) {
            None => None,
            Some(node) => Some(*node),
        }
    }

    pub fn CreateElement(&self, abstract_self: AbstractDocument, local_name: DOMString)
                         -> Fallible<AbstractNode> {
        if xml_name_type(local_name) == InvalidXMLName {
            debug!("Not a valid element name");
            return Err(InvalidCharacter);
        }
        let local_name = local_name.to_ascii_lower();
        Ok(build_element_from_tag(local_name, abstract_self))
    }

    pub fn CreateDocumentFragment(&self, abstract_self: AbstractDocument) -> AbstractNode {
        DocumentFragment::new(abstract_self)
    }

    pub fn CreateTextNode(&self, abstract_self: AbstractDocument, data: DOMString)
                          -> AbstractNode {
        Text::new(data, abstract_self)
    }

    pub fn CreateComment(&self, abstract_self: AbstractDocument, data: DOMString) -> AbstractNode {
        Comment::new(data, abstract_self)
    }

    pub fn CreateEvent(&self, interface: DOMString) -> Fallible<AbstractEvent> {
        match interface.as_slice() {
            "UIEvents" => Ok(UIEvent::new(self.window)),
            "MouseEvents" => Ok(MouseEvent::new(self.window)),
            "HTMLEvents" => Ok(Event::new(self.window)),
            _ => Err(NotSupported)
        }
    }

    pub fn Title(&self, _: AbstractDocument) -> DOMString {
        let mut title = ~"";
        match self.doctype {
            SVG => {
                fail!("no SVG document yet")
            },
            _ => {
                match self.GetDocumentElement() {
                    None => {},
                    Some(root) => {
                        for node in root.traverse_preorder() {
                            if node.type_id() != ElementNodeTypeId(HTMLTitleElementTypeId) {
                                continue;
                            }
                            for child in node.children() {
                                if child.is_text() {
                                    child.with_imm_text(|text| {
                                        title = title + text.element.Data();
                                    });
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }
        let v: ~[&str] = title.words().collect();
        title = v.connect(" ");
        title = title.trim().to_owned();
        title
    }

    pub fn SetTitle(&self, abstract_self: AbstractDocument, title: DOMString) -> ErrorResult {
        match self.doctype {
            SVG => {
                fail!("no SVG document yet")
            },
            _ => {
                match self.GetDocumentElement() {
                    None => {},
                    Some(root) => {
                        for node in root.traverse_preorder() {
                            if node.type_id() != ElementNodeTypeId(HTMLHeadElementTypeId) {
                                continue;
                            }
                            let mut has_title = false;
                            for child in node.children() {
                                if child.type_id() != ElementNodeTypeId(HTMLTitleElementTypeId) {
                                    continue;
                                }
                                has_title = true;
                                for title_child in child.children() {
                                    child.RemoveChild(title_child);
                                }
                                child.AppendChild(self.CreateTextNode(abstract_self, title.clone()));
                                break;
                            }
                            if !has_title {
                                let new_title = HTMLTitleElement::new(~"title", abstract_self);
                                new_title.AppendChild(self.CreateTextNode(abstract_self, title.clone()));
                                node.AppendChild(new_title);
                            }
                            break;
                        }
                    }
                }
            }
        }
        Ok(())
    }

    fn get_html_element(&self) -> Option<AbstractNode> {
        self.GetDocumentElement().filtered(|root| {
            match root.type_id() {
                ElementNodeTypeId(HTMLHtmlElementTypeId) => true,
                _ => false
            }
        })
    }

    // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-head
    pub fn GetHead(&self) -> Option<AbstractNode> {
        self.get_html_element().and_then(|root| {
            root.children().find(|child| {
                child.type_id() == ElementNodeTypeId(HTMLHeadElementTypeId)
            })
        })
    }

    // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-body
    pub fn GetBody(&self, _: AbstractDocument) -> Option<AbstractNode> {
        match self.get_html_element() {
            None => None,
            Some(root) => {
                root.children().find(|child| {
                    match child.type_id() {
                        ElementNodeTypeId(HTMLBodyElementTypeId) |
                        ElementNodeTypeId(HTMLFrameSetElementTypeId) => true,
                        _ => false
                    }
                })
            }
        }
    }

    // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-body
    pub fn SetBody(&self, abstract_self: AbstractDocument, new_body: Option<AbstractNode>) -> ErrorResult {
        // Step 1.
        match new_body {
            Some(node) => {
                match node.type_id() {
                    ElementNodeTypeId(HTMLBodyElementTypeId) | ElementNodeTypeId(HTMLFrameSetElementTypeId) => {}
                    _ => return Err(HierarchyRequest)
                }
            }
            None => return Err(HierarchyRequest)
        }

        // Step 2.
        let old_body: Option<AbstractNode> = self.GetBody(abstract_self);
        if old_body == new_body {
            return Ok(());
        }

        // Step 3.
        match self.get_html_element() {
            // Step 4.
            None => return Err(HierarchyRequest),
            Some(root) => {
                match old_body {
                    Some(child) => { root.ReplaceChild(new_body.unwrap(), child); }
                    None => { root.AppendChild(new_body.unwrap()); }
                }
            }
        }
        Ok(())
    }

    pub fn GetElementsByName(&self, name: DOMString) -> @mut HTMLCollection {
        self.createHTMLCollection(|elem|
            elem.get_attr(Null, "name").is_some() && eq_slice(elem.get_attr(Null, "name").unwrap(), name))
    }

    pub fn createHTMLCollection(&self, callback: |elem: &Element| -> bool) -> @mut HTMLCollection {
        let mut elements = ~[];
        match self.GetDocumentElement() {
            None => {},
            Some(root) => {
                for child in root.traverse_preorder() {
                    if child.is_element() {
                        child.with_imm_element(|elem| {
                            if callback(elem) {
                                elements.push(child);
                            }
                        });
                    }
                }
            }
        }
        HTMLCollection::new(self.window, elements)
    }

    pub fn content_changed(&self) {
        self.damage_and_reflow(ContentChangedDocumentDamage);
    }

    pub fn damage_and_reflow(&self, damage: DocumentDamageLevel) {
        self.window.damage_and_reflow(damage);
    }

    pub fn wait_until_safe_to_modify_dom(&self) {
        self.window.wait_until_safe_to_modify_dom();
    }

    pub fn register_nodes_with_id(&mut self, root: &AbstractNode) {
        foreach_ided_elements(root, |id: &DOMString, abstract_node: &AbstractNode| {
            // TODO: "in tree order, within the context object's tree"
            // http://dom.spec.whatwg.org/#dom-document-getelementbyid.
            self.idmap.find_or_insert(id.clone(), *abstract_node);
        });
    }

    pub fn unregister_nodes_with_id(&mut self, root: &AbstractNode) {
        foreach_ided_elements(root, |id: &DOMString, _| {
            // TODO: "in tree order, within the context object's tree"
            // http://dom.spec.whatwg.org/#dom-document-getelementbyid.
            self.idmap.pop(id);
        });
    }

    pub fn update_idmap(&mut self,
                        abstract_self: AbstractNode,
                        new_id: Option<DOMString>,
                        old_id: Option<DOMString>) {
        // remove old ids:
        // * if the old ones are not same as the new one,
        // * OR if the new one is none.
        match old_id {
            Some(ref old_id) if new_id.is_none() ||
                                (*new_id.get_ref() != *old_id) => {
                self.idmap.remove(old_id);
            }
            _ => ()
        }

        match new_id {
            Some(new_id) => {
                // TODO: support the case if multiple elements
                // which haves same id are in the same document.
                self.idmap.mangle(new_id, abstract_self,
                                  |_, new_node: AbstractNode| -> AbstractNode {
                                      new_node
                                  },
                                  |_, old_node: &mut AbstractNode, new_node: AbstractNode| {
                                      *old_node = new_node;
                                  });
            }
            None => ()
        }
    }
}

#[inline(always)]
fn foreach_ided_elements(root: &AbstractNode, callback: |&DOMString, &AbstractNode|) {
    for node in root.traverse_preorder() {
        if !node.is_element() {
            continue;
        }

        node.with_imm_element(|element| {
            match element.get_attr(Null, "id") {
                Some(id) => {
                    callback(&id.to_str(), &node);
                }
                None => ()
            }
        });
    }
}

impl Traceable for Document {
    fn trace(&self, tracer: *mut JSTracer) {
        self.node.trace(tracer);
    }
}
<commit_msg>auto merge of #1518 : brunoabinader/servo/remove-document-title, r=Ms2ger<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::comment::Comment;
use dom::bindings::codegen::DocumentBinding;
use dom::bindings::utils::{Reflectable, Reflector, Traceable, reflect_dom_object};
use dom::bindings::utils::{ErrorResult, Fallible, NotSupported, InvalidCharacter, HierarchyRequest};
use dom::bindings::utils::DOMString;
use dom::bindings::utils::{xml_name_type, InvalidXMLName};
use dom::documentfragment::DocumentFragment;
use dom::domimplementation::DOMImplementation;
use dom::element::{Element};
use dom::element::{HTMLHtmlElementTypeId, HTMLHeadElementTypeId, HTMLTitleElementTypeId, HTMLBodyElementTypeId, HTMLFrameSetElementTypeId};
use dom::event::{AbstractEvent, Event};
use dom::htmlcollection::HTMLCollection;
use dom::htmldocument::HTMLDocument;
use dom::mouseevent::MouseEvent;
use dom::namespace::Null;
use dom::node::{AbstractNode, Node, ElementNodeTypeId, DocumentNodeTypeId};
use dom::text::Text;
use dom::uievent::UIEvent;
use dom::window::Window;
use dom::htmltitleelement::HTMLTitleElement;
use html::hubbub_html_parser::build_element_from_tag;
use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage};

use js::jsapi::{JSObject, JSContext, JSTracer};
use std::ascii::StrAsciiExt;
use std::cast;
use std::hashmap::HashMap;
use std::str::eq_slice;
use std::unstable::raw::Box;

#[deriving(Eq)]
pub enum DocumentTypeId {
    PlainDocumentTypeId,
    HTMLDocumentTypeId
}

#[deriving(Eq)]
pub struct AbstractDocument {
    document: *mut Box<Document>
}

impl AbstractDocument {
    pub fn document<'a>(&'a self) -> &'a Document {
        unsafe {
            &(*self.document).data
        }
    }

    pub fn mut_document<'a>(&'a self) -> &'a mut Document {
        unsafe {
            &mut (*self.document).data
        }
    }

    unsafe fn transmute<T, R>(&self, f: |&T| -> R) -> R {
        let box_: *Box<T> = cast::transmute(self.document);
        f(&(*box_).data)
    }

    pub fn with_html<R>(&self, callback: |&HTMLDocument| -> R) -> R {
        match self.document().doctype {
            HTML => unsafe { self.transmute(callback) },
            _ => fail!("attempt to downcast a non-HTMLDocument to HTMLDocument")
        }
    }

    pub fn from_box<T>(ptr: *mut Box<T>) -> AbstractDocument {
        AbstractDocument {
            document: ptr as *mut Box<Document>
        }
    }
}

#[deriving(Eq)]
pub enum DocumentType {
    HTML,
    SVG,
    XML
}

pub struct Document {
    node: Node,
    reflector_: Reflector,
    window: @mut Window,
    doctype: DocumentType,
    idmap: HashMap<DOMString, AbstractNode>,
    implementation: Option<@mut DOMImplementation>
}

impl Document {
    pub fn reflect_document<D: Reflectable>
            (document:  @mut D,
             window:    @mut Window,
             wrap_fn:   extern "Rust" fn(*JSContext, *JSObject, @mut D) -> *JSObject)
             -> AbstractDocument {
        assert!(document.reflector().get_jsobject().is_null());
        let document = reflect_dom_object(document, window, wrap_fn);
        assert!(document.reflector().get_jsobject().is_not_null());

        // JS object now owns the Document, so transmute_copy is needed
        let abstract = AbstractDocument {
            document: unsafe { cast::transmute_copy(&document) }
        };
        abstract.mut_document().node.set_owner_doc(abstract);
        abstract
    }

    pub fn new_inherited(window: @mut Window, doctype: DocumentType) -> Document {
        let node_type = match doctype {
            HTML => HTMLDocumentTypeId,
            SVG | XML => PlainDocumentTypeId
        };
        Document {
            node: Node::new_without_doc(DocumentNodeTypeId(node_type)),
            reflector_: Reflector::new(),
            window: window,
            doctype: doctype,
            idmap: HashMap::new(),
            implementation: None
        }
    }

    pub fn new(window: @mut Window, doctype: DocumentType) -> AbstractDocument {
        let document = Document::new_inherited(window, doctype);
        Document::reflect_document(@mut document, window, DocumentBinding::Wrap)
    }
}

impl Document {
    pub fn Constructor(owner: @mut Window) -> Fallible<AbstractDocument> {
        Ok(Document::new(owner, XML))
    }
}

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

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

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

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

impl Document {
    pub fn Implementation(&mut self) -> @mut DOMImplementation {
        if self.implementation.is_none() {
            self.implementation = Some(DOMImplementation::new(self.window));
        }
        self.implementation.unwrap()
    }

    pub fn GetDoctype(&self) -> Option<AbstractNode> {
        self.node.children().find(|child| child.is_doctype())
    }

    pub fn GetDocumentElement(&self) -> Option<AbstractNode> {
        self.node.child_elements().next()
    }

    pub fn GetElementsByTagName(&self, tag: DOMString) -> @mut HTMLCollection {
        self.createHTMLCollection(|elem| eq_slice(elem.tag_name, tag))
    }

    pub fn GetElementsByTagNameNS(&self, _ns: Option<DOMString>, _tag: DOMString) -> @mut HTMLCollection {
        HTMLCollection::new(self.window, ~[])
    }

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

    pub fn GetElementById(&self, id: DOMString) -> Option<AbstractNode> {
        // TODO: "in tree order, within the context object's tree"
        // http://dom.spec.whatwg.org/#dom-document-getelementbyid.
        match self.idmap.find_equiv(&id) {
            None => None,
            Some(node) => Some(*node),
        }
    }

    pub fn CreateElement(&self, abstract_self: AbstractDocument, local_name: DOMString)
                         -> Fallible<AbstractNode> {
        if xml_name_type(local_name) == InvalidXMLName {
            debug!("Not a valid element name");
            return Err(InvalidCharacter);
        }
        let local_name = local_name.to_ascii_lower();
        Ok(build_element_from_tag(local_name, abstract_self))
    }

    pub fn CreateDocumentFragment(&self, abstract_self: AbstractDocument) -> AbstractNode {
        DocumentFragment::new(abstract_self)
    }

    pub fn CreateTextNode(&self, abstract_self: AbstractDocument, data: DOMString)
                          -> AbstractNode {
        Text::new(data, abstract_self)
    }

    pub fn CreateComment(&self, abstract_self: AbstractDocument, data: DOMString) -> AbstractNode {
        Comment::new(data, abstract_self)
    }

    pub fn CreateEvent(&self, interface: DOMString) -> Fallible<AbstractEvent> {
        match interface.as_slice() {
            "UIEvents" => Ok(UIEvent::new(self.window)),
            "MouseEvents" => Ok(MouseEvent::new(self.window)),
            "HTMLEvents" => Ok(Event::new(self.window)),
            _ => Err(NotSupported)
        }
    }

    pub fn Title(&self, _: AbstractDocument) -> DOMString {
        let mut title = ~"";
        match self.doctype {
            SVG => {
                fail!("no SVG document yet")
            },
            _ => {
                match self.GetDocumentElement() {
                    None => {},
                    Some(root) => {
                        for node in root.traverse_preorder() {
                            if node.type_id() != ElementNodeTypeId(HTMLTitleElementTypeId) {
                                continue;
                            }
                            for child in node.children() {
                                if child.is_text() {
                                    child.with_imm_text(|text| {
                                        title = title + text.element.Data();
                                    });
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }
        let v: ~[&str] = title.words().collect();
        title = v.connect(" ");
        title = title.trim().to_owned();
        title
    }

    pub fn SetTitle(&self, abstract_self: AbstractDocument, title: DOMString) -> ErrorResult {
        match self.doctype {
            SVG => {
                fail!("no SVG document yet")
            },
            _ => {
                match self.GetDocumentElement() {
                    None => {},
                    Some(root) => {
                        for node in root.traverse_preorder() {
                            if node.type_id() != ElementNodeTypeId(HTMLHeadElementTypeId) {
                                continue;
                            }
                            let mut has_title = false;
                            for child in node.children() {
                                if child.type_id() != ElementNodeTypeId(HTMLTitleElementTypeId) {
                                    continue;
                                }
                                has_title = true;
                                for title_child in child.children() {
                                    child.RemoveChild(title_child);
                                }
                                child.AppendChild(self.CreateTextNode(abstract_self, title.clone()));
                                break;
                            }
                            if !has_title {
                                let new_title = HTMLTitleElement::new(~"title", abstract_self);
                                new_title.AppendChild(self.CreateTextNode(abstract_self, title.clone()));
                                node.AppendChild(new_title);
                            }
                            break;
                        }
                    }
                }
            }
        }
        Ok(())
    }

    fn get_html_element(&self) -> Option<AbstractNode> {
        self.GetDocumentElement().filtered(|root| {
            match root.type_id() {
                ElementNodeTypeId(HTMLHtmlElementTypeId) => true,
                _ => false
            }
        })
    }

    // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-head
    pub fn GetHead(&self) -> Option<AbstractNode> {
        self.get_html_element().and_then(|root| {
            root.children().find(|child| {
                child.type_id() == ElementNodeTypeId(HTMLHeadElementTypeId)
            })
        })
    }

    // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-body
    pub fn GetBody(&self, _: AbstractDocument) -> Option<AbstractNode> {
        match self.get_html_element() {
            None => None,
            Some(root) => {
                root.children().find(|child| {
                    match child.type_id() {
                        ElementNodeTypeId(HTMLBodyElementTypeId) |
                        ElementNodeTypeId(HTMLFrameSetElementTypeId) => true,
                        _ => false
                    }
                })
            }
        }
    }

    // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-body
    pub fn SetBody(&self, abstract_self: AbstractDocument, new_body: Option<AbstractNode>) -> ErrorResult {
        // Step 1.
        match new_body {
            Some(node) => {
                match node.type_id() {
                    ElementNodeTypeId(HTMLBodyElementTypeId) | ElementNodeTypeId(HTMLFrameSetElementTypeId) => {}
                    _ => return Err(HierarchyRequest)
                }
            }
            None => return Err(HierarchyRequest)
        }

        // Step 2.
        let old_body: Option<AbstractNode> = self.GetBody(abstract_self);
        if old_body == new_body {
            return Ok(());
        }

        // Step 3.
        match self.get_html_element() {
            // Step 4.
            None => return Err(HierarchyRequest),
            Some(root) => {
                match old_body {
                    Some(child) => { root.ReplaceChild(new_body.unwrap(), child); }
                    None => { root.AppendChild(new_body.unwrap()); }
                }
            }
        }
        Ok(())
    }

    pub fn GetElementsByName(&self, name: DOMString) -> @mut HTMLCollection {
        self.createHTMLCollection(|elem|
            elem.get_attr(Null, "name").is_some() && eq_slice(elem.get_attr(Null, "name").unwrap(), name))
    }

    pub fn createHTMLCollection(&self, callback: |elem: &Element| -> bool) -> @mut HTMLCollection {
        let mut elements = ~[];
        match self.GetDocumentElement() {
            None => {},
            Some(root) => {
                for child in root.traverse_preorder() {
                    if child.is_element() {
                        child.with_imm_element(|elem| {
                            if callback(elem) {
                                elements.push(child);
                            }
                        });
                    }
                }
            }
        }
        HTMLCollection::new(self.window, elements)
    }

    pub fn content_changed(&self) {
        self.damage_and_reflow(ContentChangedDocumentDamage);
    }

    pub fn damage_and_reflow(&self, damage: DocumentDamageLevel) {
        self.window.damage_and_reflow(damage);
    }

    pub fn wait_until_safe_to_modify_dom(&self) {
        self.window.wait_until_safe_to_modify_dom();
    }

    pub fn register_nodes_with_id(&mut self, root: &AbstractNode) {
        foreach_ided_elements(root, |id: &DOMString, abstract_node: &AbstractNode| {
            // TODO: "in tree order, within the context object's tree"
            // http://dom.spec.whatwg.org/#dom-document-getelementbyid.
            self.idmap.find_or_insert(id.clone(), *abstract_node);
        });
    }

    pub fn unregister_nodes_with_id(&mut self, root: &AbstractNode) {
        foreach_ided_elements(root, |id: &DOMString, _| {
            // TODO: "in tree order, within the context object's tree"
            // http://dom.spec.whatwg.org/#dom-document-getelementbyid.
            self.idmap.pop(id);
        });
    }

    pub fn update_idmap(&mut self,
                        abstract_self: AbstractNode,
                        new_id: Option<DOMString>,
                        old_id: Option<DOMString>) {
        // remove old ids:
        // * if the old ones are not same as the new one,
        // * OR if the new one is none.
        match old_id {
            Some(ref old_id) if new_id.is_none() ||
                                (*new_id.get_ref() != *old_id) => {
                self.idmap.remove(old_id);
            }
            _ => ()
        }

        match new_id {
            Some(new_id) => {
                // TODO: support the case if multiple elements
                // which haves same id are in the same document.
                self.idmap.mangle(new_id, abstract_self,
                                  |_, new_node: AbstractNode| -> AbstractNode {
                                      new_node
                                  },
                                  |_, old_node: &mut AbstractNode, new_node: AbstractNode| {
                                      *old_node = new_node;
                                  });
            }
            None => ()
        }
    }
}

#[inline(always)]
fn foreach_ided_elements(root: &AbstractNode, callback: |&DOMString, &AbstractNode|) {
    for node in root.traverse_preorder() {
        if !node.is_element() {
            continue;
        }

        node.with_imm_element(|element| {
            match element.get_attr(Null, "id") {
                Some(id) => {
                    callback(&id.to_str(), &node);
                }
                None => ()
            }
        });
    }
}

impl Traceable for Document {
    fn trace(&self, tracer: *mut JSTracer) {
        self.node.trace(tracer);
    }
}
<|endoftext|>


<commit_before>use RawFd;
use error::Result;
use handler::Handler;

pub use nix::sys::epoll::{epoll_create, EpollEvent, EpollEventKind, EPOLLIN, EPOLLOUT, EPOLLERR,
                          EPOLLHUP, EPOLLET, EPOLLONESHOT, EPOLLRDHUP, EPOLLEXCLUSIVE, EPOLLWAKEUP};

use nix::sys::epoll::{epoll_ctl, epoll_wait, EpollOp};
use nix::unistd;
use std::fmt;

lazy_static! {
    static ref NO_INTEREST: EpollEvent = {
        EpollEvent {
            events: EpollEventKind::empty(),
            data: 0,
        }
    };
}

#[derive(Debug, Copy, Clone)]
pub struct EpollConfig {
  pub loop_ms: isize,
  pub buffer_capacity: usize,
}

pub struct Epoll<H> {
  pub epfd: EpollFd,
  handler: H,
  loop_ms: isize,
  buf: Vec<EpollEvent>,
}

#[derive(Debug, Copy, Clone)]
pub struct EpollFd {
  pub fd: RawFd,
}

pub enum EpollCmd {
  Shutdown,
  Poll,
}

pub trait EpollHandler {
  #[inline]
  fn interests() -> EpollEventKind;

  fn with_epfd(&mut self, epfd: EpollFd);
}

unsafe impl<H> Send for Epoll<H> {}

impl<'h, H: Handler<EpollEvent, EpollCmd> + 'h> Epoll<H> {
  pub fn from_fd(epfd: EpollFd, handler: H, config: EpollConfig) -> Epoll<H> {
    Epoll {
      epfd: epfd,
      loop_ms: config.loop_ms,
      handler: handler,
      buf: Vec::with_capacity(config.buffer_capacity),
    }
  }

  pub fn new_with<F>(config: EpollConfig, newctl: F) -> Result<Epoll<H>>
    where F: FnOnce(EpollFd) -> H,
  {

    let fd = epoll_create()?;

    let epfd = EpollFd { fd: fd };

    let handler = newctl(epfd);

    Ok(Self::from_fd(epfd, handler, config))
  }

  #[inline]
  pub fn run_once(&mut self) -> EpollCmd {
    unsafe {
      let dst = ::std::slice::from_raw_parts_mut(self.buf.as_mut_ptr(), self.buf.capacity());
      let cnt = epoll_wait(self.epfd.fd, dst, self.loop_ms).unwrap();
      self.buf.set_len(cnt);

      for ev in self.buf.drain(..) {
        self.handler.on_next(ev);
      }

      self.handler.next()
    }
  }

  pub fn run(&mut self) {
    loop {
      if let EpollCmd::Shutdown = self.run_once() {
        return;
      }
    }
  }
}

impl<H> Drop for Epoll<H> {
  fn drop(&mut self) {
    let _ = unistd::close(self.epfd.fd);
  }
}

unsafe impl Send for EpollFd {}

impl EpollFd {
  pub fn new(fd: RawFd) -> EpollFd {
    EpollFd { fd: fd }
  }

  #[inline]
  fn ctl(&self, op: EpollOp, interest: &EpollEvent, fd: RawFd) -> Result<()> {
    epoll_ctl(self.fd, op, fd, interest)?;
    Ok(())
  }

  #[inline]
  pub fn reregister(&self, fd: RawFd, interest: &EpollEvent) -> Result<()> {
    self.ctl(EpollOp::EpollCtlMod, interest, fd)?;
    Ok(())
  }

  #[inline]
  pub fn register(&self, fd: RawFd, interest: &EpollEvent) -> Result<()> {
    self.ctl(EpollOp::EpollCtlAdd, interest, fd)?;
    Ok(())
  }

  #[inline]
  pub fn unregister(&self, fd: RawFd) -> Result<()> {
    self.ctl(EpollOp::EpollCtlDel, &NO_INTEREST, fd)?;
    Ok(())
  }
}

impl fmt::Display for EpollFd {
  fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
    write!(fmt, "{}", self.fd)
  }
}

impl From<EpollFd> for i32 {
  fn from(epfd: EpollFd) -> i32 {
    epfd.fd
  }
}

impl Default for EpollConfig {
  fn default() -> EpollConfig {
    EpollConfig {
      loop_ms: -1,
      buffer_capacity: 256,
    }
  }
}

#[cfg(test)]
mod tests {
  use handler::Handler;
  use nix::fcntl::O_NONBLOCK;
  use nix::unistd;
  use ::std::sync::mpsc::*;
  use super::*;

  struct ChannelHandler {
    tx: Sender<EpollEvent>,
    state: EpollCmd
  }

  impl Handler<EpollEvent, EpollCmd> for ChannelHandler {
    fn next(&mut self) -> EpollCmd {
      self.state.clone()
    }

    fn on_next(&mut self, events: EpollEvent) {
      if self.tx.send(events).is_ok() {
        self.state = EpollCmd::Shutdown
      }

      self.state = EpollCmd::Poll;
    }
  }

  #[test]
  fn notify_handler() {

    let (tx, rx) = channel();

    let config = EpollConfig {
      loop_ms: 10,
      buffer_capacity: 100,
    };

    let mut poll = Epoll::new_with(config, |_| ChannelHandler { tx: tx }).unwrap();

    let (rfd, wfd) = unistd::pipe2(O_NONBLOCK).unwrap();

    let interest = EpollEvent {
      events: EPOLLONESHOT | EPOLLIN,
      data: rfd as u64,
    };

    unistd::write(wfd, b"hello!").unwrap();

    poll.epfd.register(rfd, &interest).unwrap();

    poll.run_once();

    let ev = rx.recv().unwrap();

    assert!(ev.events.contains(EPOLLIN));
    assert!(ev.data == rfd as u64);
  }
}
<commit_msg>fixed tests<commit_after>use RawFd;
use error::Result;
use handler::Handler;

pub use nix::sys::epoll::{epoll_create, EpollEvent, EpollEventKind, EPOLLIN, EPOLLOUT, EPOLLERR,
                          EPOLLHUP, EPOLLET, EPOLLONESHOT, EPOLLRDHUP, EPOLLEXCLUSIVE, EPOLLWAKEUP};

use nix::sys::epoll::{epoll_ctl, epoll_wait, EpollOp};
use nix::unistd;
use std::fmt;

lazy_static! {
    static ref NO_INTEREST: EpollEvent = {
        EpollEvent {
            events: EpollEventKind::empty(),
            data: 0,
        }
    };
}

#[derive(Debug, Copy, Clone)]
pub struct EpollConfig {
  pub loop_ms: isize,
  pub buffer_capacity: usize,
}

pub struct Epoll<H> {
  pub epfd: EpollFd,
  handler: H,
  loop_ms: isize,
  buf: Vec<EpollEvent>,
}

#[derive(Debug, Copy, Clone)]
pub struct EpollFd {
  pub fd: RawFd,
}

#[derive(Debug, Copy, Clone)]
pub enum EpollCmd {
  Shutdown,
  Poll,
}

pub trait EpollHandler {
  #[inline]
  fn interests() -> EpollEventKind;

  fn with_epfd(&mut self, epfd: EpollFd);
}

unsafe impl<H> Send for Epoll<H> {}

impl<'h, H: Handler<EpollEvent, EpollCmd> + 'h> Epoll<H> {
  pub fn from_fd(epfd: EpollFd, handler: H, config: EpollConfig) -> Epoll<H> {
    Epoll {
      epfd: epfd,
      loop_ms: config.loop_ms,
      handler: handler,
      buf: Vec::with_capacity(config.buffer_capacity),
    }
  }

  pub fn new_with<F>(config: EpollConfig, newctl: F) -> Result<Epoll<H>>
    where F: FnOnce(EpollFd) -> H,
  {

    let fd = epoll_create()?;

    let epfd = EpollFd { fd: fd };

    let handler = newctl(epfd);

    Ok(Self::from_fd(epfd, handler, config))
  }

  #[inline]
  pub fn run_once(&mut self) -> EpollCmd {
    unsafe {
      let dst = ::std::slice::from_raw_parts_mut(self.buf.as_mut_ptr(), self.buf.capacity());
      let cnt = epoll_wait(self.epfd.fd, dst, self.loop_ms).unwrap();
      self.buf.set_len(cnt);

      for ev in self.buf.drain(..) {
        self.handler.on_next(ev);
      }

      self.handler.next()
    }
  }

  pub fn run(&mut self) {
    loop {
      if let EpollCmd::Shutdown = self.run_once() {
        return;
      }
    }
  }
}

impl<H> Drop for Epoll<H> {
  fn drop(&mut self) {
    let _ = unistd::close(self.epfd.fd);
  }
}

unsafe impl Send for EpollFd {}

impl EpollFd {
  pub fn new(fd: RawFd) -> EpollFd {
    EpollFd { fd: fd }
  }

  #[inline]
  fn ctl(&self, op: EpollOp, interest: &EpollEvent, fd: RawFd) -> Result<()> {
    epoll_ctl(self.fd, op, fd, interest)?;
    Ok(())
  }

  #[inline]
  pub fn reregister(&self, fd: RawFd, interest: &EpollEvent) -> Result<()> {
    self.ctl(EpollOp::EpollCtlMod, interest, fd)?;
    Ok(())
  }

  #[inline]
  pub fn register(&self, fd: RawFd, interest: &EpollEvent) -> Result<()> {
    self.ctl(EpollOp::EpollCtlAdd, interest, fd)?;
    Ok(())
  }

  #[inline]
  pub fn unregister(&self, fd: RawFd) -> Result<()> {
    self.ctl(EpollOp::EpollCtlDel, &NO_INTEREST, fd)?;
    Ok(())
  }
}

impl fmt::Display for EpollFd {
  fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
    write!(fmt, "{}", self.fd)
  }
}

impl From<EpollFd> for i32 {
  fn from(epfd: EpollFd) -> i32 {
    epfd.fd
  }
}

impl Default for EpollConfig {
  fn default() -> EpollConfig {
    EpollConfig {
      loop_ms: -1,
      buffer_capacity: 256,
    }
  }
}

#[cfg(test)]
mod tests {
  use handler::Handler;
  use nix::fcntl::O_NONBLOCK;
  use nix::unistd;
  use ::std::sync::mpsc::*;
  use super::*;

  struct ChannelHandler {
    tx: Sender<EpollEvent>,
    state: EpollCmd
  }

  impl Handler<EpollEvent, EpollCmd> for ChannelHandler {
    fn next(&mut self) -> EpollCmd {
      self.state
    }

    fn on_next(&mut self, events: EpollEvent) {
      if self.tx.send(events).is_ok() {
        self.state = EpollCmd::Shutdown
      }

      self.state = EpollCmd::Poll;
    }
  }

  #[test]
  fn notify_handler() {

    let (tx, rx) = channel();

    let config = EpollConfig {
      loop_ms: 10,
      buffer_capacity: 100,
    };

    let mut poll = Epoll::new_with(config, |_| ChannelHandler { tx: tx, state: EpollCmd::Poll }).unwrap();

    let (rfd, wfd) = unistd::pipe2(O_NONBLOCK).unwrap();

    let interest = EpollEvent {
      events: EPOLLONESHOT | EPOLLIN,
      data: rfd as u64,
    };

    unistd::write(wfd, b"hello!").unwrap();

    poll.epfd.register(rfd, &interest).unwrap();

    poll.run_once();

    let ev = rx.recv().unwrap();

    assert!(ev.events.contains(EPOLLIN));
    assert!(ev.data == rfd as u64);
  }
}
<|endoftext|>


<commit_before>pub enum ErrorLevel {
    Info,
    Warning,
    Error,
}

// Use a hashmap which contains an error level and a string
pub type ErrorStack = Vec<(ErrorLevel, String)>;<commit_msg>Derive debug and change string type<commit_after>#[derive(Debug)]
pub enum ErrorLevel {
    Info,
    Warning,
    Error,
}

// Use a hashmap which contains an error level and a string
#[derive(Debug)]
pub type ErrorStack = Vec<(ErrorLevel, &'static str)>;<|endoftext|>


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

use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

use libmount;
use nix::mount::{umount2, MntFlags};

use devicemapper::{DevId, DmOptions};

use crate::engine::strat_engine::cmd;
use crate::engine::strat_engine::dm::{get_dm, get_dm_init};

// For an explanation see:
// https://github.com/rust-lang-nursery/error-chain/issues/254.
// FIXME: Drop dependence on error-chain entirely.
mod cleanup_errors {
    use libmount;
    use nix;
    use std;

    error_chain! {
        foreign_links {
            Ioe(std::io::Error);
            Mnt(libmount::mountinfo::ParseError);
            Nix(nix::Error);
        }
    }
}

use self::cleanup_errors::{Error, Result};

/// Attempt to remove all device mapper devices which match the stratis naming convention.
/// FIXME: Current implementation complicated by https://bugzilla.redhat.com/show_bug.cgi?id=1506287
fn dm_stratis_devices_remove() -> Result<()> {
    /// One iteration of removing devicemapper devices
    fn one_iteration() -> Result<(bool, Vec<String>)> {
        let mut progress_made = false;
        let mut remain = Vec::new();

        for n in get_dm()
            .list_devices()
            .map_err(|e| {
                let err_msg = "failed while listing DM devices, giving up";
                Error::with_chain(e, err_msg)
            })?
            .iter()
            .map(|d| &d.0)
            .filter(|n| n.to_string().starts_with("stratis-1"))
        {
            match get_dm().device_remove(&DevId::Name(n), &DmOptions::new()) {
                Ok(_) => progress_made = true,
                Err(_) => remain.push(n.to_string()),
            }
        }
        Ok((progress_made, remain))
    }

    /// Do one iteration of removals until progress stops. Return remaining
    /// dm devices.
    fn do_while_progress() -> Result<Vec<String>> {
        let mut result = one_iteration()?;
        while result.0 {
            result = one_iteration()?;
        }
        Ok(result.1)
    }

    || -> Result<()> {
        cmd::udev_settle().unwrap();
        get_dm_init().map_err(|err| Error::with_chain(err, "Unable to initialize DM"))?;
        do_while_progress().and_then(|remain| {
            if !remain.is_empty() {
                Err(format!("Some Stratis DM devices remaining: {:?}", remain).into())
            } else {
                Ok(())
            }
        })
    }()
    .map_err(|e| e.chain_err(|| "Failed to ensure removal of all Stratis DM devices"))
}

/// Try and un-mount any filesystems that have the name stratis in the mount point, returning
/// immediately on the first one we are unable to unmount.
fn stratis_filesystems_unmount() -> Result<()> {
    || -> Result<()> {
        let mut mount_data = String::new();
        File::open("/proc/self/mountinfo")?.read_to_string(&mut mount_data)?;
        let parser = libmount::mountinfo::Parser::new(mount_data.as_bytes());

        for mount_point in parser
            .filter_map(|x| x.ok())
            .filter_map(|m| m.mount_point.into_owned().into_string().ok())
            .filter(|mp| mp.contains("stratis"))
        {
            umount2(&PathBuf::from(mount_point), MntFlags::MNT_DETACH)?;
        }

        Ok(())
    }()
    .map_err(|e| e.chain_err(|| "Failed to ensure all Stratis filesystems were unmounted"))
}

/// When a unit test panics we can leave the system in an inconsistent state.  This function
/// tries to clean up by un-mounting any mounted file systems which contain the string
/// "stratis_testing" and then it tries to remove any device mapper tables which are also stratis
/// created.
pub fn clean_up() -> Result<()> {
    stratis_filesystems_unmount()?;
    dm_stratis_devices_remove()?;
    Ok(())
}
<commit_msg>Actually allow the deprecated usage in error-chain<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

use libmount;
use nix::mount::{umount2, MntFlags};

use devicemapper::{DevId, DmOptions};

use crate::engine::strat_engine::cmd;
use crate::engine::strat_engine::dm::{get_dm, get_dm_init};

// For an explanation see:
// https://github.com/rust-lang-nursery/error-chain/issues/254.
// FIXME: Drop dependence on error-chain entirely.
#[allow(deprecated)]
mod cleanup_errors {
    use libmount;
    use nix;
    use std;

    error_chain! {
        foreign_links {
            Ioe(std::io::Error);
            Mnt(libmount::mountinfo::ParseError);
            Nix(nix::Error);
        }
    }
}

use self::cleanup_errors::{Error, Result};

/// Attempt to remove all device mapper devices which match the stratis naming convention.
/// FIXME: Current implementation complicated by https://bugzilla.redhat.com/show_bug.cgi?id=1506287
fn dm_stratis_devices_remove() -> Result<()> {
    /// One iteration of removing devicemapper devices
    fn one_iteration() -> Result<(bool, Vec<String>)> {
        let mut progress_made = false;
        let mut remain = Vec::new();

        for n in get_dm()
            .list_devices()
            .map_err(|e| {
                let err_msg = "failed while listing DM devices, giving up";
                Error::with_chain(e, err_msg)
            })?
            .iter()
            .map(|d| &d.0)
            .filter(|n| n.to_string().starts_with("stratis-1"))
        {
            match get_dm().device_remove(&DevId::Name(n), &DmOptions::new()) {
                Ok(_) => progress_made = true,
                Err(_) => remain.push(n.to_string()),
            }
        }
        Ok((progress_made, remain))
    }

    /// Do one iteration of removals until progress stops. Return remaining
    /// dm devices.
    fn do_while_progress() -> Result<Vec<String>> {
        let mut result = one_iteration()?;
        while result.0 {
            result = one_iteration()?;
        }
        Ok(result.1)
    }

    || -> Result<()> {
        cmd::udev_settle().unwrap();
        get_dm_init().map_err(|err| Error::with_chain(err, "Unable to initialize DM"))?;
        do_while_progress().and_then(|remain| {
            if !remain.is_empty() {
                Err(format!("Some Stratis DM devices remaining: {:?}", remain).into())
            } else {
                Ok(())
            }
        })
    }()
    .map_err(|e| e.chain_err(|| "Failed to ensure removal of all Stratis DM devices"))
}

/// Try and un-mount any filesystems that have the name stratis in the mount point, returning
/// immediately on the first one we are unable to unmount.
fn stratis_filesystems_unmount() -> Result<()> {
    || -> Result<()> {
        let mut mount_data = String::new();
        File::open("/proc/self/mountinfo")?.read_to_string(&mut mount_data)?;
        let parser = libmount::mountinfo::Parser::new(mount_data.as_bytes());

        for mount_point in parser
            .filter_map(|x| x.ok())
            .filter_map(|m| m.mount_point.into_owned().into_string().ok())
            .filter(|mp| mp.contains("stratis"))
        {
            umount2(&PathBuf::from(mount_point), MntFlags::MNT_DETACH)?;
        }

        Ok(())
    }()
    .map_err(|e| e.chain_err(|| "Failed to ensure all Stratis filesystems were unmounted"))
}

/// When a unit test panics we can leave the system in an inconsistent state.  This function
/// tries to clean up by un-mounting any mounted file systems which contain the string
/// "stratis_testing" and then it tries to remove any device mapper tables which are also stratis
/// created.
pub fn clean_up() -> Result<()> {
    stratis_filesystems_unmount()?;
    dm_stratis_devices_remove()?;
    Ok(())
}
<|endoftext|>


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

use std::{fs::File, io::Read, path::PathBuf};

use nix::mount::{umount2, MntFlags};

use devicemapper::{DevId, DmOptions};

use crate::engine::strat_engine::{
    cmd::udev_settle,
    dm::{get_dm, get_dm_init},
};

mod cleanup_errors {
    error_chain! {
        foreign_links {
            Ioe(std::io::Error);
            Mnt(libmount::mountinfo::ParseError);
            Nix(nix::Error);
        }
    }
}

use self::cleanup_errors::{Error, Result};

/// Attempt to remove all device mapper devices which match the stratis naming convention.
/// FIXME: Current implementation complicated by https://bugzilla.redhat.com/show_bug.cgi?id=1506287
fn dm_stratis_devices_remove() -> Result<()> {
    /// One iteration of removing devicemapper devices
    fn one_iteration() -> Result<(bool, Vec<String>)> {
        let mut progress_made = false;
        let mut remain = Vec::new();

        for n in get_dm()
            .list_devices()
            .map_err(|e| {
                let err_msg = "failed while listing DM devices, giving up";
                Error::with_chain(e, err_msg)
            })?
            .iter()
            .map(|d| &d.0)
            .filter(|n| n.to_string().starts_with("stratis-1"))
        {
            match get_dm().device_remove(&DevId::Name(n), &DmOptions::new()) {
                Ok(_) => progress_made = true,
                Err(_) => remain.push(n.to_string()),
            }
        }
        Ok((progress_made, remain))
    }

    /// Do one iteration of removals until progress stops. Return remaining
    /// dm devices.
    fn do_while_progress() -> Result<Vec<String>> {
        let mut result = one_iteration()?;
        while result.0 {
            result = one_iteration()?;
        }
        Ok(result.1)
    }

    || -> Result<()> {
        udev_settle().unwrap();
        get_dm_init().map_err(|err| Error::with_chain(err, "Unable to initialize DM"))?;
        do_while_progress().and_then(|remain| {
            if !remain.is_empty() {
                Err(format!("Some Stratis DM devices remaining: {:?}", remain).into())
            } else {
                Ok(())
            }
        })
    }()
    .map_err(|e| e.chain_err(|| "Failed to ensure removal of all Stratis DM devices"))
}

/// Try and un-mount any filesystems that have the name stratis in the mount point, returning
/// immediately on the first one we are unable to unmount.
fn stratis_filesystems_unmount() -> Result<()> {
    || -> Result<()> {
        let mut mount_data = String::new();
        File::open("/proc/self/mountinfo")?.read_to_string(&mut mount_data)?;
        let parser = libmount::mountinfo::Parser::new(mount_data.as_bytes());

        for mount_point in parser
            .filter_map(|x| x.ok())
            .filter_map(|m| m.mount_point.into_owned().into_string().ok())
            .filter(|mp| mp.contains("stratis"))
        {
            umount2(&PathBuf::from(mount_point), MntFlags::MNT_DETACH)?;
        }

        Ok(())
    }()
    .map_err(|e| e.chain_err(|| "Failed to ensure all Stratis filesystems were unmounted"))
}

/// When a unit test panics we can leave the system in an inconsistent state.  This function
/// tries to clean up by un-mounting any mounted file systems which contain the string
/// "stratis_testing" and then it tries to remove any device mapper tables which are also stratis
/// created.
pub fn clean_up() -> Result<()> {
    stratis_filesystems_unmount().and_then(|_| dm_stratis_devices_remove())
}
<commit_msg>Add logging on device cleanup failure<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{fs::File, io::Read, path::PathBuf};

use nix::mount::{umount2, MntFlags};

use devicemapper::{DevId, DmOptions};

use crate::engine::strat_engine::{
    cmd::udev_settle,
    dm::{get_dm, get_dm_init},
};

mod cleanup_errors {
    error_chain! {
        foreign_links {
            Ioe(std::io::Error);
            Mnt(libmount::mountinfo::ParseError);
            Nix(nix::Error);
        }
    }
}

use self::cleanup_errors::{Error, Result};

/// Attempt to remove all device mapper devices which match the stratis naming convention.
/// FIXME: Current implementation complicated by https://bugzilla.redhat.com/show_bug.cgi?id=1506287
fn dm_stratis_devices_remove() -> Result<()> {
    /// One iteration of removing devicemapper devices
    fn one_iteration() -> Result<(bool, Vec<String>)> {
        let mut progress_made = false;
        let mut remain = Vec::new();

        for n in get_dm()
            .list_devices()
            .map_err(|e| {
                let err_msg = "failed while listing DM devices, giving up";
                Error::with_chain(e, err_msg)
            })?
            .iter()
            .map(|d| &d.0)
            .filter(|n| n.to_string().starts_with("stratis-1"))
        {
            match get_dm().device_remove(&DevId::Name(n), &DmOptions::new()) {
                Ok(_) => progress_made = true,
                Err(e) => {
                    let name = n.to_string();
                    debug!("Failed to remove device {}: {}", name, e);
                    remain.push(name)
                }
            }
        }
        Ok((progress_made, remain))
    }

    /// Do one iteration of removals until progress stops. Return remaining
    /// dm devices.
    fn do_while_progress() -> Result<Vec<String>> {
        let mut result = one_iteration()?;
        while result.0 {
            result = one_iteration()?;
        }
        Ok(result.1)
    }

    || -> Result<()> {
        udev_settle().unwrap();
        get_dm_init().map_err(|err| Error::with_chain(err, "Unable to initialize DM"))?;
        do_while_progress().and_then(|remain| {
            if !remain.is_empty() {
                Err(format!("Some Stratis DM devices remaining: {:?}", remain).into())
            } else {
                Ok(())
            }
        })
    }()
    .map_err(|e| e.chain_err(|| "Failed to ensure removal of all Stratis DM devices"))
}

/// Try and un-mount any filesystems that have the name stratis in the mount point, returning
/// immediately on the first one we are unable to unmount.
fn stratis_filesystems_unmount() -> Result<()> {
    || -> Result<()> {
        let mut mount_data = String::new();
        File::open("/proc/self/mountinfo")?.read_to_string(&mut mount_data)?;
        let parser = libmount::mountinfo::Parser::new(mount_data.as_bytes());

        for mount_point in parser
            .filter_map(|x| x.ok())
            .filter_map(|m| m.mount_point.into_owned().into_string().ok())
            .filter(|mp| mp.contains("stratis"))
        {
            umount2(&PathBuf::from(mount_point), MntFlags::MNT_DETACH)?;
        }

        Ok(())
    }()
    .map_err(|e| e.chain_err(|| "Failed to ensure all Stratis filesystems were unmounted"))
}

/// When a unit test panics we can leave the system in an inconsistent state.  This function
/// tries to clean up by un-mounting any mounted file systems which contain the string
/// "stratis_testing" and then it tries to remove any device mapper tables which are also stratis
/// created.
pub fn clean_up() -> Result<()> {
    stratis_filesystems_unmount().and_then(|_| dm_stratis_devices_remove())
}
<|endoftext|>


<commit_before>// (C)opyleft 2013 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_id = "geoip#0.1"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_uppercase_statics,
        unnecessary_qualification,
        managed_heap_memory)]

extern crate libc;

use libc::{c_void, c_char, c_int, c_ulong};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type GeoIP_ = *c_void;
type In6Addr = [u8, ..16];

struct GeoIPLookup {
    netmask: c_int
}

impl GeoIPLookup {
    fn new() -> GeoIPLookup {
        GeoIPLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *c_char, flags: c_int) -> GeoIP_;
    fn GeoIP_delete(db: GeoIP_);
    fn GeoIP_name_by_ipnum_gl(db: GeoIP_, ipnum: c_ulong, gl: &GeoIPLookup) -> *c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: GeoIP_, ipnum: In6Addr, gl: &GeoIPLookup) -> *c_char;
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIP {
    db: GeoIP_
}

pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

impl GeoIP {
    pub fn open(path: &Path, options: Options) -> Result<GeoIP, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().unwrap(), options as c_int)
        };
        match db.is_null() {
            true => Err(format!("Can't open {}", file)),
            false => Ok(GeoIP {
                    db: db
                })
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let gl = GeoIPLookup::new();
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_name_by_ipnum_gl(self.db, ipnum, &gl)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_name_by_ipnum_v6_gl(self.db, in6_addr, &gl)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(' ', 1);
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let asinfo = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(asinfo)
    }
}

impl Drop for GeoIP {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}
<commit_msg>Add city_info_by_ip<commit_after>// (C)opyleft 2013 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_id = "geoip#0.1"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_uppercase_statics,
        unnecessary_qualification,
        managed_heap_memory)]

extern crate libc;

use libc::{c_void, c_char, c_int, c_ulong, c_float};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type GeoIP_ = *c_void;
type In6Addr = [u8, ..16];

struct GeoIPLookup_ {
    netmask: c_int
}

impl GeoIPLookup_ {
    fn new() -> GeoIPLookup_ {
        GeoIPLookup_ {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *c_char, flags: c_int) -> GeoIP_;
    fn GeoIP_delete(db: GeoIP_);
    fn GeoIP_name_by_ipnum_gl(db: GeoIP_, ipnum: c_ulong, gl: &GeoIPLookup_) -> *c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: GeoIP_, ipnum: In6Addr, gl: &GeoIPLookup_) -> *c_char;
    fn GeoIP_record_by_ipnum(db: GeoIP_, ipnum: c_ulong) -> *GeoIPRecord_;
    fn GeoIP_record_by_ipnum_v6(db: GeoIP_, ipnum: In6Addr) -> *GeoIPRecord_;
    fn GeoIPRecord_delete(gir: *GeoIPRecord_);
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIP {
    db: GeoIP_
}

pub struct GeoIPRecord_ {
    country_code: *c_char,
    country_code3: *c_char,
    country_name: *c_char,
    region: *c_char,
    city: *c_char,
    postal_code: *c_char,
    latitude: c_float,
    longitude: c_float,
    dma_code: c_int,
    area_code: c_int,
    charset: c_int,
    continent_code: *c_char,
    netmask: c_int
}

pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

pub struct CityInfo {
    country_code: Option<String>,
    country_code3: Option<String>,
    country_name: Option<String>,
    region: Option<String>,
    city: Option<String>,
    postal_code: Option<String>,
    latitude: f32,
    longitude: f32,
    dma_code: uint,
    area_code: uint,
    charset: uint,
    continent_code: Option<String>,
    netmask: uint
}

impl CityInfo {
    fn from_geoiprecord(res: &GeoIPRecord_) -> CityInfo {
        let country_code = unsafe { if res.country_code.is_null() {
            None
        } else {
            Some(String::from_str(CString::new(res.country_code, false).as_str().unwrap()))
        }};
        let country_code3 = unsafe { if res.country_code3.is_null() {
            None
        } else {
            Some(String::from_str(CString::new(res.country_code3, false).as_str().unwrap()))
        }};
        let country_name = unsafe { if res.country_name.is_null() {
            None
        } else {
            Some(String::from_str(CString::new(res.country_name, false).as_str().unwrap()))
        }};
        let region = unsafe { if res.region.is_null() {
            None
        } else {
            Some(String::from_str(CString::new(res.region, false).as_str().unwrap()))
        }};
        let city = unsafe { if res.city.is_null() {
            None
        } else {
            Some(String::from_str(CString::new(res.city, false).as_str().unwrap()))
        }};
        let postal_code = unsafe { if res.postal_code.is_null() {
            None
        } else {
            Some(String::from_str(CString::new(res.postal_code, false).as_str().unwrap()))
        }};
        let continent_code = unsafe { if res.continent_code.is_null() {
            None
        } else {
            Some(String::from_str(CString::new(res.continent_code, false).as_str().unwrap()))
        }};
        CityInfo {
            country_code: country_code,
            country_code3: country_code3,
            country_name: country_name,
            region: region,
            city: city,
            postal_code: postal_code,
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as uint,
            area_code: res.area_code as uint,
            charset: res.charset as uint,
            continent_code: continent_code,
            netmask: res.netmask as uint
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

impl GeoIP {
    pub fn open(path: &Path, options: Options) -> Result<GeoIP, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().unwrap(), options as c_int)
        };
        match db.is_null() {
            true => Err(format!("Can't open {}", file)),
            false => Ok(GeoIP {
                db: db
            })
        }
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_record_by_ipnum(self.db, ipnum)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_record_by_ipnum_v6(self.db, in6_addr)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let city_info = CityInfo::from_geoiprecord(&unsafe { *cres });
        unsafe { GeoIPRecord_delete(cres) };
        unsafe { std::mem::forget(cres) };
        Some(city_info)
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let gl = GeoIPLookup_::new();
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_name_by_ipnum_gl(self.db, ipnum, &gl)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_name_by_ipnum_v6_gl(self.db, in6_addr, &gl)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(' ', 1);
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(as_info)
    }
}

impl Drop for GeoIP {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<|endoftext|>


<commit_before>use std::iter::repeat;

use std::ops::{Index, RangeFull};
use std::fmt::Debug;

#[derive(Debug)]
pub struct Ring<T> {
   v: Vec<T>,
   start: usize,
   end: usize,
}

impl<T> Ring<T> where T: Default + Clone + Debug {
   #[inline]
   pub fn new(capacity: usize) -> Self {
      let v = repeat(T::default()).take(capacity).collect();

      Ring { v: v, start: 0, end: 0 }
   }

   #[inline]
   pub fn len(&self) -> usize {
      self.end - self.start
   }

   #[inline]
   pub fn last(&self) -> Option<&T> {
      if self.start == self.end {
         None
      } else {
         Some(unsafe {
            self.v.get_unchecked(self.end - 1)
         })
      }
   }

   #[inline]
   pub fn start(&self) -> usize {
      self.start
   }

   #[inline]
   pub fn end(&self) -> usize {
      self.end
   }

   #[inline]
   pub fn consume_at(&mut self, marker: usize) {
      self.start = marker;
   }

   #[inline]
   pub fn consume(&mut self) {
      self.start = self.end;
   }

   #[inline]
   pub fn reserve(&mut self, additional: usize) {
      self.v.reserve(additional);
   }

   #[inline]
   pub fn rewind(&mut self, extra: usize) {
      if extra > (self.v.len() - self.end) {
         if self.start != self.end {
            for i in 0..(self.end - self.start) {
               self.v[i] = self.v[self.start + i].clone();
            }
         }

         self.end -= self.start;
         self.start = 0;
      }
   }

   #[inline]
   pub fn push(&mut self, value: T) {
      self.v[self.end] = value;
      self.end += 1;
   }

   #[inline]
   pub fn next_index(&self, index: usize) -> usize {
      let index = index + 1;
      if index == self.end {
         self.start
      } else {
         index
      }
   }

   #[inline]
   pub fn prev_index(&self, index: usize) -> usize {
      if index == self.start {
         self.end - 1
      } else {
         index - 1
      }
   }
}

impl<T> Index<usize> for Ring<T> {
   type Output = T;

   #[inline]
   fn index(&self, index: usize) -> &T {
      &self.v[index]
   }
}

impl<T> Index<RangeFull> for Ring<T> {
   type Output = [T];

   #[inline]
   fn index(&self, _index: RangeFull) -> &[T] {
      &self.v[self.start..self.end]
   }
}
<commit_msg>Ring clear method<commit_after>use std::iter::repeat;

use std::ops::{Index, RangeFull};
use std::fmt::Debug;

#[derive(Debug)]
pub struct Ring<T> {
   v: Vec<T>,
   start: usize,
   end: usize,
}

impl<T> Ring<T> where T: Default + Clone + Debug {
   #[inline]
   pub fn new(capacity: usize) -> Self {
      let v = fill_default::<T>(capacity);

      Ring { v: v, start: 0, end: 0 }
   }

   #[inline]
   pub fn len(&self) -> usize {
      self.end - self.start
   }

   #[inline]
   pub fn last(&self) -> Option<&T> {
      if self.start == self.end {
         None
      } else {
         Some(unsafe {
            self.v.get_unchecked(self.end - 1)
         })
      }
   }

   #[inline]
   pub fn start(&self) -> usize {
      self.start
   }

   #[inline]
   pub fn end(&self) -> usize {
      self.end
   }

   #[inline]
   pub fn consume_at(&mut self, marker: usize) {
      self.start = marker;
   }

   #[inline]
   pub fn consume(&mut self) {
      self.start = self.end;
   }

   #[inline]
   pub fn reserve(&mut self, additional: usize) {
      self.v.reserve(additional);
   }

   #[inline]
   pub fn rewind(&mut self, extra: usize) {
      if extra > (self.v.len() - self.end) {
         if self.start != self.end {
            for i in 0..(self.end - self.start) {
               self.v[i] = self.v[self.start + i].clone();
            }
         }

         self.end -= self.start;
         self.start = 0;
      }
   }

   #[inline]
   pub fn push(&mut self, value: T) {
      self.v[self.end] = value;
      self.end += 1;
   }

   #[inline]
   pub fn next_index(&self, index: usize) -> usize {
      let index = index + 1;
      if index == self.end {
         self.start
      } else {
         index
      }
   }

   #[inline]
   pub fn prev_index(&self, index: usize) -> usize {
      if index == self.start {
         self.end - 1
      } else {
         index - 1
      }
   }

   #[inline]
   pub fn clear(&mut self) {
      self.start = 0;
      self.end = 0;
   }
}

impl<T> Index<usize> for Ring<T> {
   type Output = T;

   #[inline]
   fn index(&self, index: usize) -> &T {
      &self.v[index]
   }
}

impl<T> Index<RangeFull> for Ring<T> {
   type Output = [T];

   #[inline]
   fn index(&self, _index: RangeFull) -> &[T] {
      &self.v[self.start..self.end]
   }
}

pub fn fill_default<T>(capacity: usize) -> Vec<T> where T: Default + Clone {
   repeat(T::default()).take(capacity).collect()
}
<|endoftext|>


<commit_before>extern crate tajo;

use tajo::schema::Column;
use tajo::types::*;
use tajo::expr::*;
use tajo::eval::*;

struct VisitOrder {
  order: Vec<Box<String>>
}

impl<'v> Visitor<'v> for VisitOrder {
  fn visit_field(&mut self, c: &'v Column) {
    println!("column: {}", c.name);
  }
}

#[test]
fn test_visitor() {

  let col1 = Column::new("c0".to_string(), Ty::Int8);
  let col2 = Column::new("c1".to_string(), Ty::Int4);

  let lhs : Box<Expr> = Box::new(Expr::new_field(&col1));
  let rhs : Box<Expr> = Box::new(Expr::new_field(&col2));

  //let lhs: Box<Eval> = Box::new(Field::new(&col1));
  //let rhs: Box<Eval> = Box::new(Field::new(&col2));
  //let plus: Box<Eval> = Box::new(Plus {lhs: lhs, rhs: rhs});

  //let mut visitor = VisitOrder{order: Vec::new()};
  //walk_expr(&visitor, plus);
}<commit_msg>Add base visitor tests.<commit_after>extern crate tajo;

use tajo::schema::Column;
use tajo::types::*;
use tajo::expr::*;
use tajo::eval::*;
use std::marker;

struct VisitOrder {
  order: i32,
}

impl<'v> Visitor<'v> for VisitOrder {
  fn visit_arithm(&mut self, op: &ArithmOp, lhs: &Expr, rhs: &Expr) {
    walk_expr(self, lhs);
    walk_expr(self, rhs);

    match *op {
      ArithmOp::Plus => println!("Plus"),
      ArithmOp::Minus => println!("Minus"),
      _ => panic!("Unknown op")
    }
  }

  fn visit_field(&mut self, c: &'v Column) {
    println!("column: {}", c.name);
  }
}

#[test]
fn test_visitor() {

  let col1 = Column::new("c0".to_string(), Ty::Int8);
  let col2 = Column::new("c1".to_string(), Ty::Int4);

  let col3 = Column::new("c2".to_string(), Ty::Int4);
  let col4 = Column::new("c3".to_string(), Ty::Int4);

  let lhs1 : Box<Expr> = Box::new(Expr::new_field(&col1));
  let rhs2 : Box<Expr> = Box::new(Expr::new_field(&col2));

  let plus1: Box<Expr> = Box::new(Expr::new_arithm(ArithmOp::Plus, lhs1, rhs2));

  let lhs2 : Box<Expr> = Box::new(Expr::new_field(&col3));
  let rhs2 : Box<Expr> = Box::new(Expr::new_field(&col4));

  let plus2: Box<Expr> = Box::new(Expr::new_arithm(ArithmOp::Plus, lhs2, rhs2));

  let plus3: Box<Expr> = Box::new(Expr::new_arithm(ArithmOp::Plus, plus1, plus2));

  //let lhs: Box<Eval> = Box::new(Field::new(&col1));
  //let rhs: Box<Eval> = Box::new(Field::new(&col2));
  //let plus: Box<Eval> = Box::new(Plus {lhs: lhs, rhs: rhs});

  let mut visitor = VisitOrder{order: 1};
  walk_expr(&mut visitor, &*plus3);
}<|endoftext|>


<commit_before>/* Copyright 2016 Torbjørn Birch Moltu
 *
 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
 * http://opensource.org/licenses/MIT>, at your option. This file may not be
 * copied, modified, or distributed except according to those terms.
 */

#![feature(unicode)]// reference implementations of to_slice

//! Test that every method gives the correct result for valid values.
//! Except iterators, which are stateful.

use std::char;
use std::str::{self,FromStr};
use std::hash::{Hash,SipHasher};
use std::io::Read;
use std::cmp::min;
extern crate encode_unicode;
use encode_unicode::*;

#[test]
fn equal_defaults() {
    assert_eq!(Utf8Char::default().to_char(), char::default());
    assert_eq!(Utf16Char::default().to_char(), char::default());
}

#[test]
fn same_size_as_char() {
    use std::mem::size_of;
    assert_eq!(size_of::<Utf8Char>(), size_of::<char>());
    assert_eq!(size_of::<Utf16Char>(), size_of::<char>());
}

#[test]
fn read_iterator() {
    let uc = 'ä'.to_utf8();
    assert_eq!(uc.len(), 2);
    for chunk in 1..5 {
        let mut buf = [b'E'; 6];
        let mut iter = uc.into_iter();
        let mut written = 0;
        for _ in 0..4 {
            assert_eq!(iter.read(&mut buf[..0]).unwrap(), 0);
            let wrote = iter.read(&mut buf[written..written+chunk]).unwrap();
            assert_eq!(wrote, min(2-written, chunk));
            written += wrote;
            for &b in &buf[written..] {assert_eq!(b, b'E');}
            assert_eq!(buf[..written], AsRef::<[u8]>::as_ref(&uc)[..written]);
        }
        assert_eq!(written, 2);
    }
}

const EDGES_AND_BETWEEN: [u32;13] = [
    0x0,// min
    0x3b,// between
    0x7f,// max 1-byte UTF-8
    0x80,// min 2-byte UTF-8
    0x111,// between
    0x7ff,// max 2-byte UTF-8
    0x800,// min 3-byte UTF-8
    0xd7ff,// before reserved
    0xe000,// after reserved
    0xffff,// max UTF-16 single and 3-byte UTF-8
    0x10000,// min UTF-16 surrogate and 4-byte UTF-8
    0xabcde,// between
    0x10ffff,// max
];

fn eq_cmp_hash(c: char) -> (Utf8Char, Utf16Char) {
    let sh = &mut SipHasher::new();
    let u8c = c.to_utf8();
    assert_eq!(u8c.to_char(), c);
    assert_eq!(u8c.hash(sh), c.hash(sh));
    let u16c = c.to_utf16();
    assert_eq!(u16c.to_char(), c);
    assert_eq!(u16c.hash(sh), c.hash(sh));

    for other in &EDGES_AND_BETWEEN {
        let other = unsafe{ char::from_u32_unchecked(*other) };

        let u8other = other.to_utf8();
        assert_eq!(u8c == u8other,  c == other);
        assert_eq!(u8c.hash(sh)==other.hash(sh),  c.hash(sh)==u8other.hash(sh));
        assert_eq!(u8c.cmp(&u8other), c.cmp(&other));

        let u16other = other.to_utf16();
        assert_eq!(u16c == u16other,  c == other);
        assert_eq!(u16c.hash(sh)==other.hash(sh),  c.hash(sh)==u16other.hash(sh));
        assert_eq!(u16c.cmp(&u16other), c.cmp(&other));
    }
    (u8c, u16c)
}

fn iterators(c: char) {
    let mut iter = c.iter_utf8_bytes();
    let mut iter_ref = c.encode_utf8();
    for _ in 0..6 {
        assert_eq!(iter.size_hint(), iter_ref.size_hint());
        assert_eq!(iter.next(), iter_ref.next());
    }

    let mut iter = c.iter_utf16_units();
    let mut iter_ref = c.encode_utf16();
    for _ in 0..4 {
        assert_eq!(iter.size_hint(), iter_ref.size_hint());
        assert_eq!(iter.next(), iter_ref.next());
    }
}

fn test(c: u32) {
    let c = char::from_u32(c).expect(&format!("{:x} is not a valid char", c));
    assert_eq!(char::from_u32_detailed(c as u32), Ok(c));
    let (u8c, u16c) = eq_cmp_hash(c);
    iterators(c);

    // UTF-8
    let reference = c.encode_utf8();
    let reference = reference.as_slice();
    let len = reference.len(); // short name because it is used in many places.
    assert_eq!(reference[0].extra_utf8_bytes(), Ok(len-1));
    assert_eq!(reference[0].extra_utf8_bytes_unchecked(), len-1);

    for i in c.len_utf8()..5 {
        let mut test_dst = [b'F';4];
        assert_eq!(c.to_utf8_slice(&mut test_dst[..i]), len);
        for b in &mut test_dst[len..] {// Test that it doesn't write too much
            assert_eq!(*b, b'F', "c={:?}, i={}, utf8_len={}", c, i, len);
            *b = 0;
        }
        assert_eq!(&test_dst[..len], reference);
        assert_eq!(AsRef::<[u8]>::as_ref(&u8c), reference);
        assert_eq!(u8c.to_array(), (test_dst, len));
    }
    let (arr,arrlen) = u8c.to_array();
    assert_eq!(arrlen, len);
    assert_eq!(Utf8Char::from_array(arr), Ok(u8c));
    assert_eq!(c.to_utf8_array(),  (arr, len));
    assert_eq!(Utf8Char::from_slice_start(&arr), Ok((u8c,len)));// Test that it doesn't read too much
    assert_eq!(Utf8Char::from_slice_start(reference), Ok((u8c,len)));

    let str_ = str::from_utf8(reference).unwrap();
    let ustr = Utf8Char::from_str(str_).unwrap();
    assert_eq!(ustr.to_array().0, arr);// bitwise equality
    assert_eq!(char::from_utf8_array(arr), Ok(c));
    assert_eq!(char::from_utf8_slice(reference), Ok((c,len)));

    // UTF-16
    let reference = c.encode_utf16();
    let reference = reference.as_slice();
    let len = reference.len();
    for i in c.len_utf16()..3 {
        let mut test_dst = [0;2];
        assert_eq!(c.to_utf16_slice(&mut test_dst[..i]), len);
        assert_eq!(&test_dst[..len], reference);
        assert_eq!(u16c.to_slice(&mut test_dst[..i]), len);
    }
    assert_eq!(reference[0].utf16_needs_extra_unit(), Some(len==2));
    assert_eq!(reference[0].utf16_is_leading_surrogate(), len==2);
    assert_eq!(char::from_utf16_slice(&reference[..len]), Ok((c,len)));
    let tuple = c.to_utf16_tuple();
    assert_eq!(tuple, (reference[0],reference.get(1).cloned()));
    assert_eq!(char::from_utf16_tuple(tuple), Ok(c));
    assert_eq!(c.to_utf16().to_char(), c);
}

#[test]
fn edges_middle() {
    for c in &EDGES_AND_BETWEEN {
        test(*c);
    }
}

#[test]
#[ignore]
fn all() {
    for c in std::iter::Iterator::chain(0..0xd800, 0xe000..0x110000) {
        test(c);
    }
}
<commit_msg>Test Utf8Char::from_slice_start() more<commit_after>/* Copyright 2016 Torbjørn Birch Moltu
 *
 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
 * http://opensource.org/licenses/MIT>, at your option. This file may not be
 * copied, modified, or distributed except according to those terms.
 */

#![feature(unicode)]// reference implementations of to_slice

//! Test that every method gives the correct result for valid values.
//! Except iterators, which are stateful.

use std::char;
use std::str::{self,FromStr};
use std::hash::{Hash,SipHasher};
use std::io::Read;
use std::cmp::min;
extern crate encode_unicode;
use encode_unicode::*;

#[test]
fn equal_defaults() {
    assert_eq!(Utf8Char::default().to_char(), char::default());
    assert_eq!(Utf16Char::default().to_char(), char::default());
}

#[test]
fn same_size_as_char() {
    use std::mem::size_of;
    assert_eq!(size_of::<Utf8Char>(), size_of::<char>());
    assert_eq!(size_of::<Utf16Char>(), size_of::<char>());
}

#[test]
fn read_iterator() {
    let uc = 'ä'.to_utf8();
    assert_eq!(uc.len(), 2);
    for chunk in 1..5 {
        let mut buf = [b'E'; 6];
        let mut iter = uc.into_iter();
        let mut written = 0;
        for _ in 0..4 {
            assert_eq!(iter.read(&mut buf[..0]).unwrap(), 0);
            let wrote = iter.read(&mut buf[written..written+chunk]).unwrap();
            assert_eq!(wrote, min(2-written, chunk));
            written += wrote;
            for &b in &buf[written..] {assert_eq!(b, b'E');}
            assert_eq!(buf[..written], AsRef::<[u8]>::as_ref(&uc)[..written]);
        }
        assert_eq!(written, 2);
    }
}

const EDGES_AND_BETWEEN: [u32;13] = [
    0x0,// min
    0x3b,// between
    0x7f,// max 1-byte UTF-8
    0x80,// min 2-byte UTF-8
    0x111,// between
    0x7ff,// max 2-byte UTF-8
    0x800,// min 3-byte UTF-8
    0xd7ff,// before reserved
    0xe000,// after reserved
    0xffff,// max UTF-16 single and 3-byte UTF-8
    0x10000,// min UTF-16 surrogate and 4-byte UTF-8
    0xabcde,// between
    0x10ffff,// max
];

fn eq_cmp_hash(c: char) -> (Utf8Char, Utf16Char) {
    let sh = &mut SipHasher::new();
    let u8c = c.to_utf8();
    assert_eq!(u8c.to_char(), c);
    assert_eq!(u8c.hash(sh), c.hash(sh));
    let u16c = c.to_utf16();
    assert_eq!(u16c.to_char(), c);
    assert_eq!(u16c.hash(sh), c.hash(sh));

    for other in &EDGES_AND_BETWEEN {
        let other = unsafe{ char::from_u32_unchecked(*other) };

        let u8other = other.to_utf8();
        assert_eq!(u8c == u8other,  c == other);
        assert_eq!(u8c.hash(sh)==other.hash(sh),  c.hash(sh)==u8other.hash(sh));
        assert_eq!(u8c.cmp(&u8other), c.cmp(&other));

        let u16other = other.to_utf16();
        assert_eq!(u16c == u16other,  c == other);
        assert_eq!(u16c.hash(sh)==other.hash(sh),  c.hash(sh)==u16other.hash(sh));
        assert_eq!(u16c.cmp(&u16other), c.cmp(&other));
    }
    (u8c, u16c)
}

fn iterators(c: char) {
    let mut iter = c.iter_utf8_bytes();
    let mut iter_ref = c.encode_utf8();
    for _ in 0..6 {
        assert_eq!(iter.size_hint(), iter_ref.size_hint());
        assert_eq!(iter.next(), iter_ref.next());
    }

    let mut iter = c.iter_utf16_units();
    let mut iter_ref = c.encode_utf16();
    for _ in 0..4 {
        assert_eq!(iter.size_hint(), iter_ref.size_hint());
        assert_eq!(iter.next(), iter_ref.next());
    }
}

fn test(c: u32) {
    let c = char::from_u32(c).expect(&format!("{:x} is not a valid char", c));
    assert_eq!(char::from_u32_detailed(c as u32), Ok(c));
    let (u8c, u16c) = eq_cmp_hash(c);
    iterators(c);

    // UTF-8
    let reference = c.encode_utf8();
    let reference = reference.as_slice();
    let len = reference.len(); // short name because it is used in many places.
    assert_eq!(reference[0].extra_utf8_bytes(), Ok(len-1));
    assert_eq!(reference[0].extra_utf8_bytes_unchecked(), len-1);

    for i in c.len_utf8()..5 {
        let mut test_dst = [b'F';4];
        assert_eq!(c.to_utf8_slice(&mut test_dst[..i]), len);
        for b in &mut test_dst[len..] {// Test that it doesn't write too much
            assert_eq!(*b, b'F', "c={:?}, i={}, utf8_len={}", c, i, len);
            *b = 0;
        }
        assert_eq!(&test_dst[..len], reference);
        assert_eq!(AsRef::<[u8]>::as_ref(&u8c), reference);
        assert_eq!(u8c.to_array(), (test_dst, len));
    }
    let (mut arr,arrlen) = u8c.to_array();
    assert_eq!(arrlen, len);
    assert_eq!(Utf8Char::from_array(arr), Ok(u8c));
    assert_eq!(c.to_utf8_array(),  (arr, len));

    let str_ = str::from_utf8(reference).unwrap();
    let ustr = Utf8Char::from_str(str_).unwrap();
    assert_eq!(ustr.to_array().0, arr);// bitwise equality
    assert_eq!(char::from_utf8_array(arr), Ok(c));
    assert_eq!(char::from_utf8_slice(reference), Ok((c,len)));
    for b in arr.iter_mut().skip(arrlen) {
        *b = b'F';// from_slice_start must not read these.
    }
    assert_eq!(Utf8Char::from_slice_start(&arr), Ok((u8c,len)));// Test that it doesn't read too much
    assert_eq!(Utf8Char::from_slice_start(reference), Ok((u8c,len)));

    // UTF-16
    let reference = c.encode_utf16();
    let reference = reference.as_slice();
    let len = reference.len();
    for i in c.len_utf16()..3 {
        let mut test_dst = [0;2];
        assert_eq!(c.to_utf16_slice(&mut test_dst[..i]), len);
        assert_eq!(&test_dst[..len], reference);
        assert_eq!(u16c.to_slice(&mut test_dst[..i]), len);
    }
    assert_eq!(reference[0].utf16_needs_extra_unit(), Some(len==2));
    assert_eq!(reference[0].utf16_is_leading_surrogate(), len==2);
    assert_eq!(char::from_utf16_slice(&reference[..len]), Ok((c,len)));
    let tuple = c.to_utf16_tuple();
    assert_eq!(tuple, (reference[0],reference.get(1).cloned()));
    assert_eq!(char::from_utf16_tuple(tuple), Ok(c));
    assert_eq!(c.to_utf16().to_char(), c);
}

#[test]
fn edges_middle() {
    for c in &EDGES_AND_BETWEEN {
        test(*c);
    }
}

#[test]
#[ignore]
fn all() {
    for c in std::iter::Iterator::chain(0..0xd800, 0xe000..0x110000) {
        test(c);
    }
}
<|endoftext|>