<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::ascii::AsciiExt;

use dom::attr::Attr;
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived, HTMLScriptElementCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::element::{Element, AttributeHandlers, ElementCreator};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::element::ElementTypeId;
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node, CloneChildrenFlag};
use dom::virtualmethods::VirtualMethods;
use dom::window::ScriptHelpers;
use script_task::{ScriptMsg, Runnable};

use encoding::all::UTF_8;
use encoding::types::{Encoding, DecoderTrap};
use net::resource_task::load_whole_resource;
use util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec};
use std::borrow::ToOwned;
use std::cell::Cell;
use string_cache::Atom;
use url::UrlParser;

#[dom_struct]
pub struct HTMLScriptElement {
    htmlelement: HTMLElement,

    error_occurred: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#already-started
    already_started: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#parser-inserted
    parser_inserted: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#non-blocking
    ///
    /// (currently unused)
    non_blocking: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#ready-to-be-parser-executed
    ///
    /// (currently unused)
    ready_to_be_parser_executed: Cell<bool>,
}

impl HTMLScriptElementDerived for EventTarget {
    fn is_htmlscriptelement(&self) -> bool {
        *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLScriptElement)))
    }
}

impl HTMLScriptElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>,
                     creator: ElementCreator) -> HTMLScriptElement {
        HTMLScriptElement {
            htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLScriptElement, localName, prefix, document),
            error_occurred: Cell::new(false),
            already_started: Cell::new(false),
            parser_inserted: Cell::new(creator == ElementCreator::ParserCreated),
            non_blocking: Cell::new(creator != ElementCreator::ParserCreated),
            ready_to_be_parser_executed: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>,
               creator: ElementCreator) -> Temporary<HTMLScriptElement> {
        let element = HTMLScriptElement::new_inherited(localName, prefix, document, creator);
        Node::reflect_node(box element, document, HTMLScriptElementBinding::Wrap)
    }
}

pub trait HTMLScriptElementHelpers {
    /// Prepare a script (<http://www.whatwg.org/html/#prepare-a-script>)
    fn prepare(self);

    /// Prepare a script, steps 6 and 7.
    fn is_javascript(self) -> bool;

    /// Set the "already started" flag (<https://whatwg.org/html/#already-started>)
    fn mark_already_started(self);

    // Queues error event
    fn queue_error_event(self);

    /// Dispatch load event.
    fn dispatch_load_event(self);

    /// Dispatch error event.
    fn dispatch_error_event(self);
}

/// Supported script types as defined by
/// <http://whatwg.org/html/#support-the-scripting-language>.
static SCRIPT_JS_MIMES: StaticStringVec = &[
    "application/ecmascript",
    "application/javascript",
    "application/x-ecmascript",
    "application/x-javascript",
    "text/ecmascript",
    "text/javascript",
    "text/javascript1.0",
    "text/javascript1.1",
    "text/javascript1.2",
    "text/javascript1.3",
    "text/javascript1.4",
    "text/javascript1.5",
    "text/jscript",
    "text/livescript",
    "text/x-ecmascript",
    "text/x-javascript",
];

enum ScriptOrigin {
    Internal,
    External,
}

impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
    fn prepare(self) {
        // https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script
        // Step 1.
        if self.already_started.get() {
            return;
        }
        // Step 2.
        let was_parser_inserted = self.parser_inserted.get();
        self.parser_inserted.set(false);

        // Step 3.
        let element: JSRef<Element> = ElementCast::from_ref(self);
        if was_parser_inserted && element.has_attribute(&atom!("async")) {
            self.non_blocking.set(true);
        }
        // Step 4.
        let text = self.Text();
        if text.len() == 0 && !element.has_attribute(&atom!("src")) {
            return;
        }
        // Step 5.
        let node: JSRef<Node> = NodeCast::from_ref(self);
        if !node.is_in_doc() {
            return;
        }
        // Step 6, 7.
        if !self.is_javascript() {
            return;
        }
        // Step 8.
        if was_parser_inserted {
            self.parser_inserted.set(true);
            self.non_blocking.set(false);
        }
        // Step 9.
        self.already_started.set(true);

        // Step 10.
        // TODO: If the element is flagged as "parser-inserted", but the element's node document is
        // not the Document of the parser that created the element, then abort these steps.

        // Step 11.
        // TODO: If scripting is disabled for the script element, then the user agent must abort
        // these steps at this point. The script is not executed.

        // Step 12.
        match element.get_attribute(ns!(""), &atom!("for")).root() {
            Some(for_script) => {
                if for_script.r().Value().to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS) != "window" {
                    return;
                }
            }
            _ => { }
        }

        match element.get_attribute(ns!(""), &Atom::from_slice("event")).root() {
            Some(event) => {
                let event = event.r().Value().to_ascii_lowercase();
                let event = event.trim_matches(HTML_SPACE_CHARACTERS);
                if event != "onload" && event != "onload()" {
                    return;
                }
            }
            _ => { }
        }
        // Step 13.
        // TODO: If the script element has a `charset` attribute, then let the script block's
        // character encoding for this script element be the result of getting an encoding from the
        // value of the `charset` attribute.

        // Step 14 and 15.
        // TODO: Add support for the `defer` and `async` attributes.  (For now, we fetch all
        // scripts synchronously and execute them immediately.)
        let window = window_from_node(self).root();
        let window = window.r();
        let page = window.page();
        let base_url = page.get_url();

        let (origin, source, url) = match element.get_attribute(ns!(""), &atom!("src")).root() {
            Some(src) => {
                if src.r().Value().is_empty() {
                    self.queue_error_event();
                    return;
                }
                match UrlParser::new().base_url(&base_url).parse(src.r().Value().as_slice()) {
                    Ok(url) => {
                        // TODO: Do a potentially CORS-enabled fetch with the mode being the current
                        // state of the element's `crossorigin` content attribute, the origin being
                        // the origin of the script element's node document, and the default origin
                        // behaviour set to taint.
                        match load_whole_resource(&page.resource_task, url) {
                            Ok((metadata, bytes)) => {
                                // TODO: use the charset from step 13.
                                let source = UTF_8.decode(bytes.as_slice(), DecoderTrap::Replace).unwrap();
                                (ScriptOrigin::External, source, metadata.final_url)
                            }
                            Err(_) => {
                                error!("error loading script {}", src.r().Value());
                                self.queue_error_event();
                                return;
                            }
                        }
                    }
                    Err(_) => {
                        error!("error parsing URL for script {}", src.r().Value());
                        self.queue_error_event();
                        return;
                    }
                }
            }
            None => (ScriptOrigin::Internal, text, base_url)
        };

        window.evaluate_script_on_global_with_result(source.as_slice(), url.serialize().as_slice());

        // https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
        // step 2, substep 10
        match origin {
            ScriptOrigin::External => {
                self.dispatch_load_event();
            }
            ScriptOrigin::Internal => {
                let chan = window.script_chan();
                let handler = Trusted::new(window.get_cx(), self, chan.clone());
                chan.send(ScriptMsg::RunnableMsg(box handler)).unwrap();
            }
        }
    }

    fn queue_error_event(self) {
        self.error_occurred.set(true);
        let window = window_from_node(self).root();
        let window = window.r();
        let chan = window.script_chan();
        let handler = Trusted::new(window.get_cx(), self, chan.clone());
        chan.send(ScriptMsg::RunnableMsg(box handler)).unwrap();
    }

    fn dispatch_load_event(self) {
        let window = window_from_node(self).root();
        let window = window.r();
        let event = Event::new(GlobalRef::Window(window),
                               "load".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable).root();
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
        event.r().fire(target);
    }

    fn dispatch_error_event(self) {
        let window = window_from_node(self).root();
        let window = window.r();
        let event = Event::new(GlobalRef::Window(window),
                               "error".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable).root();
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
        event.r().fire(target);
    }

    fn is_javascript(self) -> bool {
        let element: JSRef<Element> = ElementCast::from_ref(self);
        match element.get_attribute(ns!(""), &atom!("type")).root().map(|s| s.r().Value()) {
            Some(ref s) if s.is_empty() => {
                // type attr exists, but empty means js
                debug!("script type empty, inferring js");
                true
            },
            Some(ref s) => {
                debug!("script type={}", *s);
                SCRIPT_JS_MIMES.contains(&s.to_ascii_lowercase().as_slice().trim_matches(HTML_SPACE_CHARACTERS))
            },
            None => {
                debug!("no script type");
                match element.get_attribute(ns!(""), &atom!("language"))
                             .root()
                             .map(|s| s.r().Value()) {
                    Some(ref s) if s.is_empty() => {
                        debug!("script language empty, inferring js");
                        true
                    },
                    Some(ref s) => {
                        debug!("script language={}", *s);
                        SCRIPT_JS_MIMES.contains(&format!("text/{}", s).to_ascii_lowercase().as_slice())
                    },
                    None => {
                        debug!("no script type or language, inferring js");
                        true
                    }
                }
            }
        }
    }

    fn mark_already_started(self) {
        self.already_started.set(true);
    }
}

