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 #[cfg(feature = "veecle-telemetry")]
353 let future = veecle_telemetry::future::FutureExt::with_span(
354 future,
355 veecle_telemetry::root_span!("actor", actor = core::any::type_name::<A>()),
356 );
357
358 match future.await {
359 Err(error) => panic!("{error}"),
360 }
361}
362
363#[macro_export]
418macro_rules! execute {
419 (
420 store: [
421 $($data_type:ty),* $(,)?
422 ],
423 actors: [
424 $($actor_type:ty $(: $init_context:expr )? ),* $(,)?
425 ] $(,)?
426 ) => {{
427 async {
428 let store = core::pin::pin!(
429 $crate::__exports::make_store::<$crate::__make_cons!(@type $($data_type,)*)>(),
430 );
431 let store = store.as_ref();
432
433 let init_contexts = $crate::__exports::validate_actors::<
434 $crate::__make_cons!(@type $($actor_type,)*),
435 $crate::__make_cons!(@type $($data_type,)*),
436 _,
437 >($crate::__make_cons!(@value $(
438 { $($init_context)? },
440 )*), store);
441
442 const LEN: usize = [$($crate::discard_to_unit!($actor_type),)*].len();
444
445 let futures: [core::pin::Pin<&mut dyn core::future::Future<Output = core::convert::Infallible>>; LEN] =
446 $crate::make_futures! {
447 init_contexts: init_contexts,
448 store: store,
449 actors: [$($actor_type,)*],
450 };
451
452 static SHARED: $crate::__exports::ExecutorShared<LEN>
453 = $crate::__exports::ExecutorShared::new(&SHARED);
454
455 let executor = $crate::__exports::Executor::new(
456 &SHARED,
457 $crate::__exports::Datastore::source(store),
458 futures,
459 );
460
461 executor.run().await
462 }
463 }};
464}
465
466#[doc(hidden)]
471#[macro_export]
472macro_rules! make_futures {
473 (
474 init_contexts: $init_contexts:expr,
476 store: $store:expr,
477 actors: [
478 $($types:ty,)*
479 ],
480 ) => {
481 $crate::make_futures! {
482 init_contexts: $init_contexts,
483 store: $store,
484 done: [],
485 todo: [$($types,)*],
486 futures: [],
487 }
488 };
489
490 (
492 init_contexts: $init_contexts:expr,
493 store: $store:expr,
494 done: [$($done:ty,)*],
495 todo: [],
496 futures: [
497 $($futures:expr,)*
498 ],
499 ) => {
500 [$($futures,)*]
501 };
502
503 (
507 init_contexts: $init_contexts:expr,
508 store: $store:expr,
509 done: [$($done:ty,)*],
510 todo: [$current:ty, $($todo:ty,)*],
511 futures: [
512 $($futures:expr,)*
513 ],
514 ) => {
515 $crate::make_futures! {
516 init_contexts: $init_contexts,
517 store: $store,
518 done: [$($done,)* $current,],
519 todo: [$($todo,)*],
520 futures: [
521 $($futures,)*
522 core::pin::pin!(
523 $crate::__exports::execute_actor::<$current>(
524 $store,
525 $crate::__read_cons! {
526 from: $init_contexts,
527 depth: [$($done)*],
528 },
529 )
530 ),
531 ],
532 }
533 };
534}
535
536#[doc(hidden)]
537#[macro_export]
538macro_rules! discard_to_unit {
539 ($_:tt) => {
540 ()
541 };
542}