1#![expect(
2 private_bounds,
3 private_interfaces,
4 reason = "
5 everything defined in here except the macro are internal helpers,
6 they often mention private types
7 "
8)]
9
10use crate::actor::{Actor, Datastore, StoreRequest};
11use crate::cons::{Cons, Nil, TupleConsToCons};
12use crate::datastore::{
13 ExclusiveReader, InitializedReader, Reader, Slot, Storable, Writer, generational,
14};
15use core::any::TypeId;
16use core::pin::Pin;
17
18trait Slots {
20 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
22 where
23 T: Storable + 'static;
24
25 fn all_slots() -> impl Iterator<Item = (TypeId, &'static str)>;
27}
28
29impl Slots for Nil {
30 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
31 where
32 T: Storable + 'static,
33 {
34 panic!("no slot available for `{}`", core::any::type_name::<T>())
35 }
36
37 fn all_slots() -> impl Iterator<Item = (TypeId, &'static str)> {
38 core::iter::empty()
39 }
40}
41
42impl<U, R> Slots for Cons<Slot<U>, R>
43where
44 U: Storable + 'static,
45 R: Slots,
46{
47 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
48 where
49 T: Storable + 'static,
50 {
51 let this = self.project_ref();
52 if TypeId::of::<U>() == TypeId::of::<T>() {
53 this.0.assert_is_type()
54 } else {
55 this.1.slot::<T>()
56 }
57 }
58
59 fn all_slots() -> impl Iterator<Item = (TypeId, &'static str)> {
60 R::all_slots().chain(core::iter::once((
61 TypeId::of::<U>(),
62 core::any::type_name::<U>(),
63 )))
64 }
65}
66
67trait IntoSlots {
69 type Slots: Slots;
71
72 fn make_slots() -> Self::Slots;
74}
75
76impl IntoSlots for Nil {
77 type Slots = Nil;
78
79 fn make_slots() -> Self::Slots {
80 Nil
81 }
82}
83
84impl<T, R> IntoSlots for Cons<T, R>
85where
86 T: Storable + 'static,
87 R: IntoSlots,
88{
89 type Slots = Cons<Slot<T>, R::Slots>;
90
91 fn make_slots() -> Self::Slots {
92 Cons(Slot::<T>::new(), R::make_slots())
93 }
94}
95
96#[allow(
97 rustdoc::private_intra_doc_links,
98 reason = "
99 This lint is hit when documenting with `--document-private-items`.
100 If we use `expect`, a warning is emitted when not using `--document-private-items`.
101 If we remove the lint, a warning is emitted when using `--document-private-items`.
102 To be able to deny warning, we need to allow the lint here.
103 https://github.com/rust-lang/rust/issues/145449
104 "
105)]
106impl<S: Slots> Datastore for Cons<generational::Source, S>
108where
109 S: Slots,
110{
111 fn source(self: Pin<&Self>) -> Pin<&generational::Source> {
112 let this = self.project_ref();
113 this.0
114 }
115
116 fn slot<T>(self: Pin<&Self>) -> Pin<&Slot<T>>
117 where
118 T: Storable + 'static,
119 {
120 let this = self.project_ref();
121 this.1.slot::<T>()
122 }
123}
124
125pub fn make_store<T>() -> impl Datastore
127where
128 T: IntoSlots,
129{
130 Cons(generational::Source::new(), T::make_slots())
131}
132
133pub trait AccessKind {
135 fn writer(_type_id: TypeId) -> bool {
137 false
138 }
139
140 fn reader(_type_id: TypeId) -> bool {
142 false
143 }
144
145 fn exclusive_reader(_type_id: TypeId) -> bool {
147 false
148 }
149}
150
151impl<T> AccessKind for Writer<'_, T>
152where
153 T: Storable + 'static,
154{
155 fn writer(type_id: TypeId) -> bool {
156 type_id == TypeId::of::<T>()
157 }
158}
159
160impl<T> AccessKind for Reader<'_, 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 InitializedReader<'_, T>
170where
171 T: Storable + 'static,
172{
173 fn reader(type_id: TypeId) -> bool {
174 type_id == TypeId::of::<T>()
175 }
176}
177
178impl<T> AccessKind for ExclusiveReader<'_, T>
179where
180 T: Storable + 'static,
181{
182 fn reader(type_id: TypeId) -> bool {
183 type_id == TypeId::of::<T>()
184 }
185
186 fn exclusive_reader(type_id: TypeId) -> bool {
187 type_id == TypeId::of::<T>()
188 }
189}
190
191pub trait AccessCount {
193 fn writers(type_id: TypeId) -> usize;
195
196 fn readers(type_id: TypeId) -> usize;
198
199 fn exclusive_readers(type_id: TypeId) -> usize;
201}
202
203impl AccessCount for Nil {
204 fn writers(_type_id: TypeId) -> usize {
205 0
206 }
207
208 fn readers(_type_id: TypeId) -> usize {
209 0
210 }
211
212 fn exclusive_readers(_type_id: TypeId) -> usize {
213 0
214 }
215}
216
217impl<T, U> AccessCount for Cons<T, U>
218where
219 T: AccessKind,
220 U: AccessCount,
221{
222 fn writers(type_id: TypeId) -> usize {
223 (if T::writer(type_id) { 1 } else { 0 }) + U::writers(type_id)
224 }
225
226 fn readers(type_id: TypeId) -> usize {
227 (if T::reader(type_id) { 1 } else { 0 }) + U::readers(type_id)
228 }
229
230 fn exclusive_readers(type_id: TypeId) -> usize {
231 (if T::exclusive_reader(type_id) { 1 } else { 0 }) + U::exclusive_readers(type_id)
232 }
233}
234
235pub trait NestedAccessCount {
237 fn writers(type_id: TypeId) -> usize;
239
240 fn readers(type_id: TypeId) -> usize;
243
244 fn exclusive_readers(type_id: TypeId) -> usize;
246}
247
248impl NestedAccessCount for Nil {
249 fn writers(_type_id: TypeId) -> usize {
250 0
251 }
252
253 fn readers(_type_id: TypeId) -> usize {
254 0
255 }
256
257 fn exclusive_readers(_type_id: TypeId) -> usize {
258 0
259 }
260}
261
262impl<T, U> NestedAccessCount for Cons<T, U>
263where
264 T: AccessCount,
265 U: NestedAccessCount,
266{
267 fn writers(type_id: TypeId) -> usize {
268 T::writers(type_id) + U::writers(type_id)
269 }
270
271 fn readers(type_id: TypeId) -> usize {
272 T::readers(type_id) + U::readers(type_id)
273 }
274
275 fn exclusive_readers(type_id: TypeId) -> usize {
276 T::exclusive_readers(type_id) + U::exclusive_readers(type_id)
277 }
278}
279
280pub trait ActorList<'a> {
282 type StoreRequests: NestedAccessCount;
285
286 type InitContexts;
288}
289
290impl ActorList<'_> for Nil {
291 type StoreRequests = Nil;
292 type InitContexts = Nil;
293}
294
295impl<'a, T, U> ActorList<'a> for Cons<T, U>
296where
297 T: Actor<'a, StoreRequest: TupleConsToCons>,
298 U: ActorList<'a>,
299 <<T as Actor<'a>>::StoreRequest as TupleConsToCons>::Cons: AccessCount,
300{
301 type StoreRequests = Cons<
306 <<T as Actor<'a>>::StoreRequest as TupleConsToCons>::Cons,
307 <U as ActorList<'a>>::StoreRequests,
308 >;
309
310 type InitContexts = Cons<<T as Actor<'a>>::InitContext, <U as ActorList<'a>>::InitContexts>;
312}
313
314pub fn validate_actors<'a, A, S, I>(init_contexts: I, _store: Pin<&'a impl Datastore>) -> I
323where
324 A: ActorList<'a, InitContexts = I>,
325 S: IntoSlots,
326{
327 for (type_id, type_name) in S::Slots::all_slots() {
328 assert!(
329 A::StoreRequests::writers(type_id) > 0,
330 "missing writer for `{type_name}`",
331 );
332 assert!(
333 A::StoreRequests::readers(type_id) > 0,
334 "missing reader for `{type_name}`",
335 );
336 assert!(
337 A::StoreRequests::writers(type_id) == 1,
338 "multiple writers for `{type_name}`",
339 );
340 if A::StoreRequests::exclusive_readers(type_id) > 0 {
341 assert!(
342 A::StoreRequests::readers(type_id) == 1,
343 "conflict with exclusive reader for `{type_name}`",
344 );
345 }
346 }
347
348 init_contexts
349}
350
351pub async fn execute_actor<'a, A>(
353 store: Pin<&'a impl Datastore>,
354 init_context: A::InitContext,
355) -> core::convert::Infallible
356where
357 A: Actor<'a>,
358{
359 veecle_telemetry::future::FutureExt::with_span(
360 async move {
361 match A::new(A::StoreRequest::request(store).await, init_context)
362 .run()
363 .await
364 {
365 Err(error) => panic!("{error}"),
366 }
367 },
368 veecle_telemetry::span!("actor", actor = core::any::type_name::<A>()),
369 )
370 .await
371}
372
373#[macro_export]
428macro_rules! execute {
429 (
430 store: [
431 $($data_type:ty),* $(,)?
432 ],
433 actors: [
434 $($actor_type:ty $(: $init_context:expr )? ),* $(,)?
435 ] $(,)?
436 ) => {{
437 async {
438 let store = core::pin::pin!(
439 $crate::__exports::make_store::<$crate::__make_cons!(@type $($data_type,)*)>(),
440 );
441 let store = store.as_ref();
442
443 let init_contexts = $crate::__exports::validate_actors::<
444 $crate::__make_cons!(@type $($actor_type,)*),
445 $crate::__make_cons!(@type $($data_type,)*),
446 _,
447 >($crate::__make_cons!(@value $(
448 { $($init_context)? },
450 )*), store);
451
452 const LEN: usize = [$($crate::discard_to_unit!($actor_type),)*].len();
454
455 let futures: [core::pin::Pin<&mut dyn core::future::Future<Output = core::convert::Infallible>>; LEN] =
456 $crate::make_futures! {
457 init_contexts: init_contexts,
458 store: store,
459 actors: [$($actor_type,)*],
460 };
461
462 static SHARED: $crate::__exports::ExecutorShared<LEN>
463 = $crate::__exports::ExecutorShared::new(&SHARED);
464
465 let executor = $crate::__exports::Executor::new(
466 &SHARED,
467 $crate::__exports::Datastore::source(store),
468 futures,
469 );
470
471 executor.run().await
472 }
473 }};
474}
475
476#[doc(hidden)]
481#[macro_export]
482macro_rules! make_futures {
483 (
484 init_contexts: $init_contexts:expr,
486 store: $store:expr,
487 actors: [
488 $($types:ty,)*
489 ],
490 ) => {
491 $crate::make_futures! {
492 init_contexts: $init_contexts,
493 store: $store,
494 done: [],
495 todo: [$($types,)*],
496 futures: [],
497 }
498 };
499
500 (
502 init_contexts: $init_contexts:expr,
503 store: $store:expr,
504 done: [$($done:ty,)*],
505 todo: [],
506 futures: [
507 $($futures:expr,)*
508 ],
509 ) => {
510 [$($futures,)*]
511 };
512
513 (
517 init_contexts: $init_contexts:expr,
518 store: $store:expr,
519 done: [$($done:ty,)*],
520 todo: [$current:ty, $($todo:ty,)*],
521 futures: [
522 $($futures:expr,)*
523 ],
524 ) => {
525 $crate::make_futures! {
526 init_contexts: $init_contexts,
527 store: $store,
528 done: [$($done,)* $current,],
529 todo: [$($todo,)*],
530 futures: [
531 $($futures,)*
532 core::pin::pin!(
533 $crate::__exports::execute_actor::<$current>(
534 $store,
535 $crate::__read_cons! {
536 from: $init_contexts,
537 depth: [$($done)*],
538 },
539 )
540 ),
541 ],
542 }
543 };
544}
545
546#[doc(hidden)]
547#[macro_export]
548macro_rules! discard_to_unit {
549 ($_:tt) => {
550 ()
551 };
552}