impl<'a> VirtualMethods for JSRef<'a, HTMLScriptElement> {
    fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
        let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn after_set_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(attr),
            _ => (),
        }
        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if attr.local_name() == &atom!("src") && !self.parser_inserted.get() && node.is_in_doc() {
            self.prepare();
        }
    }

    fn child_inserted(&self, child: JSRef<Node>) {
        match self.super_type() {
            Some(ref s) => s.child_inserted(child),
            _ => (),
        }
        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if !self.parser_inserted.get() && node.is_in_doc() {
            self.prepare();
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.bind_to_tree(tree_in_doc),
            _ => ()
        }

        if tree_in_doc && !self.parser_inserted.get() {
            self.prepare();
        }
    }

    fn cloning_steps(&self, copy: JSRef<Node>, maybe_doc: Option<JSRef<Document>>,
                     clone_children: CloneChildrenFlag) {
        match self.super_type() {
            Some(ref s) => s.cloning_steps(copy, maybe_doc, clone_children),
            _ => (),
        }

        // https://whatwg.org/html/#already-started
        if self.already_started.get() {
            let copy_elem: JSRef<HTMLScriptElement> = HTMLScriptElementCast::to_ref(copy).unwrap();
            copy_elem.mark_already_started();
        }
    }
}

impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> {
    make_url_getter!(Src);

    make_setter!(SetSrc, "src");

    // http://www.whatwg.org/html/#dom-script-text
    fn Text(self) -> DOMString {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        Node::collect_text_contents(node.children())
    }

    // http://www.whatwg.org/html/#dom-script-text
    fn SetText(self, value: DOMString) {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.SetTextContent(Some(value))
    }
}

impl Runnable for Trusted<HTMLScriptElement> {
    fn handler(self: Box<Trusted<HTMLScriptElement>>) {
        let target = self.to_temporary().root();
        if target.r().error_occurred.get() {
            target.r().dispatch_error_event();
        } else {
            target.r().dispatch_load_event();
        }
    }
}
<commit_msg>auto merge of #5023 : saneyuki/servo/script, 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 std::ascii::AsciiExt;

use dom::attr::Attr;
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived, HTMLScriptElementCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::element::{Element, AttributeHandlers, ElementCreator};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::element::ElementTypeId;
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node, CloneChildrenFlag};
use dom::virtualmethods::VirtualMethods;
use dom::window::ScriptHelpers;
use script_task::{ScriptMsg, Runnable};

use encoding::all::UTF_8;
use encoding::types::{Encoding, DecoderTrap};
use net::resource_task::load_whole_resource;
use util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec};
use std::borrow::ToOwned;
use std::cell::Cell;
use string_cache::Atom;
use url::UrlParser;

#[dom_struct]
pub struct HTMLScriptElement {
    htmlelement: HTMLElement,

    /// https://html.spec.whatwg.org/multipage/scripting.html#already-started
    already_started: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#parser-inserted
    parser_inserted: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#non-blocking
    ///
    /// (currently unused)
    non_blocking: Cell<bool>,

    /// https://html.spec.whatwg.org/multipage/scripting.html#ready-to-be-parser-executed
    ///
    /// (currently unused)
    ready_to_be_parser_executed: Cell<bool>,
}

impl HTMLScriptElementDerived for EventTarget {
    fn is_htmlscriptelement(&self) -> bool {
        *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLScriptElement)))
    }
}

impl HTMLScriptElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>,
                     creator: ElementCreator) -> HTMLScriptElement {
        HTMLScriptElement {
            htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLScriptElement, localName, prefix, document),
            already_started: Cell::new(false),
            parser_inserted: Cell::new(creator == ElementCreator::ParserCreated),
            non_blocking: Cell::new(creator != ElementCreator::ParserCreated),
            ready_to_be_parser_executed: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>,
               creator: ElementCreator) -> Temporary<HTMLScriptElement> {
        let element = HTMLScriptElement::new_inherited(localName, prefix, document, creator);
        Node::reflect_node(box element, document, HTMLScriptElementBinding::Wrap)
    }
}

pub trait HTMLScriptElementHelpers {
    /// Prepare a script (<http://www.whatwg.org/html/#prepare-a-script>)
    fn prepare(self);

    /// Prepare a script, steps 6 and 7.
    fn is_javascript(self) -> bool;

    /// Set the "already started" flag (<https://whatwg.org/html/#already-started>)
    fn mark_already_started(self);

    // Queues error event
    fn queue_error_event(self);

    /// Dispatch load event.
    fn dispatch_load_event(self);

    /// Dispatch error event.
    fn dispatch_error_event(self);
}

/// Supported script types as defined by
/// <http://whatwg.org/html/#support-the-scripting-language>.
static SCRIPT_JS_MIMES: StaticStringVec = &[
    "application/ecmascript",
    "application/javascript",
    "application/x-ecmascript",
    "application/x-javascript",
    "text/ecmascript",
    "text/javascript",
    "text/javascript1.0",
    "text/javascript1.1",
    "text/javascript1.2",
    "text/javascript1.3",
    "text/javascript1.4",
    "text/javascript1.5",
    "text/jscript",
    "text/livescript",
    "text/x-ecmascript",
    "text/x-javascript",
];

enum ScriptOrigin {
    Internal,
    External,
}

impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
    fn prepare(self) {
        // https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script
        // Step 1.
        if self.already_started.get() {
            return;
        }
        // Step 2.
        let was_parser_inserted = self.parser_inserted.get();
        self.parser_inserted.set(false);

        // Step 3.
        let element: JSRef<Element> = ElementCast::from_ref(self);
        if was_parser_inserted && element.has_attribute(&atom!("async")) {
            self.non_blocking.set(true);
        }
        // Step 4.
        let text = self.Text();
        if text.len() == 0 && !element.has_attribute(&atom!("src")) {
            return;
        }
        // Step 5.
        let node: JSRef<Node> = NodeCast::from_ref(self);
        if !node.is_in_doc() {
            return;
        }
        // Step 6, 7.
        if !self.is_javascript() {
            return;
        }
        // Step 8.
        if was_parser_inserted {
            self.parser_inserted.set(true);
            self.non_blocking.set(false);
        }
        // Step 9.
        self.already_started.set(true);

        // Step 10.
        // TODO: If the element is flagged as "parser-inserted", but the element's node document is
        // not the Document of the parser that created the element, then abort these steps.

        // Step 11.
        // TODO: If scripting is disabled for the script element, then the user agent must abort
        // these steps at this point. The script is not executed.

        // Step 12.
        match element.get_attribute(ns!(""), &atom!("for")).root() {
            Some(for_script) => {
                if for_script.r().Value().to_ascii_lowercase().trim_matches(HTML_SPACE_CHARACTERS) != "window" {
                    return;
                }
            }
            _ => { }
        }

        match element.get_attribute(ns!(""), &Atom::from_slice("event")).root() {
            Some(event) => {
                let event = event.r().Value().to_ascii_lowercase();
                let event = event.trim_matches(HTML_SPACE_CHARACTERS);
                if event != "onload" && event != "onload()" {
                    return;
                }
            }
            _ => { }
        }
        // Step 13.
        // TODO: If the script element has a `charset` attribute, then let the script block's
        // character encoding for this script element be the result of getting an encoding from the
        // value of the `charset` attribute.

        // Step 14 and 15.
        // TODO: Add support for the `defer` and `async` attributes.  (For now, we fetch all
        // scripts synchronously and execute them immediately.)
        let window = window_from_node(self).root();
        let window = window.r();
        let page = window.page();
        let base_url = page.get_url();

        let (origin, source, url) = match element.get_attribute(ns!(""), &atom!("src")).root() {
            Some(src) => {
                if src.r().Value().is_empty() {
                    self.queue_error_event();
                    return;
                }
                match UrlParser::new().base_url(&base_url).parse(src.r().Value().as_slice()) {
                    Ok(url) => {
                        // TODO: Do a potentially CORS-enabled fetch with the mode being the current
                        // state of the element's `crossorigin` content attribute, the origin being
                        // the origin of the script element's node document, and the default origin
                        // behaviour set to taint.
                        match load_whole_resource(&page.resource_task, url) {
                            Ok((metadata, bytes)) => {
                                // TODO: use the charset from step 13.
                                let source = UTF_8.decode(bytes.as_slice(), DecoderTrap::Replace).unwrap();
                                (ScriptOrigin::External, source, metadata.final_url)
                            }
                            Err(_) => {
                                error!("error loading script {}", src.r().Value());
                                self.queue_error_event();
                                return;
                            }
                        }
                    }
                    Err(_) => {
                        error!("error parsing URL for script {}", src.r().Value());
                        self.queue_error_event();
                        return;
                    }
                }
            }
            None => (ScriptOrigin::Internal, text, base_url)
        };

        window.evaluate_script_on_global_with_result(source.as_slice(), url.serialize().as_slice());

        // https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
        // step 2, substep 10
        match origin {
            ScriptOrigin::External => {
                self.dispatch_load_event();
            }
            ScriptOrigin::Internal => {
                let chan = window.script_chan();
                let handler = Trusted::new(window.get_cx(), self, chan.clone());
                let dispatcher = Box::new(EventDispatcher {
                    element: handler,
                    is_error: false,
                });
                chan.send(ScriptMsg::RunnableMsg(dispatcher)).unwrap();
            }
        }
    }

    fn queue_error_event(self) {
        let window = window_from_node(self).root();
        let window = window.r();
        let chan = window.script_chan();
        let handler = Trusted::new(window.get_cx(), self, chan.clone());
        let dispatcher = Box::new(EventDispatcher {
            element: handler,
            is_error: true,
        });
        chan.send(ScriptMsg::RunnableMsg(dispatcher)).unwrap();
    }

    fn dispatch_load_event(self) {
        let window = window_from_node(self).root();
        let window = window.r();
        let event = Event::new(GlobalRef::Window(window),
                               "load".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable).root();
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
        event.r().fire(target);
    }

    fn dispatch_error_event(self) {
        let window = window_from_node(self).root();
        let window = window.r();
        let event = Event::new(GlobalRef::Window(window),
                               "error".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable).root();
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
        event.r().fire(target);
    }

    fn is_javascript(self) -> bool {
        let element: JSRef<Element> = ElementCast::from_ref(self);
        match element.get_attribute(ns!(""), &atom!("type")).root().map(|s| s.r().Value()) {
            Some(ref s) if s.is_empty() => {
                // type attr exists, but empty means js
                debug!("script type empty, inferring js");
                true
            },
            Some(ref s) => {
                debug!("script type={}", *s);
                SCRIPT_JS_MIMES.contains(&s.to_ascii_lowercase().as_slice().trim_matches(HTML_SPACE_CHARACTERS))
            },
            None => {
                debug!("no script type");
                match element.get_attribute(ns!(""), &atom!("language"))
                             .root()
                             .map(|s| s.r().Value()) {
                    Some(ref s) if s.is_empty() => {
                        debug!("script language empty, inferring js");
                        true
                    },
                    Some(ref s) => {
                        debug!("script language={}", *s);
                        SCRIPT_JS_MIMES.contains(&format!("text/{}", s).to_ascii_lowercase().as_slice())
                    },
                    None => {
                        debug!("no script type or language, inferring js");
                        true
                    }
                }
            }
        }
    }

    fn mark_already_started(self) {
        self.already_started.set(true);
    }
}

impl<'a> VirtualMethods for JSRef<'a, HTMLScriptElement> {
    fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
        let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn after_set_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(attr),
            _ => (),
        }
        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if attr.local_name() == &atom!("src") && !self.parser_inserted.get() && node.is_in_doc() {
            self.prepare();
        }
    }

    fn child_inserted(&self, child: JSRef<Node>) {
        match self.super_type() {
            Some(ref s) => s.child_inserted(child),
            _ => (),
        }
        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if !self.parser_inserted.get() && node.is_in_doc() {
            self.prepare();
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.bind_to_tree(tree_in_doc),
            _ => ()
        }

        if tree_in_doc && !self.parser_inserted.get() {
            self.prepare();
        }
    }

    fn cloning_steps(&self, copy: JSRef<Node>, maybe_doc: Option<JSRef<Document>>,
                     clone_children: CloneChildrenFlag) {
        match self.super_type() {
            Some(ref s) => s.cloning_steps(copy, maybe_doc, clone_children),
            _ => (),
        }

        // https://whatwg.org/html/#already-started
        if self.already_started.get() {
            let copy_elem: JSRef<HTMLScriptElement> = HTMLScriptElementCast::to_ref(copy).unwrap();
            copy_elem.mark_already_started();
        }
    }
}

impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> {
    make_url_getter!(Src);

    make_setter!(SetSrc, "src");

    // http://www.whatwg.org/html/#dom-script-text
    fn Text(self) -> DOMString {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        Node::collect_text_contents(node.children())
    }

    // http://www.whatwg.org/html/#dom-script-text
    fn SetText(self, value: DOMString) {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.SetTextContent(Some(value))
    }
}

struct EventDispatcher {
    element: Trusted<HTMLScriptElement>,
    is_error: bool,
}

impl Runnable for EventDispatcher {
    fn handler(self: Box<EventDispatcher>) {
        let target = self.element.to_temporary().root();
        if self.is_error {
            target.r().dispatch_error_event();
        } else {
            target.r().dispatch_load_event();
        }
    }
}
<|endoftext|>


<commit_before>use askama::{SizedTemplate, Template};

use std::collections::HashMap;

#[derive(Template)]
#[template(path = "simple.html")]
struct VariablesTemplate<'a> {
    strvar: &'a str,
    num: i64,
    i18n: String,
}

#[test]
fn test_variables() {
    let s = VariablesTemplate {
        strvar: "foo",
        num: 42,
        i18n: "Iñtërnâtiônàlizætiøn".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "\nhello world, foo\n\
         with number: 42\n\
         Iñtërnâtiônàlizætiøn is important\n\
         in vars too: Iñtërnâtiônàlizætiøn"
    );
    assert_eq!(<VariablesTemplate as SizedTemplate>::extension(), Some("html"));
}

#[derive(Template)]
#[template(path = "hello.html")]
struct EscapeTemplate<'a> {
    name: &'a str,
}

#[test]
fn test_escape() {
    let s = EscapeTemplate { name: "<>&\"'/" };

    assert_eq!(
        s.render().unwrap(),
        "Hello, <>&"'/!"
    );
}

#[derive(Template)]
#[template(path = "simple-no-escape.txt")]
struct VariablesTemplateNoEscape<'a> {
    strvar: &'a str,
    num: i64,
    i18n: String,
}

#[test]
fn test_variables_no_escape() {
    let s = VariablesTemplateNoEscape {
        strvar: "foo",
        num: 42,
        i18n: "Iñtërnâtiônàlizætiøn".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "\nhello world, foo\n\
         with number: 42\n\
         Iñtërnâtiônàlizætiøn is important\n\
         in vars too: Iñtërnâtiônàlizætiøn"
    );
}

#[derive(Template)]
#[template(path = "if.html")]
struct IfTemplate {
    cond: bool,
}

#[test]
fn test_if() {
    let s = IfTemplate { cond: true };
    assert_eq!(s.render().unwrap(), "true");
}

#[derive(Template)]
#[template(path = "else.html")]
struct ElseTemplate {
    cond: bool,
}

#[test]
fn test_else() {
    let s = ElseTemplate { cond: false };
    assert_eq!(s.render().unwrap(), "false");
}

#[derive(Template)]
#[template(path = "else-if.html")]
struct ElseIfTemplate {
    cond: bool,
    check: bool,
}

#[test]
fn test_else_if() {
    let s = ElseIfTemplate {
        cond: false,
        check: true,
    };
    assert_eq!(s.render().unwrap(), "checked");
}

#[derive(Template)]
#[template(path = "literals.html")]
struct LiteralsTemplate {}

#[test]
fn test_literals() {
    let s = LiteralsTemplate {};
    assert_eq!(s.render().unwrap(), "a\ntrue\nfalse");
}

struct Holder {
    a: usize,
}

#[derive(Template)]
#[template(path = "attr.html")]
struct AttrTemplate {
    inner: Holder,
}

#[test]
fn test_attr() {
    let t = AttrTemplate {
        inner: Holder { a: 5 },
    };
    assert_eq!(t.render().unwrap(), "5");
}

