1use core::convert::Infallible;
3use core::pin::Pin;
4
5#[doc(inline)]
6pub use veecle_os_runtime_macros::actor;
7
8use crate::datastore::{ExclusiveReader, InitializedReader, Reader, Storable, Writer};
9use crate::datastore::{Slot, generational};
10
11mod sealed {
12 pub trait Sealed {}
13}
14
15pub trait Actor<'a> {
112 type StoreRequest: StoreRequest<'a>;
114
115 type InitContext;
117
118 type Error: core::error::Error;
122
123 fn new(input: Self::StoreRequest, init_context: Self::InitContext) -> Self;
127
128 fn run(
132 self,
133 ) -> impl core::future::Future<Output = Result<core::convert::Infallible, Self::Error>>;
134}
135
136pub trait StoreRequest<'a>: sealed::Sealed {
142 #[doc(hidden)]
144 #[allow(async_fn_in_trait, reason = "it's actually private so it's fine")]
145 async fn request(datastore: Pin<&'a impl Datastore>) -> Self;
146}
147
148impl sealed::Sealed for () {}
149
150pub trait Datastore {
152 fn source(self: Pin<&Self>) -> Pin<&generational::Source>;
157
158 #[expect(
159 rustdoc::private_intra_doc_links,
160 reason = "`rustdoc` is buggy with links from `pub` but unreachable types"
161 )]
162 #[expect(private_interfaces, reason = "the methods are internal")]
168 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
169 where
170 T: Storable + 'static;
171}
172
173impl<S> Datastore for Pin<&S>
174where
175 S: Datastore,
176{
177 fn source(self: Pin<&Self>) -> Pin<&generational::Source> {
178 Pin::into_inner(self).source()
179 }
180
181 #[expect(private_interfaces, reason = "the methods are internal")]
182 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
183 where
184 T: Storable + 'static,
185 {
186 Pin::into_inner(self).slot()
187 }
188}
189
190pub(crate) trait DatastoreExt<'a>: Copy {
191 #[cfg(test)]
192 fn increment_generation(self);
196
197 fn reader<T>(self) -> Reader<'a, T>
203 where
204 T: Storable + 'static;
205
206 fn exclusive_reader<T>(self) -> ExclusiveReader<'a, T>
215 where
216 T: Storable + 'static;
217
218 fn writer<T>(self) -> Writer<'a, T>
226 where
227 T: Storable + 'static;
228}
229
230impl<'a, S> DatastoreExt<'a> for Pin<&'a S>
231where
232 S: Datastore,
233{
234 #[cfg(test)]
235 #[cfg_attr(coverage_nightly, coverage(off))]
236 fn increment_generation(self) {
237 self.source().increment_generation()
238 }
239
240 fn reader<T>(self) -> Reader<'a, T>
241 where
242 T: Storable + 'static,
243 {
244 Reader::from_slot(self.slot::<T>())
245 }
246
247 fn exclusive_reader<T>(self) -> ExclusiveReader<'a, T>
248 where
249 T: Storable + 'static,
250 {
251 ExclusiveReader::from_slot(self.slot::<T>())
252 }
253
254 fn writer<T>(self) -> Writer<'a, T>
255 where
256 T: Storable + 'static,
257 {
258 Writer::new(self.source().waiter(), self.slot::<T>())
259 }
260}
261
262impl<'a> StoreRequest<'a> for () {
264 async fn request(_store: Pin<&'a impl Datastore>) -> Self {}
265}
266
267impl<T> sealed::Sealed for Reader<'_, T> where T: Storable + 'static {}
268
269impl<'a, T> StoreRequest<'a> for Reader<'a, T>
270where
271 T: Storable + 'static,
272{
273 async fn request(datastore: Pin<&'a impl Datastore>) -> Self {
274 datastore.reader()
275 }
276}
277
278impl<T> sealed::Sealed for ExclusiveReader<'_, T> where T: Storable + 'static {}
279
280impl<'a, T> StoreRequest<'a> for ExclusiveReader<'a, T>
281where
282 T: Storable + 'static,
283{
284 async fn request(datastore: Pin<&'a impl Datastore>) -> Self {
285 datastore.exclusive_reader()
286 }
287}
288
289impl<T> sealed::Sealed for InitializedReader<'_, T> where T: Storable + 'static {}
290
291impl<'a, T> StoreRequest<'a> for InitializedReader<'a, T>
292where
293 T: Storable + 'static,
294{
295 async fn request(datastore: Pin<&'a impl Datastore>) -> Self {
296 Reader::from_slot(datastore.slot()).wait_init().await
297 }
298}
299
300impl<T> sealed::Sealed for Writer<'_, T> where T: Storable + 'static {}
301
302impl<'a, T> StoreRequest<'a> for Writer<'a, T>
303where
304 T: Storable + 'static,
305{
306 async fn request(datastore: Pin<&'a impl Datastore>) -> Self {
307 datastore.writer()
308 }
309}
310
311macro_rules! impl_request_helper {
313 ($t:ident) => {
314 #[cfg_attr(docsrs, doc(fake_variadic))]
315 impl<'a, $t> sealed::Sealed for ($t,) { }
317
318 #[cfg_attr(docsrs, doc(fake_variadic))]
319 impl<'a, $t> StoreRequest<'a> for ($t,)
321 where
322 $t: StoreRequest<'a>,
323 {
324 async fn request(datastore: Pin<&'a impl Datastore>) -> Self {
325 (<$t as StoreRequest>::request(datastore).await,)
326 }
327 }
328 };
329
330 (@impl $($t:ident)*) => {
331 #[cfg_attr(docsrs, doc(hidden))]
332 impl<'a, $($t),*> sealed::Sealed for ( $( $t, )* )
333 where
334 $($t: sealed::Sealed),*
335 { }
336
337 #[cfg_attr(docsrs, doc(hidden))]
338 impl<'a, $($t),*> StoreRequest<'a> for ( $( $t, )* )
339 where
340 $($t: StoreRequest<'a>),*
341 {
342 async fn request(datastore: Pin<&'a impl Datastore>) -> Self {
343 futures::join!($( <$t as StoreRequest>::request(datastore), )*)
349 }
350 }
351 };
352
353 ($head:ident $($rest:ident)*) => {
354 impl_request_helper!(@impl $head $($rest)*);
355 impl_request_helper!($($rest)*);
356 };
357}
358
359impl_request_helper!(Z Y X W V U T);
360
361#[diagnostic::on_unimplemented(
363 message = "#[veecle_os_runtime::actor] functions should return either a `Result<Infallible, _>` or `Infallible`",
364 label = "not a valid actor return type"
365)]
366pub trait IsActorResult: sealed::Sealed {
367 type Error;
369
370 fn into_result(self) -> Result<Infallible, Self::Error>;
372}
373
374impl<E> sealed::Sealed for Result<Infallible, E> {}
375
376impl<E> IsActorResult for Result<Infallible, E> {
377 type Error = E;
378
379 fn into_result(self) -> Result<Infallible, E> {
380 self
381 }
382}
383
384impl sealed::Sealed for Infallible {}
385
386impl IsActorResult for Infallible {
387 type Error = Infallible;
388
389 fn into_result(self) -> Result<Infallible, Self::Error> {
390 match self {}
391 }
392}
393
394#[cfg(test)]
395#[cfg_attr(coverage_nightly, coverage(off))]
396mod tests {
397 use core::future::Future;
398 use core::pin::pin;
399 use core::task::{Context, Poll};
400
401 use futures::future::FutureExt;
402
403 use crate::actor::{DatastoreExt, StoreRequest};
404 use crate::cons::{Cons, Nil};
405 use crate::datastore::{InitializedReader, Storable};
406
407 #[test]
408 fn multi_request_order_independence() {
409 #[derive(Debug, Storable)]
410 #[storable(crate = crate)]
411 struct A;
412
413 #[derive(Debug, Storable)]
414 #[storable(crate = crate)]
415 struct B;
416
417 let datastore = pin!(crate::execute::make_store::<Cons<A, Cons<B, Nil>>>());
418
419 let mut a_writer = datastore.as_ref().writer::<A>();
420 let mut b_writer = datastore.as_ref().writer::<B>();
421
422 let mut request_1 = pin!(<(InitializedReader<A>, InitializedReader<B>)>::request(
425 datastore.as_ref()
426 ));
427 let mut request_2 = pin!(<(InitializedReader<B>, InitializedReader<A>)>::request(
428 datastore.as_ref()
429 ));
430
431 let (request_1_waker, request_1_wake_count) = futures_test::task::new_count_waker();
432 let (request_2_waker, request_2_wake_count) = futures_test::task::new_count_waker();
433
434 let mut request_1_context = Context::from_waker(&request_1_waker);
435 let mut request_2_context = Context::from_waker(&request_2_waker);
436
437 assert!(matches!(
438 request_1.as_mut().poll(&mut request_1_context),
439 Poll::Pending
440 ));
441 assert!(matches!(
442 request_2.as_mut().poll(&mut request_2_context),
443 Poll::Pending
444 ));
445
446 let old_request_1_wake_count = request_1_wake_count.get();
447 let old_request_2_wake_count = request_2_wake_count.get();
448
449 datastore.as_ref().increment_generation();
450
451 a_writer.write(A).now_or_never().unwrap();
452
453 if request_1_wake_count.get() > old_request_1_wake_count {
455 assert!(matches!(
456 request_1.as_mut().poll(&mut request_1_context),
457 Poll::Pending
458 ));
459 }
460 if request_2_wake_count.get() > old_request_2_wake_count {
461 assert!(matches!(
462 request_2.as_mut().poll(&mut request_2_context),
463 Poll::Pending
464 ));
465 }
466
467 let old_request_1_wake_count = request_1_wake_count.get();
468 let old_request_2_wake_count = request_2_wake_count.get();
469
470 datastore.as_ref().increment_generation();
471
472 b_writer.write(B).now_or_never().unwrap();
473
474 assert!(request_1_wake_count.get() > old_request_1_wake_count);
476 assert!(request_2_wake_count.get() > old_request_2_wake_count);
477
478 let Poll::Ready((mut request_1_a, mut request_1_b)) =
479 request_1.as_mut().poll(&mut request_1_context)
480 else {
481 panic!("request 1 was not ready")
482 };
483
484 let Poll::Ready((mut request_2_a, mut request_2_b)) =
485 request_2.as_mut().poll(&mut request_2_context)
486 else {
487 panic!("request 2 was not ready")
488 };
489
490 assert!(request_1_a.wait_for_update().now_or_never().is_some());
492 assert!(request_1_b.wait_for_update().now_or_never().is_some());
493
494 assert!(request_2_a.wait_for_update().now_or_never().is_some());
495 assert!(request_2_b.wait_for_update().now_or_never().is_some());
496 }
497}