<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::thread;
use std::thread::JoinHandle;
use std::net::TcpStream;
use std::ops::DerefMut;
use std::os::unix::io::RawFd;
use std::collections::LinkedList;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{
    channel,
    Sender,
    Receiver,
    TryRecvError
};

use super::types::*;
use super::epoll;
use super::epoll::util::*;
use super::epoll::EpollEvent;
use super::socket::Socket;
use super::simple_stream::nbetstream::NbetStream;

#[allow(dead_code)]
pub struct EventLoop {
    /// Handle to event loop process
    prox: JoinHandle<()>,
    /// Sender<T> to proc
    prox_tx: Sender<TcpStream>
}

impl EventLoop {

    /// Returns a new EventLoop
    pub fn new(server_tx: Sender<EventTuple>) -> EventLoop {
        let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = channel();
        let prox = thread::Builder::new()
            .name("EventLoop".to_string())
            .spawn(move || {
                EventLoop::start(rx, server_tx);
            }).unwrap();

        EventLoop {
            prox: prox,
            prox_tx: tx
        }
    }

    /// Returns a clone of this EventLoop's Sender<TcpStream> channel
    pub fn sender(&self) -> Sender<TcpStream> {
        self.prox_tx.clone()
    }

    /// Main event loop
    fn start(rx: Receiver<TcpStream>, uspace_tx: Sender<EventTuple>) {
        // Master socket list
        let sockets = Arc::new(Mutex::new(LinkedList::<Socket>::new()));

        // Our error handler for this thread and it's children
        let (err_tx, err_rx): (Sender<()>, Receiver<()>) = channel();

        // Create an epoll instance
        let result = epoll::create1(0);
        if result.is_err() {
            let err = result.unwrap_err();
            panic!("Unable to create epoll instance: {}", err)
        }
        let epoll_instance = result.unwrap();

        // Start the epoll thread
        let c_sockets = sockets.clone();
        let c_epoll_instance = epoll_instance.clone();
        let epoll_prox = thread::Builder::new()
            .name("EpollLoop".to_string())
            .spawn(move || {
                EventLoop::epoll_loop(uspace_tx, c_sockets, c_epoll_instance, err_rx);
            }).unwrap();

        for new_stream in rx.iter() {
            match NbetStream::new(new_stream) {
                Ok(s_stream) => {
                    // Add to master list
                    let socket = Socket::new(s_stream);

                    // TODO - Replace with recoverable version once into_inner() is stable
                    // Unfortunately, this is the only stable way to use mutexes at the moment
                    // Hopefully recovering from poisoning will be in 1.2
                    // let mut s_guard = match sockets.lock() {
                    //     Ok(guard) => guard,
                    //     Err(poisoned) => poisoned.into_inner()
                    // };
                    let mut s_guard = sockets.lock().unwrap();
                    let s_list = s_guard.deref_mut();
                    let s_fd = socket.raw_fd();
                    s_list.push_back(socket);

                    // Add to epoll
                    let event = Box::new(EpollEvent {
                        data: s_fd as u64,
                        events: (event_type::EPOLLIN | event_type::EPOLLET)
                        });
                    match epoll::ctl(epoll_instance, ctl_op::ADD, s_fd, event) {
                        Ok(()) => println!("New socket added to epoll list"),
                        Err(e) => println!("Epoll CtrlError: {}", e)
                    };
                }
                Err(e) => {
                    // TODO - write error to log file
                    println!("Error: {}", e);
                }
            };
        }

        // If we get here, shit is real bad. It means we have lost our
        // channel to the outside world.
        // Kill off event loop, so unwinding can begin
        println!("Rust.EventLoop.start - thread finished");
        let _ = err_tx.send(());
    }

    /// Calls epoll_wait forever waiting for events to be fired up from kernel
    fn epoll_loop(uspace_tx: Sender<EventTuple>,
                  sockets: SocketList,
                  epoll_instance: RawFd,
                  err_rx: Receiver<()>) {

        // This is the maximum number of events we want to be notified of at one time
        let mut events = Vec::<EpollEvent>::with_capacity(100);
        unsafe { events.set_len(100); }

        loop {
            // Wait for epoll events
            match epoll::wait(epoll_instance, &mut events[..], -1) {
                Ok(num_events) => {
                    println!("{} epoll event(s) received", num_events);
                    for x in 0..num_events {
                        let tx_clone = uspace_tx.clone();
                        let s_clone = sockets.clone();
                        EventLoop::handle_epoll_event(tx_clone, &events[x as usize], s_clone);
                    }
                }
                Err(e) => {
                    panic!("Error on epoll::wait(): {}", e)
                }
            }

            // If we receive anything on this end or lose communication, we need to halt,
            // because we have no where to send up events, our "user space" channel is
            // gone
            match err_rx.try_recv() {
                Ok(_) => {
                    println!("Error, terminating epoll_loop");
                    break;
                }
                Err(e) => {
                    match e {
                        TryRecvError::Empty => {}
                        _ => {
                            println!("Error, terminating epoll_loo");
                            break;
                        }
                    }
                }
            }
        }
        println!("Rust.EventLoop.epoll_loop - thread finished");
    }

    /// Processes a read on the socket that is ready for reading
    ///
    /// An epoll event is passed to this function, with the fd that is ready to be read
    /// A traversal through the list of sockets is then performed in order to locate the
    /// NbetStream instance that is wrapping that file descriptor.
    /// After a read is performed on that socket, it's data will be passed back to the
    /// "user-space" section of the Server to process the payload however it needs to.
    ///
    /// TODO - Create a faster way to gain a safe reference to the NbetStream wrapping
    /// the fd instead of an O(n) lookup for every event. Currently lookup time is
    /// O(n) * MAX_EPOLL_EVENTS, where MAX_EPOLL_EVENTS is user defined in this function's
    /// caller.
    fn handle_epoll_event(uspace_tx: Sender<EventTuple>,
                          event: &EpollEvent,
                          sockets: SocketList) {

        println!("Rust.EventLoop.handle_epoll_event");

        // The fd we are traversing the list for
        let fd = event.data;

        // We need a handle before the list gets locked and used
        let s_list_clone = sockets.clone();

        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        let mut s_guard = sockets.lock().unwrap();
        let s_list = s_guard.deref_mut();
        for socket in s_list.iter_mut() {
            if socket.raw_fd() as u64 == fd {
                // We've found the socket for the event passed from epoll
                // Lets attempt to read from it
                match socket.read() {
                    Ok(()) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Ok()");
                        for msg in socket.buffer().iter() {
                            let msg_for_debug = msg.clone();
                            match String::from_utf8(msg_for_debug) {
                                Ok(msg_str) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string: {}", msg_str);
                                }
                                Err(e) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string.Err: {}", e);
                                }
                            };

                            let list_handle = s_list_clone.clone();
                            let socket_clone = socket.clone();
                            let msg_clone = msg.clone();
                            let _ = uspace_tx.send((list_handle, socket_clone, msg_clone));
                        }
                    }
                    Err(e) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Err(e): {}", e);
                    }
                }
            }
        }
    }
}
<commit_msg>Debugging statements<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::thread;
use std::thread::JoinHandle;
use std::net::TcpStream;
use std::ops::DerefMut;
use std::os::unix::io::RawFd;
use std::collections::LinkedList;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{
    channel,
    Sender,
    Receiver,
    TryRecvError
};

use super::types::*;
use super::epoll;
use super::epoll::util::*;
use super::epoll::EpollEvent;
use super::socket::Socket;
use super::simple_stream::nbetstream::NbetStream;

#[allow(dead_code)]
pub struct EventLoop {
    /// Handle to event loop process
    prox: JoinHandle<()>,
    /// Sender<T> to proc
    prox_tx: Sender<TcpStream>
}

impl EventLoop {

    /// Returns a new EventLoop
    pub fn new(server_tx: Sender<EventTuple>) -> EventLoop {
        let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = channel();
        let prox = thread::Builder::new()
            .name("EventLoop".to_string())
            .spawn(move || {
                EventLoop::start(rx, server_tx);
            }).unwrap();

        EventLoop {
            prox: prox,
            prox_tx: tx
        }
    }

    /// Returns a clone of this EventLoop's Sender<TcpStream> channel
    pub fn sender(&self) -> Sender<TcpStream> {
        self.prox_tx.clone()
    }

    /// Main event loop
    fn start(rx: Receiver<TcpStream>, uspace_tx: Sender<EventTuple>) {
        // Master socket list
        let sockets = Arc::new(Mutex::new(LinkedList::<Socket>::new()));

        // Our error handler for this thread and it's children
        let (err_tx, err_rx): (Sender<()>, Receiver<()>) = channel();

        // Create an epoll instance
        let result = epoll::create1(0);
        if result.is_err() {
            let err = result.unwrap_err();
            panic!("Unable to create epoll instance: {}", err)
        }
        let epoll_instance = result.unwrap();

        // Start the epoll thread
        let c_sockets = sockets.clone();
        let c_epoll_instance = epoll_instance.clone();
        let epoll_prox = thread::Builder::new()
            .name("EpollLoop".to_string())
            .spawn(move || {
                EventLoop::epoll_loop(uspace_tx, c_sockets, c_epoll_instance, err_rx);
            }).unwrap();

        for new_stream in rx.iter() {
            match NbetStream::new(new_stream) {
                Ok(s_stream) => {
                    // Add to master list
                    let socket = Socket::new(s_stream);

                    // TODO - Replace with recoverable version once into_inner() is stable
                    // Unfortunately, this is the only stable way to use mutexes at the moment
                    // Hopefully recovering from poisoning will be in 1.2
                    // let mut s_guard = match sockets.lock() {
                    //     Ok(guard) => guard,
                    //     Err(poisoned) => poisoned.into_inner()
                    // };
                    let mut s_guard = sockets.lock().unwrap();
                    let s_list = s_guard.deref_mut();
                    let s_fd = socket.raw_fd();
                    s_list.push_back(socket);

                    // Add to epoll
                    let event = Box::new(EpollEvent {
                        data: s_fd as u64,
                        events: (event_type::EPOLLIN | event_type::EPOLLET)
                        });
                    match epoll::ctl(epoll_instance, ctl_op::ADD, s_fd, event) {
                        Ok(()) => println!("New socket added to epoll list"),
                        Err(e) => println!("Epoll CtrlError: {}", e)
                    };
                }
                Err(e) => {
                    // TODO - write error to log file
                    println!("Error: {}", e);
                }
            };
        }

        // If we get here, shit is real bad. It means we have lost our
        // channel to the outside world.
        // Kill off event loop, so unwinding can begin
        println!("Rust.EventLoop.start - thread finished");
        let _ = err_tx.send(());
    }