#[derive(Template)]
#[template(path = "tuple-attr.html")]
struct TupleAttrTemplate<'a> {
    tuple: (&'a str, &'a str),
}

#[test]
fn test_tuple_attr() {
    let t = TupleAttrTemplate {
        tuple: ("foo", "bar"),
    };
    assert_eq!(t.render().unwrap(), "foobar");
}

struct NestedHolder {
    holder: Holder,
}

#[derive(Template)]
#[template(path = "nested-attr.html")]
struct NestedAttrTemplate {
    inner: NestedHolder,
}

#[test]
fn test_nested_attr() {
    let t = NestedAttrTemplate {
        inner: NestedHolder {
            holder: Holder { a: 5 },
        },
    };
    assert_eq!(t.render().unwrap(), "5");
}

#[derive(Template)]
#[template(path = "option.html")]
struct OptionTemplate<'a> {
    var: Option<&'a str>,
}

#[test]
fn test_option() {
    let some = OptionTemplate { var: Some("foo") };
    assert_eq!(some.render().unwrap(), "some: foo");
    let none = OptionTemplate { var: None };
    assert_eq!(none.render().unwrap(), "none");
}

#[derive(Template)]
#[template(path = "generics.html")]
struct GenericsTemplate<T: std::fmt::Display, U = u8>
where
    U: std::fmt::Display,
{
    t: T,
    u: U,
}

#[test]
fn test_generics() {
    let t = GenericsTemplate { t: "a", u: 42 };
    assert_eq!(t.render().unwrap(), "a42");
}

#[derive(Template)]
#[template(path = "composition.html")]
struct CompositionTemplate {
    foo: IfTemplate,
}

#[test]
fn test_composition() {
    let t = CompositionTemplate {
        foo: IfTemplate { cond: true },
    };
    assert_eq!(t.render().unwrap(), "composed: true");
}

#[derive(PartialEq, Eq)]
enum Alphabet {
    Alpha,
}

#[derive(Template)]
#[template(source = "{% if x == Alphabet::Alpha %}true{% endif %}", ext = "txt")]
struct PathCompareTemplate {
    x: Alphabet,
}

#[test]
fn test_path_compare() {
    let t = PathCompareTemplate { x: Alphabet::Alpha };
    assert_eq!(t.render().unwrap(), "true");
}

#[derive(Template)]
#[template(
    source = "{% for i in [\"a\", \"\"] %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ArrayTemplate {}

#[test]
fn test_slice_literal() {
    let t = ArrayTemplate {};
    assert_eq!(t.render().unwrap(), "a");
}

#[derive(Template)]
#[template(source = "  {# foo -#} ", ext = "txt")]
struct CommentTemplate {}

#[test]
fn test_comment() {
    let t = CommentTemplate {};
    assert_eq!(t.render().unwrap(), "  ");
}

#[derive(Template)]
#[template(source = "{% if !foo %}Hello{% endif %}", ext = "txt")]
struct NegationTemplate {
    foo: bool,
}

#[test]
fn test_negation() {
    let t = NegationTemplate { foo: false };
    assert_eq!(t.render().unwrap(), "Hello");
}

#[derive(Template)]
#[template(source = "{% if foo > -2 %}Hello{% endif %}", ext = "txt")]
struct MinusTemplate {
    foo: i8,
}

#[test]
fn test_minus() {
    let t = MinusTemplate { foo: 1 };
    assert_eq!(t.render().unwrap(), "Hello");
}

#[derive(Template)]
#[template(source = "{{ foo[\"bar\"] }}", ext = "txt")]
struct IndexTemplate {
    foo: HashMap<String, String>,
}

#[test]
fn test_index() {
    let mut foo = HashMap::new();
    foo.insert("bar".into(), "baz".into());
    let t = IndexTemplate { foo };
    assert_eq!(t.render().unwrap(), "baz");
}

#[derive(Template)]
#[template(source = "foo", ext = "txt")]
struct Empty;

#[test]
fn test_empty() {
    assert_eq!(Empty.render().unwrap(), "foo");
}

#[derive(Template)]
#[template(path = "raw-simple.html")]
struct RawTemplate;

#[test]
fn test_raw_simple() {
    let template = RawTemplate;
    assert_eq!(template.render().unwrap(), "\n{{ name }}\n");
}

#[derive(Template)]
#[template(path = "raw-complex.html")]
struct RawTemplateComplex;

#[test]
fn test_raw_complex() {
    let template = RawTemplateComplex;
    assert_eq!(
        template.render().unwrap(),
        "\n{% block name %}\n  {{ name }}\n{% endblock %}\n"
    );
}

mod without_import_on_derive {
    #[derive(askama::Template)]
    #[template(source = "foo", ext = "txt")]
    struct WithoutImport;

    #[test]
    fn test_without_import() {
        use askama::Template;
        assert_eq!(WithoutImport.render().unwrap(), "foo");
    }
}
<commit_msg>Fix formatting with cargo fmt<commit_after>use askama::{SizedTemplate, Template};

use std::collections::HashMap;

#[derive(Template)]
#[template(path = "simple.html")]
struct VariablesTemplate<'a> {
    strvar: &'a str,
    num: i64,
    i18n: String,
}

#[test]
fn test_variables() {
    let s = VariablesTemplate {
        strvar: "foo",
        num: 42,
        i18n: "Iñtërnâtiônàlizætiøn".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "\nhello world, foo\n\
         with number: 42\n\
         Iñtërnâtiônàlizætiøn is important\n\
         in vars too: Iñtërnâtiônàlizætiøn"
    );
    assert_eq!(
        <VariablesTemplate as SizedTemplate>::extension(),
        Some("html")
    );
}

#[derive(Template)]
#[template(path = "hello.html")]
struct EscapeTemplate<'a> {
    name: &'a str,
}

#[test]
fn test_escape() {
    let s = EscapeTemplate { name: "<>&\"'/" };

    assert_eq!(
        s.render().unwrap(),
        "Hello, <>&"'/!"
    );
}

#[derive(Template)]
#[template(path = "simple-no-escape.txt")]
struct VariablesTemplateNoEscape<'a> {
    strvar: &'a str,
    num: i64,
    i18n: String,
}

#[test]
fn test_variables_no_escape() {
    let s = VariablesTemplateNoEscape {
        strvar: "foo",
        num: 42,
        i18n: "Iñtërnâtiônàlizætiøn".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "\nhello world, foo\n\
         with number: 42\n\
         Iñtërnâtiônàlizætiøn is important\n\
         in vars too: Iñtërnâtiônàlizætiøn"
    );
}

#[derive(Template)]
#[template(path = "if.html")]
struct IfTemplate {
    cond: bool,
}

#[test]
fn test_if() {
    let s = IfTemplate { cond: true };
    assert_eq!(s.render().unwrap(), "true");
}

#[derive(Template)]
#[template(path = "else.html")]
struct ElseTemplate {
    cond: bool,
}

#[test]
fn test_else() {
    let s = ElseTemplate { cond: false };
    assert_eq!(s.render().unwrap(), "false");
}

#[derive(Template)]
#[template(path = "else-if.html")]
struct ElseIfTemplate {
    cond: bool,
    check: bool,
}

#[test]
fn test_else_if() {
    let s = ElseIfTemplate {
        cond: false,
        check: true,
    };
    assert_eq!(s.render().unwrap(), "checked");
}

#[derive(Template)]
#[template(path = "literals.html")]
struct LiteralsTemplate {}

#[test]
fn test_literals() {
    let s = LiteralsTemplate {};
    assert_eq!(s.render().unwrap(), "a\ntrue\nfalse");
}

struct Holder {
    a: usize,
}

#[derive(Template)]
#[template(path = "attr.html")]
struct AttrTemplate {
    inner: Holder,
}

#[test]
fn test_attr() {
    let t = AttrTemplate {
        inner: Holder { a: 5 },
    };
    assert_eq!(t.render().unwrap(), "5");
}

#[derive(Template)]
#[template(path = "tuple-attr.html")]
struct TupleAttrTemplate<'a> {
    tuple: (&'a str, &'a str),
}

#[test]
fn test_tuple_attr() {
    let t = TupleAttrTemplate {
        tuple: ("foo", "bar"),
    };
    assert_eq!(t.render().unwrap(), "foobar");
}

struct NestedHolder {
    holder: Holder,
}

#[derive(Template)]
#[template(path = "nested-attr.html")]
struct NestedAttrTemplate {
    inner: NestedHolder,
}

