<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 devtools;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, WorkerGlobalScopeCast};
use dom::bindings::error::{ErrorResult, report_pending_exception};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{RootCollection, Root};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::bindings::utils::Reflectable;
use dom::errorevent::ErrorEvent;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::messageevent::MessageEvent;
use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler, WorkerEventHandler, WorkerErrorHandler};
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::workerglobalscope::{WorkerGlobalScopeTypeId, WorkerGlobalScopeInit};
use script_task::{ScriptTask, ScriptChan, ScriptMsg, TimerSource, ScriptPort};
use script_task::StackRootTLS;

use devtools_traits::DevtoolScriptControlMsg;
use msg::constellation_msg::PipelineId;
use net_traits::load_whole_resource;
use profile_traits::mem::{self, Reporter, ReporterRequest};
use util::task::spawn_named;
use util::task_state;
use util::task_state::{SCRIPT, IN_WORKER};

use ipc_channel::ipc::{self, IpcReceiver};
use ipc_channel::router::ROUTER;
use js::jsapi::{JSContext, RootedValue, HandleValue};
use js::jsapi::{JSAutoRequest, JSAutoCompartment};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use url::Url;

use rand::random;
use std::mem::replace;
use std::rc::Rc;
use std::sync::mpsc::{Sender, Receiver, channel, Select, RecvError};

/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with
/// every message. While this SendableWorkerScriptChan is alive, the associated Worker object
/// will remain alive.
#[derive(JSTraceable, Clone)]
pub struct SendableWorkerScriptChan {
    sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: TrustedWorkerAddress,
}

impl ScriptChan for SendableWorkerScriptChan {
    fn send(&self, msg: ScriptMsg) -> Result<(), ()> {
        return self.sender.send((self.worker.clone(), msg)).map_err(|_| ());
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        box SendableWorkerScriptChan {
            sender: self.sender.clone(),
            worker: self.worker.clone(),
        }
    }
}

/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
/// providing a reference that can be cloned freely.
struct AutoWorkerReset<'a> {
    workerscope: &'a DedicatedWorkerGlobalScope,
    old_worker: Option<TrustedWorkerAddress>,
}

impl<'a> AutoWorkerReset<'a> {
    fn new(workerscope: &'a DedicatedWorkerGlobalScope, worker: TrustedWorkerAddress) -> AutoWorkerReset<'a> {
        AutoWorkerReset {
            workerscope: workerscope,
            old_worker: replace(&mut *workerscope.worker.borrow_mut(), Some(worker)),
        }
    }
}

impl<'a> Drop for AutoWorkerReset<'a> {
    fn drop(&mut self) {
        *self.workerscope.worker.borrow_mut() = self.old_worker.clone();
    }
}

enum MixedMessage {
    FromWorker((TrustedWorkerAddress, ScriptMsg)),
    FromDevtools(DevtoolScriptControlMsg),
}

// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
#[dom_struct]
pub struct DedicatedWorkerGlobalScope {
    workerglobalscope: WorkerGlobalScope,
    id: PipelineId,
    receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
    own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: DOMRefCell<Option<TrustedWorkerAddress>>,
    /// Sender to the parent thread.
    parent_sender: Box<ScriptChan+Send>,
}

impl DedicatedWorkerGlobalScope {
    fn new_inherited(init: WorkerGlobalScopeInit,
                     worker_url: Url,
                     id: PipelineId,
                     devtools_port: Receiver<DevtoolScriptControlMsg>,
                     runtime: Rc<Runtime>,
                     parent_sender: Box<ScriptChan+Send>,
                     own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                     receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
                     -> DedicatedWorkerGlobalScope {
        DedicatedWorkerGlobalScope {
            workerglobalscope: WorkerGlobalScope::new_inherited(
                WorkerGlobalScopeTypeId::DedicatedGlobalScope, init, worker_url,
                runtime, devtools_port),
            id: id,
            receiver: receiver,
            own_sender: own_sender,
            parent_sender: parent_sender,
            worker: DOMRefCell::new(None),
        }
    }

    pub fn new(init: WorkerGlobalScopeInit,
               worker_url: Url,
               id: PipelineId,
               devtools_port: Receiver<DevtoolScriptControlMsg>,
               runtime: Rc<Runtime>,
               parent_sender: Box<ScriptChan+Send>,
               own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
               receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
               -> Root<DedicatedWorkerGlobalScope> {
        let scope = box DedicatedWorkerGlobalScope::new_inherited(
            init, worker_url, id, devtools_port, runtime.clone(), parent_sender,
            own_sender, receiver);
        DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
    }
}

impl DedicatedWorkerGlobalScope {
    pub fn run_worker_scope(init: WorkerGlobalScopeInit,
                            worker_url: Url,
                            id: PipelineId,
                            devtools_ipc_port: IpcReceiver<DevtoolScriptControlMsg>,
                            worker: TrustedWorkerAddress,
                            parent_sender: Box<ScriptChan+Send>,
                            own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                            receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
        let serialized_worker_url = worker_url.serialize();
        spawn_named(format!("WebWorker for {}", serialized_worker_url), move || {
            task_state::initialize(SCRIPT | IN_WORKER);

            let roots = RootCollection::new();
            let _stack_roots_tls = StackRootTLS::new(&roots);

            let (url, source) = match load_whole_resource(&init.resource_task, worker_url) {
                Err(_) => {
                    println!("error loading script {}", serialized_worker_url);
                    parent_sender.send(ScriptMsg::RunnableMsg(
                        box WorkerEventHandler::new(worker))).unwrap();
                    return;
                }
                Ok((metadata, bytes)) => {
                    (metadata.final_url, String::from_utf8(bytes).unwrap())
                }
            };

            let runtime = Rc::new(ScriptTask::new_rt_and_cx());
            let serialized_url = url.serialize();
            let parent_sender_for_reporter = parent_sender.clone();

            let (devtools_mpsc_chan, devtools_mpsc_port) = channel();
            ROUTER.route_ipc_receiver_to_mpsc_sender(devtools_ipc_port, devtools_mpsc_chan);

            let global = DedicatedWorkerGlobalScope::new(
                init, url, id, devtools_mpsc_port, runtime.clone(),
                parent_sender, own_sender, receiver);
            // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
            // registration (#6631), so we instead use a random number and cross our fingers.
            let scope = WorkerGlobalScopeCast::from_ref(global.r());
            let reporter_name = format!("worker-reporter-{}", random::<u64>());

            {
                let _ar = AutoWorkerReset::new(global.r(), worker);

                match runtime.evaluate_script(
                    global.r().reflector().get_jsobject(), source, serialized_url, 1) {
                    Ok(_) => (),
                    Err(_) => {
                        // TODO: An error needs to be dispatched to the parent.
                        // https://github.com/servo/servo/issues/6422
                        println!("evaluate_script failed");
                        let _ar = JSAutoRequest::new(runtime.cx());
                        report_pending_exception(runtime.cx(), global.r().reflector().get_jsobject().get());
                    }
                }

                // Register this task as a memory reporter. This needs to be done within the
                // scope of `_ar` otherwise script_chan_as_reporter() will panic.
                let (reporter_sender, reporter_receiver) = ipc::channel().unwrap();
                ROUTER.add_route(reporter_receiver.to_opaque(), box move |reporter_request| {
                    // Just injects an appropriate event into the worker task's queue.
                    let reporter_request: ReporterRequest = reporter_request.to().unwrap();
                    parent_sender_for_reporter.send(ScriptMsg::CollectReports(
                            reporter_request.reports_channel)).unwrap()
                });
                scope.mem_profiler_chan().send(mem::ProfilerMsg::RegisterReporter(
                        reporter_name.clone(),
                        Reporter(reporter_sender)));
            }

            loop {
                let event = match global.receive_event() {
                    Ok(event) => event,
                    Err(_) => break,
                };

                match event {
                    MixedMessage::FromDevtools(msg) => {
                        let global_ref = GlobalRef::Worker(scope);
                        match msg {
                            DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) =>
                                devtools::handle_evaluate_js(&global_ref, string, sender),
                            DevtoolScriptControlMsg::GetCachedMessages(pipe_id, message_types, sender) =>
                                devtools::handle_get_cached_messages(pipe_id, message_types, sender),
                            DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) =>
                                devtools::handle_wants_live_notifications(&global_ref, bool_val),
                            _ => debug!("got an unusable devtools control message inside the worker!"),
                        }
                    },
                    MixedMessage::FromWorker((linked_worker, msg)) => {
                        let _ar = AutoWorkerReset::new(global.r(), linked_worker);
                        global.r().handle_event(msg);
                    },
                }
            }

            // Unregister this task as a memory reporter.
            let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name);
            scope.mem_profiler_chan().send(msg);
        });
    }
}

pub trait DedicatedWorkerGlobalScopeHelpers {
    fn script_chan(self) -> Box<ScriptChan+Send>;
    fn pipeline(self) -> PipelineId;
    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>);
    fn process_event(self, msg: ScriptMsg);
}

impl<'a> DedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    fn script_chan(self) -> Box<ScriptChan+Send> {
        box SendableWorkerScriptChan {
            sender: self.own_sender.clone(),
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        }
    }

    fn pipeline(self) -> PipelineId {
        self.id
    }

    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>) {
        let (tx, rx) = channel();
        let chan = box SendableWorkerScriptChan {
            sender: tx,
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        };
        (chan, box rx)
    }

    fn process_event(self, msg: ScriptMsg) {
        self.handle_event(msg);
    }
}

trait PrivateDedicatedWorkerGlobalScopeHelpers {
    fn handle_event(self, msg: ScriptMsg);
    fn dispatch_error_to_worker(self, &ErrorEvent);
    fn receive_event(self) -> Result<MixedMessage, RecvError>;
}

impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    #[allow(unsafe_code)]
    fn receive_event(self) -> Result<MixedMessage, RecvError> {
        let scope = WorkerGlobalScopeCast::from_ref(self);
        let worker_port = &self.receiver;
        let devtools_port = scope.devtools_port();

        let sel = Select::new();
        let mut worker_handle = sel.handle(worker_port);
        let mut devtools_handle = sel.handle(devtools_port);
        unsafe {
            worker_handle.add();
            if scope.devtools_sender().is_some() {
                devtools_handle.add();
            }
        }
        let ret = sel.wait();
        if ret == worker_handle.id() {
            Ok(MixedMessage::FromWorker(try!(worker_port.recv())))
        } else if ret == devtools_handle.id() {
            Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
        } else {
            panic!("unexpected select result!")
        }
    }

    fn handle_event(self, msg: ScriptMsg) {
        match msg {
            ScriptMsg::DOMMessage(data) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let target = EventTargetCast::from_ref(self);
                let _ar = JSAutoRequest::new(scope.get_cx());
                let _ac = JSAutoCompartment::new(scope.get_cx(), scope.reflector().get_jsobject().get());
                let mut message = RootedValue::new(scope.get_cx(), UndefinedValue());
                data.read(GlobalRef::Worker(scope), message.handle_mut());
                MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle());
            },
            ScriptMsg::RunnableMsg(runnable) => {
                runnable.handler()
            },
            ScriptMsg::RefcountCleanup(addr) => {
                LiveDOMReferences::cleanup(addr);
            }
            ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                scope.handle_fire_timer(timer_id);
            }
            ScriptMsg::CollectReports(reports_chan) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let cx = scope.get_cx();
                let path_seg = format!("url({})", scope.get_url());
                let reports = ScriptTask::get_reports(cx, path_seg);
                reports_chan.send(reports);
            }
            _ => panic!("Unexpected message"),
        }
    }

    fn dispatch_error_to_worker(self, errorevent: &ErrorEvent) {
        let msg = errorevent.Message();
        let file_name = errorevent.Filename();
        let line_num = errorevent.Lineno();
        let col_num = errorevent.Colno();
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerErrorHandler::new(worker, msg, file_name, line_num, col_num))).unwrap();
 }
}

impl<'a> DedicatedWorkerGlobalScopeMethods for &'a DedicatedWorkerGlobalScope {
    // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
    fn PostMessage(self, cx: *mut JSContext, message: HandleValue) -> ErrorResult {
        let data = try!(StructuredCloneData::write(cx, message));
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerMessageHandler::new(worker, data))).unwrap();
        Ok(())
    }

    event_handler!(message, GetOnmessage, SetOnmessage);
}

impl DedicatedWorkerGlobalScopeDerived for EventTarget {
    fn is_dedicatedworkerglobalscope(&self) -> bool {
        match *self.type_id() {
            EventTargetTypeId::WorkerGlobalScope(WorkerGlobalScopeTypeId::DedicatedGlobalScope) => true,
            _ => false
        }
    }
}
<commit_msg>Rename DedicatedWorkerGlobalScope::handle_event to handle_script_event.<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 devtools;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, WorkerGlobalScopeCast};
use dom::bindings::error::{ErrorResult, report_pending_exception};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{RootCollection, Root};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::bindings::utils::Reflectable;
use dom::errorevent::ErrorEvent;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::messageevent::MessageEvent;
use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler, WorkerEventHandler, WorkerErrorHandler};
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::workerglobalscope::{WorkerGlobalScopeTypeId, WorkerGlobalScopeInit};
use script_task::{ScriptTask, ScriptChan, ScriptMsg, TimerSource, ScriptPort};
use script_task::StackRootTLS;

use devtools_traits::DevtoolScriptControlMsg;
use msg::constellation_msg::PipelineId;
use net_traits::load_whole_resource;
use profile_traits::mem::{self, Reporter, ReporterRequest};
use util::task::spawn_named;
use util::task_state;
use util::task_state::{SCRIPT, IN_WORKER};

use ipc_channel::ipc::{self, IpcReceiver};
use ipc_channel::router::ROUTER;
use js::jsapi::{JSContext, RootedValue, HandleValue};
use js::jsapi::{JSAutoRequest, JSAutoCompartment};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use url::Url;

use rand::random;
use std::mem::replace;
use std::rc::Rc;
use std::sync::mpsc::{Sender, Receiver, channel, Select, RecvError};

/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with
/// every message. While this SendableWorkerScriptChan is alive, the associated Worker object
/// will remain alive.
#[derive(JSTraceable, Clone)]
pub struct SendableWorkerScriptChan {
    sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: TrustedWorkerAddress,
}

impl ScriptChan for SendableWorkerScriptChan {
    fn send(&self, msg: ScriptMsg) -> Result<(), ()> {
        return self.sender.send((self.worker.clone(), msg)).map_err(|_| ());
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        box SendableWorkerScriptChan {
            sender: self.sender.clone(),
            worker: self.worker.clone(),
        }
    }
}

/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
/// providing a reference that can be cloned freely.
struct AutoWorkerReset<'a> {
    workerscope: &'a DedicatedWorkerGlobalScope,
    old_worker: Option<TrustedWorkerAddress>,
}

impl<'a> AutoWorkerReset<'a> {
    fn new(workerscope: &'a DedicatedWorkerGlobalScope, worker: TrustedWorkerAddress) -> AutoWorkerReset<'a> {
        AutoWorkerReset {
            workerscope: workerscope,
            old_worker: replace(&mut *workerscope.worker.borrow_mut(), Some(worker)),
        }
    }
}

impl<'a> Drop for AutoWorkerReset<'a> {
    fn drop(&mut self) {
        *self.workerscope.worker.borrow_mut() = self.old_worker.clone();
    }
}

enum MixedMessage {
    FromWorker((TrustedWorkerAddress, ScriptMsg)),
    FromDevtools(DevtoolScriptControlMsg),
}

// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
#[dom_struct]
pub struct DedicatedWorkerGlobalScope {
    workerglobalscope: WorkerGlobalScope,
    id: PipelineId,
    receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
    own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: DOMRefCell<Option<TrustedWorkerAddress>>,
    /// Sender to the parent thread.
    parent_sender: Box<ScriptChan+Send>,
}

impl DedicatedWorkerGlobalScope {
    fn new_inherited(init: WorkerGlobalScopeInit,
                     worker_url: Url,
                     id: PipelineId,
                     devtools_port: Receiver<DevtoolScriptControlMsg>,
                     runtime: Rc<Runtime>,
                     parent_sender: Box<ScriptChan+Send>,
                     own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                     receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
                     -> DedicatedWorkerGlobalScope {
        DedicatedWorkerGlobalScope {
            workerglobalscope: WorkerGlobalScope::new_inherited(
                WorkerGlobalScopeTypeId::DedicatedGlobalScope, init, worker_url,
                runtime, devtools_port),
            id: id,
            receiver: receiver,
            own_sender: own_sender,
            parent_sender: parent_sender,
            worker: DOMRefCell::new(None),
        }
    }

    pub fn new(init: WorkerGlobalScopeInit,
               worker_url: Url,
               id: PipelineId,
               devtools_port: Receiver<DevtoolScriptControlMsg>,
               runtime: Rc<Runtime>,
               parent_sender: Box<ScriptChan+Send>,
               own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
               receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
               -> Root<DedicatedWorkerGlobalScope> {
        let scope = box DedicatedWorkerGlobalScope::new_inherited(
            init, worker_url, id, devtools_port, runtime.clone(), parent_sender,
            own_sender, receiver);
        DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
    }
}

impl DedicatedWorkerGlobalScope {
    pub fn run_worker_scope(init: WorkerGlobalScopeInit,
                            worker_url: Url,
                            id: PipelineId,
                            devtools_ipc_port: IpcReceiver<DevtoolScriptControlMsg>,
                            worker: TrustedWorkerAddress,
                            parent_sender: Box<ScriptChan+Send>,
                            own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                            receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
        let serialized_worker_url = worker_url.serialize();
        spawn_named(format!("WebWorker for {}", serialized_worker_url), move || {
            task_state::initialize(SCRIPT | IN_WORKER);

            let roots = RootCollection::new();
            let _stack_roots_tls = StackRootTLS::new(&roots);

            let (url, source) = match load_whole_resource(&init.resource_task, worker_url) {
                Err(_) => {
                    println!("error loading script {}", serialized_worker_url);
                    parent_sender.send(ScriptMsg::RunnableMsg(
                        box WorkerEventHandler::new(worker))).unwrap();
                    return;
                }
                Ok((metadata, bytes)) => {
                    (metadata.final_url, String::from_utf8(bytes).unwrap())
                }
            };

            let runtime = Rc::new(ScriptTask::new_rt_and_cx());
            let serialized_url = url.serialize();
            let parent_sender_for_reporter = parent_sender.clone();

            let (devtools_mpsc_chan, devtools_mpsc_port) = channel();
            ROUTER.route_ipc_receiver_to_mpsc_sender(devtools_ipc_port, devtools_mpsc_chan);

            let global = DedicatedWorkerGlobalScope::new(
                init, url, id, devtools_mpsc_port, runtime.clone(),
                parent_sender, own_sender, receiver);
            // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
            // registration (#6631), so we instead use a random number and cross our fingers.
            let scope = WorkerGlobalScopeCast::from_ref(global.r());
            let reporter_name = format!("worker-reporter-{}", random::<u64>());

            {
                let _ar = AutoWorkerReset::new(global.r(), worker);

                match runtime.evaluate_script(
                    global.r().reflector().get_jsobject(), source, serialized_url, 1) {
                    Ok(_) => (),
                    Err(_) => {
                        // TODO: An error needs to be dispatched to the parent.
                        // https://github.com/servo/servo/issues/6422
                        println!("evaluate_script failed");
                        let _ar = JSAutoRequest::new(runtime.cx());
                        report_pending_exception(runtime.cx(), global.r().reflector().get_jsobject().get());
                    }
                }

                // Register this task as a memory reporter. This needs to be done within the
                // scope of `_ar` otherwise script_chan_as_reporter() will panic.
                let (reporter_sender, reporter_receiver) = ipc::channel().unwrap();
                ROUTER.add_route(reporter_receiver.to_opaque(), box move |reporter_request| {
                    // Just injects an appropriate event into the worker task's queue.
                    let reporter_request: ReporterRequest = reporter_request.to().unwrap();
                    parent_sender_for_reporter.send(ScriptMsg::CollectReports(
                            reporter_request.reports_channel)).unwrap()
                });
                scope.mem_profiler_chan().send(mem::ProfilerMsg::RegisterReporter(
                        reporter_name.clone(),
                        Reporter(reporter_sender)));
            }

            loop {
                let event = match global.receive_event() {
                    Ok(event) => event,
                    Err(_) => break,
                };

                match event {
                    MixedMessage::FromDevtools(msg) => {
                        let global_ref = GlobalRef::Worker(scope);
                        match msg {
                            DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) =>
                                devtools::handle_evaluate_js(&global_ref, string, sender),
                            DevtoolScriptControlMsg::GetCachedMessages(pipe_id, message_types, sender) =>
                                devtools::handle_get_cached_messages(pipe_id, message_types, sender),
                            DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) =>
                                devtools::handle_wants_live_notifications(&global_ref, bool_val),
                            _ => debug!("got an unusable devtools control message inside the worker!"),
                        }
                    },
                    MixedMessage::FromWorker((linked_worker, msg)) => {
                        let _ar = AutoWorkerReset::new(global.r(), linked_worker);
                        global.r().handle_script_event(msg);
                    },
                }
            }

            // Unregister this task as a memory reporter.
            let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name);
            scope.mem_profiler_chan().send(msg);
        });
    }
}

