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

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebSocketBinding;
use dom::bindings::codegen::Bindings::WebSocketBinding::WebSocketMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::codegen::InheritTypes::EventCast;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::error::Error::{InvalidAccess, Syntax};
use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::js::Root;
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::USVString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::reflect_dom_object;
use dom::closeevent::CloseEvent;
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use script_task::Runnable;
use script_task::ScriptMsg;
use std::cell::{Cell, RefCell};
use std::borrow::ToOwned;
use util::str::DOMString;

use websocket::Message;
use websocket::ws::sender::Sender as Sender_Object;
use websocket::client::sender::Sender;
use websocket::client::receiver::Receiver;
use websocket::stream::WebSocketStream;
use websocket::client::request::Url;
use websocket::Client;

#[derive(JSTraceable, PartialEq, Copy, Clone)]
enum WebSocketRequestState {
    Connecting = 0,
    Open = 1,
    Closing = 2,
    Closed = 3,
}

no_jsmanaged_fields!(Sender<WebSocketStream>);
no_jsmanaged_fields!(Receiver<WebSocketStream>);

#[dom_struct]
pub struct WebSocket {
    eventtarget: EventTarget,
    url: Url,
    global: GlobalField,
    ready_state: Cell<WebSocketRequestState>,
    sender: RefCell<Option<Sender<WebSocketStream>>>,
    receiver: RefCell<Option<Receiver<WebSocketStream>>>,
    failed: Cell<bool>, //Flag to tell if websocket was closed due to failure
    full: Cell<bool>, //Flag to tell if websocket queue is full
    clean_close: Cell<bool>, //Flag to tell if the websocket closed cleanly (not due to full or fail)
    code: Cell<u16>, //Closing code
    reason: DOMRefCell<DOMString>, //Closing reason
    data: DOMRefCell<DOMString>, //Data from send - TODO: Remove after buffer is added.
    sendCloseFrame: Cell<bool>
}

fn parse_web_socket_url(url_str: &str) -> Fallible<(Url, String, u16, String, bool)> {
    // https://html.spec.whatwg.org/multipage/#parse-a-websocket-url's-components
    // Steps 1 and 2
    let parsed_url = Url::parse(url_str);
    let parsed_url = match parsed_url {
        Ok(parsed_url) => parsed_url,
        Err(_) => return Err(Error::Syntax),
    };

    // Step 4
    if parsed_url.fragment != None {
        return Err(Error::Syntax);
    }

    // Steps 3 and 5
    let secure = match parsed_url.scheme.as_ref() {
        "ws" => false,
        "wss" => true,
        _ => return Err(Error::Syntax), // step 3
    };

    let host = parsed_url.host().unwrap().serialize(); // Step 6
    let port = parsed_url.port_or_default().unwrap(); // Steps 7 and 8
    let mut resource = parsed_url.path().unwrap().connect("/"); // Step 9
    if resource.is_empty() {
        resource = "/".to_owned(); // Step 10
    }

    // Step 11
    if let Some(ref query) = parsed_url.query {
        resource.push('?');
        resource.push_str(query);
    }

    // Step 12
    // FIXME remove parsed_url once it's no longer used in WebSocket::new
    Ok((parsed_url, host, port, resource, secure))
}

impl WebSocket {
    pub fn new_inherited(global: GlobalRef, url: Url) -> WebSocket {
        WebSocket {
            eventtarget: EventTarget::new_inherited(EventTargetTypeId::WebSocket),
            url: url,
            global: GlobalField::from_rooted(&global),
            ready_state: Cell::new(WebSocketRequestState::Connecting),
            failed: Cell::new(false),
            sender: RefCell::new(None),
            receiver: RefCell::new(None),
            full: Cell::new(false),
            clean_close: Cell::new(true),
            code: Cell::new(0),
            reason: DOMRefCell::new("".to_owned()),
            data: DOMRefCell::new("".to_owned()),
            sendCloseFrame: Cell::new(false)
        }

    }

    pub fn new(global: GlobalRef, url: DOMString) -> Fallible<Root<WebSocket>> {
        // Step 1.
        // FIXME extract the right variables once Client::connect
        // implementation is fixed to follow the RFC 6455 properly.
        let (url, _, _, _, _) = try!(parse_web_socket_url(&url));

        /*TODO: This constructor is only a prototype, it does not accomplish the specs
          defined here:
          http://html.spec.whatwg.org
          The remaining 8 items must be satisfied.
          TODO: This constructor should be responsible for spawning a thread for the
          receive loop after ws.r().Open() - See comment
        */
        let ws = reflect_dom_object(box WebSocket::new_inherited(global, url.clone()),
                                    global,
                                    WebSocketBinding::Wrap);

        // TODO Client::connect does not conform to RFC 6455
        // see https://github.com/cyderize/rust-websocket/issues/38
        let request = match Client::connect(url) {
            Ok(request) => request,
            Err(_) => {
                let global_root = ws.r().global.root();
                let address = Trusted::new(global_root.r().get_cx(), ws.r(), global_root.r().script_chan().clone());
                let task = box WebSocketTaskHandler::new(address, WebSocketTask::Close);
                global_root.r().script_chan().send(ScriptMsg::RunnableMsg(task)).unwrap();
                return Ok(ws);
            }
        };
        let response = request.send().unwrap();
        response.validate().unwrap();

        let (temp_sender, temp_receiver) = response.begin().split();
        *ws.r().sender.borrow_mut() = Some(temp_sender);
        *ws.r().receiver.borrow_mut() = Some(temp_receiver);

        //Create everything necessary for starting the open asynchronous task, then begin the task.
        let global_root = ws.r().global.root();
        let addr: Trusted<WebSocket> =
            Trusted::new(global_root.r().get_cx(), ws.r(), global_root.r().script_chan().clone());
        let open_task = box WebSocketTaskHandler::new(addr, WebSocketTask::ConnectionEstablished);
        global_root.r().script_chan().send(ScriptMsg::RunnableMsg(open_task)).unwrap();
        //TODO: Spawn thread here for receive loop
        /*TODO: Add receive loop here and make new thread run this
          Receive is an infinite loop "similiar" the one shown here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop however does need to follow the spec. These are outlined here
          under "WebSocket message has been received" items 1-5:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop also needs to dispatch an asynchronous event as stated here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: When the receive loop receives a close message from the server,
          it confirms the websocket is now closed. This requires the close event
          to be fired (dispatch_close fires the close event - see implementation below)
        */
        Ok(ws)
    }

    pub fn Constructor(global: GlobalRef, url: DOMString) -> Fallible<Root<WebSocket>> {
        WebSocket::new(global, url)
    }
}

impl<'a> WebSocketMethods for &'a WebSocket {
    event_handler!(open, GetOnopen, SetOnopen);
    event_handler!(close, GetOnclose, SetOnclose);
    event_handler!(error, GetOnerror, SetOnerror);

    fn Url(self) -> DOMString {
        self.url.serialize()
    }

    fn ReadyState(self) -> u16 {
        self.ready_state.get() as u16
    }

    fn Send(self, data: Option<USVString>)-> Fallible<()>{
        /*TODO: This is not up to spec see http://html.spec.whatwg.org/multipage/comms.html search for
                "If argument is a string"
          TODO: Need to buffer data
          TODO: bufferedAmount attribute returns the size of the buffer in bytes -
                this is a required attribute defined in the websocket.webidl file
          TODO: The send function needs to flag when full by using the following
          self.full.set(true). This needs to be done when the buffer is full
        */
        let mut other_sender = self.sender.borrow_mut();
        let my_sender = other_sender.as_mut().unwrap();
        if self.sendCloseFrame.get() { //TODO: Also check if the buffer is full
            self.sendCloseFrame.set(false);
            let _ = my_sender.send_message(Message::Close(None));
            return Ok(());
        }
        let _ = my_sender.send_message(Message::Text(data.unwrap().0));
        return Ok(())
    }