#[test]
fn test_nested_attr() {
    let t = NestedAttrTemplate {
        inner: NestedHolder {
            holder: Holder { a: 5 },
        },
    };
    assert_eq!(t.render().unwrap(), "5");
}

#[derive(Template)]
#[template(path = "option.html")]
struct OptionTemplate<'a> {
    var: Option<&'a str>,
}

#[test]
fn test_option() {
    let some = OptionTemplate { var: Some("foo") };
    assert_eq!(some.render().unwrap(), "some: foo");
    let none = OptionTemplate { var: None };
    assert_eq!(none.render().unwrap(), "none");
}

#[derive(Template)]
#[template(path = "generics.html")]
struct GenericsTemplate<T: std::fmt::Display, U = u8>
where
    U: std::fmt::Display,
{
    t: T,
    u: U,
}

#[test]
fn test_generics() {
    let t = GenericsTemplate { t: "a", u: 42 };
    assert_eq!(t.render().unwrap(), "a42");
}

#[derive(Template)]
#[template(path = "composition.html")]
struct CompositionTemplate {
    foo: IfTemplate,
}

#[test]
fn test_composition() {
    let t = CompositionTemplate {
        foo: IfTemplate { cond: true },
    };
    assert_eq!(t.render().unwrap(), "composed: true");
}

#[derive(PartialEq, Eq)]
enum Alphabet {
    Alpha,
}

#[derive(Template)]
#[template(source = "{% if x == Alphabet::Alpha %}true{% endif %}", ext = "txt")]
struct PathCompareTemplate {
    x: Alphabet,
}

#[test]
fn test_path_compare() {
    let t = PathCompareTemplate { x: Alphabet::Alpha };
    assert_eq!(t.render().unwrap(), "true");
}

#[derive(Template)]
#[template(
    source = "{% for i in [\"a\", \"\"] %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ArrayTemplate {}

#[test]
fn test_slice_literal() {
    let t = ArrayTemplate {};
    assert_eq!(t.render().unwrap(), "a");
}

#[derive(Template)]
#[template(source = "  {# foo -#} ", ext = "txt")]
struct CommentTemplate {}

#[test]
fn test_comment() {
    let t = CommentTemplate {};
    assert_eq!(t.render().unwrap(), "  ");
}

#[derive(Template)]
#[template(source = "{% if !foo %}Hello{% endif %}", ext = "txt")]
struct NegationTemplate {
    foo: bool,
}

#[test]
fn test_negation() {
    let t = NegationTemplate { foo: false };
    assert_eq!(t.render().unwrap(), "Hello");
}

#[derive(Template)]
#[template(source = "{% if foo > -2 %}Hello{% endif %}", ext = "txt")]
struct MinusTemplate {
    foo: i8,
}

#[test]
fn test_minus() {
    let t = MinusTemplate { foo: 1 };
    assert_eq!(t.render().unwrap(), "Hello");
}

#[derive(Template)]
#[template(source = "{{ foo[\"bar\"] }}", ext = "txt")]
struct IndexTemplate {
    foo: HashMap<String, String>,
}

#[test]
fn test_index() {
    let mut foo = HashMap::new();
    foo.insert("bar".into(), "baz".into());
    let t = IndexTemplate { foo };
    assert_eq!(t.render().unwrap(), "baz");
}

#[derive(Template)]
#[template(source = "foo", ext = "txt")]
struct Empty;

#[test]
fn test_empty() {
    assert_eq!(Empty.render().unwrap(), "foo");
}

#[derive(Template)]
#[template(path = "raw-simple.html")]
struct RawTemplate;

#[test]
fn test_raw_simple() {
    let template = RawTemplate;
    assert_eq!(template.render().unwrap(), "\n{{ name }}\n");
}

#[derive(Template)]
#[template(path = "raw-complex.html")]
struct RawTemplateComplex;

#[test]
fn test_raw_complex() {
    let template = RawTemplateComplex;
    assert_eq!(
        template.render().unwrap(),
        "\n{% block name %}\n  {{ name }}\n{% endblock %}\n"
    );
}

mod without_import_on_derive {
    #[derive(askama::Template)]
    #[template(source = "foo", ext = "txt")]
    struct WithoutImport;

    #[test]
    fn test_without_import() {
        use askama::Template;
        assert_eq!(WithoutImport.render().unwrap(), "foo");
    }
}
<|endoftext|>


<commit_before>use std::env::consts::EXE_SUFFIX;
use std::env;
use std::io::{self, Write};
use std::path::{PathBuf, Path};
use std::process::Command;

fn main() {
    let mut args = env::args_os().skip(1);
    match args.next().as_ref().and_then(|s| s.to_str()) {
        Some("--version") => {
            let me = env::current_exe().unwrap();
            let mut version_file = PathBuf::from(format!("{}.version", me.display()));
            let mut hash_file = PathBuf::from(format!("{}.version-hash", me.display()));
            if !version_file.exists() {
                // There's a "MAJOR HACKS" statement in `toolchain.rs` right
                // now where custom toolchains use a `cargo.exe` that's
                // temporarily located elsewhere so they can execute the correct
                // `rustc.exe`. This means that our dummy version files may not
                // be just next to use.
                //
                // Detect this here and work around it.
                assert!(cfg!(windows));
                assert!(env::var_os("RUSTUP_TOOLCHAIN").is_some());
                let mut alt = me.clone();
                alt.pop(); // remove our filename
                assert!(alt.ends_with("fallback"));
                alt.pop(); // pop 'fallback'
                alt.push("toolchains");

                let mut part = PathBuf::from("bin");
                part.push(me.file_name().unwrap());

                let path = alt.read_dir().unwrap()
                    .map(|e| e.unwrap().path().join(&part))
                    .filter(|p| p.exists())
                    .find(|p| equivalent(&p, &me))
                    .unwrap();

                version_file = format!("{}.version", path.display()).into();
                hash_file = format!("{}.version-hash", path.display()).into();
            }
            let version = std::fs::read_to_string(&version_file).unwrap();
            let hash = std::fs::read_to_string(&hash_file).unwrap();
            println!("{} ({})", version, hash);
        }
        Some("--empty-arg-test") => {
            assert_eq!(args.next().unwrap(), "");
        }
        Some("--huge-output") => {
            let mut out = io::stderr();
            for _ in 0 .. 10000 {
                out.write_all(b"error: a value named `fail` has already been defined in this module [E0428]\n").unwrap();
            }
        }
        Some("--call-rustc") => {
            // Used by the fallback_cargo_calls_correct_rustc test. Tests that
            // the environment has been set up right such that invoking rustc
            // will actually invoke the wrapper
            let rustc = &format!("rustc{}", EXE_SUFFIX);
            Command::new(rustc).arg("--version").status().unwrap();
        }
        Some("--echo-args") => {
            let mut out = io::stderr();
            for arg in args {
                writeln!(out, "{}", arg.to_string_lossy()).unwrap();
            }
        }
        _ => panic!("bad mock proxy commandline"),
    }
}

#[cfg(unix)]
fn equivalent(_: &Path, _: &Path) -> bool { false }

