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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
use alloc::alloc::{dealloc, Layout};
use alloc::boxed::Box;
use core::default::Default;
use core::future::Future;
use core::pin::Pin;
use core::task::{self, Poll};
use core::{mem, ptr};

// Are auto implement. Is for document
#[cfg(nightly)]
use core::marker::{Send, Sync};

use crate::{stc_to_ptr, Context};

pub type Return<T> = Pin<Box<dyn Future<Output = T> + 'static>>;

/// App are object which encapsulate state and behavior
///
/// App communicate exclusively by directional exchanging messages.
// TODO: derive
pub trait App: Default + 'static {
    type BlackBox;
    type Output: 'static;
    type Message: 'static;

    /// TODO
    fn __render(&mut self, _addr: A<Self>) {}

    /// TODO
    fn __hydrate(&mut self, addr: A<Self>) -> Return<Self::Output>;

    /// TODO
    fn __dispatch(&mut self, msg: Self::Message, addr: A<Self>);
}

/// The address of App
pub struct Addr<A: App>(pub(crate) Context<A>);

/// Macro to create a `Runtime<App>`
///
/// Use in the main thread
#[macro_export]
macro_rules! run {
    ($ty:ty) => {
        unsafe { $crate::A::run(<$ty as core::default::Default>::default()) }
    };
}

/// DeLorean for your app. Easy and safe traveling to the future in your thread and the nightly
///
/// No Send and No Sync wrapper static reference
pub struct A<I: App>(&'static Addr<I>);
pub use self::A as DeLorean;

#[cfg(nightly)]
impl<I: App> !Send for A<I> {}
#[cfg(nightly)]
impl<I: App> !Sync for A<I> {}

impl<I: App> Clone for A<I> {
    #[inline(always)]
    fn clone(&self) -> Self {
        A(self.0)
    }
}

impl<I: App> Copy for A<I> {}

impl<I: App> A<I> {
    /// Make new Address for App and run it
    ///
    /// # Safety
    /// Can broke needed atomicity of unique references and queue pop
    pub unsafe fn run(a: I) -> Runtime<I> {
        let addr = A(Addr::new(a));
        // SAFETY: only run one time
        let hydrate = addr.hydrate();
        Runtime(hydrate, Dispatch(addr))
    }

    /// Dealloc Address
    /// Probably if you are here, you do NOT need this method.
    ///
    /// Use in testing or for awesome fail recovery features
    ///
    /// # Safety
    /// Broke `'static` lifetime and all copies are nothing.
    /// Make sure your runtime is ended BEFORE dealloc app.
    /// World could explode
    pub unsafe fn dealloc(self) {
        self.0.dealloc();
    }

    /// Sends a message
    ///
    /// The message is always queued
    pub fn send(self, msg: I::Message) {
        self.ctx().push(msg);
    }

    /// Hydrate app
    #[inline]
    unsafe fn hydrate(self) -> Hydrate<I> {
        let ctx = self.ctx();
        debug_assert!(!ctx.is_ready());
        // SAFETY: only use one time
        let fut = ctx.app().__hydrate(self);
        ctx.ready(true);
        Hydrate(fut)
    }

    #[inline]
    fn ctx(&self) -> &Context<I> {
        &(self.0).0
    }
}

/// Recover the owner of app and dealloc Context
///
/// Basically you can recovery your App with a version of your current state
///
/// # Safety
/// Ultra unsafe function, NEVER call inside your App, Only Outside.
/// Current is replace for `Default::default()`, check it's correctly with yours expected behaviour
/// Make sure to ALL your runtime is ended.
///
/// Drop all your old `DeLorean<I>` references
///
pub unsafe fn recovery<I: App>(addr: A<I>) -> I {
    let app = addr.0;
    let app = (app.0).app();
    let app = mem::take(app);

    addr.dealloc();
    app
}

/// TODO
pub struct Runtime<A: App>(Hydrate<A>, Dispatch<A>);

#[cfg(nightly)]
impl<A: App> !Send for Runtime<A> {}
#[cfg(nightly)]
impl<A: App> !Sync for Runtime<A> {}

impl<A: App> Future for Runtime<A> {
    type Output = A::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        let Runtime(hydrate, dispatch) = self.get_mut();
        match Pin::new(hydrate).poll(cx) {
            // Dispatch ever return Pending
            Poll::Pending => Pin::new(dispatch).poll(cx),
            Poll::Ready(a) => Poll::Ready(a),
        }
    }
}

/// TODO
pub struct Hydrate<A: App>(Return<A::Output>);

impl<A: App> Future for Hydrate<A> {
    type Output = A::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        (self.get_mut().0).as_mut().poll(cx)
    }
}

/// TODO
pub struct Dispatch<A: App>(DeLorean<A>);

impl<A: App> Future for Dispatch<A> {
    type Output = A::Output;

    fn poll(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        let addr = self.0;
        let ctx = &(addr.0).0;
        if ctx.is_ready() {
            ctx.ready(false);
            // SAFETY: UB is checked by ready Cell
            unsafe {
                while let Some(msg) = ctx.pop() {
                    ctx.app().__dispatch(msg, addr);
                    while let Some(msg) = ctx.pop() {
                        ctx.app().__dispatch(msg, addr);
                    }
                    ctx.app().__render(addr);
                }
            }
            ctx.ready(true);
        }
        Poll::Pending
    }
}

// TODO: NEW with Reference Counter for use outside of main thread
/// Constructor and destructor
impl<I: App> Addr<I> {
    /// Make new Address for App
    #[inline]
    fn new(a: I) -> &'static Addr<I> {
        Box::leak(Box::new(Addr(Context::new(a))))
    }

    /// Dealloc Address
    ///
    /// # Safety
    /// Broke `'static` lifetime
    #[cfg(debug_assertions)]
    pub(crate) unsafe fn dealloc(&'static self) {
        let p = stc_to_ptr(self);
        ptr::drop_in_place(p);
        dealloc(p as *mut u8, Layout::new::<Addr<I>>());
    }
}