    fn Close(self, code: Option<u16>, reason: Option<USVString>) -> Fallible<()>{
        if let Some(code) = code {
            //Check code is NOT 1000 NOR in the range of 3000-4999 (inclusive)
            if  code != 1000 && (code < 3000 || code > 4999) {
                return Err(Error::InvalidAccess);
            }
        }
        if let Some(ref reason) = reason {
            if reason.0.as_bytes().len() > 123 { //reason cannot be larger than 123 bytes
                return Err(Error::Syntax);
            }
        }

        match self.ready_state.get() {
            WebSocketRequestState::Closing | WebSocketRequestState::Closed  => {} //Do nothing
            WebSocketRequestState::Connecting => { //Connection is not yet established
                /*By setting the state to closing, the open function
                  will abort connecting the websocket*/
                self.ready_state.set(WebSocketRequestState::Closing);
                self.failed.set(true);
                self.sendCloseFrame.set(true);
                //Dispatch send task to send close frame
                //TODO: Sending here is just empty string, though no string is really needed. Another send, empty
                //      send, could be used.
                let _ = self.Send(None);
                //Note: After sending the close message, the receive loop confirms a close message from the server and
                //      must fire a close event
            }
            WebSocketRequestState::Open => {
                //Closing handshake not started - still in open
                //Start the closing by setting the code and reason if they exist
                if let Some(code) = code {
                    self.code.set(code);
                }
                if let Some(reason) = reason {
                    *self.reason.borrow_mut() = reason.0;
                }
                self.ready_state.set(WebSocketRequestState::Closing);
                self.sendCloseFrame.set(true);
                //Dispatch send task to send close frame
                let _ = self.Send(None);
                //Note: After sending the close message, the receive loop confirms a close message from the server and
                //      must fire a close event
            }
        }
        Ok(()) //Return Ok
    }
}

pub enum WebSocketTask {
    /// Task queued when *the WebSocket connection is established*.
    ConnectionEstablished,
    Close,
}

pub struct WebSocketTaskHandler {
    addr: Trusted<WebSocket>,
    task: WebSocketTask,
}

impl WebSocketTaskHandler {
    pub fn new(addr: Trusted<WebSocket>, task: WebSocketTask) -> WebSocketTaskHandler {
        WebSocketTaskHandler {
            addr: addr,
            task: task,
        }
    }

    fn connection_established(&self) {
        /*TODO: Items 1, 3, 4, & 5 under "WebSocket connection is established" as specified here:
          https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol
        */
        let ws = self.addr.root();

        // Step 1: Protocols.

        // Step 2.
        ws.ready_state.set(WebSocketRequestState::Open);

        // Step 3: Extensions.
        // Step 4: Protocols.
        // Step 5: Cookies.

        // Step 6.
        let global = ws.global.root();
        let event = Event::new(global.r(), "open".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable);
        event.fire(EventTargetCast::from_ref(ws.r()));
    }

    fn dispatch_close(&self) {
        let ws = self.addr.root();
        let ws = ws.r();
        let global = ws.global.root();
        ws.ready_state.set(WebSocketRequestState::Closed);
        //If failed or full, fire error event
        if ws.failed.get() || ws.full.get() {
            ws.failed.set(false);
            ws.full.set(false);
            //A Bad close
            ws.clean_close.set(false);
            let event = Event::new(global.r(),
                                   "error".to_owned(),
                                   EventBubbles::DoesNotBubble,
                                   EventCancelable::Cancelable);
            let target = EventTargetCast::from_ref(ws);
            event.r().fire(target);
        }
        let rsn = ws.reason.borrow();
        let rsn_clone = rsn.clone();
        /*In addition, we also have to fire a close even if error event fired
         https://html.spec.whatwg.org/multipage/#closeWebSocket
        */
        let close_event = CloseEvent::new(global.r(),
                                          "close".to_owned(),
                                          EventBubbles::DoesNotBubble,
                                          EventCancelable::NotCancelable,
                                          ws.clean_close.get(),
                                          ws.code.get(),
                                          rsn_clone);
        let target = EventTargetCast::from_ref(ws);
        let event = EventCast::from_ref(close_event.r());
        event.fire(target);
    }
}

impl Runnable for WebSocketTaskHandler {
    fn handler(self: Box<WebSocketTaskHandler>) {
        match self.task {
            WebSocketTask::ConnectionEstablished => {
                self.connection_established();
            }
            WebSocketTask::Close => {
                self.dispatch_close();
            }
        }
    }
}

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

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebSocketBinding;
use dom::bindings::codegen::Bindings::WebSocketBinding::WebSocketMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::codegen::InheritTypes::EventCast;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::error::Error::{InvalidAccess, Syntax};
use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::js::Root;
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::USVString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::reflect_dom_object;
use dom::closeevent::CloseEvent;
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use script_task::Runnable;
use script_task::ScriptMsg;
use std::cell::{Cell, RefCell};
use std::borrow::ToOwned;
use util::str::DOMString;

use websocket::Message;
use websocket::ws::sender::Sender as Sender_Object;
use websocket::client::sender::Sender;
use websocket::client::receiver::Receiver;
use websocket::stream::WebSocketStream;
use websocket::client::request::Url;
use websocket::Client;
use websocket::header::Origin;

#[derive(JSTraceable, PartialEq, Copy, Clone)]
enum WebSocketRequestState {
    Connecting = 0,
    Open = 1,
    Closing = 2,
    Closed = 3,
}

no_jsmanaged_fields!(Sender<WebSocketStream>);
no_jsmanaged_fields!(Receiver<WebSocketStream>);

#[dom_struct]
pub struct WebSocket {
    eventtarget: EventTarget,
    url: Url,
    global: GlobalField,
    ready_state: Cell<WebSocketRequestState>,
    sender: RefCell<Option<Sender<WebSocketStream>>>,
    receiver: RefCell<Option<Receiver<WebSocketStream>>>,
    failed: Cell<bool>, //Flag to tell if websocket was closed due to failure
    full: Cell<bool>, //Flag to tell if websocket queue is full
    clean_close: Cell<bool>, //Flag to tell if the websocket closed cleanly (not due to full or fail)
    code: Cell<u16>, //Closing code
    reason: DOMRefCell<DOMString>, //Closing reason
    data: DOMRefCell<DOMString>, //Data from send - TODO: Remove after buffer is added.
    sendCloseFrame: Cell<bool>
}

fn parse_web_socket_url(url_str: &str) -> Fallible<(Url, String, u16, String, bool)> {
    // https://html.spec.whatwg.org/multipage/#parse-a-websocket-url's-components
    // Steps 1 and 2
    let parsed_url = Url::parse(url_str);
    let parsed_url = match parsed_url {
        Ok(parsed_url) => parsed_url,
        Err(_) => return Err(Error::Syntax),
    };

    // Step 4
    if parsed_url.fragment != None {
        return Err(Error::Syntax);
    }

    // Steps 3 and 5
    let secure = match parsed_url.scheme.as_ref() {
        "ws" => false,
        "wss" => true,
        _ => return Err(Error::Syntax), // step 3
    };

    let host = parsed_url.host().unwrap().serialize(); // Step 6
    let port = parsed_url.port_or_default().unwrap(); // Steps 7 and 8
    let mut resource = parsed_url.path().unwrap().connect("/"); // Step 9
    if resource.is_empty() {
        resource = "/".to_owned(); // Step 10
    }

    // Step 11
    if let Some(ref query) = parsed_url.query {
        resource.push('?');
        resource.push_str(query);
    }

    // Step 12
    // FIXME remove parsed_url once it's no longer used in WebSocket::new
    Ok((parsed_url, host, port, resource, secure))
}

impl WebSocket {
    pub fn new_inherited(global: GlobalRef, url: Url) -> WebSocket {
        WebSocket {
            eventtarget: EventTarget::new_inherited(EventTargetTypeId::WebSocket),
            url: url,
            global: GlobalField::from_rooted(&global),
            ready_state: Cell::new(WebSocketRequestState::Connecting),
            failed: Cell::new(false),
            sender: RefCell::new(None),
            receiver: RefCell::new(None),
            full: Cell::new(false),
            clean_close: Cell::new(true),
            code: Cell::new(0),
            reason: DOMRefCell::new("".to_owned()),
            data: DOMRefCell::new("".to_owned()),
            sendCloseFrame: Cell::new(false)
        }

    }