#[cfg(windows)]
#[allow(warnings)]
fn equivalent(a: &Path, b: &Path) -> bool {
    use std::mem;
    use std::os::windows::prelude::*;
    use std::os::windows::raw::HANDLE;
    use std::fs::File;

    extern "system" {
        fn GetFileInformationByHandle(a: HANDLE, b: *mut BY_HANDLE_FILE_INFORMATION)
            -> i32;
    }

    #[repr(C)]
    struct BY_HANDLE_FILE_INFORMATION {
        dwFileAttributes: u32,
        ftCreationTime: FILETIME,
        ftLastAccessTime: FILETIME,
        ftLastWriteTime: FILETIME,
        dwVolumeSerialNumber: u32,
        nFileSizeHigh: u32,
        nFileSizeLow: u32,
        nNumberOfLinks: u32,
        nFileIndexHigh: u32,
        nFileIndexLow: u32,
    }

    #[repr(C)]
    struct FILETIME {
        dwLowDateTime: u32,
        dwHighDateTime: u32,
    }

    let a = File::open(a).unwrap();
    let b = File::open(b).unwrap();

    unsafe {
        let mut ainfo = mem::zeroed();
        let mut binfo = mem::zeroed();
        GetFileInformationByHandle(a.as_raw_handle(), &mut ainfo);
        GetFileInformationByHandle(b.as_raw_handle(), &mut binfo);

        ainfo.dwVolumeSerialNumber == binfo.dwVolumeSerialNumber &&
            ainfo.nFileIndexHigh == binfo.nFileIndexHigh &&
            ainfo.nFileIndexLow == binfo.nFileIndexLow
    }
}
<commit_msg>refactor mock_bin_src.rs<commit_after>use std::env;
use std::env::consts::EXE_SUFFIX;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    let mut args = env::args_os().skip(1);
    match args.next().as_ref().and_then(|s| s.to_str()) {
        Some("--version") => {
            let me = env::current_exe().unwrap();
            let mut version_file = PathBuf::from(format!("{}.version", me.display()));
            let mut hash_file = PathBuf::from(format!("{}.version-hash", me.display()));
            if !version_file.exists() {
                // There's a "MAJOR HACKS" statement in `toolchain.rs` right
                // now where custom toolchains use a `cargo.exe` that's
                // temporarily located elsewhere so they can execute the correct
                // `rustc.exe`. This means that our dummy version files may not
                // be just next to use.
                //
                // Detect this here and work around it.
                assert!(cfg!(windows));
                assert!(env::var_os("RUSTUP_TOOLCHAIN").is_some());
                let mut alt = me.clone();
                alt.pop(); // remove our filename
                assert!(alt.ends_with("fallback"));
                alt.pop(); // pop 'fallback'
                alt.push("toolchains");

                let mut part = PathBuf::from("bin");
                part.push(me.file_name().unwrap());

                let path = alt
                    .read_dir()
                    .unwrap()
                    .map(|e| e.unwrap().path().join(&part))
                    .filter(|p| p.exists())
                    .find(|p| equivalent(&p, &me))
                    .unwrap();

                version_file = format!("{}.version", path.display()).into();
                hash_file = format!("{}.version-hash", path.display()).into();
            }
            let version = std::fs::read_to_string(&version_file).unwrap();
            let hash = std::fs::read_to_string(&hash_file).unwrap();
            println!("{} ({})", version, hash);
        }
        Some("--empty-arg-test") => {
            assert_eq!(args.next().unwrap(), "");
        }
        Some("--huge-output") => {
            let mut out = io::stderr();
            for _ in 0..10000 {
                out.write_all(b"error: a value named `fail` has already been defined in this module [E0428]\n").unwrap();
            }
        }
        Some("--call-rustc") => {
            // Used by the fallback_cargo_calls_correct_rustc test. Tests that
            // the environment has been set up right such that invoking rustc
            // will actually invoke the wrapper
            let rustc = &format!("rustc{}", EXE_SUFFIX);
            Command::new(rustc).arg("--version").status().unwrap();
        }
        Some("--echo-args") => {
            let mut out = io::stderr();
            for arg in args {
                writeln!(out, "{}", arg.to_string_lossy()).unwrap();
            }
        }
        _ => panic!("bad mock proxy commandline"),
    }
}

#[cfg(unix)]
fn equivalent(_: &Path, _: &Path) -> bool {
    false
}

#[cfg(windows)]
#[allow(non_snake_case)]
fn equivalent(a: &Path, b: &Path) -> bool {
    use std::fs::File;
    use std::mem::MaybeUninit;
    use std::os::windows::io::AsRawHandle;
    use std::os::windows::raw::HANDLE;

    #[repr(C)]
    struct FILETIME {
        dwLowDateTime: u32,
        dwHighDateTime: u32,
    }

    #[repr(C)]
    struct BY_HANDLE_FILE_INFORMATION {
        dwFileAttributes: u32,
        ftCreationTime: FILETIME,
        ftLastAccessTime: FILETIME,
        ftLastWriteTime: FILETIME,
        dwVolumeSerialNumber: u32,
        nFileSizeHigh: u32,
        nFileSizeLow: u32,
        nNumberOfLinks: u32,
        nFileIndexHigh: u32,
        nFileIndexLow: u32,
    }

    extern "system" {
        fn GetFileInformationByHandle(a: HANDLE, b: *mut BY_HANDLE_FILE_INFORMATION) -> i32;
    }

    let a = File::open(a).unwrap();
    let b = File::open(b).unwrap();
    let (ainfo, binfo) = unsafe {
        let mut ainfo = MaybeUninit::uninit();
        let mut binfo = MaybeUninit::uninit();
        if GetFileInformationByHandle(a.as_raw_handle(), ainfo.as_mut_ptr()) == 0 {
            return false;
        }
        if GetFileInformationByHandle(b.as_raw_handle(), binfo.as_mut_ptr()) == 0 {
            return false;
        }
        (ainfo.assume_init(), binfo.assume_init())
    };

    ainfo.dwVolumeSerialNumber == binfo.dwVolumeSerialNumber
        && ainfo.nFileIndexHigh == binfo.nFileIndexHigh
        && ainfo.nFileIndexLow == binfo.nFileIndexLow
}
<|endoftext|>


<commit_before>extern crate image;
extern crate img_hash;
extern crate postgres;
#[macro_use] extern crate log;
extern crate log4rs;
extern crate time;
#[macro_use] extern crate hyper;
extern crate url;
extern crate toml;

use std::env;
use postgres::{Connection, SslMode};
use img_hash::{ImageHash, HashType};
use std::thread;
use std::default::Default;
use time::{Timespec, Duration};
use hyper::Client;
use hyper::header::ContentType;
use image::ImageFormat;
use std::io::Read;
use hyper::status::StatusCode;
use postgres::stmt::Statement;
use std::fs::File;
use url::form_urlencoded;

header! { (XExtra, "X-Extra") => [String] }
header! { (XStream, "X-Stream") => [String] }

fn prepare_statement<'a>(conn: &'a Connection, stmt: &str) -> Statement<'a> {
    loop {
        let prep_stmt = conn.prepare_cached(stmt);
        if prep_stmt.is_err() {
            error!("Error in prepare for '{}': {}",
                   stmt,
                   prep_stmt.unwrap_err());
            thread::sleep(Duration::seconds(4).to_std().unwrap());
            continue;
        }
        return prep_stmt.unwrap();
    }
}

fn main() {
    let mut config_string = String::new();
    File::open("config.toml").unwrap().read_to_string(&mut config_string).unwrap();
    let mut parser = toml::Parser::new(&config_string);

    let config = parser.parse().unwrap();
    let config_table = config.get("config").unwrap();
    let interval = Duration::minutes(config_table.lookup("refresh_minutes")
                                                 .unwrap()
                                                 .as_integer()
                                                 .unwrap());
    let imager_url: &str = &(String::from(config_table.lookup("imager_host")
                                                      .unwrap()
                                                      .as_str()
                                                      .unwrap()) +
                             &String::from("/streams"));
    let check_ms = Duration::milliseconds(
        config_table.lookup("check_ms").unwrap().as_integer().unwrap())
        .to_std().unwrap();

    log4rs::init_file("log.toml", Default::default()).unwrap();
    let db_url: &str = &env::var("DATABASE_URL").unwrap();
    let conn = Connection::connect(db_url, SslMode::None).unwrap();

    let query_stmt = prepare_statement(&conn,
                                       "select * from videos_video order by \"lastRetrieved\" \
                                        asc limit 1");
    let update_stmt = prepare_statement(&conn,
                                        "update videos_video set working = true, hash = $1, \
                                         motion = $2, \"lastRetrieved\" = $3, extra = $4, \"streamURL\" = $5 where id = $6");
    let not_working_stmt = prepare_statement(&conn,
                                     "update videos_video set working = false, \"lastRetrieved\" = $1 where id = $2");

    info!("Connected to Postgres and ready to update video...");
    loop {
        let res = query_stmt.query(&[]);
        if res.is_err() {
            error!("Error in query: {}", res.unwrap_err());
            thread::sleep(check_ms);
            continue;
        }

        let items = res.unwrap();
        if items.len() == 0 {
            warn!("No video feeds");
            thread::sleep(check_ms);
            continue;
        }
        let item = items.get(0);
        let url: String = item.get("url");
        let when: postgres::Result<Timespec> = item.get_opt("lastRetrieved").unwrap();
        let id: i32 = item.get("id");
        let old_hash: postgres::Result<String> = item.get_opt("hash").unwrap();

        if when.is_err() {
            debug!("Item: {}, When: never", url);
        } else {
            let now = time::now().to_timespec();
            let diff: Duration = now - when.unwrap();
            debug!("Item: {}, When: {}", url, diff);
            if diff < interval {
                debug!("oldest item is young: {}", diff);
                thread::sleep(check_ms);
                continue;
            }
        }
        info!("Updating {}", url);

        let data: String = form_urlencoded::Serializer::new(String::new())
            .append_pair("url", &url)
            .finish();

        let raw_resp = Client::new()
                           .post(imager_url)
                           .header(ContentType::form_url_encoded())
                           .body(&data)
                           .send();

        if raw_resp.is_err() {
            warn!("Can't connect to imager");
            thread::sleep(check_ms);
            continue;
        }

        let mut resp = raw_resp.unwrap();

        if resp.status != StatusCode::Ok {
            warn!("{:?}", resp.status_raw());
            let now = time::now().to_timespec();
            not_working_stmt.execute(&[&now, &id]).unwrap();
            thread::sleep(check_ms);
            continue;
        }

        let extra: String = match resp.headers.get::<XExtra>() {
            Some(val) => val.to_string(),
            None => "{}".to_string()
        };

        let stream: String = match resp.headers.get::<XStream>() {
            Some(val) => val.to_string(),
            None => "".to_string()
        };

        let mut buf = Vec::new();
        resp.read_to_end(&mut buf).unwrap();
        let image = image::load_from_memory_with_format(&buf, ImageFormat::PNG).unwrap();
        let hash = ImageHash::hash(&image, 16, HashType::DoubleGradient);

        debug!("Image hash: {}", hash.to_base64());
        let motion: f64 = match old_hash {
            Ok(val) => {
                match ImageHash::from_base64(&val) {
                    Ok(val) => val.dist_ratio(&hash) as f64,
                    Err(_) => -1 as f64
                }
            },
            Err(_) => -1 as f64
        };
        debug!("Difference {}", motion);
        let now = time::now().to_timespec();
        match update_stmt.execute(&[&hash.to_base64(), &motion, &now, &extra, &stream, &id]) {
            Ok(_) => info!("Updated {} with motion {}", url, motion),
            Err(err) => warn!("Error executing update: {:?}", err),
        }
    }
}
<commit_msg>Don't use "len() == 0" (thanks Clippy)<commit_after>extern crate image;
extern crate img_hash;
extern crate postgres;
#[macro_use] extern crate log;
extern crate log4rs;
extern crate time;
#[macro_use] extern crate hyper;
extern crate url;
extern crate toml;

