Files
aho_corasick
anyhow
drone_config
drone_core
drone_core_macros
drone_ctypes
drone_macros_core
futures
futures_channel
futures_core
futures_io
futures_macro
futures_sink
futures_task
futures_util
if_chain
inflector
cases
camelcase
case
classcase
kebabcase
pascalcase
screamingsnakecase
sentencecase
snakecase
tablecase
titlecase
traincase
numbers
deordinalize
ordinalize
string
constants
deconstantize
demodulize
pluralize
singularize
suffix
foreignkey
lazy_static
memchr
pin_project_lite
pin_utils
proc_macro2
proc_macro_hack
proc_macro_nested
quote
regex
regex_syntax
serde
serde_derive
syn
toml
typenum
unicode_xid
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! This module provides interface to wrap a stackful synchronous code into an
//! asynchronous command loop.
//!
//! **NOTE** A Drone platform crate may re-export this module with its own
//! additions under the same name, in which case it should be used instead.

#![allow(clippy::wildcard_imports)]

use crate::{fib, fib::Fiber};
use core::{future::Future, mem::ManuallyDrop, pin::Pin};

type SessFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

/// The trait for declaring a synchronous command loop.
///
/// This trait uses only associated items, thus it doesn't require the type to
/// ever be instantiated.
pub trait ProcLoop: Send + 'static {
    /// Token type that allows suspending the task while waiting for a request
    /// result.
    type Context: Context<Self::Req, Self::ReqRes>;

    /// `enum` of all possible commands.
    type Cmd: Send + 'static;

    /// `union` of all possible command results.
    type CmdRes: Send + 'static;

    /// `enum` of all possible requests.
    type Req: Send + 'static;

    /// `union` of all possible request results.
    type ReqRes: Send + 'static;

    /// Size of the process stack in bytes.
    const STACK_SIZE: usize;

    /// The commands runner.
    ///
    /// See [`ProcLoop`] for examples.
    fn run_cmd(cmd: Self::Cmd, context: Self::Context) -> Self::CmdRes;

    /// Runs on the process creation.
    #[inline]
    fn on_create() {}

    /// Runs inside the synchronous context before the command loop.
    #[inline]
    fn on_enter() {}

    /// Runs on the process destruction.
    #[inline]
    fn on_drop() {}
}

/// A session type for the synchronous command loop [`ProcLoop`].
///
/// A type that implements this trait should wrap the fiber for the command
/// loop.
pub trait Sess: Send {
    /// The command loop interface.
    type ProcLoop: ProcLoop;

    /// Fiber that runs the command loop.
    type Fiber: Fiber<
            Input = In<<Self::ProcLoop as ProcLoop>::Cmd, <Self::ProcLoop as ProcLoop>::ReqRes>,
            Yield = Out<<Self::ProcLoop as ProcLoop>::Req, <Self::ProcLoop as ProcLoop>::CmdRes>,
            Return = !,
        > + Send;

    /// Request error type.
    type Error: Send;

    /// Returns a pinned mutable reference to the fiber.
    fn fib(&mut self) -> Pin<&mut Self::Fiber>;

    /// Returns a future that will return a result for the request `req`.
    fn run_req(
        &mut self,
        req: <Self::ProcLoop as ProcLoop>::Req,
    ) -> SessFuture<'_, Result<<Self::ProcLoop as ProcLoop>::ReqRes, Self::Error>>;

    /// Returns a future that will return a result for the command `cmd`.
    fn cmd(
        &mut self,
        cmd: <Self::ProcLoop as ProcLoop>::Cmd,
    ) -> SessFuture<'_, Result<<Self::ProcLoop as ProcLoop>::CmdRes, Self::Error>> {
        let mut input = In::from_cmd(cmd);
        Box::pin(async move {
            loop {
                let fib::Yielded(output) = self.fib().resume(input);
                input = match output {
                    Out::Req(req) => In::from_req_res(self.run_req(req).await?),
                    Out::CmdRes(res) => break Ok(res),
                }
            }
        })
    }
}

/// A token that allows suspending synchronous code.
pub trait Context<Req, ReqRes>: Copy + 'static {
    /// Creates a new token.
    ///
    /// # Safety
    ///
    /// It is unsafe to create a token inside an inappropriate context.
    unsafe fn new() -> Self;

    /// Makes a new request `req`.
    ///
    /// This method suspends execution of the current task allowing to escape
    /// from synchronous code.
    fn req(self, req: Req) -> ReqRes;
}

/// [`Sess::Fiber`] input.
///
/// See also [`Out`].
pub union In<Cmd, ReqRes> {
    /// Command to run by the command loop.
    cmd: ManuallyDrop<Cmd>,
    /// Result for the last request.
    req_res: ManuallyDrop<ReqRes>,
}

/// [`Sess::Fiber`] output.
///
/// See also [`In`].
pub enum Out<Req, CmdRes> {
    /// Request that the command loop is waiting for.
    Req(Req),
    /// Result for the last command.
    CmdRes(CmdRes),
}

impl<Cmd, ReqRes> In<Cmd, ReqRes> {
    /// Creates a new command input.
    pub fn from_cmd(cmd: Cmd) -> Self {
        Self { cmd: ManuallyDrop::new(cmd) }
    }

    /// Creates a new request result input.
    pub fn from_req_res(req_res: ReqRes) -> Self {
        Self { req_res: ManuallyDrop::new(req_res) }
    }

    /// Interprets the input as a command.
    ///
    /// # Safety
    ///
    /// Whether the input is really a command object is unchecked.
    pub unsafe fn into_cmd(self) -> Cmd {
        ManuallyDrop::into_inner(unsafe { self.cmd })
    }

    /// Interprets the input as a request result.
    ///
    /// # Safety
    ///
    /// Whether the input is really a request result object is unchecked.
    pub unsafe fn into_req_res(self) -> ReqRes {
        ManuallyDrop::into_inner(unsafe { self.req_res })
    }
}