pub trait DedicatedWorkerGlobalScopeHelpers {
    fn script_chan(self) -> Box<ScriptChan+Send>;
    fn pipeline(self) -> PipelineId;
    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>);
    fn process_event(self, msg: ScriptMsg);
}

impl<'a> DedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    fn script_chan(self) -> Box<ScriptChan+Send> {
        box SendableWorkerScriptChan {
            sender: self.own_sender.clone(),
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        }
    }

    fn pipeline(self) -> PipelineId {
        self.id
    }

    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>) {
        let (tx, rx) = channel();
        let chan = box SendableWorkerScriptChan {
            sender: tx,
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        };
        (chan, box rx)
    }

    fn process_event(self, msg: ScriptMsg) {
        self.handle_script_event(msg);
    }
}

trait PrivateDedicatedWorkerGlobalScopeHelpers {
    fn handle_script_event(self, msg: ScriptMsg);
    fn dispatch_error_to_worker(self, &ErrorEvent);
    fn receive_event(self) -> Result<MixedMessage, RecvError>;
}

impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    #[allow(unsafe_code)]
    fn receive_event(self) -> Result<MixedMessage, RecvError> {
        let scope = WorkerGlobalScopeCast::from_ref(self);
        let worker_port = &self.receiver;
        let devtools_port = scope.devtools_port();

        let sel = Select::new();
        let mut worker_handle = sel.handle(worker_port);
        let mut devtools_handle = sel.handle(devtools_port);
        unsafe {
            worker_handle.add();
            if scope.devtools_sender().is_some() {
                devtools_handle.add();
            }
        }
        let ret = sel.wait();
        if ret == worker_handle.id() {
            Ok(MixedMessage::FromWorker(try!(worker_port.recv())))
        } else if ret == devtools_handle.id() {
            Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
        } else {
            panic!("unexpected select result!")
        }
    }

    fn handle_script_event(self, msg: ScriptMsg) {
        match msg {
            ScriptMsg::DOMMessage(data) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let target = EventTargetCast::from_ref(self);
                let _ar = JSAutoRequest::new(scope.get_cx());
                let _ac = JSAutoCompartment::new(scope.get_cx(), scope.reflector().get_jsobject().get());
                let mut message = RootedValue::new(scope.get_cx(), UndefinedValue());
                data.read(GlobalRef::Worker(scope), message.handle_mut());
                MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle());
            },
            ScriptMsg::RunnableMsg(runnable) => {
                runnable.handler()
            },
            ScriptMsg::RefcountCleanup(addr) => {
                LiveDOMReferences::cleanup(addr);
            }
            ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                scope.handle_fire_timer(timer_id);
            }
            ScriptMsg::CollectReports(reports_chan) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let cx = scope.get_cx();
                let path_seg = format!("url({})", scope.get_url());
                let reports = ScriptTask::get_reports(cx, path_seg);
                reports_chan.send(reports);
            }
            _ => panic!("Unexpected message"),
        }
    }

    fn dispatch_error_to_worker(self, errorevent: &ErrorEvent) {
        let msg = errorevent.Message();
        let file_name = errorevent.Filename();
        let line_num = errorevent.Lineno();
        let col_num = errorevent.Colno();
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerErrorHandler::new(worker, msg, file_name, line_num, col_num))).unwrap();
 }
}

impl<'a> DedicatedWorkerGlobalScopeMethods for &'a DedicatedWorkerGlobalScope {
    // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
    fn PostMessage(self, cx: *mut JSContext, message: HandleValue) -> ErrorResult {
        let data = try!(StructuredCloneData::write(cx, message));
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerMessageHandler::new(worker, data))).unwrap();
        Ok(())
    }

    event_handler!(message, GetOnmessage, SetOnmessage);
}

impl DedicatedWorkerGlobalScopeDerived for EventTarget {
    fn is_dedicatedworkerglobalscope(&self) -> bool {
        match *self.type_id() {
            EventTargetTypeId::WorkerGlobalScope(WorkerGlobalScopeTypeId::DedicatedGlobalScope) => true,
            _ => false
        }
    }
}
<|endoftext|>


<commit_before>#[cfg(unix)]
extern crate unix_socket;

use crate::common::util::*;

#[test]
fn test_output_multi_files_print_all_chars() {
    new_ucmd!()
        .args(&["alpha.txt", "256.txt", "-A", "-n"])
        .succeeds()
        .stdout_only(
            "     1\tabcde$\n     2\tfghij$\n     3\tklmno$\n     4\tpqrst$\n     \
             5\tuvwxyz$\n     6\t^@^A^B^C^D^E^F^G^H^I$\n     \
             7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ \
             !\"#$%&\'()*+,-./0123456789:;\
             <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^\
             BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^V\
             M-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- \
             M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:\
             M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-U\
             M-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-\
             pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?",
        );
}

#[test]
fn test_numbered_lines_no_trailing_newline() {
    new_ucmd!()
        .args(&["nonewline.txt", "alpha.txt", "-n"])
        .succeeds()
        .stdout_only(
            "     1\ttext without a trailing newlineabcde\n     2\tfghij\n     \
             3\tklmno\n     4\tpqrst\n     5\tuvwxyz\n",
        );
}

#[test]
fn test_stdin_show_nonprinting() {
    for same_param in vec!["-v", "--show-nonprinting"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("\t^@\n");
    }
}

#[test]
fn test_stdin_show_tabs() {
    for same_param in vec!["-T", "--show-tabs"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("^I\0\n");
    }
}

#[test]
fn test_stdin_show_ends() {
    for same_param in vec!["-E", "--show-ends"] {
        new_ucmd!()
            .args(&[same_param, "-"])
            .pipe_in("\t\0\n\t")
            .succeeds()
            .stdout_only("\t\0$\n\t");
    }
}

#[test]
fn test_stdin_show_all() {
    for same_param in vec!["-A", "--show-all"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("^I^@$\n");
    }
}

#[test]
fn test_stdin_nonprinting_and_endofline() {
    new_ucmd!()
        .args(&["-e"])
        .pipe_in("\t\0\n")
        .succeeds()
        .stdout_only("\t^@$\n");
}

#[test]
fn test_stdin_nonprinting_and_tabs() {
    new_ucmd!()
        .args(&["-t"])
        .pipe_in("\t\0\n")
        .succeeds()
        .stdout_only("^I^@\n");
}

#[test]
fn test_stdin_squeeze_blank() {
    for same_param in vec!["-s", "--squeeze-blank"] {
        new_ucmd!()
            .arg(same_param)
            .pipe_in("\n\na\n\n\n\n\nb\n\n\n")
            .succeeds()
            .stdout_only("\na\n\nb\n\n");
    }
}

#[test]
fn test_stdin_number_non_blank() {
    for same_param in vec!["-b", "--number-nonblank"] {
        new_ucmd!()
            .arg(same_param)
            .arg("-")
            .pipe_in("\na\nb\n\n\nc")
            .succeeds()
            .stdout_only("\n     1\ta\n     2\tb\n\n\n     3\tc");
    }
}

#[test]
fn test_non_blank_overrides_number() {
    for same_param in vec!["-b", "--number-nonblank"] {
        new_ucmd!()
            .args(&[same_param, "-"])
            .pipe_in("\na\nb\n\n\nc")
            .succeeds()
            .stdout_only("\n     1\ta\n     2\tb\n\n\n     3\tc");
    }
}

#[test]
fn test_squeeze_blank_before_numbering() {
    for same_param in vec!["-s", "--squeeze-blank"] {
        new_ucmd!()
            .args(&[same_param, "-n", "-"])
            .pipe_in("a\n\n\nb")
            .succeeds()
            .stdout_only("     1\ta\n     2\t\n     3\tb");
    }
}

#[test]
#[cfg(foo)]
fn test_domain_socket() {
    use self::tempdir::TempDir;
    use self::unix_socket::UnixListener;
    use std::io::prelude::*;
    use std::thread;

    let dir = TempDir::new("unix_socket").expect("failed to create dir");
    let socket_path = dir.path().join("sock");
    let listener = UnixListener::bind(&socket_path).expect("failed to create socket");

    let thread = thread::spawn(move || {
        let mut stream = listener.accept().expect("failed to accept connection").0;
        stream
            .write_all(b"a\tb")
            .expect("failed to write test data");
    });

    new_ucmd!()
        .args(&[socket_path])
        .succeeds()
        .stdout_only("a\tb");

    thread.join().unwrap();
}
<commit_msg>cat: add a trivial test<commit_after>#[cfg(unix)]
extern crate unix_socket;