    /// Calls epoll_wait forever waiting for events to be fired up from kernel
    fn epoll_loop(uspace_tx: Sender<EventTuple>,
                  sockets: SocketList,
                  epoll_instance: RawFd,
                  err_rx: Receiver<()>) {

        // This is the maximum number of events we want to be notified of at one time
        let mut events = Vec::<EpollEvent>::with_capacity(100);
        unsafe { events.set_len(100); }

        loop {
            // Wait for epoll events
            match epoll::wait(epoll_instance, &mut events[..], -1) {
                Ok(num_events) => {
                    println!("{} epoll event(s) received", num_events);
                    for x in 0..num_events {
                        let tx_clone = uspace_tx.clone();
                        let s_clone = sockets.clone();
                        EventLoop::handle_epoll_event(tx_clone, &events[x as usize], s_clone);
                    }
                }
                Err(e) => {
                    panic!("Error on epoll::wait(): {}", e)
                }
            }

            // If we receive anything on this end or lose communication, we need to halt,
            // because we have no where to send up events, our "user space" channel is
            // gone
            match err_rx.try_recv() {
                Ok(_) => {
                    println!("Error, terminating epoll_loop");
                    break;
                }
                Err(e) => {
                    match e {
                        TryRecvError::Empty => {}
                        _ => {
                            println!("Error, terminating epoll_loo");
                            break;
                        }
                    }
                }
            }
        }
        println!("Rust.EventLoop.epoll_loop - thread finished");
    }

    /// Processes a read on the socket that is ready for reading
    ///
    /// An epoll event is passed to this function, with the fd that is ready to be read
    /// A traversal through the list of sockets is then performed in order to locate the
    /// NbetStream instance that is wrapping that file descriptor.
    /// After a read is performed on that socket, it's data will be passed back to the
    /// "user-space" section of the Server to process the payload however it needs to.
    ///
    /// TODO - Create a faster way to gain a safe reference to the NbetStream wrapping
    /// the fd instead of an O(n) lookup for every event. Currently lookup time is
    /// O(n) * MAX_EPOLL_EVENTS, where MAX_EPOLL_EVENTS is user defined in this function's
    /// caller.
    fn handle_epoll_event(uspace_tx: Sender<EventTuple>,
                          event: &EpollEvent,
                          sockets: SocketList) {

        println!("Rust.EventLoop.handle_epoll_event");

        // The fd we are traversing the list for
        let fd = event.data;

        // We need a handle before the list gets locked and used
        let s_list_clone = sockets.clone();

        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        let mut s_guard = sockets.lock().unwrap();
        let s_list = s_guard.deref_mut();
        for socket in s_list.iter_mut() {
            if socket.raw_fd() as u64 == fd {
                // We've found the socket for the event passed from epoll
                // Lets attempt to read from it
                match socket.read() {
                    Ok(()) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Ok()");
                        for msg in socket.buffer().iter() {
                            let msg_for_debug = msg.clone();
                            match String::from_utf8(msg_for_debug) {
                                Ok(msg_str) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string: {}", msg_str);
                                }
                                Err(e) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string.Err: {}", e);
                                }
                            };

                            // let list_handle = s_list_clone.clone();
                            // let socket_clone = socket.clone();
                            // let msg_clone = msg.clone();
                            // let _ = uspace_tx.send((list_handle, socket_clone, msg_clone));
                        }
                    }
                    Err(e) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Err(e): {}", e);
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::assert_stream;
use crate::stream::{Fuse, StreamExt};
use core::{fmt, pin::Pin};
use futures_core::stream::{FusedStream, Stream};
use futures_core::task::{Context, Poll};
use pin_project_lite::pin_project;

/// Type to tell [`SelectWithStrategy`] which stream to poll next.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum PollNext {
    /// Poll the first stream.
    Left,
    /// Poll the second stream.
    Right,
}

impl PollNext {
    /// Toggle the value and return the old one.
    pub fn toggle(&mut self) -> Self {
        let old = *self;

        match self {
            PollNext::Left => *self = PollNext::Right,
            PollNext::Right => *self = PollNext::Left,
        }

        old
    }
}

impl Default for PollNext {
    fn default() -> Self {
        PollNext::Left
    }
}

pin_project! {
    /// Stream for the [`select_with_strategy()`] function. See function docs for details.
    #[must_use = "streams do nothing unless polled"]
    pub struct SelectWithStrategy<St1, St2, Clos, State> {
        #[pin]
        stream1: Fuse<St1>,
        #[pin]
        stream2: Fuse<St2>,
        state: State,
        clos: Clos,
    }
}

/// This function will attempt to pull items from both streams. You provide a
/// closure to tell [`SelectWithStrategy`] which stream to poll. The closure can
/// store state on `SelectWithStrategy` to which it will receive a `&mut` on every
/// invocation. This allows basing the strategy on prior choices.
///
/// After one of the two input streams completes, the remaining one will be
/// polled exclusively. The returned stream completes when both input
/// streams have completed.
///
/// Note that this function consumes both streams and returns a wrapped
/// version of them.
///
/// ## Examples
///
/// ### Priority
/// This example shows how to always prioritize the left stream.
///
/// ```rust
/// # futures::executor::block_on(async {
/// use futures::stream::{ repeat, select_with_strategy, PollNext, StreamExt };
///
/// let left = repeat(1);
/// let right = repeat(2);
///
/// // We don't need any state, so let's make it an empty tuple.
/// // We must provide some type here, as there is no way for the compiler
/// // to infer it. As we don't need to capture variables, we can just
/// // use a function pointer instead of a closure.
/// fn prio_left(_: &mut ()) -> PollNext { PollNext::Left }
///
/// let mut out = select_with_strategy(left, right, prio_left);
///
/// for _ in 0..100 {
///     // Whenever we poll out, we will alwas get `1`.
///     assert_eq!(1, out.select_next_some().await);
/// }
/// # });
/// ```
///
/// ### Round Robin
/// This example shows how to select from both streams round robin.
/// Note: this special case is provided by [`futures-util::stream::select`].
///
/// ```rust
/// # futures::executor::block_on(async {
/// use futures::stream::{ repeat, select_with_strategy, PollNext, StreamExt };
///
/// let left = repeat(1);
/// let right = repeat(2);
///
/// // We don't need any state, so let's make it an empty tuple.
/// let rrobin = |last: &mut PollNext| last.toggle();
///
/// let mut out = select_with_strategy(left, right, rrobin);
///
/// for _ in 0..100 {
///     // We should be alternating now.
///     assert_eq!(1, out.select_next_some().await);
///     assert_eq!(2, out.select_next_some().await);
/// }
/// # });
/// ```
pub fn select_with_strategy<St1, St2, Clos, State>(
    stream1: St1,
    stream2: St2,
    which: Clos,
) -> SelectWithStrategy<St1, St2, Clos, State>
where
    St1: Stream,
    St2: Stream<Item = St1::Item>,
    Clos: FnMut(&mut State) -> PollNext,
    State: Default,
{
    assert_stream::<St1::Item, _>(SelectWithStrategy {
        stream1: stream1.fuse(),
        stream2: stream2.fuse(),
        state: Default::default(),
        clos: which,
    })
}

impl<St1, St2, Clos, State> SelectWithStrategy<St1, St2, Clos, State> {
    /// Acquires a reference to the underlying streams that this combinator is
    /// pulling from.
    pub fn get_ref(&self) -> (&St1, &St2) {
        (self.stream1.get_ref(), self.stream2.get_ref())
    }

    /// Acquires a mutable reference to the underlying streams that this
    /// combinator is pulling from.
    ///
    /// Note that care must be taken to avoid tampering with the state of the
    /// stream which may otherwise confuse this combinator.
    pub fn get_mut(&mut self) -> (&mut St1, &mut St2) {
        (self.stream1.get_mut(), self.stream2.get_mut())
    }

    /// Acquires a pinned mutable reference to the underlying streams that this
    /// combinator is pulling from.
    ///
    /// Note that care must be taken to avoid tampering with the state of the
    /// stream which may otherwise confuse this combinator.
    pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) {
        let this = self.project();
        (this.stream1.get_pin_mut(), this.stream2.get_pin_mut())
    }

    /// Consumes this combinator, returning the underlying streams.
    ///
    /// Note that this may discard intermediate state of this combinator, so
    /// care should be taken to avoid losing resources when this is called.
    pub fn into_inner(self) -> (St1, St2) {
        (self.stream1.into_inner(), self.stream2.into_inner())
    }
}