    pub fn new(global: GlobalRef, url: DOMString) -> Fallible<Root<WebSocket>> {
        // Step 1.
        // FIXME extract the right variables once Client::connect
        // implementation is fixed to follow the RFC 6455 properly.
        let (url, _, _, _, _) = try!(parse_web_socket_url(&url));

        /*TODO: This constructor is only a prototype, it does not accomplish the specs
          defined here:
          http://html.spec.whatwg.org
          The remaining 8 items must be satisfied.
          TODO: This constructor should be responsible for spawning a thread for the
          receive loop after ws.r().Open() - See comment
        */
        let ws = reflect_dom_object(box WebSocket::new_inherited(global, url.clone()),
                                    global,
                                    WebSocketBinding::Wrap);

        // TODO Client::connect does not conform to RFC 6455
        // see https://github.com/cyderize/rust-websocket/issues/38
        let mut request = match Client::connect(url) {
            Ok(request) => request,
            Err(_) => {
                let global_root = ws.r().global.root();
                let address = Trusted::new(global_root.r().get_cx(), ws.r(), global_root.r().script_chan().clone());
                let task = box WebSocketTaskHandler::new(address, WebSocketTask::Close);
                global_root.r().script_chan().send(ScriptMsg::RunnableMsg(task)).unwrap();
                return Ok(ws);
            }
        };
        request.headers.set(Origin(global.get_url().serialize()));
        let response = request.send().unwrap();
        response.validate().unwrap();

        let (temp_sender, temp_receiver) = response.begin().split();
        *ws.r().sender.borrow_mut() = Some(temp_sender);
        *ws.r().receiver.borrow_mut() = Some(temp_receiver);

        //Create everything necessary for starting the open asynchronous task, then begin the task.
        let global_root = ws.r().global.root();
        let addr: Trusted<WebSocket> =
            Trusted::new(global_root.r().get_cx(), ws.r(), global_root.r().script_chan().clone());
        let open_task = box WebSocketTaskHandler::new(addr, WebSocketTask::ConnectionEstablished);
        global_root.r().script_chan().send(ScriptMsg::RunnableMsg(open_task)).unwrap();
        //TODO: Spawn thread here for receive loop
        /*TODO: Add receive loop here and make new thread run this
          Receive is an infinite loop "similiar" the one shown here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop however does need to follow the spec. These are outlined here
          under "WebSocket message has been received" items 1-5:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop also needs to dispatch an asynchronous event as stated here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: When the receive loop receives a close message from the server,
          it confirms the websocket is now closed. This requires the close event
          to be fired (dispatch_close fires the close event - see implementation below)
        */
        Ok(ws)
    }

    pub fn Constructor(global: GlobalRef, url: DOMString) -> Fallible<Root<WebSocket>> {
        WebSocket::new(global, url)
    }
}

impl<'a> WebSocketMethods for &'a WebSocket {
    event_handler!(open, GetOnopen, SetOnopen);
    event_handler!(close, GetOnclose, SetOnclose);
    event_handler!(error, GetOnerror, SetOnerror);

    fn Url(self) -> DOMString {
        self.url.serialize()
    }

    fn ReadyState(self) -> u16 {
        self.ready_state.get() as u16
    }

    fn Send(self, data: Option<USVString>)-> Fallible<()>{
        /*TODO: This is not up to spec see http://html.spec.whatwg.org/multipage/comms.html search for
                "If argument is a string"
          TODO: Need to buffer data
          TODO: bufferedAmount attribute returns the size of the buffer in bytes -
                this is a required attribute defined in the websocket.webidl file
          TODO: The send function needs to flag when full by using the following
          self.full.set(true). This needs to be done when the buffer is full
        */
        let mut other_sender = self.sender.borrow_mut();
        let my_sender = other_sender.as_mut().unwrap();
        if self.sendCloseFrame.get() { //TODO: Also check if the buffer is full
            self.sendCloseFrame.set(false);
            let _ = my_sender.send_message(Message::Close(None));
            return Ok(());
        }
        let _ = my_sender.send_message(Message::Text(data.unwrap().0));
        return Ok(())
    }

    fn Close(self, code: Option<u16>, reason: Option<USVString>) -> Fallible<()>{
        if let Some(code) = code {
            //Check code is NOT 1000 NOR in the range of 3000-4999 (inclusive)
            if  code != 1000 && (code < 3000 || code > 4999) {
                return Err(Error::InvalidAccess);
            }
        }
        if let Some(ref reason) = reason {
            if reason.0.as_bytes().len() > 123 { //reason cannot be larger than 123 bytes
                return Err(Error::Syntax);
            }
        }

        match self.ready_state.get() {
            WebSocketRequestState::Closing | WebSocketRequestState::Closed  => {} //Do nothing
            WebSocketRequestState::Connecting => { //Connection is not yet established
                /*By setting the state to closing, the open function
                  will abort connecting the websocket*/
                self.ready_state.set(WebSocketRequestState::Closing);
                self.failed.set(true);
                self.sendCloseFrame.set(true);
                //Dispatch send task to send close frame
                //TODO: Sending here is just empty string, though no string is really needed. Another send, empty
                //      send, could be used.
                let _ = self.Send(None);
                //Note: After sending the close message, the receive loop confirms a close message from the server and
                //      must fire a close event
            }
            WebSocketRequestState::Open => {
                //Closing handshake not started - still in open
                //Start the closing by setting the code and reason if they exist
                if let Some(code) = code {
                    self.code.set(code);
                }
                if let Some(reason) = reason {
                    *self.reason.borrow_mut() = reason.0;
                }
                self.ready_state.set(WebSocketRequestState::Closing);
                self.sendCloseFrame.set(true);
                //Dispatch send task to send close frame
                let _ = self.Send(None);
                //Note: After sending the close message, the receive loop confirms a close message from the server and
                //      must fire a close event
            }
        }
        Ok(()) //Return Ok
    }
}

pub enum WebSocketTask {
    /// Task queued when *the WebSocket connection is established*.
    ConnectionEstablished,
    Close,
}

pub struct WebSocketTaskHandler {
    addr: Trusted<WebSocket>,
    task: WebSocketTask,
}

impl WebSocketTaskHandler {
    pub fn new(addr: Trusted<WebSocket>, task: WebSocketTask) -> WebSocketTaskHandler {
        WebSocketTaskHandler {
            addr: addr,
            task: task,
        }
    }

    fn connection_established(&self) {
        /*TODO: Items 1, 3, 4, & 5 under "WebSocket connection is established" as specified here:
          https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol
        */
        let ws = self.addr.root();

        // Step 1: Protocols.

        // Step 2.
        ws.ready_state.set(WebSocketRequestState::Open);

        // Step 3: Extensions.
        // Step 4: Protocols.
        // Step 5: Cookies.

        // Step 6.
        let global = ws.global.root();
        let event = Event::new(global.r(), "open".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable);
        event.fire(EventTargetCast::from_ref(ws.r()));
    }

    fn dispatch_close(&self) {
        let ws = self.addr.root();
        let ws = ws.r();
        let global = ws.global.root();
        ws.ready_state.set(WebSocketRequestState::Closed);
        //If failed or full, fire error event
        if ws.failed.get() || ws.full.get() {
            ws.failed.set(false);
            ws.full.set(false);
            //A Bad close
            ws.clean_close.set(false);
            let event = Event::new(global.r(),
                                   "error".to_owned(),
                                   EventBubbles::DoesNotBubble,
                                   EventCancelable::Cancelable);
            let target = EventTargetCast::from_ref(ws);
            event.r().fire(target);
        }
        let rsn = ws.reason.borrow();
        let rsn_clone = rsn.clone();
        /*In addition, we also have to fire a close even if error event fired
         https://html.spec.whatwg.org/multipage/#closeWebSocket
        */
        let close_event = CloseEvent::new(global.r(),
                                          "close".to_owned(),
                                          EventBubbles::DoesNotBubble,
                                          EventCancelable::NotCancelable,
                                          ws.clean_close.get(),
                                          ws.code.get(),
                                          rsn_clone);
        let target = EventTargetCast::from_ref(ws);
        let event = EventCast::from_ref(close_event.r());
        event.fire(target);
    }
}

impl Runnable for WebSocketTaskHandler {
    fn handler(self: Box<WebSocketTaskHandler>) {
        match self.task {
            WebSocketTask::ConnectionEstablished => {
                self.connection_established();
            }
            WebSocketTask::Close => {
                self.dispatch_close();
            }
        }
    }
}

<|endoftext|>


<commit_before>use crate::core::compiler::{Compilation, CompileKind, Doctest, UnitOutput};
use crate::core::shell::Verbosity;
use crate::core::{TargetKind, Workspace};
use crate::ops;
use crate::util::errors::CargoResult;
use crate::util::{add_path_args, CargoTestError, Config, Test};
use cargo_util::ProcessError;
use std::ffi::OsString;

pub struct TestOptions {
    pub compile_opts: ops::CompileOptions,
    pub no_run: bool,
    pub no_fail_fast: bool,
}

pub fn run_tests(
    ws: &Workspace<'_>,
    options: &TestOptions,
    test_args: &[&str],
) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        return Ok(None);
    }
    let (test, mut errors) = run_unit_tests(ws.config(), options, test_args, &compilation)?;

    // If we have an error and want to fail fast, then return.
    if !errors.is_empty() && !options.no_fail_fast {
        return Ok(Some(CargoTestError::new(test, errors)));
    }

    let (doctest, docerrors) = run_doc_tests(ws, options, test_args, &compilation)?;
    let test = if docerrors.is_empty() { test } else { doctest };
    errors.extend(docerrors);
    if errors.is_empty() {
        Ok(None)
    } else {
        Ok(Some(CargoTestError::new(test, errors)))
    }
}

pub fn run_benches(
    ws: &Workspace<'_>,
    options: &TestOptions,
    args: &[&str],
) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        return Ok(None);
    }

    let mut args = args.to_vec();
    args.push("--bench");

    let (test, errors) = run_unit_tests(ws.config(), options, &args, &compilation)?;

    match errors.len() {
        0 => Ok(None),
        _ => Ok(Some(CargoTestError::new(test, errors))),
    }
}

fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult<Compilation<'a>> {
    let mut compilation = ops::compile(ws, &options.compile_opts)?;
    compilation.tests.sort();
    Ok(compilation)
}

/// Runs the unit and integration tests of a package.
fn run_unit_tests(
    config: &Config,
    options: &TestOptions,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(Test, Vec<ProcessError>)> {
    let cwd = config.cwd();
    let mut errors = Vec::new();

    for UnitOutput {
        unit,
        path,
        script_meta,
    } in compilation.tests.iter()
    {
        let test = unit.target.name().to_string();

        let test_path = unit.target.src_path().path().unwrap();
        let exe_display = if let TargetKind::Test = unit.target.kind() {
            format!(
                "{} ({})",
                test_path
                    .strip_prefix(unit.pkg.root())
                    .unwrap_or(test_path)
                    .display(),
                path.strip_prefix(cwd).unwrap_or(path).display()
            )
        } else {
            format!(
                "unittests ({})",
                path.strip_prefix(cwd).unwrap_or(path).display()
            )
        };

        let mut cmd = compilation.target_process(path, unit.kind, &unit.pkg, *script_meta)?;
        cmd.args(test_args);
        if unit.target.harness() && config.shell().verbosity() == Verbosity::Quiet {
            cmd.arg("--quiet");
        }
        config
            .shell()
            .concise(|shell| shell.status("Running", &exe_display))?;
        config
            .shell()
            .verbose(|shell| shell.status("Running", &cmd))?;

        let result = cmd.exec();

        match result {
            Err(e) => {
                let e = e.downcast::<ProcessError>()?;
                errors.push((
                    unit.target.kind().clone(),
                    test.clone(),
                    unit.pkg.name().to_string(),
                    e,
                ));
                if !options.no_fail_fast {
                    break;
                }
            }
            Ok(()) => {}
        }
    }

    if errors.len() == 1 {
        let (kind, name, pkg_name, e) = errors.pop().unwrap();
        Ok((
            Test::UnitTest {
                kind,
                name,
                pkg_name,
            },
            vec![e],
        ))
    } else {
        Ok((
            Test::Multiple,
            errors.into_iter().map(|(_, _, _, e)| e).collect(),
        ))
    }
}

fn run_doc_tests(
    ws: &Workspace<'_>,
    options: &TestOptions,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(Test, Vec<ProcessError>)> {
    let config = ws.config();
    let mut errors = Vec::new();
    let doctest_xcompile = config.cli_unstable().doctest_xcompile;
    let doctest_in_workspace = config.cli_unstable().doctest_in_workspace;

    for doctest_info in &compilation.to_doc_test {
        let Doctest {
            args,
            unstable_opts,
            unit,
            linker,
            script_meta,
        } = doctest_info;

        if !doctest_xcompile {
            match unit.kind {
                CompileKind::Host => {}
                CompileKind::Target(target) => {
                    if target.short_name() != compilation.host {
                        // Skip doctests, -Zdoctest-xcompile not enabled.
                        continue;
                    }
                }
            }
        }

        config.shell().status("Doc-tests", unit.target.name())?;
        let mut p = compilation.rustdoc_process(unit, *script_meta)?;
        p.arg("--crate-name").arg(&unit.target.crate_name());
        p.arg("--test");

        if doctest_in_workspace {
            add_path_args(ws, unit, &mut p);
            // FIXME(swatinem): remove the `unstable-options` once rustdoc stabilizes the `test-run-directory` option
            p.arg("-Z").arg("unstable-options");
            p.arg("--test-run-directory")
                .arg(unit.pkg.root().to_path_buf());
        } else {
            p.arg(unit.target.src_path().path().unwrap());
        }

        if doctest_xcompile {
            if let CompileKind::Target(target) = unit.kind {
                // use `rustc_target()` to properly handle JSON target paths
                p.arg("--target").arg(target.rustc_target());
            }
            p.arg("-Zunstable-options");
            p.arg("--enable-per-target-ignores");
            if let Some((runtool, runtool_args)) = compilation.target_runner(unit.kind) {
                p.arg("--runtool").arg(runtool);
                for arg in runtool_args {
                    p.arg("--runtool-arg").arg(arg);
                }
            }
            if let Some(linker) = linker {
                let mut joined = OsString::from("linker=");
                joined.push(linker);
                p.arg("-C").arg(joined);
            }
        }

        for &rust_dep in &[
            &compilation.deps_output[&unit.kind],
            &compilation.deps_output[&CompileKind::Host],
        ] {
            let mut arg = OsString::from("dependency=");
            arg.push(rust_dep);
            p.arg("-L").arg(arg);
        }

        for native_dep in compilation.native_dirs.iter() {
            p.arg("-L").arg(native_dep);
        }

        for arg in test_args {
            p.arg("--test-args").arg(arg);
        }

        if config.shell().verbosity() == Verbosity::Quiet {
            p.arg("--test-args").arg("--quiet");
        }

        p.args(args);

        if *unstable_opts {
            p.arg("-Zunstable-options");
        }

        config
            .shell()
            .verbose(|shell| shell.status("Running", p.to_string()))?;
        if let Err(e) = p.exec() {
            let e = e.downcast::<ProcessError>()?;
            errors.push(e);
            if !options.no_fail_fast {
                return Ok((Test::Doc, errors));
            }
        }
    }
    Ok((Test::Doc, errors))
}
<commit_msg>Auto merge of #10051 - gilescope:one-waffer-thin-alloc-less, r=Eh2406<commit_after>use crate::core::compiler::{Compilation, CompileKind, Doctest, UnitOutput};
use crate::core::shell::Verbosity;
use crate::core::{TargetKind, Workspace};
use crate::ops;
use crate::util::errors::CargoResult;
use crate::util::{add_path_args, CargoTestError, Config, Test};
use cargo_util::ProcessError;
use std::ffi::OsString;

pub struct TestOptions {
    pub compile_opts: ops::CompileOptions,
    pub no_run: bool,
    pub no_fail_fast: bool,
}

pub fn run_tests(
    ws: &Workspace<'_>,
    options: &TestOptions,
    test_args: &[&str],
) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        return Ok(None);
    }
    let (test, mut errors) = run_unit_tests(ws.config(), options, test_args, &compilation)?;

    // If we have an error and want to fail fast, then return.
    if !errors.is_empty() && !options.no_fail_fast {
        return Ok(Some(CargoTestError::new(test, errors)));
    }

    let (doctest, docerrors) = run_doc_tests(ws, options, test_args, &compilation)?;
    let test = if docerrors.is_empty() { test } else { doctest };
    errors.extend(docerrors);
    if errors.is_empty() {
        Ok(None)
    } else {
        Ok(Some(CargoTestError::new(test, errors)))
    }
}

pub fn run_benches(
    ws: &Workspace<'_>,
    options: &TestOptions,
    args: &[&str],
) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        return Ok(None);
    }

    let mut args = args.to_vec();
    args.push("--bench");

    let (test, errors) = run_unit_tests(ws.config(), options, &args, &compilation)?;

    match errors.len() {
        0 => Ok(None),
        _ => Ok(Some(CargoTestError::new(test, errors))),
    }
}

fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult<Compilation<'a>> {
    let mut compilation = ops::compile(ws, &options.compile_opts)?;
    compilation.tests.sort();
    Ok(compilation)
}