use crate::common::util::*;

#[test]
fn test_output_simple() {
    new_ucmd!()
        .args(&["alpha.txt"])
        .succeeds()
        .stdout_only("abcde\nfghij\nklmno\npqrst\nuvwxyz\n");
}

#[test]
fn test_output_multi_files_print_all_chars() {
    new_ucmd!()
        .args(&["alpha.txt", "256.txt", "-A", "-n"])
        .succeeds()
        .stdout_only(
            "     1\tabcde$\n     2\tfghij$\n     3\tklmno$\n     4\tpqrst$\n     \
             5\tuvwxyz$\n     6\t^@^A^B^C^D^E^F^G^H^I$\n     \
             7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ \
             !\"#$%&\'()*+,-./0123456789:;\
             <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^\
             BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^V\
             M-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- \
             M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:\
             M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-U\
             M-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-\
             pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?",
        );
}

#[test]
fn test_numbered_lines_no_trailing_newline() {
    new_ucmd!()
        .args(&["nonewline.txt", "alpha.txt", "-n"])
        .succeeds()
        .stdout_only(
            "     1\ttext without a trailing newlineabcde\n     2\tfghij\n     \
             3\tklmno\n     4\tpqrst\n     5\tuvwxyz\n",
        );
}

#[test]
fn test_stdin_show_nonprinting() {
    for same_param in vec!["-v", "--show-nonprinting"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("\t^@\n");
    }
}

#[test]
fn test_stdin_show_tabs() {
    for same_param in vec!["-T", "--show-tabs"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("^I\0\n");
    }
}

#[test]
fn test_stdin_show_ends() {
    for same_param in vec!["-E", "--show-ends"] {
        new_ucmd!()
            .args(&[same_param, "-"])
            .pipe_in("\t\0\n\t")
            .succeeds()
            .stdout_only("\t\0$\n\t");
    }
}

#[test]
fn test_stdin_show_all() {
    for same_param in vec!["-A", "--show-all"] {
        new_ucmd!()
            .args(&[same_param])
            .pipe_in("\t\0\n")
            .succeeds()
            .stdout_only("^I^@$\n");
    }
}

#[test]
fn test_stdin_nonprinting_and_endofline() {
    new_ucmd!()
        .args(&["-e"])
        .pipe_in("\t\0\n")
        .succeeds()
        .stdout_only("\t^@$\n");
}

#[test]
fn test_stdin_nonprinting_and_tabs() {
    new_ucmd!()
        .args(&["-t"])
        .pipe_in("\t\0\n")
        .succeeds()
        .stdout_only("^I^@\n");
}

#[test]
fn test_stdin_squeeze_blank() {
    for same_param in vec!["-s", "--squeeze-blank"] {
        new_ucmd!()
            .arg(same_param)
            .pipe_in("\n\na\n\n\n\n\nb\n\n\n")
            .succeeds()
            .stdout_only("\na\n\nb\n\n");
    }
}

#[test]
fn test_stdin_number_non_blank() {
    for same_param in vec!["-b", "--number-nonblank"] {
        new_ucmd!()
            .arg(same_param)
            .arg("-")
            .pipe_in("\na\nb\n\n\nc")
            .succeeds()
            .stdout_only("\n     1\ta\n     2\tb\n\n\n     3\tc");
    }
}

#[test]
fn test_non_blank_overrides_number() {
    for same_param in vec!["-b", "--number-nonblank"] {
        new_ucmd!()
            .args(&[same_param, "-"])
            .pipe_in("\na\nb\n\n\nc")
            .succeeds()
            .stdout_only("\n     1\ta\n     2\tb\n\n\n     3\tc");
    }
}

#[test]
fn test_squeeze_blank_before_numbering() {
    for same_param in vec!["-s", "--squeeze-blank"] {
        new_ucmd!()
            .args(&[same_param, "-n", "-"])
            .pipe_in("a\n\n\nb")
            .succeeds()
            .stdout_only("     1\ta\n     2\t\n     3\tb");
    }
}

#[test]
#[cfg(foo)]
fn test_domain_socket() {
    use self::tempdir::TempDir;
    use self::unix_socket::UnixListener;
    use std::io::prelude::*;
    use std::thread;

    let dir = TempDir::new("unix_socket").expect("failed to create dir");
    let socket_path = dir.path().join("sock");
    let listener = UnixListener::bind(&socket_path).expect("failed to create socket");

    let thread = thread::spawn(move || {
        let mut stream = listener.accept().expect("failed to accept connection").0;
        stream
            .write_all(b"a\tb")
            .expect("failed to write test data");
    });

    new_ucmd!()
        .args(&[socket_path])
        .succeeds()
        .stdout_only("a\tb");

    thread.join().unwrap();
}
<|endoftext|>


<commit_before>//! This file tests for the DOC_MARKDOWN lint
//~^ ERROR: you should put `DOC_MARKDOWN` between ticks

#![feature(plugin)]
#![plugin(clippy)]

#![deny(doc_markdown)]

/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
//~^ ERROR: you should put `foo_bar` between ticks
//~| ERROR: you should put `foo::bar` between ticks
/// Markdown is _weird_. I mean _really weird_.  This \_ is ok. So is `_`. But not Foo::some_fun
//~^ ERROR: you should put `Foo::some_fun` between ticks
/// which should be reported only once despite being __doubly bad__.
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn foo_bar() {
}

/// That one tests multiline ticks.
/// ```rust
/// foo_bar FOO_BAR
/// _foo bar_
/// ```
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn multiline_ticks() {
}

/// This _is a test for
/// multiline
/// emphasis_.
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn test_emphasis() {
}

/// This tests units. See also #835.
/// kiB MiB GiB TiB PiB EiB
/// kib Mib Gib Tib Pib Eib
/// kB MB GB TB PB EB
/// kb Mb Gb Tb Pb Eb
/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
/// 32kB 32MB 32GB 32TB 32PB 32EB
/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn test_units() {
}

/// This one checks we don’t try to split unicode codepoints
/// `ß`
/// `ℝ`
/// `💣`
/// `❤`
/// ß_foo
//~^ ERROR: you should put `ß_foo` between ticks
/// ℝ_foo
//~^ ERROR: you should put `ℝ_foo` between ticks
/// 💣_foo
/// ❤_foo
/// foo_ß
//~^ ERROR: you should put `foo_ß` between ticks
/// foo_ℝ
//~^ ERROR: you should put `foo_ℝ` between ticks
/// foo_💣
/// foo_❤
/// [ßdummy textß][foo_ß]
/// [ℝdummy textℝ][foo_ℝ]
/// [💣dummy tex💣t][foo_💣]
/// [❤dummy text❤][foo_❤]
/// [ßdummy textß](foo_ß)
/// [ℝdummy textℝ](foo_ℝ)
/// [💣dummy tex💣t](foo_💣)
/// [❤dummy text❤](foo_❤)
/// [foo_ß]: dummy text
/// [foo_ℝ]: dummy text
/// [foo_💣]: dummy text
/// [foo_❤]: dummy text
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn test_unicode() {
}

/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
//~^ ERROR: you should put `link_with_underscores` between ticks
/// See also [the issue tracker](https://github.com/Manishearth/rust-clippy/search?q=doc_markdown&type=Issues)
/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
/// It can also be [inline_link2].
//~^ ERROR: you should put `inline_link2` between ticks
///
/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
/// [inline_link]: https://foobar
/// [inline_link2]: https://foobar

/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
/// `multiline_ticks` functions.
///
/// expression of the type  `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn main() {
    foo_bar();
    multiline_ticks();
    test_emphasis();
    test_units();
}

/// I am confused by brackets? (`x_y`)
/// I am confused by brackets? (foo `x_y`)
/// I am confused by brackets? (`x_y` foo)
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn issue900() {
}
<commit_msg>Test previously reported false positive<commit_after>//! This file tests for the DOC_MARKDOWN lint
//~^ ERROR: you should put `DOC_MARKDOWN` between ticks

#![feature(plugin)]
#![plugin(clippy)]

#![deny(doc_markdown)]

/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
//~^ ERROR: you should put `foo_bar` between ticks
//~| ERROR: you should put `foo::bar` between ticks
/// Markdown is _weird_. I mean _really weird_.  This \_ is ok. So is `_`. But not Foo::some_fun
//~^ ERROR: you should put `Foo::some_fun` between ticks
/// which should be reported only once despite being __doubly bad__.
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn foo_bar() {
}

/// That one tests multiline ticks.
/// ```rust
/// foo_bar FOO_BAR
/// _foo bar_
/// ```
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn multiline_ticks() {
}

/// This _is a test for
/// multiline
/// emphasis_.
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn test_emphasis() {
}

/// This tests units. See also #835.
/// kiB MiB GiB TiB PiB EiB
/// kib Mib Gib Tib Pib Eib
/// kB MB GB TB PB EB
/// kb Mb Gb Tb Pb Eb
/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
/// 32kB 32MB 32GB 32TB 32PB 32EB
/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn test_units() {
}

/// This one checks we don’t try to split unicode codepoints
/// `ß`
/// `ℝ`
/// `💣`
/// `❤`
/// ß_foo
//~^ ERROR: you should put `ß_foo` between ticks
/// ℝ_foo
//~^ ERROR: you should put `ℝ_foo` between ticks
/// 💣_foo
/// ❤_foo
/// foo_ß
//~^ ERROR: you should put `foo_ß` between ticks
/// foo_ℝ
//~^ ERROR: you should put `foo_ℝ` between ticks
/// foo_💣
/// foo_❤
/// [ßdummy textß][foo_ß]
/// [ℝdummy textℝ][foo_ℝ]
/// [💣dummy tex💣t][foo_💣]
/// [❤dummy text❤][foo_❤]
/// [ßdummy textß](foo_ß)
/// [ℝdummy textℝ](foo_ℝ)
/// [💣dummy tex💣t](foo_💣)
/// [❤dummy text❤](foo_❤)
/// [foo_ß]: dummy text
/// [foo_ℝ]: dummy text
/// [foo_💣]: dummy text
/// [foo_❤]: dummy text
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn test_unicode() {
}

