1#![expect(private_bounds)]
3#![expect(private_interfaces)]
4
5use crate::actor::{Actor, Datastore, StoreRequest};
6use crate::cons::{Cons, Nil, TupleConsToCons};
7use crate::datastore::{
8 ExclusiveReader, InitializedReader, Reader, Slot, Storable, Writer, generational,
9};
10use core::any::TypeId;
11use core::pin::Pin;
12
13trait Slots {
15 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
17 where
18 T: Storable + 'static;
19
20 fn all_slots() -> impl Iterator<Item = (TypeId, &'static str)>;
22}
23
24impl Slots for Nil {
25 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
26 where
27 T: Storable + 'static,
28 {
29 panic!("no slot available for `{}`", core::any::type_name::<T>())
30 }
31
32 fn all_slots() -> impl Iterator<Item = (TypeId, &'static str)> {
33 core::iter::empty()
34 }
35}
36
37impl<U, R> Slots for Cons<Slot<U>, R>
38where
39 U: Storable + 'static,
40 R: Slots,
41{
42 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
43 where
44 T: Storable + 'static,
45 {
46 let this = self.project_ref();
47 if TypeId::of::<U>() == TypeId::of::<T>() {
48 this.0.assert_is_type()
49 } else {
50 this.1.slot::<T>()
51 }
52 }
53
54 fn all_slots() -> impl Iterator<Item = (TypeId, &'static str)> {
55 R::all_slots().chain(core::iter::once((
56 TypeId::of::<U>(),
57 core::any::type_name::<U>(),
58 )))
59 }
60}
61
62trait IntoSlots {
64 type Slots: Slots;
66
67 fn make_slots() -> Self::Slots;
69}
70
71impl IntoSlots for Nil {
72 type Slots = Nil;
73
74 fn make_slots() -> Self::Slots {
75 Nil
76 }
77}
78
79impl<T, R> IntoSlots for Cons<T, R>
80where
81 T: Storable + 'static,
82 R: IntoSlots,
83{
84 type Slots = Cons<Slot<T>, R::Slots>;
85
86 fn make_slots() -> Self::Slots {
87 Cons(Slot::<T>::new(), R::make_slots())
88 }
89}
90
91#[allow(rustdoc::private_intra_doc_links)]
97impl<S: Slots> Datastore for Cons<generational::Source, S>
99where
100 S: Slots,
101{
102 fn source(self: Pin<&Self>) -> Pin<&generational::Source> {
103 let this = self.project_ref();
104 this.0
105 }
106
107 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
108 where
109 T: Storable + 'static,
110 {
111 let this = self.project_ref();
112 this.1.slot::<T>()
113 }
114}
115
116pub fn make_store<T>() -> impl Datastore
118where
119 T: IntoSlots,
120{
121 Cons(generational::Source::new(), T::make_slots())
122}
123
124pub trait AccessKind {
126 fn writer(_type_id: TypeId) -> bool {
128 false
129 }
130
131 fn reader(_type_id: TypeId) -> bool {
133 false
134 }
135
136 fn exclusive_reader(_type_id: TypeId) -> bool {
138 false
139 }
140}
141
142impl<T> AccessKind for Writer<'_, T>
143where
144 T: Storable + 'static,
145{
146 fn writer(type_id: TypeId) -> bool {
147 type_id == TypeId::of::<T>()
148 }
149}
150
151impl<T> AccessKind for Reader<'_, T>
152where
153 T: Storable + 'static,
154{
155 fn reader(type_id: TypeId) -> bool {
156 type_id == TypeId::of::<T>()
157 }
158}
159
160impl<T> AccessKind for InitializedReader<'_, T>
161where
162 T: Storable + 'static,
163{
164 fn reader(type_id: TypeId) -> bool {
165 type_id == TypeId::of::<T>()
166 }
167}
168
169impl<T> AccessKind for ExclusiveReader<'_, T>
170where
171 T: Storable + 'static,
172{
173 fn reader(type_id: TypeId) -> bool {
174 type_id == TypeId::of::<T>()
175 }
176
177 fn exclusive_reader(type_id: TypeId) -> bool {
178 type_id == TypeId::of::<T>()
179 }
180}
181
182pub trait AccessCount {
184 fn writers(type_id: TypeId) -> usize;
186
187 fn readers(type_id: TypeId) -> usize;
189
190 fn exclusive_readers(type_id: TypeId) -> usize;
192}
193
194impl AccessCount for Nil {
195 fn writers(_type_id: TypeId) -> usize {
196 0
197 }
198
199 fn readers(_type_id: TypeId) -> usize {
200 0
201 }
202
203 fn exclusive_readers(_type_id: TypeId) -> usize {
204 0
205 }
206}
207
208impl<T, U> AccessCount for Cons<T, U>
209where
210 T: AccessKind,
211 U: AccessCount,
212{
213 fn writers(type_id: TypeId) -> usize {
214 (if T::writer(type_id) { 1 } else { 0 }) + U::writers(type_id)
215 }
216
217 fn readers(type_id: TypeId) -> usize {
218 (if T::reader(type_id) { 1 } else { 0 }) + U::readers(type_id)
219 }
220
221 fn exclusive_readers(type_id: TypeId) -> usize {
222 (if T::exclusive_reader(type_id) { 1 } else { 0 }) + U::exclusive_readers(type_id)
223 }
224}
225
226pub trait NestedAccessCount {
228 fn writers(type_id: TypeId) -> usize;
230
231 fn readers(type_id: TypeId) -> usize;
234
235 fn exclusive_readers(type_id: TypeId) -> usize;
237}
238
239impl NestedAccessCount for Nil {
240 fn writers(_type_id: TypeId) -> usize {
241 0
242 }
243
244 fn readers(_type_id: TypeId) -> usize {
245 0
246 }
247
248 fn exclusive_readers(_type_id: TypeId) -> usize {
249 0
250 }
251}
252
253impl<T, U> NestedAccessCount for Cons<T, U>
254where
255 T: AccessCount,
256 U: NestedAccessCount,
257{
258 fn writers(type_id: TypeId) -> usize {
259 T::writers(type_id) + U::writers(type_id)
260 }
261
262 fn readers(type_id: TypeId) -> usize {
263 T::readers(type_id) + U::readers(type_id)
264 }
265
266 fn exclusive_readers(type_id: TypeId) -> usize {
267 T::exclusive_readers(type_id) + U::exclusive_readers(type_id)
268 }
269}
270
271pub trait ActorList<'a> {
273 type StoreRequests: NestedAccessCount;
276
277 type InitContexts;
279}
280
281impl ActorList<'_> for Nil {
282 type StoreRequests = Nil;
283 type InitContexts = Nil;
284}
285
286impl<'a, T, U> ActorList<'a> for Cons<T, U>
287where
288 T: Actor<'a, StoreRequest: TupleConsToCons>,
289 U: ActorList<'a>,
290 <<T as Actor<'a>>::StoreRequest as TupleConsToCons>::Cons: AccessCount,
291{
292 type StoreRequests = Cons<
297 <<T as Actor<'a>>::StoreRequest as TupleConsToCons>::Cons,
298 <U as ActorList<'a>>::StoreRequests,
299 >;
300
301 type InitContexts = Cons<<T as Actor<'a>>::InitContext, <U as ActorList<'a>>::InitContexts>;
303}
304
305pub fn validate_actors<'a, A, S, I>(init_contexts: I, _store: Pin<&'a impl Datastore>) -> I
314where
315 A: ActorList<'a, InitContexts = I>,
316 S: IntoSlots,
317{
318 for (type_id, type_name) in S::Slots::all_slots() {
319 assert!(
320 A::StoreRequests::writers(type_id) > 0,
321 "missing writer for `{type_name}`",
322 );
323 assert!(
324 A::StoreRequests::readers(type_id) > 0,
325 "missing reader for `{type_name}`",
326 );
327 assert!(
328 A::StoreRequests::writers(type_id) == 1,
329 "multiple writers for `{type_name}`",
330 );
331 if A::StoreRequests::exclusive_readers(type_id) > 0 {
332 assert!(
333 A::StoreRequests::readers(type_id) == 1,
334 "conflict with exclusive reader for `{type_name}`",
335 );
336 }
337 }
338
339 init_contexts
340}
341
342pub async fn execute_actor<'a, A>(
344 store: Pin<&'a impl Datastore>,
345 init_context: A::InitContext,
346) -> core::convert::Infallible
347where
348 A: Actor<'a>,
349{
350 let future = A::new(A::StoreRequest::request(store).await, init_context).run();
351
352 let future = veecle_telemetry::future::FutureExt::with_span(
353 future,
354 veecle_telemetry::span!("actor", actor = core::any::type_name::<A>()),
355 );
356
357 match future.await {
358 Err(error) => panic!("{error}"),
359 }
360}
361
362#[macro_export]
417macro_rules! execute {
418 (
419 store: [
420 $($data_type:ty),* $(,)?
421 ],
422 actors: [
423 $($actor_type:ty $(: $init_context:expr )? ),* $(,)?
424 ] $(,)?
425 ) => {{
426 async {
427 let store = core::pin::pin!(
428 $crate::__exports::make_store::<$crate::__make_cons!(@type $($data_type,)*)>(),
429 );
430 let store = store.as_ref();
431
432 let init_contexts = $crate::__exports::validate_actors::<
433 $crate::__make_cons!(@type $($actor_type,)*),
434 $crate::__make_cons!(@type $($data_type,)*),
435 _,
436 >($crate::__make_cons!(@value $(
437 { $($init_context)? },
439 )*), store);
440
441 const LEN: usize = [$($crate::discard_to_unit!($actor_type),)*].len();
443
444 let futures: [core::pin::Pin<&mut dyn core::future::Future<Output = core::convert::Infallible>>; LEN] =
445 $crate::make_futures! {
446 init_contexts: init_contexts,
447 store: store,
448 actors: [$($actor_type,)*],
449 };
450
451 static SHARED: $crate::__exports::ExecutorShared<LEN>
452 = $crate::__exports::ExecutorShared::new(&SHARED);
453
454 let executor = $crate::__exports::Executor::new(
455 &SHARED,
456 $crate::__exports::Datastore::source(store),
457 futures,
458 );
459
460 executor.run().await
461 }
462 }};
463}
464
465#[doc(hidden)]
470#[macro_export]
471macro_rules! make_futures {
472 (
473 init_contexts: $init_contexts:expr,
475 store: $store:expr,
476 actors: [
477 $($types:ty,)*
478 ],
479 ) => {
480 $crate::make_futures! {
481 init_contexts: $init_contexts,
482 store: $store,
483 done: [],
484 todo: [$($types,)*],
485 futures: [],
486 }
487 };
488
489 (
491 init_contexts: $init_contexts:expr,
492 store: $store:expr,
493 done: [$($done:ty,)*],
494 todo: [],
495 futures: [
496 $($futures:expr,)*
497 ],
498 ) => {
499 [$($futures,)*]
500 };
501
502 (
506 init_contexts: $init_contexts:expr,
507 store: $store:expr,
508 done: [$($done:ty,)*],
509 todo: [$current:ty, $($todo:ty,)*],
510 futures: [
511 $($futures:expr,)*
512 ],
513 ) => {
514 $crate::make_futures! {
515 init_contexts: $init_contexts,
516 store: $store,
517 done: [$($done,)* $current,],
518 todo: [$($todo,)*],
519 futures: [
520 $($futures,)*
521 core::pin::pin!(
522 $crate::__exports::execute_actor::<$current>(
523 $store,
524 $crate::__read_cons! {
525 from: $init_contexts,
526 depth: [$($done)*],
527 },
528 )
529 ),
530 ],
531 }
532 };
533}
534
535#[doc(hidden)]
536#[macro_export]
537macro_rules! discard_to_unit {
538 ($_:tt) => {
539 ()
540 };
541}