impl<St1, St2, Clos, State> FusedStream for SelectWithStrategy<St1, St2, Clos, State>
where
    St1: Stream,
    St2: Stream<Item = St1::Item>,
    Clos: FnMut(&mut State) -> PollNext,
{
    fn is_terminated(&self) -> bool {
        self.stream1.is_terminated() && self.stream2.is_terminated()
    }
}

impl<St1, St2, Clos, State> Stream for SelectWithStrategy<St1, St2, Clos, State>
where
    St1: Stream,
    St2: Stream<Item = St1::Item>,
    Clos: FnMut(&mut State) -> PollNext,
{
    type Item = St1::Item;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<St1::Item>> {
        let this = self.project();

        match (this.clos)(this.state) {
            PollNext::Left => poll_inner(this.stream1, this.stream2, cx),
            PollNext::Right => poll_inner(this.stream2, this.stream1, cx),
        }
    }
}

fn poll_inner<St1, St2>(
    a: Pin<&mut St1>,
    b: Pin<&mut St2>,
    cx: &mut Context<'_>,
) -> Poll<Option<St1::Item>>
where
    St1: Stream,
    St2: Stream<Item = St1::Item>,
{
    let a_done = match a.poll_next(cx) {
        Poll::Ready(Some(item)) => return Poll::Ready(Some(item)),
        Poll::Ready(None) => true,
        Poll::Pending => false,
    };

    match b.poll_next(cx) {
        Poll::Ready(Some(item)) => Poll::Ready(Some(item)),
        Poll::Ready(None) if a_done => Poll::Ready(None),
        Poll::Ready(None) | Poll::Pending => Poll::Pending,
    }
}

impl<St1, St2, Clos, State> fmt::Debug for SelectWithStrategy<St1, St2, Clos, State>
where
    St1: fmt::Debug,
    St2: fmt::Debug,
    State: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SelectWithStrategy")
            .field("stream1", &self.stream1)
            .field("stream2", &self.stream2)
            .field("state", &self.state)
            .finish()
    }
}
<commit_msg>Remove an incorrect comment (#2456)<commit_after>use super::assert_stream;
use crate::stream::{Fuse, StreamExt};
use core::{fmt, pin::Pin};
use futures_core::stream::{FusedStream, Stream};
use futures_core::task::{Context, Poll};
use pin_project_lite::pin_project;

/// Type to tell [`SelectWithStrategy`] which stream to poll next.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum PollNext {
    /// Poll the first stream.
    Left,
    /// Poll the second stream.
    Right,
}

impl PollNext {
    /// Toggle the value and return the old one.
    pub fn toggle(&mut self) -> Self {
        let old = *self;

        match self {
            PollNext::Left => *self = PollNext::Right,
            PollNext::Right => *self = PollNext::Left,
        }

        old
    }
}

impl Default for PollNext {
    fn default() -> Self {
        PollNext::Left
    }
}

pin_project! {
    /// Stream for the [`select_with_strategy()`] function. See function docs for details.
    #[must_use = "streams do nothing unless polled"]
    pub struct SelectWithStrategy<St1, St2, Clos, State> {
        #[pin]
        stream1: Fuse<St1>,
        #[pin]
        stream2: Fuse<St2>,
        state: State,
        clos: Clos,
    }
}

/// This function will attempt to pull items from both streams. You provide a
/// closure to tell [`SelectWithStrategy`] which stream to poll. The closure can
/// store state on `SelectWithStrategy` to which it will receive a `&mut` on every
/// invocation. This allows basing the strategy on prior choices.
///
/// After one of the two input streams completes, the remaining one will be
/// polled exclusively. The returned stream completes when both input
/// streams have completed.
///
/// Note that this function consumes both streams and returns a wrapped
/// version of them.
///
/// ## Examples
///
/// ### Priority
/// This example shows how to always prioritize the left stream.
///
/// ```rust
/// # futures::executor::block_on(async {
/// use futures::stream::{ repeat, select_with_strategy, PollNext, StreamExt };
///
/// let left = repeat(1);
/// let right = repeat(2);
///
/// // We don't need any state, so let's make it an empty tuple.
/// // We must provide some type here, as there is no way for the compiler
/// // to infer it. As we don't need to capture variables, we can just
/// // use a function pointer instead of a closure.
/// fn prio_left(_: &mut ()) -> PollNext { PollNext::Left }
///
/// let mut out = select_with_strategy(left, right, prio_left);
///
/// for _ in 0..100 {
///     // Whenever we poll out, we will alwas get `1`.
///     assert_eq!(1, out.select_next_some().await);
/// }
/// # });
/// ```
///
/// ### Round Robin
/// This example shows how to select from both streams round robin.
/// Note: this special case is provided by [`futures-util::stream::select`].
///
/// ```rust
/// # futures::executor::block_on(async {
/// use futures::stream::{ repeat, select_with_strategy, PollNext, StreamExt };
///
/// let left = repeat(1);
/// let right = repeat(2);
///
/// let rrobin = |last: &mut PollNext| last.toggle();
///
/// let mut out = select_with_strategy(left, right, rrobin);
///
/// for _ in 0..100 {
///     // We should be alternating now.
///     assert_eq!(1, out.select_next_some().await);
///     assert_eq!(2, out.select_next_some().await);
/// }
/// # });
/// ```
pub fn select_with_strategy<St1, St2, Clos, State>(
    stream1: St1,
    stream2: St2,
    which: Clos,
) -> SelectWithStrategy<St1, St2, Clos, State>
where
    St1: Stream,
    St2: Stream<Item = St1::Item>,
    Clos: FnMut(&mut State) -> PollNext,
    State: Default,
{
    assert_stream::<St1::Item, _>(SelectWithStrategy {
        stream1: stream1.fuse(),
        stream2: stream2.fuse(),
        state: Default::default(),
        clos: which,
    })
}

impl<St1, St2, Clos, State> SelectWithStrategy<St1, St2, Clos, State> {
    /// Acquires a reference to the underlying streams that this combinator is
    /// pulling from.
    pub fn get_ref(&self) -> (&St1, &St2) {
        (self.stream1.get_ref(), self.stream2.get_ref())
    }

    /// Acquires a mutable reference to the underlying streams that this
    /// combinator is pulling from.
    ///
    /// Note that care must be taken to avoid tampering with the state of the
    /// stream which may otherwise confuse this combinator.
    pub fn get_mut(&mut self) -> (&mut St1, &mut St2) {
        (self.stream1.get_mut(), self.stream2.get_mut())
    }

    /// Acquires a pinned mutable reference to the underlying streams that this
    /// combinator is pulling from.
    ///
    /// Note that care must be taken to avoid tampering with the state of the
    /// stream which may otherwise confuse this combinator.
    pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) {
        let this = self.project();
        (this.stream1.get_pin_mut(), this.stream2.get_pin_mut())
    }

    /// Consumes this combinator, returning the underlying streams.
    ///
    /// Note that this may discard intermediate state of this combinator, so
    /// care should be taken to avoid losing resources when this is called.
    pub fn into_inner(self) -> (St1, St2) {
        (self.stream1.into_inner(), self.stream2.into_inner())
    }
}

impl<St1, St2, Clos, State> FusedStream for SelectWithStrategy<St1, St2, Clos, State>
where
    St1: Stream,
    St2: Stream<Item = St1::Item>,
    Clos: FnMut(&mut State) -> PollNext,
{
    fn is_terminated(&self) -> bool {
        self.stream1.is_terminated() && self.stream2.is_terminated()
    }
}

impl<St1, St2, Clos, State> Stream for SelectWithStrategy<St1, St2, Clos, State>
where
    St1: Stream,
    St2: Stream<Item = St1::Item>,
    Clos: FnMut(&mut State) -> PollNext,
{
    type Item = St1::Item;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<St1::Item>> {
        let this = self.project();

        match (this.clos)(this.state) {
            PollNext::Left => poll_inner(this.stream1, this.stream2, cx),
            PollNext::Right => poll_inner(this.stream2, this.stream1, cx),
        }
    }
}

fn poll_inner<St1, St2>(
    a: Pin<&mut St1>,
    b: Pin<&mut St2>,
    cx: &mut Context<'_>,
) -> Poll<Option<St1::Item>>
where
    St1: Stream,
    St2: Stream<Item = St1::Item>,
{
    let a_done = match a.poll_next(cx) {
        Poll::Ready(Some(item)) => return Poll::Ready(Some(item)),
        Poll::Ready(None) => true,
        Poll::Pending => false,
    };

    match b.poll_next(cx) {
        Poll::Ready(Some(item)) => Poll::Ready(Some(item)),
        Poll::Ready(None) if a_done => Poll::Ready(None),
        Poll::Ready(None) | Poll::Pending => Poll::Pending,
    }
}

impl<St1, St2, Clos, State> fmt::Debug for SelectWithStrategy<St1, St2, Clos, State>
where
    St1: fmt::Debug,
    St2: fmt::Debug,
    State: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SelectWithStrategy")
            .field("stream1", &self.stream1)
            .field("stream2", &self.stream2)
            .field("state", &self.state)
            .finish()
    }
}
<|endoftext|>


<commit_before>/* diosix debug console output code
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

/* to avoid warnings about super::hardware when qemuprint is active */
#![allow(unused_imports)]

use super::error::Cause;
use core::fmt;
use spin::Mutex;
use alloc::string::String;
use super::hardware;