/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
//~^ ERROR: you should put `link_with_underscores` between ticks
/// See also [the issue tracker](https://github.com/Manishearth/rust-clippy/search?q=doc_markdown&type=Issues)
/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
/// It can also be [inline_link2].
//~^ ERROR: you should put `inline_link2` between ticks
///
/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
/// [inline_link]: https://foobar
/// [inline_link2]: https://foobar

/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
/// `multiline_ticks` functions.
///
/// expression of the type  `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn main() {
    foo_bar();
    multiline_ticks();
    test_emphasis();
    test_units();
}

/// I am confused by brackets? (`x_y`)
/// I am confused by brackets? (foo `x_y`)
/// I am confused by brackets? (`x_y` foo)
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn issue900() {
}

/// Diesel queries also have a similar problem to [Iterator][iterator], where
/// /// More talking
/// returning them from a function requires exposing the implementation of that
/// function. The [`helper_types`][helper_types] module exists to help with this,
/// but you might want to hide the return type or have it conditionally change.
/// Boxing can achieve both.
///
/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
/// [helper_types]: ../helper_types/index.html
/// be_sure_we_got_to_the_end_of_it
//~^ ERROR: you should put `be_sure_we_got_to_the_end_of_it` between ticks
fn issue883() {
}
<|endoftext|>


<commit_before>#![crate_name = "uu_printf"]
#![allow(dead_code)]

extern crate itertools;
extern crate uucore;

mod cli;
mod memo;
mod tokenize;

static NAME: &str = "printf";
static VERSION: &str = "0.0.1";
static SHORT_USAGE: &str = "printf: usage: printf [-v var] format [arguments]";
static LONGHELP_LEAD: &str = "printf

 USAGE: printf FORMATSTRING [ARGUMENT]...

 basic anonymous string templating:

 prints format string at least once, repeating as long as there are remaining arguments
 output prints escaped literals in the format string as character literals
 output replaces anonymous fields with the next unused argument, formatted according to the field.

Options:
    --help              display this help and exit
    --version           output version information and exit

";
static LONGHELP_BODY: &str = "
  Prints the , replacing escaped character sequences with character literals
    and substitution field sequences with passed arguments

 literally, with the exception of the below
    escaped character sequences, and the substitution sequences described further down.

  ESCAPE SEQUENCES

  The following escape sequences, organized here in alphabetical order,
  will print the corresponding character literal:

  \"   double quote

  \\\\    backslash

  \\a  alert (BEL)

  \\b  backspace

  \\c  End-of-Input

  \\e  escape

  \\f  form feed

  \\n  new line

  \\r  carriage return

  \\t  horizontal tab

  \\v  vertical tab

  \\NNN byte with value expressed in octal value NNN (1 to 3 digits)
        values greater than 256 will be treated

  \\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits)

  \\uHHHH Unicode (IEC 10646) character with value expressed in hexadecimal value HHHH (4 digits)

  \\uHHHH Unicode character with value expressed in hexadecimal value HHHH (8 digits)

  %% a single %

  SUBSTITUTIONS

  SUBSTITUTION QUICK REFERENCE

  Fields

  %s - string
  %b - string parsed for literals
    second parameter is max length

  %c - char
    no second parameter

  %i or %d - 64-bit integer
  %u - 64 bit unsigned integer
  %x or %X - 64-bit unsigned integer as hex
  %o - 64-bit unsigned integer as octal
    second parameter is min-width, integer
     output below that width is padded with leading zeroes

  %f or %F - decimal floating point value
  %e or %E - scientific notation floating point value
  %g or %G - shorter of specially interpreted decimal or SciNote floating point value.
    second parameter is
      -max places after decimal point for floating point output
      -max number of significant digits for scientific notation output

  parameterizing fields

  examples:

  printf '%4.3i' 7
  has a first parameter of 4
     and a second parameter of 3
  will result in ' 007'

  printf '%.1s' abcde
  has no first parameter
     and a second parameter of 1
  will result in 'a'

  printf '%4c' q
  has a first parameter of 4
     and no second parameter
  will result in  '   q'

  The first parameter of a field is the minimum width to pad the output to
   if the output is less than this absolute value of this width,
   it will be padded with leading spaces, or, if the argument is negative,
   with trailing spaces. the default is zero.

  The second parameter of a field is particular to the output field type.
   defaults can be found in the full substitution help below

  special prefixes to numeric arguments
    0 (e.g. 010) - interpret argument as octal (integer output fields only)
    0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only)
    \' (e.g. \'a) - interpret argument as a character constant

  HOW TO USE SUBSTITUTIONS

  Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a
  particular way. E.g.

      printf 'the letter %X comes before the letter %X' 10 11

  will print

     'the letter A comes before the letter B'

  because the substitution field %X means
  'take an integer argument and write it as a hexadecimal number'

  Passing more arguments than are in the format string will cause the format string to be
   repeated for the remaining substitutions

     printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York

  will print

     'it is 22 F in Portland
      it is 25 F in Boston
      it is 27 F in Boston
     '
  If a format string is printed but there are less arguments remaining
   than there are substitution fields, substitution fields without
   an argument will default to empty strings, or for numeric fields
   the value 0

  AVAILABLE SUBSTITUTIONS

  This program, like GNU coreutils printf,
  interprets a modified subset of the POSIX C printf spec,
  a quick reference to substitutions is below.

   STRING SUBSTITUTIONS
    All string fields have a 'max width' parameter
    %.3s means 'print no more than three characters of the original input'

   %s - string

   %b - escaped string - the string will be checked for any escaped literals from
         the escaped literal list above, and translate them to literal charcters.
         e.g. \\n will be transformed into a newline character.

        One special rule about %b mode is that octal literals are intepreted differently
        In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
        instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
        still be interpreted and not throw a warning, you will have problems if you use this for a
        literal whose code begins with zero, as it will be viewed as in \\0NNN form.)

   CHAR SUBSTITUTIONS
    The character field does not have a secondary parameter.

   %c - a single character

   INTEGER SUBSTITUTIONS
    All integer fields have a 'pad with zero' parameter
    %.4i means an integer which if it is less than 4 digits in length,
    is padded with leading zeros until it is 4 digits in length.

   %d or %i - 64-bit integer

   %u - 64 bit unsigned integer

   %x or %X - 64 bit unsigned integer printed in Hexadecimal (base 16)
            %X instead of %x means to use uppercase letters for 'a' through 'f'

   %o - 64 bit unsigned integer printed in octal (base 8)

   FLOATING POINT SUBSTITUTIONS

    All floating point fields have a 'max decimal places / max significant digits' parameter
    %.10f means a decimal floating point with 7 decimal places past 0
    %.10e means a scientific notation number with 10 significant digits
    %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
          of each's output.

    Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
    double first before being rendered to text. For both implementations do not expect meaningful
    precision past the 18th decimal place. When using a number of decimal places that is 18 or
    higher, you can expect variation in output between GNU coreutils printf and this printf at the
    18th decimal place of +/- 1

   %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
        default. There is not past-double behavior parity with Coreutils printf, values are not
        estimated or adjusted beyond input values.

   %e or %E - floating point value presented in scientific notation
            7 significant digits by default
            %E means use to use uppercase E for the mantissa.

   %g or %G - floating point value presented in the shorter of decimal and scientific notation
            behaves differently from %f and %E, please see posix printf spec for full details,
            some examples of different behavior:

            Sci Note has 6 significant digits by default
            Trailing zeroes are removed
            Instead of being truncated, digit after last is rounded

   Like other behavior in this utility, the design choices of floating point
    behavior in this utility is selected to reproduce in exact
    the behavior of GNU coreutils' printf from an inputs and outputs standpoint.

  USING PARAMETERS
   Most substitution fields can be parameterized using up to 2 numbers that can
   be passed to the field, between the % sign and the field letter.

   The 1st parameter always indicates the minimum width of output, it is useful for creating
     columnar output. Any output that would be less than this minimum width is padded with
     leading spaces
   The 2nd parameter is proceeded by a dot.
   You do not have to use parameters

  SPECIAL FORMS OF INPUT
   For numeric input, the following additional forms of input are accepted besides decimal:

    Octal (only with integer): if the argument begins with a 0 the proceeding characters
      will be interpreted as octal (base 8) for integer fields

    Hexadecimal: if the argument begins with 0x the proceeding characters will be interpreted
      will be interpreted as hex (base 16) for any numeric fields
      for float fields, hexadecimal input results in a precision
      limit (in converting input past the decimal point) of 10^-15

    Character Constant: if the argument begins with a single quote character, the first byte
      of the next character will be interpreted as an 8-bit unsigned integer. If there are
      additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
      is set)

WRITTEN BY :
  Nathan E. Ross, et al. for the uutils project

MORE INFO :
  https://github.com/uutils/coreutils

COPYRIGHT :
  Copyright 2015 uutils project.
  Licensed under the MIT License, please see LICENSE file for details

";

pub fn uumain(args: Vec<String>) -> i32 {
    let location = &args[0];
    if args.len() <= 1 {
        println!(
            "{0}: missing operand\nTry '{0} --help' for more information.",
            location
        );
        return 1;
    }
    let ref formatstr = args[1];

    if formatstr == "--help" {
        print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
    } else if formatstr == "--version" {
        println!("{} {}", NAME, VERSION);
    } else {
        let printf_args = &args[2..];
        memo::Memo::run_all(formatstr, printf_args);
    }
    return 0;
}
<commit_msg>Correct some typos in printf.rs<commit_after>#![crate_name = "uu_printf"]
#![allow(dead_code)]

extern crate itertools;
extern crate uucore;

mod cli;
mod memo;
mod tokenize;

static NAME: &str = "printf";
static VERSION: &str = "0.0.1";
static SHORT_USAGE: &str = "printf: usage: printf [-v var] format [arguments]";
static LONGHELP_LEAD: &str = "printf

 USAGE: printf FORMATSTRING [ARGUMENT]...

 basic anonymous string templating:

 prints format string at least once, repeating as long as there are remaining arguments
 output prints escaped literals in the format string as character literals
 output replaces anonymous fields with the next unused argument, formatted according to the field.