use std::env;
use postgres::{Connection, SslMode};
use img_hash::{ImageHash, HashType};
use std::thread;
use std::default::Default;
use time::{Timespec, Duration};
use hyper::Client;
use hyper::header::ContentType;
use image::ImageFormat;
use std::io::Read;
use hyper::status::StatusCode;
use postgres::stmt::Statement;
use std::fs::File;
use url::form_urlencoded;

header! { (XExtra, "X-Extra") => [String] }
header! { (XStream, "X-Stream") => [String] }

fn prepare_statement<'a>(conn: &'a Connection, stmt: &str) -> Statement<'a> {
    loop {
        let prep_stmt = conn.prepare_cached(stmt);
        if prep_stmt.is_err() {
            error!("Error in prepare for '{}': {}",
                   stmt,
                   prep_stmt.unwrap_err());
            thread::sleep(Duration::seconds(4).to_std().unwrap());
            continue;
        }
        return prep_stmt.unwrap();
    }
}

fn main() {
    let mut config_string = String::new();
    File::open("config.toml").unwrap().read_to_string(&mut config_string).unwrap();
    let mut parser = toml::Parser::new(&config_string);

    let config = parser.parse().unwrap();
    let config_table = config.get("config").unwrap();
    let interval = Duration::minutes(config_table.lookup("refresh_minutes")
                                                 .unwrap()
                                                 .as_integer()
                                                 .unwrap());
    let imager_url: &str = &(String::from(config_table.lookup("imager_host")
                                                      .unwrap()
                                                      .as_str()
                                                      .unwrap()) +
                             &String::from("/streams"));
    let check_ms = Duration::milliseconds(
        config_table.lookup("check_ms").unwrap().as_integer().unwrap())
        .to_std().unwrap();

    log4rs::init_file("log.toml", Default::default()).unwrap();
    let db_url: &str = &env::var("DATABASE_URL").unwrap();
    let conn = Connection::connect(db_url, SslMode::None).unwrap();

    let query_stmt = prepare_statement(&conn,
                                       "select * from videos_video order by \"lastRetrieved\" \
                                        asc limit 1");
    let update_stmt = prepare_statement(&conn,
                                        "update videos_video set working = true, hash = $1, \
                                         motion = $2, \"lastRetrieved\" = $3, extra = $4, \"streamURL\" = $5 where id = $6");
    let not_working_stmt = prepare_statement(&conn,
                                     "update videos_video set working = false, \"lastRetrieved\" = $1 where id = $2");

    info!("Connected to Postgres and ready to update video...");
    loop {
        let res = query_stmt.query(&[]);
        if res.is_err() {
            error!("Error in query: {}", res.unwrap_err());
            thread::sleep(check_ms);
            continue;
        }

        let items = res.unwrap();
        if items.is_empty() {
            warn!("No video feeds");
            thread::sleep(check_ms);
            continue;
        }
        let item = items.get(0);
        let url: String = item.get("url");
        let when: postgres::Result<Timespec> = item.get_opt("lastRetrieved").unwrap();
        let id: i32 = item.get("id");
        let old_hash: postgres::Result<String> = item.get_opt("hash").unwrap();

        if when.is_err() {
            debug!("Item: {}, When: never", url);
        } else {
            let now = time::now().to_timespec();
            let diff: Duration = now - when.unwrap();
            debug!("Item: {}, When: {}", url, diff);
            if diff < interval {
                debug!("oldest item is young: {}", diff);
                thread::sleep(check_ms);
                continue;
            }
        }
        info!("Updating {}", url);

        let data: String = form_urlencoded::Serializer::new(String::new())
            .append_pair("url", &url)
            .finish();

        let raw_resp = Client::new()
                           .post(imager_url)
                           .header(ContentType::form_url_encoded())
                           .body(&data)
                           .send();

        if raw_resp.is_err() {
            warn!("Can't connect to imager");
            thread::sleep(check_ms);
            continue;
        }

        let mut resp = raw_resp.unwrap();

        if resp.status != StatusCode::Ok {
            warn!("{:?}", resp.status_raw());
            let now = time::now().to_timespec();
            not_working_stmt.execute(&[&now, &id]).unwrap();
            thread::sleep(check_ms);
            continue;
        }

        let extra: String = match resp.headers.get::<XExtra>() {
            Some(val) => val.to_string(),
            None => "{}".to_string()
        };

        let stream: String = match resp.headers.get::<XStream>() {
            Some(val) => val.to_string(),
            None => "".to_string()
        };

        let mut buf = Vec::new();
        resp.read_to_end(&mut buf).unwrap();
        let image = image::load_from_memory_with_format(&buf, ImageFormat::PNG).unwrap();
        let hash = ImageHash::hash(&image, 16, HashType::DoubleGradient);

        debug!("Image hash: {}", hash.to_base64());
        let motion: f64 = match old_hash {
            Ok(val) => {
                match ImageHash::from_base64(&val) {
                    Ok(val) => val.dist_ratio(&hash) as f64,
                    Err(_) => -1 as f64
                }
            },
            Err(_) => -1 as f64
        };
        debug!("Difference {}", motion);
        let now = time::now().to_timespec();
        match update_stmt.execute(&[&hash.to_base64(), &motion, &now, &extra, &stream, &id]) {
            Ok(_) => info!("Updated {} with motion {}", url, motion),
            Err(err) => warn!("Error executing update: {:?}", err),
        }
    }
}
<|endoftext|>


<commit_before>use super::*;
use wtools::error::BasicError;
use wca::command::Command;
use wca::instruction::Instruction;
use wca::string::parse_request::OpType::Primitive;

//

fn commands_form() -> std::collections::HashMap< String, Command >
{
  let help_command : Command = wca::CommandOptions::default()
  .hint( "Get help." )
  .long_hint( "Get help for command [command]" )
  .phrase( ".help" )
  .routine( &| _i : &Instruction | { println!( "this is help" ); Ok( () ) } )
  .form();
  let list_command : Command = wca::CommandOptions::default()
  .hint( "Get list." )
  .long_hint( "Get list of" )
  .phrase( ".list" )
  .subject_hint( "some subject" )
  .routine( &| _i : &Instruction | { println!( "this is list" ); Ok( () ) } )
  .form();
  let err_command : Command = wca::CommandOptions::default()
  .hint( "Error." )
  .long_hint( "Throw error" )
  .phrase( ".error" )
  .routine( &| _i : &Instruction | { Err( BasicError::new( "err" ) ) } )
  .form();

  let commands : std::collections::HashMap< String, Command > = std::collections::HashMap::from
  ([
    ( ".help".to_string(), help_command ),
    ( ".list".to_string(), list_command ),
    ( ".error".to_string(), err_command ),
  ]);
  commands
}