lazy_static!
{
    pub static ref DEBUG_LOCK: Mutex<bool> = Mutex::new(false);
    static ref DEBUG_QUEUE: Mutex<String> = Mutex::new(String::new());
}

/* top level debug macros */
/* useful messages */
#[macro_export]
macro_rules! hvlog
{
    ($fmt:expr) => (hvprintln!("[+] CPU {}: {}", $crate::pcore::PhysicalCore::get_id(), $fmt));
    ($fmt:expr, $($arg:tt)*) => (hvprintln!(concat!("[+] CPU {}: ", $fmt), $crate::pcore::PhysicalCore::get_id(), $($arg)*));
}

/* bad news: bug detection, failures, etc. */
#[macro_export]
macro_rules! hvalert
{
    ($fmt:expr) => (hvprintln!("[!] CPU {}: {}", $crate::pcore::PhysicalCore::get_id(), $fmt));
    ($fmt:expr, $($arg:tt)*) => (hvprintln!(concat!("[!] CPU {}: ", $fmt), $crate::pcore::PhysicalCore::get_id(), $($arg)*));
}

/* only output if debug build is enabled */
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! hvdebug
{
    ($fmt:expr) => (hvprintln!("[?] CPU {}: {}", $crate::pcore::PhysicalCore::get_id(), $fmt));
    ($fmt:expr, $($arg:tt)*) => (hvprintln!(concat!("[?] CPU {}: ", $fmt), $crate::pcore::PhysicalCore::get_id(), $($arg)*));
}

/* silence debug if disabled */
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! hvdebug
{
    ($fmt:expr) => ({});
    ($fmt:expr, $($arg:tt)*) => ({});
}

/* don't include any metadata nor add a newline */
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! hvdebugraw
{
    ($fmt:expr) => (hvprint!("{}", $fmt));
    ($fmt:expr, $($arg:tt)*) => (hvprint!(concat!($fmt), $($arg)*));
}

/* silence debug if disabled */
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! hvdebugraw
{
    ($fmt:expr) => ({});
    ($fmt:expr, $($arg:tt)*) => ({});
}

/* low-level macros for hypervisor-only hvprintln and hvprint debug output routines */
macro_rules! hvprintln
{
    ($fmt:expr) => (hvprint!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (hvprint!(concat!($fmt, "\n"), $($arg)*));
}

macro_rules! hvprint
{
    ($($arg:tt)*) =>
    ({
        use core::fmt::Write;
        {
            let mut hvprint_lock = $crate::debug::DEBUG_LOCK.lock();
            *hvprint_lock = true;

            unsafe { $crate::debug::CONSOLE.write_fmt(format_args!($($arg)*)).unwrap(); }
        }
    });
}

macro_rules! debughousekeeper
{
    () => ($crate::debug::drain_queue());
}

/* create a generic debug console writer */
pub struct ConsoleWriter;
pub static mut CONSOLE: ConsoleWriter = ConsoleWriter {};

impl fmt::Write for ConsoleWriter
{
    #[cfg(not(feature = "qemuprint"))]
    fn write_str(&mut self, s: &str) -> core::fmt::Result
    {
        DEBUG_QUEUE.lock().push_str(s);
        Ok(())
    }

    #[cfg(feature = "qemuprint")]
    fn write_str(&mut self, s: &str) -> core::fmt::Result
    {
        /* force debug output to Qemu's serial port. useful for early debugging */
        for c in s.as_bytes()
        {
            /* FIXME: hardwired to the RISC-V Qemu serial port */
            unsafe { *(0x10000000 as *mut u8) = *c };
        }
        Ok(())
    }
}

/* attempt to empty queue out to the device-tree-defined debug port */
#[cfg(not(feature = "qemuprint"))]
pub fn drain_queue()
{
    /* don't block if we can't write at this time */
    if let Some(mut debug_lock) = DEBUG_LOCK.try_lock()
    {
        *debug_lock = true;
        if let Some(mut debug_queue) = DEBUG_QUEUE.try_lock()
        {
            if hardware::write_debug_string(&debug_queue) == true
            {
                debug_queue.clear();
            }
        }
    }
}

#[cfg(feature = "qemuprint")]
pub fn drain_queue() { }<commit_msg>Use is_locked() rather than try_Lock() to avoid blocking when draining the debug queue as we're no longer using spin-rs.<commit_after>/* diosix debug console output code
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

/* to avoid warnings about super::hardware when qemuprint is active */
#![allow(unused_imports)]

use super::error::Cause;
use core::fmt;
use super::lock::Mutex;
use alloc::string::String;
use super::hardware;

lazy_static!
{
    pub static ref DEBUG_LOCK: Mutex<bool> = Mutex::new(false);
    static ref DEBUG_QUEUE: Mutex<String> = Mutex::new(String::new());
}

/* top level debug macros */
/* useful messages */
#[macro_export]
macro_rules! hvlog
{
    ($fmt:expr) => (hvprintln!("[+] CPU {}: {}", $crate::pcore::PhysicalCore::get_id(), $fmt));
    ($fmt:expr, $($arg:tt)*) => (hvprintln!(concat!("[+] CPU {}: ", $fmt), $crate::pcore::PhysicalCore::get_id(), $($arg)*));
}

/* bad news: bug detection, failures, etc. */
#[macro_export]
macro_rules! hvalert
{
    ($fmt:expr) => (hvprintln!("[!] CPU {}: {}", $crate::pcore::PhysicalCore::get_id(), $fmt));
    ($fmt:expr, $($arg:tt)*) => (hvprintln!(concat!("[!] CPU {}: ", $fmt), $crate::pcore::PhysicalCore::get_id(), $($arg)*));
}

/* only output if debug build is enabled */
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! hvdebug
{
    ($fmt:expr) => (hvprintln!("[?] CPU {}: {}", $crate::pcore::PhysicalCore::get_id(), $fmt));
    ($fmt:expr, $($arg:tt)*) => (hvprintln!(concat!("[?] CPU {}: ", $fmt), $crate::pcore::PhysicalCore::get_id(), $($arg)*));
}

/* silence debug if disabled */
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! hvdebug
{
    ($fmt:expr) => ({});
    ($fmt:expr, $($arg:tt)*) => ({});
}

/* don't include any metadata nor add a newline */
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! hvdebugraw
{
    ($fmt:expr) => (hvprint!("{}", $fmt));
    ($fmt:expr, $($arg:tt)*) => (hvprint!(concat!($fmt), $($arg)*));
}

/* silence debug if disabled */
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! hvdebugraw
{
    ($fmt:expr) => ({});
    ($fmt:expr, $($arg:tt)*) => ({});
}

/* low-level macros for hypervisor-only hvprintln and hvprint debug output routines */
macro_rules! hvprintln
{
    ($fmt:expr) => (hvprint!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (hvprint!(concat!($fmt, "\n"), $($arg)*));
}

macro_rules! hvprint
{
    ($($arg:tt)*) =>
    ({
        use core::fmt::Write;
        {
            let mut hvprint_lock = $crate::debug::DEBUG_LOCK.lock();
            *hvprint_lock = true;

            unsafe { $crate::debug::CONSOLE.write_fmt(format_args!($($arg)*)).unwrap(); }
        }
    });
}

macro_rules! debughousekeeper
{
    () => ($crate::debug::drain_queue());
}

/* create a generic debug console writer */
pub struct ConsoleWriter;
pub static mut CONSOLE: ConsoleWriter = ConsoleWriter {};

impl fmt::Write for ConsoleWriter
{
    #[cfg(not(feature = "qemuprint"))]
    fn write_str(&mut self, s: &str) -> core::fmt::Result
    {
        DEBUG_QUEUE.lock().push_str(s);
        Ok(())
    }

    #[cfg(feature = "qemuprint")]
    fn write_str(&mut self, s: &str) -> core::fmt::Result
    {
        /* force debug output to Qemu's serial port. useful for early debugging */
        for c in s.as_bytes()
        {
            /* FIXME: hardwired to the RISC-V Qemu serial port */
            unsafe { *(0x10000000 as *mut u8) = *c };
        }
        Ok(())
    }
}

/* attempt to empty queue out to the device-tree-defined debug port */
#[cfg(not(feature = "qemuprint"))]
pub fn drain_queue()
{
    /* avoid blocking if we can't write at this time */
    if DEBUG_LOCK.is_locked() == false
    {
        let debug_lock = DEBUG_LOCK.lock();
        let mut debug_queue = DEBUG_QUEUE.lock();

        if hardware::write_debug_string(&debug_queue) == true
        {
            debug_queue.clear();
        }

        drop(debug_queue);
        drop(debug_lock);
    }
}

#[cfg(feature = "qemuprint")]
pub fn drain_queue() { }<|endoftext|>


<commit_before>use std::{fmt, path::Path};

use async_trait::async_trait;
use indexmap::IndexMap;
use language_tags::LanguageTag;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use serde_json::to_string_pretty;

use crate::build::chromeos::keymap::CHROMEOS_KEYS;
use crate::build::chromeos::manifest::{
    ChromeOsManifest, ManifestBackground, ManifestIcons, ManifestInputComponent,
};
use crate::{
    build::BuildStep,
    bundle::{layout::chrome::ChromeOsKbdLayer, KbdgenBundle},
    util::split_keys,
};

const BACKGROUND_FILE_NAME: &str = "background.js";
const MANIFEST_FILE_NAME: &str = "manifest.json";
const KEYBOARD_TEMPLATE: &str = include_str!("../../../resources/template-chromeos-keyboard.js");
const DEFAULT_LOCALE: &str = "en";

const DEFAULT_LONG_LOCALE: &str = "en-US";
const DEFAULT_XKB_LAYOUT: &str = "us";

const KEYBOARD_NAMES: Lazy<IndexMap<String, String>> = Lazy::new(|| {
    let mut map = IndexMap::new();

    {
        let arr = [
            ("nb", "{} tastatur"),
            ("no", "{} tastatur"),
            ("nn", "{} tastatur"),
            ("da", "{} tastatur"),
            ("sv", "{} tangentbord"),
            ("en", "{} keyboard"),
            ("fi", "{} näppäimistö"),
        ];

        for (key, value) in arr {
            map.insert(key.to_string(), value.to_string());
        }
    }

    map
});

// LOCALE START

#[derive(Serialize, Deserialize)]
pub struct LocaleMessage {
    message: String,
}

#[derive(Serialize, Deserialize)]
pub struct Locale {
    #[serde(flatten)]
    locales: IndexMap<String, LocaleMessage>,
    name: LocaleMessage,
    description: LocaleMessage,
}

// LOCALE END

#[derive(Serialize, Deserialize)]
pub struct ChromeOsBackground {
    template: String,
    descriptor: IndexMap<LanguageTag, ChromeOsDescriptor>,
}

impl fmt::Display for ChromeOsBackground {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}\n", self.template)?;
        write!(f, "\n")?;
        write!(
            f,
            "const descriptor = {}\n",
            to_string_pretty(&self.descriptor).map_err(|_err| fmt::Error)?
        )?;
        write!(f, "\nKeyboard.install(descriptor)")?;

        Ok(())
    }
}