Options:
    --help              display this help and exit
    --version           output version information and exit

";
static LONGHELP_BODY: &str = "
  Prints the , replacing escaped character sequences with character literals
    and substitution field sequences with passed arguments

 literally, with the exception of the below
    escaped character sequences, and the substitution sequences described further down.

  ESCAPE SEQUENCES

  The following escape sequences, organized here in alphabetical order,
  will print the corresponding character literal:

  \"   double quote

  \\\\    backslash

  \\a  alert (BEL)

  \\b  backspace

  \\c  End-of-Input

  \\e  escape

  \\f  form feed

  \\n  new line

  \\r  carriage return

  \\t  horizontal tab

  \\v  vertical tab

  \\NNN byte with value expressed in octal value NNN (1 to 3 digits)
        values greater than 256 will be treated

  \\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits)

  \\uHHHH Unicode (IEC 10646) character with value expressed in hexadecimal value HHHH (4 digits)

  \\uHHHH Unicode character with value expressed in hexadecimal value HHHH (8 digits)

  %% a single %

  SUBSTITUTIONS

  SUBSTITUTION QUICK REFERENCE

  Fields

  %s - string
  %b - string parsed for literals
    second parameter is max length

  %c - char
    no second parameter

  %i or %d - 64-bit integer
  %u - 64 bit unsigned integer
  %x or %X - 64-bit unsigned integer as hex
  %o - 64-bit unsigned integer as octal
    second parameter is min-width, integer
     output below that width is padded with leading zeroes

  %f or %F - decimal floating point value
  %e or %E - scientific notation floating point value
  %g or %G - shorter of specially interpreted decimal or SciNote floating point value.
    second parameter is
      -max places after decimal point for floating point output
      -max number of significant digits for scientific notation output

  parameterizing fields

  examples:

  printf '%4.3i' 7
  has a first parameter of 4
     and a second parameter of 3
  will result in ' 007'

  printf '%.1s' abcde
  has no first parameter
     and a second parameter of 1
  will result in 'a'

  printf '%4c' q
  has a first parameter of 4
     and no second parameter
  will result in  '   q'

  The first parameter of a field is the minimum width to pad the output to
   if the output is less than this absolute value of this width,
   it will be padded with leading spaces, or, if the argument is negative,
   with trailing spaces. the default is zero.

  The second parameter of a field is particular to the output field type.
   defaults can be found in the full substitution help below

  special prefixes to numeric arguments
    0 (e.g. 010) - interpret argument as octal (integer output fields only)
    0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only)
    \' (e.g. \'a) - interpret argument as a character constant

  HOW TO USE SUBSTITUTIONS

  Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a
  particular way. E.g.

      printf 'the letter %X comes before the letter %X' 10 11

  will print

     'the letter A comes before the letter B'

  because the substitution field %X means
  'take an integer argument and write it as a hexadecimal number'

  Passing more arguments than are in the format string will cause the format string to be
   repeated for the remaining substitutions

     printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York

  will print

     'it is 22 F in Portland
      it is 25 F in Boston
      it is 27 F in Boston
     '
  If a format string is printed but there are less arguments remaining
   than there are substitution fields, substitution fields without
   an argument will default to empty strings, or for numeric fields
   the value 0

  AVAILABLE SUBSTITUTIONS

  This program, like GNU coreutils printf,
  interprets a modified subset of the POSIX C printf spec,
  a quick reference to substitutions is below.

   STRING SUBSTITUTIONS
    All string fields have a 'max width' parameter
    %.3s means 'print no more than three characters of the original input'

   %s - string

   %b - escaped string - the string will be checked for any escaped literals from
         the escaped literal list above, and translate them to literal characters.
         e.g. \\n will be transformed into a newline character.

        One special rule about %b mode is that octal literals are interpreted differently
        In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
        instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
        still be interpreted and not throw a warning, you will have problems if you use this for a
        literal whose code begins with zero, as it will be viewed as in \\0NNN form.)

   CHAR SUBSTITUTIONS
    The character field does not have a secondary parameter.

   %c - a single character

   INTEGER SUBSTITUTIONS
    All integer fields have a 'pad with zero' parameter
    %.4i means an integer which if it is less than 4 digits in length,
    is padded with leading zeros until it is 4 digits in length.

   %d or %i - 64-bit integer

   %u - 64 bit unsigned integer

   %x or %X - 64 bit unsigned integer printed in Hexadecimal (base 16)
            %X instead of %x means to use uppercase letters for 'a' through 'f'

   %o - 64 bit unsigned integer printed in octal (base 8)

   FLOATING POINT SUBSTITUTIONS

    All floating point fields have a 'max decimal places / max significant digits' parameter
    %.10f means a decimal floating point with 7 decimal places past 0
    %.10e means a scientific notation number with 10 significant digits
    %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
          of each's output.

    Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
    double first before being rendered to text. For both implementations do not expect meaningful
    precision past the 18th decimal place. When using a number of decimal places that is 18 or
    higher, you can expect variation in output between GNU coreutils printf and this printf at the
    18th decimal place of +/- 1

   %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
        default. There is not past-double behavior parity with Coreutils printf, values are not
        estimated or adjusted beyond input values.

   %e or %E - floating point value presented in scientific notation
            7 significant digits by default
            %E means use to use uppercase E for the mantissa.

   %g or %G - floating point value presented in the shorter of decimal and scientific notation
            behaves differently from %f and %E, please see posix printf spec for full details,
            some examples of different behavior:

            Sci Note has 6 significant digits by default
            Trailing zeroes are removed
            Instead of being truncated, digit after last is rounded

   Like other behavior in this utility, the design choices of floating point
    behavior in this utility is selected to reproduce in exact
    the behavior of GNU coreutils' printf from an inputs and outputs standpoint.

  USING PARAMETERS
   Most substitution fields can be parameterized using up to 2 numbers that can
   be passed to the field, between the % sign and the field letter.

   The 1st parameter always indicates the minimum width of output, it is useful for creating
     columnar output. Any output that would be less than this minimum width is padded with
     leading spaces
   The 2nd parameter is proceeded by a dot.
   You do not have to use parameters

  SPECIAL FORMS OF INPUT
   For numeric input, the following additional forms of input are accepted besides decimal:

    Octal (only with integer): if the argument begins with a 0 the proceeding characters
      will be interpreted as octal (base 8) for integer fields

    Hexadecimal: if the argument begins with 0x the proceeding characters will be interpreted
      will be interpreted as hex (base 16) for any numeric fields
      for float fields, hexadecimal input results in a precision
      limit (in converting input past the decimal point) of 10^-15

    Character Constant: if the argument begins with a single quote character, the first byte
      of the next character will be interpreted as an 8-bit unsigned integer. If there are
      additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
      is set)

WRITTEN BY :
  Nathan E. Ross, et al. for the uutils project

MORE INFO :
  https://github.com/uutils/coreutils

COPYRIGHT :
  Copyright 2015 uutils project.
  Licensed under the MIT License, please see LICENSE file for details

";

pub fn uumain(args: Vec<String>) -> i32 {
    let location = &args[0];
    if args.len() <= 1 {
        println!(
            "{0}: missing operand\nTry '{0} --help' for more information.",
            location
        );
        return 1;
    }
    let ref formatstr = args[1];

    if formatstr == "--help" {
        print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
    } else if formatstr == "--version" {
        println!("{} {}", NAME, VERSION);
    } else {
        let printf_args = &args[2..];
        memo::Memo::run_all(formatstr, printf_args);
    }
    return 0;
}
<|endoftext|>


<commit_before>use middleware::{Middleware, Continue, MiddlewareResult};
use hyper::uri::RequestUri::AbsolutePath;
use request::Request;
use response::Response;
use router::HttpRouter;
use hyper::method::Method;
use hyper::status::StatusCode;
use router::{Matcher, FORMAT_PARAM};

/// A Route is the basic data structure that stores both the path
/// and the handler that gets executed for the route.
/// The path can contain variable pattern such as `user/:userid/invoices`
pub struct Route {
    pub method: Method,
    pub handler: Box<Middleware + Send + Sync + 'static>,
    matcher: Matcher
}

/// A RouteResult is what the router returns when `match_route` is called.
/// It contains the matched `route` and also a `params` property holding
/// a HashMap with the keys being the variable names and the value being the
/// evaluated string
pub struct RouteResult<'a> {
    pub route: &'a Route,
    params: Vec<(String, String)>
}

impl<'a> RouteResult<'a> {
    pub fn param(&self, key: &str) -> &str {
        for &(ref k, ref v) in &self.params {
            if k == &key {
                return &v[..]
            }
        }

        // FIXME: should have a default format
        if key == FORMAT_PARAM { return "" }
        panic!("unknown param: {}", key)
    }
}

/// The Router's job is it to hold routes and to resolve them later against
/// concrete URLs. The router is also a regular middleware and needs to be
/// added to the middleware stack with `server.utilize(router)`.
pub struct Router {
    routes: Vec<Route>,
}

impl Router {
    pub fn new () -> Router {
        Router {
            routes: Vec::new()
        }
    }

    pub fn match_route<'a>(&'a self, method: &Method, path: &str) -> Option<RouteResult<'a>> {
        // Strip off the querystring when matching a route
        let path = path.splitn(2, '?').next().unwrap();

        self.routes
            .iter()
            .find(|item| item.method == *method && item.matcher.is_match(path))
            .map(|route|
                RouteResult {
                    params: extract_params(route, path),
                    route: route
                }
            )
    }
}

fn extract_params(route: &Route, path: &str) -> Vec<(String, String)> {
    match route.matcher.captures(path) {
        Some(captures) => {
            captures.iter_named()
                    .filter_map(|(name, subcap)| {
                        subcap.map(|cap| (name.to_string(), cap.to_string()))
                    })
                    .collect()
        }
        None => vec![]
    }
}