tests_impls!
{
  fn basic()
  {
    let ca = wca::commands_aggregator()
    .form();
    a_id!( ca.base_path, None );
    a_id!( ca.command_prefix, "".to_string() );
    a_id!( ca.delimeter, vec![ ".".to_string(), " ".to_string() ] );
    a_id!( ca.command_explicit_delimeter, ";".to_string() );
    a_id!( ca.command_implicit_delimeter, " ".to_string() );
    a_id!( ca.commands_explicit_delimiting, true );
    a_id!( ca.commands_implicit_delimiting, false );
    a_id!( ca.properties_map_parsing, false );
    a_id!( ca.several_values, true );
    a_id!( ca.with_help, true );
    a_id!( ca.changing_exit_code, true );
    a_id!( ca.commands, std::collections::HashMap::new() );
  }

  //

  fn program_perform_basic()
  {
    /* single command, returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* single command, returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );

    /* */

    /* two commands, explicit delimeter, returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".help ; .list" );
    a_id!( got, Ok( () ) );

    /* two commands, explicit delimeter, second command returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list ; .error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );

    /* */

    /* two commands, implicit delimeter, returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".help .list" );
    a_id!( got, Ok( () ) );

    /* two commands, implicit delimeter, second command returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list .error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );
  }

  //

  fn instruction_perform_basic()
  {
    /* no commands in aggregator */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .form();
    let got = ca.instruction_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* command returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.instruction_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* command returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.instruction_perform( ".error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );
  }

  //

  fn instructions_parse_basic()
  {
    let ca = wca::commands_aggregator()
    .form();

    /* */

    /* empty program */
    let got = ca.instructions_parse( "" );
    a_id!( got, vec![] );

    /* */

    /* single command without subject and map */
    let got = ca.instructions_parse( ".help" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction ] );

    /* single command with subject */
    let got = ca.instructions_parse( ".help command" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction ] );

    /* single command with subject and map */
    let got = ca.instructions_parse( ".help command v:3" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction ] );

    /* */

    /* two commands without subject and map, explicit delimeter */
    let got = ca.instructions_parse( ".help ; .version" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject, explicit delimeter */
    let got = ca.instructions_parse( ".help command ; .version delta" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject and map, explicit delimeter */
    let got = ca.instructions_parse( ".help command v:3 ; .version delta n:5" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::from([ ( "n".to_string(), Primitive( "5".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* */

    /* two commands without subject and map, implicit delimeter */
    let got = ca.instructions_parse( ".help .version" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject, implicit delimeter */
    let got = ca.instructions_parse( ".help command .version delta" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject and map, implicit delimeter */
    let got = ca.instructions_parse( ".help command v:3 .version delta n:5" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::from([ ( "n".to_string(), Primitive( "5".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );
  }
}

//

tests_index!
{
  basic,
  program_perform_basic,
  instruction_perform_basic,
  instructions_parse_basic,
}

<commit_msg>Write test routine `program_perform_with_dotted_paths`<commit_after>use super::*;
use wtools::error::BasicError;
use wca::command::Command;
use wca::instruction::Instruction;
use wca::string::parse_request::OpType::Primitive;

//

fn commands_form() -> std::collections::HashMap< String, Command >
{
  let help_command : Command = wca::CommandOptions::default()
  .hint( "Get help." )
  .long_hint( "Get help for command [command]" )
  .phrase( ".help" )
  .subject_hint( "some command" )
  .routine( &| _i : &Instruction | { println!( "this is help" ); Ok( () ) } )
  .form();
  let list_command : Command = wca::CommandOptions::default()
  .hint( "Get list." )
  .long_hint( "Get list of" )
  .phrase( ".list" )
  .subject_hint( "some subject" )
  .routine( &| _i : &Instruction | { println!( "this is list" ); Ok( () ) } )
  .form();
  let err_command : Command = wca::CommandOptions::default()
  .hint( "Error." )
  .long_hint( "Throw error" )
  .phrase( ".error" )
  .routine( &| _i : &Instruction | { Err( BasicError::new( "err" ) ) } )
  .form();

  let commands : std::collections::HashMap< String, Command > = std::collections::HashMap::from
  ([
    ( ".help".to_string(), help_command ),
    ( ".list".to_string(), list_command ),
    ( ".error".to_string(), err_command ),
  ]);
  commands
}

tests_impls!
{
  fn basic()
  {
    let ca = wca::commands_aggregator()
    .form();
    a_id!( ca.base_path, None );
    a_id!( ca.command_prefix, "".to_string() );
    a_id!( ca.delimeter, vec![ ".".to_string(), " ".to_string() ] );
    a_id!( ca.command_explicit_delimeter, ";".to_string() );
    a_id!( ca.command_implicit_delimeter, " ".to_string() );
    a_id!( ca.commands_explicit_delimiting, true );
    a_id!( ca.commands_implicit_delimiting, false );
    a_id!( ca.properties_map_parsing, false );
    a_id!( ca.several_values, true );
    a_id!( ca.with_help, true );
    a_id!( ca.changing_exit_code, true );
    a_id!( ca.commands, std::collections::HashMap::new() );
  }

  //

  fn program_perform_basic()
  {
    /* single command, returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* single command, returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );

    /* */

    /* two commands, explicit delimeter, returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".help ; .list" );
    a_id!( got, Ok( () ) );

    /* two commands, explicit delimeter, second command returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list ; .error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );

    /* */

    /* two commands, implicit delimeter, returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".help .list" );
    a_id!( got, Ok( () ) );

    /* two commands, implicit delimeter, second command returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list .error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );
  }

  //

  fn program_perform_with_dotted_paths()
  {
    /* single command with subject as single dot */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list ." );
    a_id!( got, Ok( () ) );

    /* single command with subject as double dot */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list .." );
    a_id!( got, Ok( () ) );

    /* single command with subject as dot and slash */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list ./" );
    a_id!( got, Ok( () ) );

    /* single command with subject as double dot and slash */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list ../" );
    a_id!( got, Ok( () ) );

    /* single command with subject as dot and backslash */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list .\\" );
    a_id!( got, Ok( () ) );

    /* single command with subject as double dot and backslash */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list ..\\" );
    a_id!( got, Ok( () ) );

    /* */

    /* two commands with subjects with dots */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.program_perform( ".list ..\\ .help ./some" );
    a_id!( got, Ok( () ) );
  }

  //

  fn instruction_perform_basic()
  {
    /* no commands in aggregator */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .form();
    let got = ca.instruction_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* command returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.instruction_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* command returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.instruction_perform( ".error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );
  }

  //

  fn instructions_parse_basic()
  {
    let ca = wca::commands_aggregator()
    .form();

    /* */

    /* empty program */
    let got = ca.instructions_parse( "" );
    a_id!( got, vec![] );

    /* */

    /* single command without subject and map */
    let got = ca.instructions_parse( ".help" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction ] );

    /* single command with subject */
    let got = ca.instructions_parse( ".help command" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction ] );

    /* single command with subject and map */
    let got = ca.instructions_parse( ".help command v:3" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction ] );

    /* */

    /* two commands without subject and map, explicit delimeter */
    let got = ca.instructions_parse( ".help ; .version" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject, explicit delimeter */
    let got = ca.instructions_parse( ".help command ; .version delta" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject and map, explicit delimeter */
    let got = ca.instructions_parse( ".help command v:3 ; .version delta n:5" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::from([ ( "n".to_string(), Primitive( "5".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* */

    /* two commands without subject and map, implicit delimeter */
    let got = ca.instructions_parse( ".help .version" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject, implicit delimeter */
    let got = ca.instructions_parse( ".help command .version delta" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject and map, implicit delimeter */
    let got = ca.instructions_parse( ".help command v:3 .version delta n:5" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::from([ ( "n".to_string(), Primitive( "5".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );
  }
}

//

tests_index!
{
  basic,
  program_perform_basic,
  program_perform_with_dotted_paths,
  instruction_perform_basic,
  instructions_parse_basic,
}

<|endoftext|>