#[derive(Serialize, Deserialize)]
pub struct ChromeOsDescriptor {
    pub dead_keys: IndexMap<ChromeOsKbdLayer, Vec<String>>,
    pub transforms: IndexMap<String, IndexMap<String, String>>,
    pub layers: IndexMap<ChromeOsKbdLayer, IndexMap<String, String>>,
}

impl fmt::Display for ChromeOsDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            to_string_pretty(&self.dead_keys).map_err(|_err| fmt::Error)?
        )?;
        write!(
            f,
            "{}",
            to_string_pretty(&self.transforms).map_err(|_err| fmt::Error)?
        )?;

        Ok(())
    }
}

pub struct GenerateChromeOs;

#[async_trait(?Send)]
impl BuildStep for GenerateChromeOs {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let background_file_path = output_path.join(BACKGROUND_FILE_NAME);
        let manifest_file_path = output_path.join(MANIFEST_FILE_NAME);

        let template = KEYBOARD_TEMPLATE.clone();

        let mut descriptor = IndexMap::new();

        let mut json_transforms: IndexMap<String, IndexMap<String, String>>;

        let mut manifest_input_components: Vec<ManifestInputComponent> = Vec::new();
        let mut display_name_map: IndexMap<LanguageTag, String> = IndexMap::new();
        // layout information is to be aggregated into a descriptor and then appended
        // to the end of the template
        for (language_tag, layout) in &bundle.layouts {
            if let Some(chromeos_target) = &layout.chrome_os {
                display_name_map.extend(layout.display_names.clone());

                let input_component = ManifestInputComponent::from_config(
                    language_tag.to_string(),
                    chromeos_target
                        .config
                        .locale
                        .as_ref()
                        .map(|x| x.clone())
                        .unwrap_or_else(|| DEFAULT_LONG_LOCALE.parse().unwrap()),
                    chromeos_target
                        .config
                        .xkb_layout
                        .clone()
                        .unwrap_or_else(|| DEFAULT_XKB_LAYOUT.to_string()),
                );

                manifest_input_components.push(input_component);

                let mut json_dead_keys = IndexMap::new();
                if let Some(dead_keys) = &chromeos_target.dead_keys {
                    for (dead_key_name, dead_key_list) in dead_keys {
                        json_dead_keys.insert(dead_key_name.clone(), dead_key_list.to_vec());
                    }
                }
                if let Some(_layout_transforms) = &layout.transforms {
                    let layout_file_path = bundle
                        .path
                        .join("layouts")
                        .join(format!("{}.yaml", layout.language_tag));

                    let yaml_file = std::fs::File::open(&layout_file_path).unwrap();
                    let original_layout_yaml: serde_yaml::Value =
                        serde_yaml::from_reader(yaml_file).unwrap();

                    let owning_transform = *&original_layout_yaml.get("transforms").unwrap();

                    json_transforms = serde_yaml::from_value(owning_transform.clone()).unwrap();
                } else {
                    json_transforms = IndexMap::new();
                }

                let layers = &chromeos_target.primary.layers;

                let mut json_layers = IndexMap::new();
                for (layer_name, key_map) in layers {
                    let key_map: Vec<String> = split_keys(&key_map);

                    tracing::debug!(
                        "iso len: {}; keymap len: {}",
                        CHROMEOS_KEYS.len(),
                        key_map.len()
                    );
                    if CHROMEOS_KEYS.len() > key_map.len() {
                        panic!(
                            r#"Provided layer does not have enough keys, expected {} keys but got {}, in {}:{}:{}:{:?}: \n{:?}"#,
                            CHROMEOS_KEYS.len(),
                            key_map.len(),
                            language_tag.to_string(),
                            "Windows",
                            "Primary",
                            layer_name,
                            key_map
                        );
                    }

                    let mut modifiers = IndexMap::new();
                    for (cursor, (_iso_key, modifier_name)) in CHROMEOS_KEYS.iter().enumerate() {
                        modifiers.insert(modifier_name.clone(), key_map[cursor].clone());
                    }

                    json_layers.insert(layer_name.clone(), modifiers);
                }

                descriptor.insert(
                    language_tag.clone(),
                    ChromeOsDescriptor {
                        dead_keys: json_dead_keys,
                        transforms: json_transforms,
                        layers: json_layers,
                    },
                );
            }
        }

        let background = ChromeOsBackground {
            template: template.to_string(),
            descriptor,
        };

        std::fs::write(
            output_path.join(background_file_path),
            background.to_string(),
        )
        .unwrap();

        if let Some(target) = &bundle.targets.chromeos {
            let manifest = ChromeOsManifest {
                name: "__MSG_name__".to_string(),
                version: target.build.clone(),
                version_name: target.version.clone(),
                manifest_version: 2,
                description: "__MSG_description__".to_string(),
                background: ManifestBackground {
                    scripts: vec![BACKGROUND_FILE_NAME.to_string()],
                },
                permissions: vec!["input".to_string()],
                input_components: manifest_input_components,
                default_locale: DEFAULT_LOCALE.to_string(),
                icons: ManifestIcons {
                    icon_16: "icon16.png".to_string(),
                    icon_48: "icon48.png".to_string(),
                    icon_128: "icon128.png".to_string(),
                },
            };

            std::fs::write(
                output_path.join(manifest_file_path),
                serde_json::to_string_pretty(&manifest).unwrap(),
            )
            .unwrap();

            let locales_path = output_path.join("_locales");
            std::fs::create_dir_all(&locales_path).unwrap();

            let default_locale_metadata = bundle
                .project
                .locales
                .get(&DEFAULT_LOCALE.to_string())
                .expect(&format!(
                    "{} must be present in the project.yaml file",
                    DEFAULT_LOCALE
                ));

            for (key, value) in display_name_map {
                println!("POPULATING LOCALES: {}: {}", key, value);
                let locale_path = locales_path.join(key.to_string());
                std::fs::create_dir_all(&locale_path).unwrap();

                let messages_path = locale_path.join("messages.json");
                let locale_metadata = bundle
                    .project
                    .locales
                    .get(&key.to_string())
                    .unwrap_or(default_locale_metadata);

                let mut locale_display_names: IndexMap<String, LocaleMessage> = IndexMap::new();
                for (layout_name, layout) in &bundle.layouts {
                    if let Some(_target) = &layout.chrome_os {
                        let display_name = layout.display_names.get(&key);

                        if let Some(display_name) = display_name {
                            let temp = KEYBOARD_NAMES
                                .get(&key.to_string())
                                .cloned()
                                .or_else(|| KEYBOARD_NAMES.get("en").cloned())
                                .expect(&format!(
                                    "no {} display name exists in {}.yaml",
                                    DEFAULT_LOCALE, layout_name
                                ));
                            locale_display_names.insert(
                                layout_name.to_string(),
                                LocaleMessage {
                                    message: temp.replace("{}", display_name),
                                },
                            );
                        }
                    }
                }

                std::fs::write(
                    output_path.join(messages_path),
                    serde_json::to_string_pretty(&Locale {
                        locales: locale_display_names,
                        name: LocaleMessage {
                            message: locale_metadata.name.clone(),
                        },
                        description: LocaleMessage {
                            message: locale_metadata.description.clone(),
                        },
                    })
                    .unwrap(),
                )
                .unwrap();
            }
        }
    }
}
<commit_msg>fn to convert locale string to chrome fmt<commit_after>use std::{fmt, path::Path};

use async_trait::async_trait;
use indexmap::IndexMap;
use language_tags::LanguageTag;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use serde_json::to_string_pretty;

use crate::build::chromeos::keymap::CHROMEOS_KEYS;
use crate::build::chromeos::manifest::{
    ChromeOsManifest, ManifestBackground, ManifestIcons, ManifestInputComponent,
};
use crate::{
    build::BuildStep,
    bundle::{layout::chrome::ChromeOsKbdLayer, KbdgenBundle},
    util::split_keys,
};