/// Runs the unit and integration tests of a package.
fn run_unit_tests(
    config: &Config,
    options: &TestOptions,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(Test, Vec<ProcessError>)> {
    let cwd = config.cwd();
    let mut errors = Vec::new();

    for UnitOutput {
        unit,
        path,
        script_meta,
    } in compilation.tests.iter()
    {
        let test_path = unit.target.src_path().path().unwrap();
        let exe_display = if let TargetKind::Test = unit.target.kind() {
            format!(
                "{} ({})",
                test_path
                    .strip_prefix(unit.pkg.root())
                    .unwrap_or(test_path)
                    .display(),
                path.strip_prefix(cwd).unwrap_or(path).display()
            )
        } else {
            format!(
                "unittests ({})",
                path.strip_prefix(cwd).unwrap_or(path).display()
            )
        };

        let mut cmd = compilation.target_process(path, unit.kind, &unit.pkg, *script_meta)?;
        cmd.args(test_args);
        if unit.target.harness() && config.shell().verbosity() == Verbosity::Quiet {
            cmd.arg("--quiet");
        }
        config
            .shell()
            .concise(|shell| shell.status("Running", &exe_display))?;
        config
            .shell()
            .verbose(|shell| shell.status("Running", &cmd))?;

        let result = cmd.exec();

        if let Err(e) = result {
            let e = e.downcast::<ProcessError>()?;
            errors.push((
                unit.target.kind().clone(),
                unit.target.name().to_string(),
                unit.pkg.name().to_string(),
                e,
            ));
            if !options.no_fail_fast {
                break;
            }
        }
    }

    if errors.len() == 1 {
        let (kind, name, pkg_name, e) = errors.pop().unwrap();
        Ok((
            Test::UnitTest {
                kind,
                name,
                pkg_name,
            },
            vec![e],
        ))
    } else {
        Ok((
            Test::Multiple,
            errors.into_iter().map(|(_, _, _, e)| e).collect(),
        ))
    }
}

fn run_doc_tests(
    ws: &Workspace<'_>,
    options: &TestOptions,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(Test, Vec<ProcessError>)> {
    let config = ws.config();
    let mut errors = Vec::new();
    let doctest_xcompile = config.cli_unstable().doctest_xcompile;
    let doctest_in_workspace = config.cli_unstable().doctest_in_workspace;

    for doctest_info in &compilation.to_doc_test {
        let Doctest {
            args,
            unstable_opts,
            unit,
            linker,
            script_meta,
        } = doctest_info;

        if !doctest_xcompile {
            match unit.kind {
                CompileKind::Host => {}
                CompileKind::Target(target) => {
                    if target.short_name() != compilation.host {
                        // Skip doctests, -Zdoctest-xcompile not enabled.
                        continue;
                    }
                }
            }
        }

        config.shell().status("Doc-tests", unit.target.name())?;
        let mut p = compilation.rustdoc_process(unit, *script_meta)?;
        p.arg("--crate-name").arg(&unit.target.crate_name());
        p.arg("--test");

        if doctest_in_workspace {
            add_path_args(ws, unit, &mut p);
            // FIXME(swatinem): remove the `unstable-options` once rustdoc stabilizes the `test-run-directory` option
            p.arg("-Z").arg("unstable-options");
            p.arg("--test-run-directory")
                .arg(unit.pkg.root().to_path_buf());
        } else {
            p.arg(unit.target.src_path().path().unwrap());
        }

        if doctest_xcompile {
            if let CompileKind::Target(target) = unit.kind {
                // use `rustc_target()` to properly handle JSON target paths
                p.arg("--target").arg(target.rustc_target());
            }
            p.arg("-Zunstable-options");
            p.arg("--enable-per-target-ignores");
            if let Some((runtool, runtool_args)) = compilation.target_runner(unit.kind) {
                p.arg("--runtool").arg(runtool);
                for arg in runtool_args {
                    p.arg("--runtool-arg").arg(arg);
                }
            }
            if let Some(linker) = linker {
                let mut joined = OsString::from("linker=");
                joined.push(linker);
                p.arg("-C").arg(joined);
            }
        }

        for &rust_dep in &[
            &compilation.deps_output[&unit.kind],
            &compilation.deps_output[&CompileKind::Host],
        ] {
            let mut arg = OsString::from("dependency=");
            arg.push(rust_dep);
            p.arg("-L").arg(arg);
        }

        for native_dep in compilation.native_dirs.iter() {
            p.arg("-L").arg(native_dep);
        }

        for arg in test_args {
            p.arg("--test-args").arg(arg);
        }

        if config.shell().verbosity() == Verbosity::Quiet {
            p.arg("--test-args").arg("--quiet");
        }

        p.args(args);

        if *unstable_opts {
            p.arg("-Zunstable-options");
        }

        config
            .shell()
            .verbose(|shell| shell.status("Running", p.to_string()))?;
        if let Err(e) = p.exec() {
            let e = e.downcast::<ProcessError>()?;
            errors.push(e);
            if !options.no_fail_fast {
                return Ok((Test::Doc, errors));
            }
        }
    }
    Ok((Test::Doc, errors))
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: Apache-2.0

extern crate glob;

use std::cell::RefCell;
use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

use glob::{MatchOptions, Pattern};

//================================================
// Commands
//================================================

thread_local! {
    /// The errors encountered by the build script while executing commands.
    static COMMAND_ERRORS: RefCell<HashMap<String, Vec<String>>> = RefCell::default();
}

/// Adds an error encountered by the build script while executing a command.
fn add_command_error(name: &str, path: &str, arguments: &[&str], message: String) {
    COMMAND_ERRORS.with(|e| {
        e.borrow_mut()
            .entry(name.into())
            .or_insert_with(Vec::new)
            .push(format!(
                "couldn't execute `{} {}` (path={}) ({})",
                name,
                arguments.join(" "),
                path,
                message,
            ))
    });
}

/// A struct that prints the errors encountered by the build script while
/// executing commands when dropped (unless explictly discarded).
///
/// This is handy because we only want to print these errors when the build
/// script fails to link to an instance of `libclang`. For example, if
/// `llvm-config` couldn't be executed but an instance of `libclang` was found
/// anyway we don't want to pollute the build output with irrelevant errors.
#[derive(Default)]
pub struct CommandErrorPrinter {
    discard: bool,
}

impl CommandErrorPrinter {
    pub fn discard(mut self) {
        self.discard = true;
    }
}

impl Drop for CommandErrorPrinter {
    fn drop(&mut self) {
        if self.discard {
            return;
        }

        let errors = COMMAND_ERRORS.with(|e| e.borrow().clone());

        if let Some(errors) = errors.get("llvm-config") {
            println!(
                "cargo:warning=could not execute `llvm-config` one or more \
                times, if the LLVM_CONFIG_PATH environment variable is set to \
                a full path to valid `llvm-config` executable it will be used \
                to try to find an instance of `libclang` on your system: {}",
                errors
                    .iter()
                    .map(|e| format!("\"{}\"", e))
                    .collect::<Vec<_>>()
                    .join("\n  "),
            )
        }

        if let Some(errors) = errors.get("xcode-select") {
            println!(
                "cargo:warning=could not execute `xcode-select` one or more \
                times, if a valid instance of this executable is on your PATH \
                it will be used to try to find an instance of `libclang` on \
                your system: {}",
                errors
                    .iter()
                    .map(|e| format!("\"{}\"", e))
                    .collect::<Vec<_>>()
                    .join("\n  "),
            )
        }
    }
}

/// Executes a command and returns the `stdout` output if the command was
/// successfully executed (errors are added to `COMMAND_ERRORS`).
fn run_command(name: &str, path: &str, arguments: &[&str]) -> Option<String> {
    let output = match Command::new(path).args(arguments).output() {
        Ok(output) => output,
        Err(error) => {
            let message = format!("error: {}", error);
            add_command_error(name, path, arguments, message);
            return None;
        }
    };

    if output.status.success() {
        Some(String::from_utf8_lossy(&output.stdout).into_owned())
    } else {
        let message = format!("exit code: {}", output.status);
        add_command_error(name, path, arguments, message);
        None
    }
}

/// Executes the `llvm-config` command and returns the `stdout` output if the
/// command was successfully executed (errors are added to `COMMAND_ERRORS`).
pub fn run_llvm_config(arguments: &[&str]) -> Option<String> {
    let path = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into());
    run_command("llvm-config", &path, arguments)
}

/// Executes the `xcode-select` command and returns the `stdout` output if the
/// command was successfully executed (errors are added to `COMMAND_ERRORS`).
pub fn run_xcode_select(arguments: &[&str]) -> Option<String> {
    run_command("xcode-select", "xcode-select", arguments)
}

//================================================
// Search Directories
//================================================

/// `libclang` directory patterns for Haiku.
const DIRECTORIES_HAIKU: &[&str] = &[
    "/boot/system/lib",
    "/boot/system/develop/lib",
    "/boot/system/non-packaged/lib",
    "/boot/system/non-packaged/develop/lib",
    "/boot/home/config/non-packaged/lib",
    "/boot/home/config/non-packaged/develop/lib",
];

/// `libclang` directory patterns for Linux (and FreeBSD).
const DIRECTORIES_LINUX: &[&str] = &[
    "/usr/lib*",
    "/usr/lib*/*",
    "/usr/lib*/*/*",
    "/usr/local/lib*",
    "/usr/local/lib*/*",
    "/usr/local/lib*/*/*",
    "/usr/local/llvm*/lib*",
];

/// `libclang` directory patterns for macOS.
const DIRECTORIES_MACOS: &[&str] = &[
    "/usr/local/opt/llvm*/lib",
    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
    "/Library/Developer/CommandLineTools/usr/lib",
    "/usr/local/opt/llvm*/lib/llvm*/lib",
];

/// `libclang` directory patterns for Windows.
const DIRECTORIES_WINDOWS: &[&str] = &[
    "C:\\LLVM\\lib",
    "C:\\Program Files*\\LLVM\\lib",
    "C:\\MSYS*\\MinGW*\\lib",
    // LLVM + Clang can be installed as a component of Visual Studio.
    // https://github.com/KyleMayes/clang-sys/issues/121
    "C:\\Program Files*\\Microsoft Visual Studio\\*\\BuildTools\\VC\\Tools\\Llvm\\**\\bin",
    // LLVM + Clang can be installed using Scoop (https://scoop.sh).
    // Other Windows package managers install LLVM + Clang to previously listed
    // system-wide directories.
    "C:\\Users\\*\\scoop\\apps\\llvm\\current\\bin",
];

//================================================
// Searching
//================================================

/// Finds the files in a directory that match one or more filename glob patterns
/// and returns the paths to and filenames of those files.
fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
    // Escape the specified directory in case it contains characters that have
    // special meaning in glob patterns (e.g., `[` or `]`).
    let directory = Pattern::escape(directory.to_str().unwrap());
    let directory = Path::new(&directory);

    // Join the escaped directory to the filename glob patterns to obtain
    // complete glob patterns for the files being searched for.
    let paths = filenames
        .iter()
        .map(|f| directory.join(f).to_str().unwrap().to_owned());

    // Prevent wildcards from matching path separators to ensure that the search
    // is limited to the specified directory.
    let mut options = MatchOptions::new();
    options.require_literal_separator = true;

    paths
        .map(|p| glob::glob_with(&p, options))
        .filter_map(Result::ok)
        .flatten()
        .filter_map(|p| {
            let path = p.ok()?;
            let filename = path.file_name()?.to_str().unwrap();

            // The `libclang_shared` library has been renamed to `libclang-cpp`
            // in Clang 10. This can cause instances of this library (e.g.,
            // `libclang-cpp.so.10`) to be matched by patterns looking for
            // instances of `libclang`.
            if filename.contains("-cpp.") {
                return None;
            }

            Some((directory.to_owned(), filename.into()))
        })
        .collect::<Vec<_>>()
}

/// Finds the files in a directory (and any relevant sibling directories) that
/// match one or more filename glob patterns and returns the paths to and
/// filenames of those files.
fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
    let mut results = search_directory(directory, filenames);

    // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory
    // while `libclang.lib` is usually found in the LLVM `lib` directory. To
    // keep things consistent with other platforms, only LLVM `lib` directories
    // are included in the backup search directory globs so we need to search
    // the LLVM `bin` directory here.
    if cfg!(target_os = "windows") && directory.ends_with("lib") {
        let sibling = directory.parent().unwrap().join("bin");
        results.extend(search_directory(&sibling, filenames).into_iter());
    }

    results
}

