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}