impl HttpRouter for Router {
    fn add_route<M: Into<Matcher>, H: Middleware>(&mut self, method: Method, matcher: M, handler: H) {
        let route = Route {
            matcher: matcher.into(),
            method: method,
            handler: Box::new(handler),
        };

        self.routes.push(route);
    }
}

impl Middleware for Router {
    fn invoke<'a, 'b>(&'a self, req: &mut Request<'b, 'a, 'b>, mut res: Response<'a>)
                        -> MiddlewareResult<'a> {
        debug!("Router::invoke for '{:?}'", req.origin.uri);
        let route_result = match req.origin.uri {
            AbsolutePath(ref url) => self.match_route(&req.origin.method, &**url),
            _ => None
        };
        debug!("route_result.route.path: {:?}", route_result.as_ref().map(|r| r.route.matcher.path()));

        match route_result {
            Some(route_result) => {
                res.set_status(StatusCode::Ok);
                let handler = &route_result.route.handler;
                req.route_result = Some(route_result);
                handler.invoke(req, res)
            },
            None => Ok(Continue(res))
        }
    }
}

#[test]
fn creates_regex_with_captures () {
    let matcher: Matcher = "foo/:uid/bar/:groupid".into();
    let caps = matcher.captures("foo/4711/bar/5490").unwrap();

    assert_eq!(matcher.path(), "foo/:uid/bar/:groupid(\\.:format)?");
    assert_eq!(caps.at(1).unwrap(), "4711");
    assert_eq!(caps.at(2).unwrap(), "5490");

    let matcher: Matcher = "foo/*/:uid/bar/:groupid".into();
    let caps = matcher.captures("foo/test/4711/bar/5490").unwrap();

    assert_eq!(matcher.path(), "foo/*/:uid/bar/:groupid(\\.:format)?");
    assert_eq!(caps.at(1).unwrap(), "4711");
    assert_eq!(caps.at(2).unwrap(), "5490");

    let matcher: Matcher = "foo/**/:uid/bar/:groupid".into();
    let caps = matcher.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(matcher.path(), "foo/**/:uid/bar/:groupid(\\.:format)?");
    assert_eq!(caps.at(1).unwrap(), "4711");
    assert_eq!(caps.at(2).unwrap(), "5490");

    let matcher: Matcher = "foo/**/:format/bar/:groupid".into();
    let caps = matcher.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(matcher.path(), "foo/**/:format/bar/:groupid");
    assert_eq!(caps.name("format").unwrap(), "4711");
    assert_eq!(caps.name("groupid").unwrap(), "5490");
}

#[test]
fn creates_valid_regex_for_routes () {
    let regex1: Matcher = "foo/:uid/bar/:groupid".into();
    let regex2: Matcher = "foo/*/bar".into();
    let regex3: Matcher = "foo/**/bar".into();

    assert_eq!(regex1.is_match("foo/4711/bar/5490"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=true&bar=false"), false);
    assert_eq!(regex1.is_match("foo/4711/bar/test%20spacing"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5281?foo=test%20spacing&bar=false"), true);
    assert_eq!(regex1.is_match("foo/alice/bar/bob"), true);

    assert_eq!(regex2.is_match("foo/4711/bar"), true);
    assert_eq!(regex2.is_match("foo/4711/barr"), false);
    assert_eq!(regex2.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex2.is_match("foo/4711/4712/bar"), false);
    assert_eq!(regex2.is_match("foo/4711/4712/bar?foo=true&bar=false"), false);
    assert_eq!(regex2.is_match("foo/alice/bar/bob"), false);
    assert_eq!(regex2.is_match("foo/alice/bar"), true);

    assert_eq!(regex3.is_match("foo/4711/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar?foo=true&bar=false"), true);
    assert_eq!(regex3.is_match("foo/alice/bar/bob"), false);

    //ensure that this works with commas too
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=1,2,3&bar=false"), false);

    //ensure that this works with hyphens too
    assert_eq!(regex1.is_match("foo/alice-anne/bar/bob-gates"), true);

}

#[test]
fn can_match_var_routes () {
    let route_store = &mut Router::new();

    route_store.add_route(Method::Get, "/foo/:userid", middleware! { "hello from foo" });
    route_store.add_route(Method::Get, "/bar", middleware! { "hello from foo" });
    route_store.add_route(Method::Get, "/file/:format/:file", middleware! { "hello from foo" });

    let route_result = route_store.match_route(&Method::Get, "/foo/4711").unwrap();
    assert_eq!(route_result.param("userid"), "4711");

    let route_result = route_store.match_route(&Method::Get, "/bar/4711");
    assert!(route_result.is_none());

    let route_result = route_store.match_route(&Method::Get, "/foo");
    assert!(route_result.is_none());

    // ensure that this will work with commas too
    let route_result = route_store.match_route(&Method::Get, "/foo/123,456");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("userid"), "123,456");

    // ensure that this will work with spacing too
    let route_result = route_store.match_route(&Method::Get, "/foo/John%20Doe");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("userid"), "John%20Doe");

    // check for optional format param
    let route_result = route_store.match_route(&Method::Get, "/foo/John%20Doe.json");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("userid"), "John%20Doe");
    assert_eq!(route_result.param("format"), "json");

    // ensure format works with queries
    let route_result = route_store.match_route(&Method::Get,
    "/foo/5490,1234.csv?foo=true&bar=false");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    // NOTE: `.param` doesn't cover query params currently
    assert_eq!(route_result.param("userid"), "5490,1234");
    assert_eq!(route_result.param("format"), "csv");

    // ensure format works with no format
    let route_result = route_store.match_route(&Method::Get,
                                               "/foo/5490,1234?foo=true&bar=false").unwrap();

    assert_eq!(route_result.param("format"), "");

    // ensure format works if defined by user
    let route_result = route_store.match_route(&Method::Get, "/file/markdown/something?foo=true");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    // NOTE: `.param` doesn't cover query params currently
    assert_eq!(route_result.param("file"), "something");
    assert_eq!(route_result.param("format"), "markdown");
}

#[test]
fn params_lifetime() {
    let route_store = &mut Router::new();
    let handler = middleware! { "hello from foo" };

    route_store.add_route(Method::Get, "/file/:format/:file", handler);

    let route_result = route_store.match_route(&Method::Get, "/file/txt/manual");
    assert!(route_result.is_some());

    // Ensure two params can live without borrowck problems
    let route_result = route_result.unwrap();
    let format = route_result.param("format");
    let file = route_result.param("file");
    assert_eq!(format, "txt");
    assert_eq!(file, "manual");
}

#[test]
fn regex_path() {
    use regex::Regex;

    let route_store = &mut Router::new();

    let regex = Regex::new("/(foo|bar)").unwrap();
    route_store.add_route(Method::Get, regex, middleware! { "hello from foo" });

    let route_result = route_store.match_route(&Method::Get, "/foo");
    assert!(route_result.is_some());

    let route_result = route_store.match_route(&Method::Get, "/bar");
    assert!(route_result.is_some());

    let route_result = route_store.match_route(&Method::Get, "/bar?foo");
    assert!(route_result.is_some());

    let route_result = route_store.match_route(&Method::Get, "/baz");
    assert!(route_result.is_none());
}

#[test]
fn regex_path_named() {
    use regex::Regex;

    let route_store = &mut Router::new();

    let regex = Regex::new("/(?Pfoo|bar)/b").unwrap();
    route_store.add_route(Method::Get, regex, middleware! { "hello from foo" });

    let route_result = route_store.match_route(&Method::Get, "/foo/b");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("a"), "foo");

    let route_result = route_store.match_route(&Method::Get, "/bar/b");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("a"), "bar");

    let route_result = route_store.match_route(&Method::Get, "/baz/b");
    assert!(route_result.is_none());
}

#[test]
fn ignores_querystring() {
    use regex::Regex;

    let route_store = &mut Router::new();

    let regex = Regex::new("/(?Pfoo|bar)/b").unwrap();
    route_store.add_route(Method::Get, regex, middleware! { "hello from foo" });
    route_store.add_route(Method::Get, "/:foo", middleware! { "hello from foo" });

    // Should ignore the querystring
    let route_result = route_store.match_route(&Method::Get, "/moo?foo");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("foo"), "moo");

    let route_result = route_store.match_route(&Method::Get, "/bar/b?foo");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("a"), "bar");
}
<commit_msg>refactor(router): cleaned up regex routes test<commit_after>use middleware::{Middleware, Continue, MiddlewareResult};
use hyper::uri::RequestUri::AbsolutePath;
use request::Request;
use response::Response;
use router::HttpRouter;
use hyper::method::Method;
use hyper::status::StatusCode;
use router::{Matcher, FORMAT_PARAM};

/// A Route is the basic data structure that stores both the path
/// and the handler that gets executed for the route.
/// The path can contain variable pattern such as `user/:userid/invoices`
pub struct Route {
    pub method: Method,
    pub handler: Box<Middleware + Send + Sync + 'static>,
    matcher: Matcher
}

/// A RouteResult is what the router returns when `match_route` is called.
/// It contains the matched `route` and also a `params` property holding
/// a HashMap with the keys being the variable names and the value being the
/// evaluated string
pub struct RouteResult<'a> {
    pub route: &'a Route,
    params: Vec<(String, String)>
}

impl<'a> RouteResult<'a> {
    pub fn param(&self, key: &str) -> &str {
        for &(ref k, ref v) in &self.params {
            if k == &key {
                return &v[..]
            }
        }

        // FIXME: should have a default format
        if key == FORMAT_PARAM { return "" }
        panic!("unknown param: {}", key)
    }
}

/// The Router's job is it to hold routes and to resolve them later against
/// concrete URLs. The router is also a regular middleware and needs to be
/// added to the middleware stack with `server.utilize(router)`.
pub struct Router {
    routes: Vec<Route>,
}

impl Router {
    pub fn new () -> Router {
        Router {
            routes: Vec::new()
        }
    }