/// Finds the `libclang` static or dynamic libraries matching one or more
/// filename glob patterns and returns the paths to and filenames of those files.
pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<(PathBuf, String)> {
    // Search only the path indicated by the relevant environment variable
    // (e.g., `LIBCLANG_PATH`) if it is set.
    if let Ok(path) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) {
        // Check if the path is a matching file.
        if let Some(parent) = path.parent() {
            let filename = path.file_name().unwrap().to_str().unwrap();
            let libraries = search_directories(parent, filenames);
            if libraries.iter().any(|(_, f)| f == filename) {
                return vec![(parent.into(), filename.into())];
            }
        }

        // Check if the path is directory containing a matching file.
        return search_directories(&path, filenames);
    }

    let mut found = vec![];

    // Search the `bin` and `lib` directories in the directory returned by
    // `llvm-config --prefix`.
    if let Some(output) = run_llvm_config(&["--prefix"]) {
        let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
        found.extend(search_directories(&directory.join("bin"), filenames));
        found.extend(search_directories(&directory.join("lib"), filenames));
        found.extend(search_directories(&directory.join("lib64"), filenames));
    }

    // Search the toolchain directory in the directory returned by
    // `xcode-select --print-path`.
    if cfg!(target_os = "macos") {
        if let Some(output) = run_xcode_select(&["--print-path"]) {
            let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
            let directory = directory.join("Toolchains/XcodeDefault.xctoolchain/usr/lib");
            found.extend(search_directories(&directory, filenames));
        }
    }

    // Search the directories in the `LD_LIBRARY_PATH` environment variable.
    if let Ok(path) = env::var("LD_LIBRARY_PATH") {
        for directory in env::split_paths(&path) {
            found.extend(search_directories(&directory, filenames));
        }
    }

    // Determine the `libclang` directory patterns.
    let directories = if cfg!(target_os = "haiku") {
        DIRECTORIES_HAIKU
    } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
        DIRECTORIES_LINUX
    } else if cfg!(target_os = "macos") {
        DIRECTORIES_MACOS
    } else if cfg!(target_os = "windows") {
        DIRECTORIES_WINDOWS
    } else {
        &[]
    };

    // Search the directories provided by the `libclang` directory patterns.
    let mut options = MatchOptions::new();
    options.case_sensitive = false;
    options.require_literal_separator = true;
    for directory in directories.iter().rev() {
        if let Ok(directories) = glob::glob_with(directory, options) {
            for directory in directories.filter_map(Result::ok).filter(|p| p.is_dir()) {
                found.extend(search_directories(&directory, filenames));
            }
        }
    }

    found
}
<commit_msg>Support illumos / omnios during libclang search (#141)<commit_after>// SPDX-License-Identifier: Apache-2.0

extern crate glob;

use std::cell::RefCell;
use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

use glob::{MatchOptions, Pattern};

//================================================
// Commands
//================================================

thread_local! {
    /// The errors encountered by the build script while executing commands.
    static COMMAND_ERRORS: RefCell<HashMap<String, Vec<String>>> = RefCell::default();
}

/// Adds an error encountered by the build script while executing a command.
fn add_command_error(name: &str, path: &str, arguments: &[&str], message: String) {
    COMMAND_ERRORS.with(|e| {
        e.borrow_mut()
            .entry(name.into())
            .or_insert_with(Vec::new)
            .push(format!(
                "couldn't execute `{} {}` (path={}) ({})",
                name,
                arguments.join(" "),
                path,
                message,
            ))
    });
}

/// A struct that prints the errors encountered by the build script while
/// executing commands when dropped (unless explictly discarded).
///
/// This is handy because we only want to print these errors when the build
/// script fails to link to an instance of `libclang`. For example, if
/// `llvm-config` couldn't be executed but an instance of `libclang` was found
/// anyway we don't want to pollute the build output with irrelevant errors.
#[derive(Default)]
pub struct CommandErrorPrinter {
    discard: bool,
}

impl CommandErrorPrinter {
    pub fn discard(mut self) {
        self.discard = true;
    }
}

impl Drop for CommandErrorPrinter {
    fn drop(&mut self) {
        if self.discard {
            return;
        }

        let errors = COMMAND_ERRORS.with(|e| e.borrow().clone());

        if let Some(errors) = errors.get("llvm-config") {
            println!(
                "cargo:warning=could not execute `llvm-config` one or more \
                times, if the LLVM_CONFIG_PATH environment variable is set to \
                a full path to valid `llvm-config` executable it will be used \
                to try to find an instance of `libclang` on your system: {}",
                errors
                    .iter()
                    .map(|e| format!("\"{}\"", e))
                    .collect::<Vec<_>>()
                    .join("\n  "),
            )
        }

        if let Some(errors) = errors.get("xcode-select") {
            println!(
                "cargo:warning=could not execute `xcode-select` one or more \
                times, if a valid instance of this executable is on your PATH \
                it will be used to try to find an instance of `libclang` on \
                your system: {}",
                errors
                    .iter()
                    .map(|e| format!("\"{}\"", e))
                    .collect::<Vec<_>>()
                    .join("\n  "),
            )
        }
    }
}

/// Executes a command and returns the `stdout` output if the command was
/// successfully executed (errors are added to `COMMAND_ERRORS`).
fn run_command(name: &str, path: &str, arguments: &[&str]) -> Option<String> {
    let output = match Command::new(path).args(arguments).output() {
        Ok(output) => output,
        Err(error) => {
            let message = format!("error: {}", error);
            add_command_error(name, path, arguments, message);
            return None;
        }
    };

    if output.status.success() {
        Some(String::from_utf8_lossy(&output.stdout).into_owned())
    } else {
        let message = format!("exit code: {}", output.status);
        add_command_error(name, path, arguments, message);
        None
    }
}

/// Executes the `llvm-config` command and returns the `stdout` output if the
/// command was successfully executed (errors are added to `COMMAND_ERRORS`).
pub fn run_llvm_config(arguments: &[&str]) -> Option<String> {
    let path = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into());
    run_command("llvm-config", &path, arguments)
}