const BACKGROUND_FILE_NAME: &str = "background.js";
const MANIFEST_FILE_NAME: &str = "manifest.json";
const KEYBOARD_TEMPLATE: &str = include_str!("../../../resources/template-chromeos-keyboard.js");
const DEFAULT_LOCALE: &str = "en";

const DEFAULT_LONG_LOCALE: &str = "en-US";
const DEFAULT_XKB_LAYOUT: &str = "us";

const KEYBOARD_NAMES: Lazy<IndexMap<String, String>> = Lazy::new(|| {
    let mut map = IndexMap::new();

    {
        let arr = [
            ("nb", "{} tastatur"),
            ("no", "{} tastatur"),
            ("nn", "{} tastatur"),
            ("da", "{} tastatur"),
            ("sv", "{} tangentbord"),
            ("en", "{} keyboard"),
            ("fi", "{} näppäimistö"),
        ];

        for (key, value) in arr {
            map.insert(key.to_string(), value.to_string());
        }
    }

    map
});

// LOCALE START

#[derive(Serialize, Deserialize)]
pub struct LocaleMessage {
    message: String,
}

#[derive(Serialize, Deserialize)]
pub struct Locale {
    #[serde(flatten)]
    locales: IndexMap<String, LocaleMessage>,
    name: LocaleMessage,
    description: LocaleMessage,
}

// LOCALE END

#[derive(Serialize, Deserialize)]
pub struct ChromeOsBackground {
    template: String,
    descriptor: IndexMap<LanguageTag, ChromeOsDescriptor>,
}

impl fmt::Display for ChromeOsBackground {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}\n", self.template)?;
        write!(f, "\n")?;
        write!(
            f,
            "const descriptor = {}\n",
            to_string_pretty(&self.descriptor).map_err(|_err| fmt::Error)?
        )?;
        write!(f, "\nKeyboard.install(descriptor)")?;

        Ok(())
    }
}

#[derive(Serialize, Deserialize)]
pub struct ChromeOsDescriptor {
    pub dead_keys: IndexMap<ChromeOsKbdLayer, Vec<String>>,
    pub transforms: IndexMap<String, IndexMap<String, String>>,
    pub layers: IndexMap<ChromeOsKbdLayer, IndexMap<String, String>>,
}

impl fmt::Display for ChromeOsDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            to_string_pretty(&self.dead_keys).map_err(|_err| fmt::Error)?
        )?;
        write!(
            f,
            "{}",
            to_string_pretty(&self.transforms).map_err(|_err| fmt::Error)?
        )?;

        Ok(())
    }
}

fn to_chrome_locale(locale: String) -> String {
    locale.replace("-", "_")
}

pub struct GenerateChromeOs;

#[async_trait(?Send)]
impl BuildStep for GenerateChromeOs {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let background_file_path = output_path.join(BACKGROUND_FILE_NAME);
        let manifest_file_path = output_path.join(MANIFEST_FILE_NAME);

        let template = KEYBOARD_TEMPLATE.clone();

        let mut descriptor = IndexMap::new();

        let mut json_transforms: IndexMap<String, IndexMap<String, String>>;

        let mut manifest_input_components: Vec<ManifestInputComponent> = Vec::new();
        let mut display_name_map: IndexMap<LanguageTag, String> = IndexMap::new();
        // layout information is to be aggregated into a descriptor and then appended
        // to the end of the template
        for (language_tag, layout) in &bundle.layouts {
            if let Some(chromeos_target) = &layout.chrome_os {
                display_name_map.extend(layout.display_names.clone());

                let input_component = ManifestInputComponent::from_config(
                    language_tag.to_string(),
                    chromeos_target
                        .config
                        .locale
                        .as_ref()
                        .map(|x| x.clone())
                        .unwrap_or_else(|| DEFAULT_LONG_LOCALE.parse().unwrap()),
                    chromeos_target
                        .config
                        .xkb_layout
                        .clone()
                        .unwrap_or_else(|| DEFAULT_XKB_LAYOUT.to_string()),
                );

                manifest_input_components.push(input_component);

                let mut json_dead_keys = IndexMap::new();
                if let Some(dead_keys) = &chromeos_target.dead_keys {
                    for (dead_key_name, dead_key_list) in dead_keys {
                        json_dead_keys.insert(dead_key_name.clone(), dead_key_list.to_vec());
                    }
                }
                if let Some(_layout_transforms) = &layout.transforms {
                    let layout_file_path = bundle
                        .path
                        .join("layouts")
                        .join(format!("{}.yaml", layout.language_tag));

                    let yaml_file = std::fs::File::open(&layout_file_path).unwrap();
                    let original_layout_yaml: serde_yaml::Value =
                        serde_yaml::from_reader(yaml_file).unwrap();

                    let owning_transform = *&original_layout_yaml.get("transforms").unwrap();

                    json_transforms = serde_yaml::from_value(owning_transform.clone()).unwrap();
                } else {
                    json_transforms = IndexMap::new();
                }

                let layers = &chromeos_target.primary.layers;

                let mut json_layers = IndexMap::new();
                for (layer_name, key_map) in layers {
                    let key_map: Vec<String> = split_keys(&key_map);

                    tracing::debug!(
                        "iso len: {}; keymap len: {}",
                        CHROMEOS_KEYS.len(),
                        key_map.len()
                    );
                    if CHROMEOS_KEYS.len() > key_map.len() {
                        panic!(
                            r#"Provided layer does not have enough keys, expected {} keys but got {}, in {}:{}:{}:{:?}: \n{:?}"#,
                            CHROMEOS_KEYS.len(),
                            key_map.len(),
                            language_tag.to_string(),
                            "Windows",
                            "Primary",
                            layer_name,
                            key_map
                        );
                    }

                    let mut modifiers = IndexMap::new();
                    for (cursor, (_iso_key, modifier_name)) in CHROMEOS_KEYS.iter().enumerate() {
                        modifiers.insert(modifier_name.clone(), key_map[cursor].clone());
                    }

                    json_layers.insert(layer_name.clone(), modifiers);
                }

                descriptor.insert(
                    language_tag.clone(),
                    ChromeOsDescriptor {
                        dead_keys: json_dead_keys,
                        transforms: json_transforms,
                        layers: json_layers,
                    },
                );
            }
        }

        let background = ChromeOsBackground {
            template: template.to_string(),
            descriptor,
        };

        std::fs::write(
            output_path.join(background_file_path),
            background.to_string(),
        )
        .unwrap();

