Skip to main content

dope_runtime/
client.rs

1//! Runtime-scoped client capabilities.
2//!
3//! A [`Scope`] can only be issued by the runtime from the exact selected
4//! provider. Providers turn that zero-sized capability into their public
5//! client handle without retaining the transient selector borrow.
6//!
7//! The application lifetime cannot escape [`crate::executor::session::Session::with_app`]:
8//!
9//! ```compile_fail
10//! use dope_runtime::{
11//!     client::Provider,
12//!     executor::{self, Application},
13//! };
14//!
15//! fn escape<'scope, 'd, D>(
16//!     session: &mut executor::session::Session<'scope, 'd>,
17//!     dispatcher: D,
18//! ) -> D::Client<'d>
19//! where
20//!     'd: 'scope,
21//!     D: Application<'d> + Provider<'d>,
22//! {
23//!     session.with_app(dispatcher, |mut app| app.client(|dispatcher| dispatcher))
24//! }
25//! ```
26//!
27//! A scope cannot be constructed outside the runtime:
28//!
29//! ```compile_fail,E0624
30//! use dope_runtime::client::Scope;
31//!
32//! let _ = Scope::<'static, 'static, ()>::new();
33//! ```
34//!
35//! The provider-erased application lease is equally unforgeable:
36//!
37//! ```compile_fail,E0624
38//! use dope_runtime::client::Lease;
39//!
40//! let _ = Lease::<'static, 'static>::new();
41//! ```
42//!
43//! A provider cannot turn the selector's transient borrow into an
44//! application-scoped reference:
45//!
46//! ```compile_fail
47//! use std::pin::Pin;
48//! use dope_runtime::client::{Provider, Scope};
49//!
50//! struct Borrowing;
51//!
52//! impl<'d> Provider<'d> for Borrowing {
53//!     type Client<'app> = &'app Borrowing where 'd: 'app;
54//!
55//!     fn provide<'app>(
56//!         self: Pin<&Self>,
57//!         _scope: Scope<'app, 'd, Self>,
58//!     ) -> Self::Client<'app>
59//!     where
60//!         'd: 'app,
61//!     {
62//!         self.get_ref()
63//!     }
64//! }
65//! ```
66//!
67//! The higher-ranked selector likewise cannot save its dispatcher borrow:
68//!
69//! ```compile_fail
70//! use std::pin::Pin;
71//! use dope_runtime::{
72//!     client::Provider,
73//!     executor::{self, Application},
74//! };
75//!
76//! fn transient<'app, 'd, D>(
77//!     app: &mut executor::session::Application<'app, 'd, D>,
78//! ) -> Pin<&'app D>
79//! where
80//!     'd: 'app,
81//!     D: Application<'d> + Provider<'d>,
82//! {
83//!     let mut leaked = None;
84//!     let _ = app.client(|dispatcher| {
85//!         leaked = Some(dispatcher);
86//!         dispatcher
87//!     });
88//!     leaked.unwrap()
89//! }
90//! ```
91
92use std::{marker, pin};
93
94use crate::executor::session;
95
96/// Address-stable provider root borrowed for one scoped composition.
97/// Its private constructor pairs the client scope with this exact root.
98#[repr(transparent)]
99pub struct Anchor<'app, T> {
100    root: pin::Pin<&'app mut T>,
101}
102
103impl<'app, T> Anchor<'app, T> {
104    pub(crate) fn new(root: pin::Pin<&'app mut T>) -> Self {
105        Self { root }
106    }
107
108    /// Reborrows the address-stable root mutably.
109    #[doc(hidden)]
110    pub fn as_mut(self: pin::Pin<&mut Self>) -> pin::Pin<&mut T> {
111        self.get_mut().root.as_mut()
112    }
113
114    /// Reborrows the address-stable root immutably.
115    #[doc(hidden)]
116    pub fn as_ref(self: pin::Pin<&Self>) -> pin::Pin<&T> {
117        self.get_ref().root.as_ref()
118    }
119}
120
121/// Runs one application composition around an address-stable provider root.
122/// Neither scoped input can appear in [`Self::Output`].
123pub trait Composition<'scope, 'd: 'scope, S, Q, O, P>
124where
125    P: Provider<'d>,
126{
127    type Output;
128
129    fn compose<'app>(
130        self,
131        client: P::Client<'app>,
132        root: Anchor<'app, O>,
133        session: &mut session::Session<'scope, 'd, S, Q>,
134    ) -> Self::Output
135    where
136        'd: 'app;
137}
138
139/// Provider-erased, zero-sized runtime scope proof.
140/// It does not claim which provider issued it or remains installed.
141pub struct Lease<'app, 'd: 'app> {
142    app: marker::PhantomData<*mut &'app ()>,
143    driver: marker::PhantomData<*mut &'d ()>,
144}
145
146impl<'app, 'd: 'app> Lease<'app, 'd> {
147    const fn new() -> Self {
148        Self {
149            app: marker::PhantomData,
150            driver: marker::PhantomData,
151        }
152    }
153}
154
155impl Copy for Lease<'_, '_> {}
156
157impl Clone for Lease<'_, '_> {
158    fn clone(&self) -> Self {
159        *self
160    }
161}
162
163/// An unforgeable, zero-sized proof that exact provider type `P` issued a
164/// client in runtime scope `'app` and driver scope `'d`.
165pub struct Scope<'app, 'd: 'app, P: ?Sized> {
166    app: marker::PhantomData<*mut &'app ()>,
167    driver: marker::PhantomData<*mut &'d ()>,
168    provider: marker::PhantomData<*mut P>,
169}
170
171impl<'app, 'd: 'app, P: ?Sized> Scope<'app, 'd, P> {
172    pub(crate) const fn new() -> Self {
173        Self {
174            app: marker::PhantomData,
175            driver: marker::PhantomData,
176            provider: marker::PhantomData,
177        }
178    }
179
180    /// Erases the selected provider while preserving the exact application
181    /// and driver liveness proof.
182    pub const fn lease(self) -> Lease<'app, 'd> {
183        Lease::new()
184    }
185
186    /// Narrows this proof to the lifetime of a borrow without changing its
187    /// provider or driver identity.
188    pub const fn reborrow<'short>(&'short self) -> Scope<'short, 'd, P>
189    where
190        'app: 'short,
191    {
192        Scope {
193            app: marker::PhantomData,
194            driver: marker::PhantomData,
195            provider: marker::PhantomData,
196        }
197    }
198}
199
200impl<P: ?Sized> Copy for Scope<'_, '_, P> {}
201
202impl<P: ?Sized> Clone for Scope<'_, '_, P> {
203    fn clone(&self) -> Self {
204        *self
205    }
206}
207
208/// Produces a client from the exact selected provider.
209/// The result may retain only runtime scope, not the selector borrow.
210pub trait Provider<'d> {
211    type Client<'app>: 'app
212    where
213        'd: 'app;
214
215    fn provide<'app>(self: pin::Pin<&Self>, scope: Scope<'app, 'd, Self>) -> Self::Client<'app>
216    where
217        'd: 'app;
218}