/// Executes the `xcode-select` command and returns the `stdout` output if the
/// command was successfully executed (errors are added to `COMMAND_ERRORS`).
pub fn run_xcode_select(arguments: &[&str]) -> Option<String> {
    run_command("xcode-select", "xcode-select", arguments)
}

//================================================
// Search Directories
//================================================

/// `libclang` directory patterns for Haiku.
const DIRECTORIES_HAIKU: &[&str] = &[
    "/boot/system/lib",
    "/boot/system/develop/lib",
    "/boot/system/non-packaged/lib",
    "/boot/system/non-packaged/develop/lib",
    "/boot/home/config/non-packaged/lib",
    "/boot/home/config/non-packaged/develop/lib",
];

/// `libclang` directory patterns for Linux (and FreeBSD).
const DIRECTORIES_LINUX: &[&str] = &[
    "/usr/lib*",
    "/usr/lib*/*",
    "/usr/lib*/*/*",
    "/usr/local/lib*",
    "/usr/local/lib*/*",
    "/usr/local/lib*/*/*",
    "/usr/local/llvm*/lib*",
];

/// `libclang` directory patterns for macOS.
const DIRECTORIES_MACOS: &[&str] = &[
    "/usr/local/opt/llvm*/lib",
    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
    "/Library/Developer/CommandLineTools/usr/lib",
    "/usr/local/opt/llvm*/lib/llvm*/lib",
];

/// `libclang` directory patterns for Windows.
const DIRECTORIES_WINDOWS: &[&str] = &[
    "C:\\LLVM\\lib",
    "C:\\Program Files*\\LLVM\\lib",
    "C:\\MSYS*\\MinGW*\\lib",
    // LLVM + Clang can be installed as a component of Visual Studio.
    // https://github.com/KyleMayes/clang-sys/issues/121
    "C:\\Program Files*\\Microsoft Visual Studio\\*\\BuildTools\\VC\\Tools\\Llvm\\**\\bin",
    // LLVM + Clang can be installed using Scoop (https://scoop.sh).
    // Other Windows package managers install LLVM + Clang to previously listed
    // system-wide directories.
    "C:\\Users\\*\\scoop\\apps\\llvm\\current\\bin",
];

/// `libclang` directory patterns for illumos
const DIRECTORIES_ILLUMOS: &[&str] = &[
    "/opt/ooce/clang-*/lib",
    "/opt/ooce/llvm-*/lib",
];

//================================================
// Searching
//================================================

/// Finds the files in a directory that match one or more filename glob patterns
/// and returns the paths to and filenames of those files.
fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
    // Escape the specified directory in case it contains characters that have
    // special meaning in glob patterns (e.g., `[` or `]`).
    let directory = Pattern::escape(directory.to_str().unwrap());
    let directory = Path::new(&directory);

    // Join the escaped directory to the filename glob patterns to obtain
    // complete glob patterns for the files being searched for.
    let paths = filenames
        .iter()
        .map(|f| directory.join(f).to_str().unwrap().to_owned());

    // Prevent wildcards from matching path separators to ensure that the search
    // is limited to the specified directory.
    let mut options = MatchOptions::new();
    options.require_literal_separator = true;

    paths
        .map(|p| glob::glob_with(&p, options))
        .filter_map(Result::ok)
        .flatten()
        .filter_map(|p| {
            let path = p.ok()?;
            let filename = path.file_name()?.to_str().unwrap();

            // The `libclang_shared` library has been renamed to `libclang-cpp`
            // in Clang 10. This can cause instances of this library (e.g.,
            // `libclang-cpp.so.10`) to be matched by patterns looking for
            // instances of `libclang`.
            if filename.contains("-cpp.") {
                return None;
            }

            Some((directory.to_owned(), filename.into()))
        })
        .collect::<Vec<_>>()
}

/// Finds the files in a directory (and any relevant sibling directories) that
/// match one or more filename glob patterns and returns the paths to and
/// filenames of those files.
fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
    let mut results = search_directory(directory, filenames);

    // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory
    // while `libclang.lib` is usually found in the LLVM `lib` directory. To
    // keep things consistent with other platforms, only LLVM `lib` directories
    // are included in the backup search directory globs so we need to search
    // the LLVM `bin` directory here.
    if cfg!(target_os = "windows") && directory.ends_with("lib") {
        let sibling = directory.parent().unwrap().join("bin");
        results.extend(search_directory(&sibling, filenames).into_iter());
    }

    results
}

/// Finds the `libclang` static or dynamic libraries matching one or more
/// filename glob patterns and returns the paths to and filenames of those files.
pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<(PathBuf, String)> {
    // Search only the path indicated by the relevant environment variable
    // (e.g., `LIBCLANG_PATH`) if it is set.
    if let Ok(path) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) {
        // Check if the path is a matching file.
        if let Some(parent) = path.parent() {
            let filename = path.file_name().unwrap().to_str().unwrap();
            let libraries = search_directories(parent, filenames);
            if libraries.iter().any(|(_, f)| f == filename) {
                return vec![(parent.into(), filename.into())];
            }
        }

        // Check if the path is directory containing a matching file.
        return search_directories(&path, filenames);
    }

    let mut found = vec![];

    // Search the `bin` and `lib` directories in the directory returned by
    // `llvm-config --prefix`.
    if let Some(output) = run_llvm_config(&["--prefix"]) {
        let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
        found.extend(search_directories(&directory.join("bin"), filenames));
        found.extend(search_directories(&directory.join("lib"), filenames));
        found.extend(search_directories(&directory.join("lib64"), filenames));
    }

    // Search the toolchain directory in the directory returned by
    // `xcode-select --print-path`.
    if cfg!(target_os = "macos") {
        if let Some(output) = run_xcode_select(&["--print-path"]) {
            let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
            let directory = directory.join("Toolchains/XcodeDefault.xctoolchain/usr/lib");
            found.extend(search_directories(&directory, filenames));
        }
    }

    // Search the directories in the `LD_LIBRARY_PATH` environment variable.
    if let Ok(path) = env::var("LD_LIBRARY_PATH") {
        for directory in env::split_paths(&path) {
            found.extend(search_directories(&directory, filenames));
        }
    }

    // Determine the `libclang` directory patterns.
    let directories = if cfg!(target_os = "haiku") {
        DIRECTORIES_HAIKU
    } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
        DIRECTORIES_LINUX
    } else if cfg!(target_os = "macos") {
        DIRECTORIES_MACOS
    } else if cfg!(target_os = "windows") {
        DIRECTORIES_WINDOWS
    } else if cfg!(target_os = "illumos") {
        DIRECTORIES_ILLUMOS
    } else {
        &[]
    };

    // Search the directories provided by the `libclang` directory patterns.
    let mut options = MatchOptions::new();
    options.case_sensitive = false;
    options.require_literal_separator = true;
    for directory in directories.iter().rev() {
        if let Ok(directories) = glob::glob_with(directory, options) {
            for directory in directories.filter_map(Result::ok).filter(|p| p.is_dir()) {
                found.extend(search_directories(&directory, filenames));
            }
        }
    }

    found
}
<|endoftext|>


<commit_before>use curl::easy::{Easy, List};
use config::Config;

pub struct InoReaderClient<'a> {
    client: Easy,
    app_id: &'a String,
    app_key: &'a String,
    app_token: &'a String,
}

impl<'a> InoReaderClient<'a> {
    pub fn new(conf: &Config) -> InoReaderClient {
        let mut client = InoReaderClient {
            client: Easy::new(),
            app_id: &conf.inoreader_appid,
            app_token: &conf.inoreader_token,
            app_key: &conf.inoreader_appkey,
        };
        client.set_headers();
        client
    }

    pub fn get_user_info(&mut self) {
        let api_endpoint = "https://www.inoreader.com/reader/api/0/user-info";

        &self.client.url(api_endpoint).unwrap();
        &self.client.perform().unwrap();

        println!("{}", &self.client.response_code().unwrap());
    }

    pub fn get_subscribe_list(&mut self) {
        let api_endpoint = "https://www.inoreader.com/reader/api/0/subscription/list";
        &self.client.url(api_endpoint).unwrap();
        &self.client.perform().unwrap();

        println!("{}", &self.client.response_code().unwrap());
    }

    fn set_headers(&mut self) {
        let mut header_list = List::new();

        let mut app_id: String = "AppId: ".to_owned();
        app_id.push_str(&self.app_id);
        header_list.append(&app_id).unwrap();

        let mut app_key: String = "AppKey: ".to_owned();
        app_key.push_str(&self.app_key);
        header_list.append(&app_key).unwrap();

        let mut app_token: String = "Authorization: GoogleLogin auth=".to_owned();
        app_token.push_str(&self.app_token);
        header_list.append(&app_token).unwrap();

        &self.client.http_headers(header_list).unwrap();
    }
}<commit_msg>реализована функция для запроса данных от сервера<commit_after>use curl::easy::{Easy, List};
use config::Config;