        if let Some(target) = &bundle.targets.chromeos {
            let manifest = ChromeOsManifest {
                name: "__MSG_name__".to_string(),
                version: target.build.clone(),
                version_name: target.version.clone(),
                manifest_version: 2,
                description: "__MSG_description__".to_string(),
                background: ManifestBackground {
                    scripts: vec![BACKGROUND_FILE_NAME.to_string()],
                },
                permissions: vec!["input".to_string()],
                input_components: manifest_input_components,
                default_locale: DEFAULT_LOCALE.to_string(),
                icons: ManifestIcons {
                    icon_16: "icon16.png".to_string(),
                    icon_48: "icon48.png".to_string(),
                    icon_128: "icon128.png".to_string(),
                },
            };

            std::fs::write(
                output_path.join(manifest_file_path),
                serde_json::to_string_pretty(&manifest).unwrap(),
            )
            .unwrap();

            let locales_path = output_path.join("_locales");
            std::fs::create_dir_all(&locales_path).unwrap();

            let default_locale_metadata = bundle
                .project
                .locales
                .get(&DEFAULT_LOCALE.to_string())
                .expect(&format!(
                    "{} must be present in the project.yaml file",
                    DEFAULT_LOCALE
                ));

            for (key, value) in display_name_map {
                let locale_path = locales_path.join(to_chrome_locale(key.to_string()));
                std::fs::create_dir_all(&locale_path).unwrap();

                let messages_path = locale_path.join("messages.json");
                let locale_metadata = bundle
                    .project
                    .locales
                    .get(&key.to_string())
                    .unwrap_or(default_locale_metadata);

                let mut locale_display_names: IndexMap<String, LocaleMessage> = IndexMap::new();
                for (layout_name, layout) in &bundle.layouts {
                    if let Some(_target) = &layout.chrome_os {
                        let display_name = layout.display_names.get(&key);

                        if let Some(display_name) = display_name {
                            let temp = KEYBOARD_NAMES
                                .get(&key.to_string())
                                .cloned()
                                .or_else(|| KEYBOARD_NAMES.get("en").cloned())
                                .expect(&format!(
                                    "no {} display name exists in {}.yaml",
                                    DEFAULT_LOCALE, layout_name
                                ));
                            locale_display_names.insert(
                                to_chrome_locale(layout_name.to_string()),
                                LocaleMessage {
                                    message: temp.replace("{}", display_name),
                                },
                            );
                        }
                    }
                }

                std::fs::write(
                    output_path.join(messages_path),
                    serde_json::to_string_pretty(&Locale {
                        locales: locale_display_names,
                        name: LocaleMessage {
                            message: locale_metadata.name.clone(),
                        },
                        description: LocaleMessage {
                            message: locale_metadata.description.clone(),
                        },
                    })
                    .unwrap(),
                )
                .unwrap();
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::default::Default;

use csv::{ByteString, Error, Result};
use rustc_serialize::Decoder;

/// Decoder to extract field names from types that implement
/// `rustc_serialize::Decodable`.
#[derive(Debug)]
pub struct FieldNamesDecoder {
    stack: Vec<ByteString>,
    popped: usize,
    field_names: Vec<ByteString>,
}

impl FieldNamesDecoder {
    /// Creates a new `FieldNamesDecoder` from a record of byte strings.
    pub fn new(mut bytes: Vec<ByteString>) -> FieldNamesDecoder {
        bytes.reverse();
        FieldNamesDecoder {
            stack: bytes,
            popped: 0,
            field_names: Vec::new(),
        }
    }

    pub fn into_field_names(self) -> Vec<ByteString> {
        self.field_names
    }

    fn len(&self) -> usize {
        self.stack.len()
    }
}

impl FieldNamesDecoder {
    fn pop(&mut self) -> Result<ByteString> {
        self.popped += 1;
        match self.stack.pop() {
            None => {
                self.err(format!("Expected a record with length at least {}, but got a record \
                                  with length {}.",
                                 self.popped,
                                 self.popped - 1))
            }
            Some(bytes) => Ok(bytes),
        }
    }

    fn pop_string(&mut self) -> Result<String> {
        String::from_utf8(try!(self.pop())).map_err(|bytes| {
            Error::Decode(format!("Could not convert bytes '{:?}' to UTF-8.", bytes))
        })
    }

    fn push(&mut self, s: ByteString) {
        self.stack.push(s);
    }

    fn push_string(&mut self, s: String) {
        self.push(s.into_bytes());
    }

    fn err<'a, T, S>(&self, msg: S) -> Result<T>
        where S: Into<String>
    {
        Err(Error::Decode(msg.into()))
    }
}

impl Decoder for FieldNamesDecoder {
    type Error = Error;

    fn error(&mut self, err: &str) -> Error {
        Error::Decode(err.into())
    }

    fn read_nil(&mut self) -> Result<()> {
        unimplemented!()
    }

    fn read_usize(&mut self) -> Result<usize> {
        Ok(Default::default())
    }

    fn read_u64(&mut self) -> Result<u64> {
        Ok(Default::default())
    }

    fn read_u32(&mut self) -> Result<u32> {
        Ok(Default::default())
    }

    fn read_u16(&mut self) -> Result<u16> {
        Ok(Default::default())
    }

    fn read_u8(&mut self) -> Result<u8> {
        Ok(Default::default())
    }

    fn read_isize(&mut self) -> Result<isize> {
        Ok(Default::default())
    }

    fn read_i64(&mut self) -> Result<i64> {
        Ok(Default::default())
    }

    fn read_i32(&mut self) -> Result<i32> {
        Ok(Default::default())
    }

    fn read_i16(&mut self) -> Result<i16> {
        Ok(Default::default())
    }

    fn read_i8(&mut self) -> Result<i8> {
        Ok(Default::default())
    }

    fn read_bool(&mut self) -> Result<bool> {
        Ok(Default::default())
    }

    fn read_f64(&mut self) -> Result<f64> {
        Ok(Default::default())
    }

    fn read_f32(&mut self) -> Result<f32> {
        Ok(Default::default())
    }

    fn read_char(&mut self) -> Result<char> {
        Ok(Default::default())
    }

    fn read_str(&mut self) -> Result<String> {
        Ok(Default::default())
    }

    fn read_enum<T, F>(&mut self, _: &str, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F) -> Result<T>
        where F: FnMut(&mut Self, usize) -> Result<T>
    {
        for i in 0..names.len() {
            let cur = self.pop_string()?;
            self.push_string(cur.clone());
            match f(self, i) {
                Ok(v) => return Ok(v),
                Err(_) => {
                    self.push_string(cur);
                }
            }
        }
        self.err(format!("Could not load value into any variant in {:?}", names))
    }

    fn read_enum_variant_arg<T, F>(&mut self, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_enum_struct_variant<T, F>(&mut self, names: &[&str], f: F) -> Result<T>
        where F: FnMut(&mut Self, usize) -> Result<T>
    {
        self.read_enum_variant(names, f)
    }

    fn read_enum_struct_variant_field<T, F>(&mut self, _: &str, f_idx: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        self.read_enum_variant_arg(f_idx, f)
    }

    fn read_struct<T, F>(&mut self, _: &str, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_struct_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        // Heuristic to ignore field names in tuple structs.
        // See https://github.com/rust-lang/rust/issues/15659
        // and https://github.com/rust-lang/rust/issues/17158
        if f_name != format!("_field{}", f_idx) {
            self.field_names.push(f_name.to_owned().into_bytes());
        }
        f(self)
    }

    fn read_tuple<T, F>(&mut self, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_tuple_arg<T, F>(&mut self, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_tuple_struct<T, F>(&mut self, _: &str, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!()
    }

    fn read_tuple_struct_arg<T, F>(&mut self, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!()
    }

    fn read_option<T, F>(&mut self, mut f: F) -> Result<T>
        where F: FnMut(&mut Self, bool) -> Result<T>
    {
        let s = try!(self.pop_string());
        if s.is_empty() {
            f(self, false)
        } else {
            self.push_string(s);
            match f(self, true) {
                Ok(v) => Ok(v),
                Err(_) => f(self, false),
            }
        }
    }

    fn read_seq<T, F>(&mut self, f: F) -> Result<T>
        where F: FnOnce(&mut Self, usize) -> Result<T>
    {
        let len = self.len();
        f(self, len)
    }

    fn read_seq_elt<T, F>(&mut self, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_map<T, F>(&mut self, _: F) -> Result<T>
        where F: FnOnce(&mut Self, usize) -> Result<T>
    {
        unimplemented!()
    }

    fn read_map_elt_key<T, F>(&mut self, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!()
    }

    fn read_map_elt_val<T, F>(&mut self, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!()
    }
}
<commit_msg>Remove unnecessary decoder logic for enums<commit_after>use std::default::Default;

use csv::{ByteString, Error, Result};
use rustc_serialize::Decoder;

/// Decoder to extract field names from types that implement
/// `rustc_serialize::Decodable`.
#[derive(Debug)]
pub struct FieldNamesDecoder {
    field_names: Vec<ByteString>,
}

impl FieldNamesDecoder {
    /// Creates a new `FieldNamesDecoder` from a record of byte strings.
    pub fn new(mut bytes: Vec<ByteString>) -> FieldNamesDecoder {
        bytes.reverse();
        FieldNamesDecoder {
            field_names: Vec::new(),
        }
    }

    pub fn into_field_names(self) -> Vec<ByteString> {
        self.field_names
    }
}

impl Decoder for FieldNamesDecoder {
    type Error = Error;

    fn error(&mut self, err: &str) -> Error {
        Error::Decode(err.into())
    }

    fn read_nil(&mut self) -> Result<()> {
        unimplemented!()
    }

    fn read_usize(&mut self) -> Result<usize> {
        Ok(Default::default())
    }

    fn read_u64(&mut self) -> Result<u64> {
        Ok(Default::default())
    }

    fn read_u32(&mut self) -> Result<u32> {
        Ok(Default::default())
    }

    fn read_u16(&mut self) -> Result<u16> {
        Ok(Default::default())
    }

    fn read_u8(&mut self) -> Result<u8> {
        Ok(Default::default())
    }

    fn read_isize(&mut self) -> Result<isize> {
        Ok(Default::default())
    }

    fn read_i64(&mut self) -> Result<i64> {
        Ok(Default::default())
    }

    fn read_i32(&mut self) -> Result<i32> {
        Ok(Default::default())
    }

    fn read_i16(&mut self) -> Result<i16> {
        Ok(Default::default())
    }

    fn read_i8(&mut self) -> Result<i8> {
        Ok(Default::default())
    }

    fn read_bool(&mut self) -> Result<bool> {
        Ok(Default::default())
    }

    fn read_f64(&mut self) -> Result<f64> {
        Ok(Default::default())
    }

    fn read_f32(&mut self) -> Result<f32> {
        Ok(Default::default())
    }

    fn read_char(&mut self) -> Result<char> {
        Ok(Default::default())
    }

    fn read_str(&mut self) -> Result<String> {
        Ok(Default::default())
    }

    fn read_enum<T, F>(&mut self, _: &str, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_enum_variant<T, F>(&mut self, _: &[&str], mut f: F) -> Result<T>
        where F: FnMut(&mut Self, usize) -> Result<T>
    {
        f(self, 0)
    }

    fn read_enum_variant_arg<T, F>(&mut self, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_enum_struct_variant<T, F>(&mut self, names: &[&str], f: F) -> Result<T>
        where F: FnMut(&mut Self, usize) -> Result<T>
    {
        self.read_enum_variant(names, f)
    }

    fn read_enum_struct_variant_field<T, F>(&mut self, _: &str, f_idx: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        self.read_enum_variant_arg(f_idx, f)
    }

    fn read_struct<T, F>(&mut self, _: &str, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_struct_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        // Heuristic to ignore field names in tuple structs.
        // See https://github.com/rust-lang/rust/issues/15659
        // and https://github.com/rust-lang/rust/issues/17158
        if f_name != format!("_field{}", f_idx) {
            self.field_names.push(f_name.to_owned().into_bytes());
        }
        f(self)
    }

    fn read_tuple<T, F>(&mut self, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_tuple_arg<T, F>(&mut self, _: usize, f: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        f(self)
    }

    fn read_tuple_struct<T, F>(&mut self, _: &str, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!()
    }

    fn read_tuple_struct_arg<T, F>(&mut self, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!()
    }

    fn read_option<T, F>(&mut self, mut f: F) -> Result<T>
        where F: FnMut(&mut Self, bool) -> Result<T>
    {
        f(self, false)
    }

    fn read_seq<T, F>(&mut self, _: F) -> Result<T>
        where F: FnOnce(&mut Self, usize) -> Result<T>
    {
        unimplemented!();
    }

    fn read_seq_elt<T, F>(&mut self, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!();
    }

    fn read_map<T, F>(&mut self, _: F) -> Result<T>
        where F: FnOnce(&mut Self, usize) -> Result<T>
    {
        unimplemented!()
    }

    fn read_map_elt_key<T, F>(&mut self, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!()
    }

    fn read_map_elt_val<T, F>(&mut self, _: usize, _: F) -> Result<T>
        where F: FnOnce(&mut Self) -> Result<T>
    {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>use crate::{task::JoinHandle, util::error::CONTEXT_MISSING_ERROR};

use std::future::Future;

cfg_rt! {
    /// Spawns a new asynchronous task, returning a
    /// [`JoinHandle`](super::JoinHandle) for it.
    ///
    /// Spawning a task enables the task to execute concurrently to other tasks. The
    /// spawned task may execute on the current thread, or it may be sent to a
    /// different thread to be executed. The specifics depend on the current
    /// [`Runtime`](crate::runtime::Runtime) configuration.
    ///
    /// There is no guarantee that a spawned task will execute to completion.
    /// When a runtime is shutdown, all outstanding tasks are dropped,
    /// regardless of the lifecycle of that task.
    ///
    /// This function must be called from the context of a Tokio runtime. Tasks running on
    /// the Tokio runtime are always inside its context, but you can also enter the context
    /// using the [`Runtime::enter`](crate::runtime::Runtime::enter()) method.
    ///
    /// # Examples
    ///
    /// In this example, a server is started and `spawn` is used to start a new task
    /// that processes each received connection.
    ///
    /// ```no_run
    /// use tokio::net::{TcpListener, TcpStream};
    ///
    /// use std::io;
    ///
    /// async fn process(socket: TcpStream) {
    ///     // ...
    /// # drop(socket);
    /// }
    ///
    /// #[tokio::main]
    /// async fn main() -> io::Result<()> {
    ///     let listener = TcpListener::bind("127.0.0.1:8080").await?;
    ///
    ///     loop {
    ///         let (socket, _) = listener.accept().await?;
    ///
    ///         tokio::spawn(async move {
    ///             // Process each socket concurrently.
    ///             process(socket).await
    ///         });
    ///     }
    /// }
    /// ```
    ///
    /// # Panics
    ///
    /// Panics if called from **outside** of the Tokio runtime.
    ///
    /// # Using `!Send` values from a task
    ///
    /// The task supplied to `spawn` must implement `Send`. However, it is
    /// possible to **use** `!Send` values from the task as long as they only
    /// exist between calls to `.await`.
    ///
    /// For example, this will work:
    ///
    /// ```
    /// use tokio::task;
    ///
    /// use std::rc::Rc;
    ///
    /// fn use_rc(rc: Rc<()>) {
    ///     // Do stuff w/ rc
    /// # drop(rc);
    /// }
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     tokio::spawn(async {
    ///         // Force the `Rc` to stay in a scope with no `.await`
    ///         {
    ///             let rc = Rc::new(());
    ///             use_rc(rc.clone());
    ///         }
    ///
    ///         task::yield_now().await;
    ///     }).await.unwrap();
    /// }
    /// ```
    ///
    /// This will **not** work:
    ///
    /// ```compile_fail
    /// use tokio::task;
    ///
    /// use std::rc::Rc;
    ///
    /// fn use_rc(rc: Rc<()>) {
    ///     // Do stuff w/ rc
    /// # drop(rc);
    /// }
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     tokio::spawn(async {
    ///         let rc = Rc::new(());
    ///
    ///         task::yield_now().await;
    ///
    ///         use_rc(rc.clone());
    ///     }).await.unwrap();
    /// }
    /// ```
    ///
    /// Holding on to a `!Send` value across calls to `.await` will result in
    /// an unfriendly compile error message similar to:
    ///
    /// ```text
    /// `[... some type ...]` cannot be sent between threads safely
    /// ```
    ///
    /// or:
    ///
    /// ```text
    /// error[E0391]: cycle detected when processing `main`
    /// ```
    #[cfg_attr(tokio_track_caller, track_caller)]
    pub fn spawn<T>(future: T) -> JoinHandle<T::Output>
    where
        T: Future + Send + 'static,
        T::Output: Send + 'static,
    {
        spawn_inner(future, None)
    }

    #[cfg_attr(tokio_track_caller, track_caller)]
    pub(super) fn spawn_inner<T>(future: T, name: Option<&str>) -> JoinHandle<T::Output>
    where
        T: Future + Send + 'static,
        T::Output: Send + 'static,
    {
        let spawn_handle = crate::runtime::context::spawn_handle().expect(CONTEXT_MISSING_ERROR);
        let task = crate::util::trace::task(future, "task", name);
        spawn_handle.spawn(task)
    }
}
<commit_msg>task: quickly send task to heap on debug mode (#4009)<commit_after>use crate::{task::JoinHandle, util::error::CONTEXT_MISSING_ERROR};

use std::future::Future;

cfg_rt! {
    /// Spawns a new asynchronous task, returning a
    /// [`JoinHandle`](super::JoinHandle) for it.
    ///
    /// Spawning a task enables the task to execute concurrently to other tasks. The
    /// spawned task may execute on the current thread, or it may be sent to a
    /// different thread to be executed. The specifics depend on the current
    /// [`Runtime`](crate::runtime::Runtime) configuration.
    ///
    /// There is no guarantee that a spawned task will execute to completion.
    /// When a runtime is shutdown, all outstanding tasks are dropped,
    /// regardless of the lifecycle of that task.
    ///
    /// This function must be called from the context of a Tokio runtime. Tasks running on
    /// the Tokio runtime are always inside its context, but you can also enter the context
    /// using the [`Runtime::enter`](crate::runtime::Runtime::enter()) method.
    ///
    /// # Examples
    ///
    /// In this example, a server is started and `spawn` is used to start a new task
    /// that processes each received connection.
    ///
    /// ```no_run
    /// use tokio::net::{TcpListener, TcpStream};
    ///
    /// use std::io;
    ///
    /// async fn process(socket: TcpStream) {
    ///     // ...
    /// # drop(socket);
    /// }
    ///
    /// #[tokio::main]
    /// async fn main() -> io::Result<()> {
    ///     let listener = TcpListener::bind("127.0.0.1:8080").await?;
    ///
    ///     loop {
    ///         let (socket, _) = listener.accept().await?;
    ///
    ///         tokio::spawn(async move {
    ///             // Process each socket concurrently.
    ///             process(socket).await
    ///         });
    ///     }
    /// }
    /// ```
    ///
    /// # Panics
    ///
    /// Panics if called from **outside** of the Tokio runtime.
    ///
    /// # Using `!Send` values from a task
    ///
    /// The task supplied to `spawn` must implement `Send`. However, it is
    /// possible to **use** `!Send` values from the task as long as they only
    /// exist between calls to `.await`.
    ///
    /// For example, this will work:
    ///
    /// ```
    /// use tokio::task;
    ///
    /// use std::rc::Rc;
    ///
    /// fn use_rc(rc: Rc<()>) {
    ///     // Do stuff w/ rc
    /// # drop(rc);
    /// }
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     tokio::spawn(async {
    ///         // Force the `Rc` to stay in a scope with no `.await`
    ///         {
    ///             let rc = Rc::new(());
    ///             use_rc(rc.clone());
    ///         }
    ///
    ///         task::yield_now().await;
    ///     }).await.unwrap();
    /// }
    /// ```
    ///
    /// This will **not** work:
    ///
    /// ```compile_fail
    /// use tokio::task;
    ///
    /// use std::rc::Rc;
    ///
    /// fn use_rc(rc: Rc<()>) {
    ///     // Do stuff w/ rc
    /// # drop(rc);
    /// }
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     tokio::spawn(async {
    ///         let rc = Rc::new(());
    ///
    ///         task::yield_now().await;
    ///
    ///         use_rc(rc.clone());
    ///     }).await.unwrap();
    /// }
    /// ```
    ///
    /// Holding on to a `!Send` value across calls to `.await` will result in
    /// an unfriendly compile error message similar to:
    ///
    /// ```text
    /// `[... some type ...]` cannot be sent between threads safely
    /// ```
    ///
    /// or:
    ///
    /// ```text
    /// error[E0391]: cycle detected when processing `main`
    /// ```
    #[cfg_attr(tokio_track_caller, track_caller)]
    pub fn spawn<T>(future: T) -> JoinHandle<T::Output>
    where
        T: Future + Send + 'static,
        T::Output: Send + 'static,
    {
        // preventing stack overflows on debug mode, by quickly sending the
        // task to the heap.
        if cfg!(debug_assertions) && std::mem::size_of::<T>() > 2048 {
            spawn_inner(Box::pin(future), None)
        } else {
            spawn_inner(future, None)
        }
    }

    #[cfg_attr(tokio_track_caller, track_caller)]
    pub(super) fn spawn_inner<T>(future: T, name: Option<&str>) -> JoinHandle<T::Output>
    where
        T: Future + Send + 'static,
        T::Output: Send + 'static,
    {
        let spawn_handle = crate::runtime::context::spawn_handle().expect(CONTEXT_MISSING_ERROR);
        let task = crate::util::trace::task(future, "task", name);
        spawn_handle.spawn(task)
    }
}
<|endoftext|>