    pub fn match_route<'a>(&'a self, method: &Method, path: &str) -> Option<RouteResult<'a>> {
        // Strip off the querystring when matching a route
        let path = path.splitn(2, '?').next().unwrap();

        self.routes
            .iter()
            .find(|item| item.method == *method && item.matcher.is_match(path))
            .map(|route|
                RouteResult {
                    params: extract_params(route, path),
                    route: route
                }
            )
    }
}

fn extract_params(route: &Route, path: &str) -> Vec<(String, String)> {
    match route.matcher.captures(path) {
        Some(captures) => {
            captures.iter_named()
                    .filter_map(|(name, subcap)| {
                        subcap.map(|cap| (name.to_string(), cap.to_string()))
                    })
                    .collect()
        }
        None => vec![]
    }
}

impl HttpRouter for Router {
    fn add_route<M: Into<Matcher>, H: Middleware>(&mut self, method: Method, matcher: M, handler: H) {
        let route = Route {
            matcher: matcher.into(),
            method: method,
            handler: Box::new(handler),
        };

        self.routes.push(route);
    }
}

impl Middleware for Router {
    fn invoke<'a, 'b>(&'a self, req: &mut Request<'b, 'a, 'b>, mut res: Response<'a>)
                        -> MiddlewareResult<'a> {
        debug!("Router::invoke for '{:?}'", req.origin.uri);
        let route_result = match req.origin.uri {
            AbsolutePath(ref url) => self.match_route(&req.origin.method, &**url),
            _ => None
        };
        debug!("route_result.route.path: {:?}", route_result.as_ref().map(|r| r.route.matcher.path()));

        match route_result {
            Some(route_result) => {
                res.set_status(StatusCode::Ok);
                let handler = &route_result.route.handler;
                req.route_result = Some(route_result);
                handler.invoke(req, res)
            },
            None => Ok(Continue(res))
        }
    }
}

#[test]
fn creates_regex_with_captures () {
    let matcher: Matcher = "foo/:uid/bar/:groupid".into();
    let caps = matcher.captures("foo/4711/bar/5490").unwrap();

    assert_eq!(matcher.path(), "foo/:uid/bar/:groupid(\\.:format)?");
    assert_eq!(caps.at(1).unwrap(), "4711");
    assert_eq!(caps.at(2).unwrap(), "5490");

    let matcher: Matcher = "foo/*/:uid/bar/:groupid".into();
    let caps = matcher.captures("foo/test/4711/bar/5490").unwrap();

    assert_eq!(matcher.path(), "foo/*/:uid/bar/:groupid(\\.:format)?");
    assert_eq!(caps.at(1).unwrap(), "4711");
    assert_eq!(caps.at(2).unwrap(), "5490");

    let matcher: Matcher = "foo/**/:uid/bar/:groupid".into();
    let caps = matcher.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(matcher.path(), "foo/**/:uid/bar/:groupid(\\.:format)?");
    assert_eq!(caps.at(1).unwrap(), "4711");
    assert_eq!(caps.at(2).unwrap(), "5490");

    let matcher: Matcher = "foo/**/:format/bar/:groupid".into();
    let caps = matcher.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(matcher.path(), "foo/**/:format/bar/:groupid");
    assert_eq!(caps.name("format").unwrap(), "4711");
    assert_eq!(caps.name("groupid").unwrap(), "5490");
}

#[test]
fn creates_valid_regex_for_routes () {
    let multi_params: Matcher = "foo/:uid/bar/:groupid".into();

    assert!(multi_params.is_match("foo/4711/bar/5490"));
    assert!(multi_params.is_match("foo/4711/bar/5490?foo=true&bar=false"));
    assert!(!multi_params.is_match("foo/4711/bar"));
    assert!(!multi_params.is_match("foo/4711/bar?foo=true&bar=false"));
    assert!(multi_params.is_match("foo/4711/bar/test%20spacing"));
    assert!(multi_params.is_match("foo/4711/bar/5281?foo=test%20spacing&bar=false"));
    assert!(multi_params.is_match("foo/alice/bar/bob"));

    //ensure that this works with commas too
    assert!(multi_params.is_match("foo/4711/bar/5490,1234"));
    assert!(multi_params.is_match("foo/4711/bar/5490,1234?foo=true&bar=false"));
    assert!(!multi_params.is_match("foo/4711/bar"));
    assert!(!multi_params.is_match("foo/4711/bar?foo=1,2,3&bar=false"));

    //ensure that this works with hyphens too
    assert!(multi_params.is_match("foo/alice-anne/bar/bob-gates"));

    let single_asterisk: Matcher = "foo/*/bar".into();

    assert!(single_asterisk.is_match("foo/4711/bar"));
    assert!(!single_asterisk.is_match("foo/4711/barr"));
    assert!(single_asterisk.is_match("foo/4711/bar?foo=true&bar=false"));
    assert!(!single_asterisk.is_match("foo/4711/4712/bar"));
    assert!(!single_asterisk.is_match("foo/4711/4712/bar?foo=true&bar=false"));
    assert!(!single_asterisk.is_match("foo/alice/bar/bob"));
    assert!(single_asterisk.is_match("foo/alice/bar"));

    let double_asterisk: Matcher = "foo/**/bar".into();

    assert!(double_asterisk.is_match("foo/4711/bar"));
    assert!(double_asterisk.is_match("foo/4711/bar?foo=true&bar=false"));
    assert!(double_asterisk.is_match("foo/4711/4712/bar"));
    assert!(double_asterisk.is_match("foo/4711/4712/bar?foo=true&bar=false"));
    assert!(!double_asterisk.is_match("foo/alice/bar/bob"));
}

#[test]
fn can_match_var_routes () {
    let route_store = &mut Router::new();

    route_store.add_route(Method::Get, "/foo/:userid", middleware! { "hello from foo" });
    route_store.add_route(Method::Get, "/bar", middleware! { "hello from foo" });
    route_store.add_route(Method::Get, "/file/:format/:file", middleware! { "hello from foo" });

    let route_result = route_store.match_route(&Method::Get, "/foo/4711").unwrap();
    assert_eq!(route_result.param("userid"), "4711");

    let route_result = route_store.match_route(&Method::Get, "/bar/4711");
    assert!(route_result.is_none());

    let route_result = route_store.match_route(&Method::Get, "/foo");
    assert!(route_result.is_none());

    // ensure that this will work with commas too
    let route_result = route_store.match_route(&Method::Get, "/foo/123,456");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("userid"), "123,456");

    // ensure that this will work with spacing too
    let route_result = route_store.match_route(&Method::Get, "/foo/John%20Doe");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("userid"), "John%20Doe");

    // check for optional format param
    let route_result = route_store.match_route(&Method::Get, "/foo/John%20Doe.json");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("userid"), "John%20Doe");
    assert_eq!(route_result.param("format"), "json");

    // ensure format works with queries
    let route_result = route_store.match_route(&Method::Get,
    "/foo/5490,1234.csv?foo=true&bar=false");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    // NOTE: `.param` doesn't cover query params currently
    assert_eq!(route_result.param("userid"), "5490,1234");
    assert_eq!(route_result.param("format"), "csv");

    // ensure format works with no format
    let route_result = route_store.match_route(&Method::Get,
                                               "/foo/5490,1234?foo=true&bar=false").unwrap();

    assert_eq!(route_result.param("format"), "");

    // ensure format works if defined by user
    let route_result = route_store.match_route(&Method::Get, "/file/markdown/something?foo=true");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    // NOTE: `.param` doesn't cover query params currently
    assert_eq!(route_result.param("file"), "something");
    assert_eq!(route_result.param("format"), "markdown");
}

#[test]
fn params_lifetime() {
    let route_store = &mut Router::new();
    let handler = middleware! { "hello from foo" };

    route_store.add_route(Method::Get, "/file/:format/:file", handler);

    let route_result = route_store.match_route(&Method::Get, "/file/txt/manual");
    assert!(route_result.is_some());

    // Ensure two params can live without borrowck problems
    let route_result = route_result.unwrap();
    let format = route_result.param("format");
    let file = route_result.param("file");
    assert_eq!(format, "txt");
    assert_eq!(file, "manual");
}

#[test]
fn regex_path() {
    use regex::Regex;

    let route_store = &mut Router::new();

    let regex = Regex::new("/(foo|bar)").unwrap();
    route_store.add_route(Method::Get, regex, middleware! { "hello from foo" });

    let route_result = route_store.match_route(&Method::Get, "/foo");
    assert!(route_result.is_some());

    let route_result = route_store.match_route(&Method::Get, "/bar");
    assert!(route_result.is_some());

    let route_result = route_store.match_route(&Method::Get, "/bar?foo");
    assert!(route_result.is_some());

    let route_result = route_store.match_route(&Method::Get, "/baz");
    assert!(route_result.is_none());
}

#[test]
fn regex_path_named() {
    use regex::Regex;

    let route_store = &mut Router::new();

    let regex = Regex::new("/(?Pfoo|bar)/b").unwrap();
    route_store.add_route(Method::Get, regex, middleware! { "hello from foo" });

    let route_result = route_store.match_route(&Method::Get, "/foo/b");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("a"), "foo");

    let route_result = route_store.match_route(&Method::Get, "/bar/b");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("a"), "bar");

    let route_result = route_store.match_route(&Method::Get, "/baz/b");
    assert!(route_result.is_none());
}

#[test]
fn ignores_querystring() {
    use regex::Regex;

    let route_store = &mut Router::new();

    let regex = Regex::new("/(?Pfoo|bar)/b").unwrap();
    route_store.add_route(Method::Get, regex, middleware! { "hello from foo" });
    route_store.add_route(Method::Get, "/:foo", middleware! { "hello from foo" });

    // Should ignore the querystring
    let route_result = route_store.match_route(&Method::Get, "/moo?foo");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("foo"), "moo");

    let route_result = route_store.match_route(&Method::Get, "/bar/b?foo");
    assert!(route_result.is_some());

    let route_result = route_result.unwrap();
    assert_eq!(route_result.param("a"), "bar");
}
<|endoftext|>