pub struct InoReaderClient<'a> {
    client: Easy,
    app_id: &'a String,
    app_key: &'a String,
    app_token: &'a String,
    endpoint: &'a String
}

impl<'a> InoReaderClient<'a> {
    pub fn new(conf: &Config) -> InoReaderClient {
        let mut client = InoReaderClient {
            client: Easy::new(),
            app_id: &conf.inoreader_appid,
            app_token: &conf.inoreader_token,
            app_key: &conf.inoreader_appkey,
            endpoint: &conf.inoreader_endpoint
        };
        client.set_headers();
        client
    }

    pub fn get_user_info(&mut self) {
        let api_endpoint = self.get("/user-info");
        println!("{}", api_endpoint);
    }

    pub fn get_subscribe_list(&mut self) {
        let api_endpoint = self.get("/subscription/list");
        println!("{}", api_endpoint);
    }

    fn set_headers(&mut self) {
        let mut header_list = List::new();

        let mut app_id: String = "AppId: ".to_owned();
        app_id.push_str(&self.app_id);
        header_list.append(&app_id).unwrap();

        let mut app_key: String = "AppKey: ".to_owned();
        app_key.push_str(&self.app_key);
        header_list.append(&app_key).unwrap();

        let mut app_token: String = "Authorization: GoogleLogin auth=".to_owned();
        app_token.push_str(&self.app_token);
        header_list.append(&app_token).unwrap();

        &self.client.http_headers(header_list).unwrap();
    }

    fn get(&mut self, endpoint: &'a str) -> String {
        let mut ep = self.endpoint.to_owned();
        let mut dst = Vec::new();

        ep.push_str(endpoint);
        &self.client.url(&ep).unwrap();

        // вынесено в для освобождения заимствования после получения ответа
        {
            let mut transfer = &mut self.client.transfer();
            transfer.write_function(|data| {
                dst.extend_from_slice(data);
                Ok(data.len())
            }).unwrap();
            transfer.perform().unwrap();
        }
        println!("{}", &self.client.response_code().unwrap());
        String::from_utf8(dst).unwrap()
    }
}<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    config::Config,
    config::{OperationPersister, PersistConfig},
    errors::BuildProjectError,
    Artifact, ArtifactContent,
};
use common::PerfLogEvent;
use lazy_static::lazy_static;
use log::debug;
use md5::{Digest, Md5};
use regex::Regex;
use std::{fs, path::PathBuf};

lazy_static! {
    static ref RELAY_HASH_REGEX: Regex = Regex::new(r#"@relayHash (\w{32})\n"#).unwrap();
    static ref REQUEST_ID_REGEX: Regex = Regex::new(r#"@relayRequestID (.+)\n"#).unwrap();
}

pub async fn persist_operations(
    artifacts: &mut [Artifact],
    root_dir: &PathBuf,
    persist_config: &PersistConfig,
    config: &Config,
    operation_persister: &'_ (dyn OperationPersister + Send + Sync),
    log_event: &impl PerfLogEvent,
) -> Result<(), BuildProjectError> {
    let handles = artifacts
        .iter_mut()
        .flat_map(|artifact| {
            if let ArtifactContent::Operation {
                ref text,
                ref mut id_and_text_hash,
                ..
            } = artifact.content
            {
                let text_hash = md5(text);
                let artifact_path = root_dir.join(&artifact.path);
                let extracted_persist_id = if config.repersist_operations {
                    None
                } else {
                    extract_persist_id(&artifact_path, &text_hash)
                };
                if let Some(id) = extracted_persist_id {
                    *id_and_text_hash = Some((id, text_hash));
                    None
                } else {
                    let text = text.clone();
                    Some(async move {
                        operation_persister
                            .persist_artifact(text, persist_config)
                            .await
                            .map(|id| {
                                *id_and_text_hash = Some((id, text_hash));
                            })
                    })
                }
            } else {
                None
            }
        })
        .collect::<Vec<_>>();
    log_event.number("persist_documents", handles.len());
    log_event.number("worker_count", operation_persister.worker_count());
    let results = futures::future::join_all(handles).await;
    debug!("done persisting");
    let errors = results
        .into_iter()
        .filter_map(Result::err)
        .collect::<Vec<_>>();
    if !errors.is_empty() {
        return Err(BuildProjectError::PersistErrors { errors });
    }
    Ok(())
}

fn extract_persist_id(path: &PathBuf, text_hash: &str) -> Option<String> {
    let content = fs::read_to_string(path).ok()?;

    // Looks like a merge conflict, let's not trust this file.
    if content.contains("<<<<") || content.contains(">>>>") {
        return None;
    }

    // If the existing hash doesn't match the hash of the new query test, abort.
    if let Some(existing_hash) = extract_relay_hash(&content) {
        if existing_hash != text_hash {
            return None;
        }
    }

    extract_request_id(&content)
}

fn extract_relay_hash(content: &str) -> Option<&str> {
    RELAY_HASH_REGEX
        .captures(content)
        .and_then(|captures| captures.get(1).map(|m| m.as_str()))
}

fn extract_request_id(content: &str) -> Option<String> {
    REQUEST_ID_REGEX
        .captures(content)
        .map(|captures| captures[1].to_owned())
}

fn md5(data: &str) -> String {
    let mut md5 = Md5::new();
    md5.input(data);
    hex::encode(md5.result())
}
<commit_msg>Parallelize more of the persist_operations<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    config::Config,
    config::{OperationPersister, PersistConfig},
    errors::BuildProjectError,
    Artifact, ArtifactContent,
};
use common::{sync::ParallelIterator, PerfLogEvent};
use lazy_static::lazy_static;
use log::debug;
use md5::{Digest, Md5};
use rayon::iter::IntoParallelRefMutIterator;
use regex::Regex;
use std::{fs, path::PathBuf};

lazy_static! {
    static ref RELAY_HASH_REGEX: Regex = Regex::new(r#"@relayHash (\w{32})\n"#).unwrap();
    static ref REQUEST_ID_REGEX: Regex = Regex::new(r#"@relayRequestID (.+)\n"#).unwrap();
}

pub async fn persist_operations(
    artifacts: &mut [Artifact],
    root_dir: &PathBuf,
    persist_config: &PersistConfig,
    config: &Config,
    operation_persister: &'_ (dyn OperationPersister + Send + Sync),
    log_event: &impl PerfLogEvent,
) -> Result<(), BuildProjectError> {
    let handles = artifacts
        .par_iter_mut()
        .flat_map(|artifact| {
            if let ArtifactContent::Operation {
                ref text,
                ref mut id_and_text_hash,
                ..
            } = artifact.content
            {
                let text_hash = md5(text);
                let artifact_path = root_dir.join(&artifact.path);
                let extracted_persist_id = if config.repersist_operations {
                    None
                } else {
                    extract_persist_id(&artifact_path, &text_hash)
                };
                if let Some(id) = extracted_persist_id {
                    *id_and_text_hash = Some((id, text_hash));
                    None
                } else {
                    let text = text.clone();
                    Some(async move {
                        operation_persister
                            .persist_artifact(text, persist_config)
                            .await
                            .map(|id| {
                                *id_and_text_hash = Some((id, text_hash));
                            })
                    })
                }
            } else {
                None
            }
        })
        .collect::<Vec<_>>();
    log_event.number("persist_documents", handles.len());
    log_event.number("worker_count", operation_persister.worker_count());
    let results = futures::future::join_all(handles).await;
    debug!("done persisting");
    let errors = results
        .into_iter()
        .filter_map(Result::err)
        .collect::<Vec<_>>();
    if !errors.is_empty() {
        return Err(BuildProjectError::PersistErrors { errors });
    }
    Ok(())
}

fn extract_persist_id(path: &PathBuf, text_hash: &str) -> Option<String> {
    let content = fs::read_to_string(path).ok()?;

    // Looks like a merge conflict, let's not trust this file.
    if content.contains("<<<<") || content.contains(">>>>") {
        return None;
    }

    // If the existing hash doesn't match the hash of the new query test, abort.
    if let Some(existing_hash) = extract_relay_hash(&content) {
        if existing_hash != text_hash {
            return None;
        }
    }

    extract_request_id(&content)
}

fn extract_relay_hash(content: &str) -> Option<&str> {
    RELAY_HASH_REGEX
        .captures(content)
        .and_then(|captures| captures.get(1).map(|m| m.as_str()))
}

fn extract_request_id(content: &str) -> Option<String> {
    REQUEST_ID_REGEX
        .captures(content)
        .map(|captures| captures[1].to_owned())
}

fn md5(data: &str) -> String {
    let mut md5 = Md5::new();
    md5.input(data);
    hex::encode(md5.result())
}
<|endoftext|>