Skip to main content

lenso_kernel/
kernel.rs

1use super::{
2    ActivateContext, AppAdmission, AppReadyGate, BTreeMap, CancellationToken, Cell,
3    DeactivationReason, DriverControl, ExecutionAdapterCatalog, ManagedResourceScope,
4    ManagedTaskScope, ModuleDependencies, ModuleDependency, ModuleDependencyHandle,
5    ModuleEventDependencyHandle, ModuleStreamDependencyHandle, NativeApp, NativeAppRuntime,
6    NativeBindingTable, NativeEndpointBinding, NativeEndpointState, NativeEndpointStateTable,
7    NativeEventBindingTable, NativeEventEndpointStateTable, NativeExecutionAdapter,
8    NativeModuleGeneration, NativeModuleRuntime, NativeStreamBindingTable,
9    NativeStreamEndpointBinding, NativeStreamEndpointState, NativeStreamEndpointStateTable,
10    PlanResolutionError, PrepareContext, PreparedBinding, PreparedEventBinding, PreparedNativeApp,
11    PreparedNativeModule, PreparedStreamBinding, Rc, RefCell, RequestAdmission, ResolvedAppPlan,
12    RuntimeDiagnostics, RuntimeDriver, RuntimeFailure, ShutdownCoordinator, Weak,
13    begin_module_supervision, deactivate_in_reverse, event, handle_supervision_schedule_failure,
14    module_supervision, schedule_module_supervision, validate_native_endpoint_set,
15};
16
17/// A reason the Kernel rejected a Resolved App Plan before boot.
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub enum PlanValidationError {
20    /// The Plan schema cannot be executed by this Kernel version.
21    UnsupportedSchemaVersion { expected: u32, actual: u32 },
22    /// The Plan graph is structurally invalid and cannot be booted.
23    InvalidResolvedPlan { detail: String },
24}
25
26impl std::fmt::Display for PlanValidationError {
27    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            Self::UnsupportedSchemaVersion { expected, actual } => write!(
30                formatter,
31                "unsupported Resolved App Plan schema {actual}; expected {expected}"
32            ),
33            Self::InvalidResolvedPlan { detail } => {
34                write!(formatter, "invalid Resolved App Plan: {detail}")
35            }
36        }
37    }
38}
39
40impl std::error::Error for PlanValidationError {}
41
42/// The portable App execution engine.
43#[derive(Debug)]
44pub struct Kernel;
45
46impl Kernel {
47    /// Starts one App backed by a single statically linked native Adapter package.
48    pub async fn start_native<D: RuntimeDriver, A: NativeExecutionAdapter>(
49        plan: ResolvedAppPlan,
50        driver: D,
51        adapter: A,
52    ) -> Result<NativeApp, RuntimeFailure> {
53        Self::start_native_with_diagnostics(plan, driver, adapter, RuntimeDiagnostics::new()).await
54    }
55
56    /// Starts one native Adapter package with an opt-in Runtime Diagnostics port.
57    pub async fn start_native_with_diagnostics<D: RuntimeDriver, A: NativeExecutionAdapter>(
58        plan: ResolvedAppPlan,
59        driver: D,
60        adapter: A,
61        diagnostics: RuntimeDiagnostics,
62    ) -> Result<NativeApp, RuntimeFailure> {
63        Self::start_with_diagnostics(
64            plan,
65            driver,
66            ExecutionAdapterCatalog::single(adapter),
67            diagnostics,
68        )
69        .await
70    }
71
72    /// Starts Module Instances through the Adapter catalog assembled by the Runner.
73    pub async fn start<D: RuntimeDriver>(
74        plan: ResolvedAppPlan,
75        driver: D,
76        adapters: ExecutionAdapterCatalog,
77    ) -> Result<NativeApp, RuntimeFailure> {
78        Self::start_with_diagnostics(plan, driver, adapters, RuntimeDiagnostics::new()).await
79    }
80
81    /// Starts an App with an opt-in Runtime Diagnostics port.
82    pub async fn start_with_diagnostics<D: RuntimeDriver>(
83        plan: ResolvedAppPlan,
84        driver: D,
85        adapters: ExecutionAdapterCatalog,
86        diagnostics: RuntimeDiagnostics,
87    ) -> Result<NativeApp, RuntimeFailure> {
88        if let Err(error) = plan.validate() {
89            let error = runtime_plan_error(&error);
90            diagnostics.emit_runtime_failure(driver.now(), None, &error);
91            return Err(error);
92        }
93
94        let activation_order = match plan.activation_order() {
95            Ok(order) => order,
96            Err(error) => {
97                let error = runtime_plan_error(&error);
98                diagnostics.emit_runtime_failure(driver.now(), None, &error);
99                return Err(error);
100            }
101        };
102        let adapters = Rc::new(adapters);
103        let PreparedNativeApp {
104            bindings: prepared_bindings,
105            stream_bindings: prepared_stream_bindings,
106            event_bindings: prepared_event_bindings,
107            generations,
108        } = match adapters.prepare(&plan) {
109            Ok(prepared) => prepared,
110            Err(error) => {
111                diagnostics.emit_runtime_failure(driver.now(), None, &error);
112                return Err(error);
113            }
114        };
115        if let Err(error) = validate_prepared_native_app(
116            &plan,
117            &prepared_bindings,
118            &prepared_stream_bindings,
119            &prepared_event_bindings,
120            &generations,
121        ) {
122            diagnostics.emit_runtime_failure(driver.now(), None, &error);
123            return Err(error);
124        }
125        let (bindings, endpoint_states) = native_bindings(&plan, &prepared_bindings);
126        let (stream_bindings, stream_endpoint_states) =
127            native_stream_bindings(&plan, &prepared_stream_bindings);
128        let (event_bindings, event_endpoint_states) =
129            native_event_bindings(&plan, &prepared_event_bindings);
130        let runtime_link = Rc::new(RefCell::new(Weak::new()));
131        let dependencies = module_dependencies(
132            &plan,
133            &bindings,
134            &stream_bindings,
135            &event_bindings,
136            &runtime_link,
137        );
138        let driver_control = DriverControl::new(&driver);
139        let admission = AppAdmission::new();
140        let module_runtimes = native_module_runtimes(&plan, &driver, generations);
141        let ready_gate = AppReadyGate::new();
142        let supervision = module_supervision(&plan);
143        let runtime = Rc::new(NativeAppRuntime {
144            plan,
145            adapters,
146            modules: module_runtimes,
147            dependencies,
148            endpoint_states,
149            stream_endpoint_states,
150            event_endpoint_states,
151            supervision: RefCell::new(supervision),
152            supervision_tasks: RefCell::new(BTreeMap::new()),
153            activation_order,
154            ready_gate,
155            admission,
156            driver: driver_control,
157            diagnostics: diagnostics.clone(),
158            request_ids: Rc::new(Cell::new(1)),
159            supervision_cancellation: CancellationToken::new(),
160            shutdown_started: Cell::new(false),
161            shutdown: ShutdownCoordinator::default(),
162            shutdown_task: RefCell::new(None),
163            terminal_failure: RefCell::new(None),
164        });
165        runtime_link.replace(Rc::downgrade(&runtime));
166        attach_managed_task_failure_handlers(&runtime);
167        runtime.diagnostics.emit(
168            super::DiagnosticSource::Lifecycle,
169            (runtime.driver.now)(),
170            |_| super::DiagnosticEvent::AppStarted {
171                module_count: runtime.plan.module_instances().len(),
172            },
173        );
174        let prepared_instances = prepare_native_modules(&runtime).await?;
175        if let Err(error) = activate_native_modules(&runtime).await {
176            let _ = deactivate_in_reverse(
177                &runtime.modules,
178                &runtime.dependencies,
179                &prepared_instances,
180                DeactivationReason::StartupRollback,
181                &runtime.admission,
182                &runtime.diagnostics,
183                &runtime.driver,
184            )
185            .await;
186            runtime
187                .diagnostics
188                .emit_runtime_failure((runtime.driver.now)(), None, &error);
189            return Err(error);
190        }
191        open_native_readiness(&runtime).await;
192        Ok(NativeApp {
193            bindings,
194            stream_bindings,
195            event_bindings,
196            diagnostics,
197            runtime,
198        })
199    }
200}
201
202pub(super) fn attach_managed_task_failure_handlers(runtime: &Rc<NativeAppRuntime>) {
203    for (instance_key, module) in &runtime.modules {
204        let Some((_, tasks, _)) = module.generation_parts() else {
205            continue;
206        };
207        attach_managed_task_failure_handler(runtime, instance_key, &tasks);
208    }
209}
210
211pub(super) fn attach_managed_task_failure_handler(
212    runtime: &Rc<NativeAppRuntime>,
213    instance_key: &str,
214    tasks: &ManagedTaskScope,
215) {
216    let task_runtime = Rc::downgrade(runtime);
217    let task_instance_key = instance_key.to_owned();
218    let handler: Rc<dyn Fn()> = Rc::new(move || {
219        let Some(runtime) = task_runtime.upgrade() else {
220            return;
221        };
222        if begin_module_supervision(&runtime, &task_instance_key).unwrap_or(false)
223            && let Err(error) = schedule_module_supervision(&runtime, &task_instance_key)
224        {
225            let _ = handle_supervision_schedule_failure(&runtime, &task_instance_key, error);
226        }
227    });
228    tasks.set_failure_handler(&handler);
229}
230
231pub(super) fn runtime_plan_error(error: &PlanResolutionError) -> RuntimeFailure {
232    RuntimeFailure::InvalidResolvedPlan {
233        detail: error.to_string(),
234    }
235}
236
237pub(super) fn validate_prepared_native_app(
238    plan: &ResolvedAppPlan,
239    bindings: &[PreparedBinding],
240    stream_bindings: &[PreparedStreamBinding],
241    event_bindings: &[PreparedEventBinding],
242    generations: &BTreeMap<String, PreparedNativeModule>,
243) -> Result<(), RuntimeFailure> {
244    if generations.len() != plan.module_instances().len() {
245        return Err(RuntimeFailure::InvalidResolvedPlan {
246            detail: format!(
247                "Execution Adapters prepared {} Module generations; expected {}",
248                generations.len(),
249                plan.module_instances().len()
250            ),
251        });
252    }
253    for instance in plan.module_instances() {
254        let generation = generations.get(instance.instance_key()).ok_or_else(|| {
255            RuntimeFailure::InvalidResolvedPlan {
256                detail: format!(
257                    "Execution Adapters did not prepare Module Instance `{}`",
258                    instance.instance_key()
259                ),
260            }
261        })?;
262        validate_native_endpoint_set(
263            instance.instance_key(),
264            instance,
265            generation.endpoints(),
266            generation.stream_endpoints(),
267            generation.event_endpoints(),
268        )?;
269    }
270    if let Some(instance_key) = generations.keys().find(|instance_key| {
271        !plan
272            .module_instances()
273            .iter()
274            .any(|instance| instance.instance_key() == instance_key.as_str())
275    }) {
276        return Err(RuntimeFailure::InvalidResolvedPlan {
277            detail: format!("Execution Adapter prepared unknown Module Instance `{instance_key}`"),
278        });
279    }
280
281    let expected_request_bindings = plan
282        .capability_bindings()
283        .iter()
284        .filter(|binding| {
285            plan.module_instance(binding.provider_instance())
286                .and_then(|provider| {
287                    provider
288                        .provided_capabilities()
289                        .iter()
290                        .find(|endpoint| endpoint.capability_id() == binding.capability_id())
291                })
292                .is_some_and(|endpoint| !endpoint.request_operations().is_empty())
293        })
294        .count();
295    let expected_stream_bindings = plan
296        .capability_bindings()
297        .iter()
298        .filter(|binding| {
299            plan.module_instance(binding.provider_instance())
300                .and_then(|provider| {
301                    provider
302                        .provided_capabilities()
303                        .iter()
304                        .find(|endpoint| endpoint.capability_id() == binding.capability_id())
305                })
306                .is_some_and(|endpoint| !endpoint.stream_operations().is_empty())
307        })
308        .count();
309    let expected_event_bindings = plan
310        .capability_bindings()
311        .iter()
312        .filter(|binding| {
313            plan.module_instance(binding.provider_instance())
314                .and_then(|provider| {
315                    provider
316                        .provided_capabilities()
317                        .iter()
318                        .find(|endpoint| endpoint.capability_id() == binding.capability_id())
319                })
320                .is_some_and(|endpoint| !endpoint.event_operations().is_empty())
321        })
322        .count();
323    if bindings.len() != expected_request_bindings {
324        return Err(RuntimeFailure::InvalidResolvedPlan {
325            detail: if expected_stream_bindings == 0 && stream_bindings.is_empty() {
326                format!(
327                    "Execution Adapters prepared {} bindings; expected {}",
328                    bindings.len(),
329                    expected_request_bindings
330                )
331            } else {
332                format!(
333                    "Execution Adapters prepared {} request bindings; expected {}",
334                    bindings.len(),
335                    expected_request_bindings
336                )
337            },
338        });
339    }
340    if stream_bindings.len() != expected_stream_bindings {
341        return Err(RuntimeFailure::InvalidResolvedPlan {
342            detail: format!(
343                "Execution Adapters prepared {} stream bindings; expected {}",
344                stream_bindings.len(),
345                expected_stream_bindings
346            ),
347        });
348    }
349    if event_bindings.len() != expected_event_bindings {
350        return Err(RuntimeFailure::InvalidResolvedPlan {
351            detail: format!(
352                "Execution Adapters prepared {} Event bindings; expected {}",
353                event_bindings.len(),
354                expected_event_bindings
355            ),
356        });
357    }
358    for planned in plan.capability_bindings() {
359        let provider = generations
360            .get(planned.provider_instance())
361            .expect("the resolved Plan references one validated provider generation");
362        let descriptor = plan
363            .module_instance(planned.provider_instance())
364            .and_then(|provider| {
365                provider
366                    .provided_capabilities()
367                    .iter()
368                    .find(|endpoint| endpoint.capability_id() == planned.capability_id())
369            })
370            .expect("the resolved Plan references one validated provider endpoint");
371        if !descriptor.request_operations().is_empty() {
372            let matching: Vec<_> = bindings
373                .iter()
374                .filter(|prepared| {
375                    prepared.consumer_instance == planned.consumer_instance()
376                        && prepared.provider_instance == planned.provider_instance()
377                        && prepared.endpoint.capability_id() == planned.capability_id()
378                        && prepared.endpoint.descriptor_version() == planned.descriptor_version()
379                })
380                .collect();
381            if matching.len() != 1 {
382                return Err(RuntimeFailure::InvalidResolvedPlan {
383                    detail: format!(
384                        "Execution Adapters prepared {} request bindings for `{}:{}:{}`; expected 1",
385                        matching.len(),
386                        planned.consumer_instance(),
387                        planned.capability_id(),
388                        planned.provider_instance()
389                    ),
390                });
391            }
392            if !provider
393                .endpoints()
394                .iter()
395                .any(|endpoint| Rc::ptr_eq(endpoint, &matching[0].endpoint))
396            {
397                return Err(RuntimeFailure::InvalidResolvedPlan {
398                    detail: format!(
399                        "request binding `{}:{}:{}` does not reference its provider generation endpoint",
400                        planned.consumer_instance(),
401                        planned.capability_id(),
402                        planned.provider_instance()
403                    ),
404                });
405            }
406        }
407        if !descriptor.stream_operations().is_empty() {
408            let matching: Vec<_> = stream_bindings
409                .iter()
410                .filter(|prepared| {
411                    prepared.consumer_instance == planned.consumer_instance()
412                        && prepared.provider_instance == planned.provider_instance()
413                        && prepared.endpoint.capability_id() == planned.capability_id()
414                        && prepared.endpoint.descriptor_version() == planned.descriptor_version()
415                })
416                .collect();
417            if matching.len() != 1 {
418                return Err(RuntimeFailure::InvalidResolvedPlan {
419                    detail: format!(
420                        "Execution Adapters prepared {} stream bindings for `{}:{}:{}`; expected 1",
421                        matching.len(),
422                        planned.consumer_instance(),
423                        planned.capability_id(),
424                        planned.provider_instance()
425                    ),
426                });
427            }
428            if !provider
429                .stream_endpoints()
430                .iter()
431                .any(|endpoint| Rc::ptr_eq(endpoint, &matching[0].endpoint))
432            {
433                return Err(RuntimeFailure::InvalidResolvedPlan {
434                    detail: format!(
435                        "stream binding `{}:{}:{}` does not reference its provider generation endpoint",
436                        planned.consumer_instance(),
437                        planned.capability_id(),
438                        planned.provider_instance()
439                    ),
440                });
441            }
442        }
443        if !descriptor.event_operations().is_empty() {
444            let matching: Vec<_> = event_bindings
445                .iter()
446                .filter(|prepared| {
447                    prepared.consumer_instance == planned.consumer_instance()
448                        && prepared.provider_instance == planned.provider_instance()
449                        && prepared.endpoint.capability_id() == planned.capability_id()
450                        && prepared.endpoint.descriptor_version() == planned.descriptor_version()
451                })
452                .collect();
453            if matching.len() != 1 {
454                return Err(RuntimeFailure::InvalidResolvedPlan {
455                    detail: format!(
456                        "Execution Adapters prepared {} Event bindings for `{}:{}:{}`; expected 1",
457                        matching.len(),
458                        planned.consumer_instance(),
459                        planned.capability_id(),
460                        planned.provider_instance()
461                    ),
462                });
463            }
464            if !provider
465                .event_endpoints()
466                .iter()
467                .any(|endpoint| Rc::ptr_eq(endpoint, &matching[0].endpoint))
468            {
469                return Err(RuntimeFailure::InvalidResolvedPlan {
470                    detail: format!(
471                        "Event binding `{}:{}:{}` does not reference its provider generation endpoint",
472                        planned.consumer_instance(),
473                        planned.capability_id(),
474                        planned.provider_instance()
475                    ),
476                });
477            }
478        }
479    }
480    Ok(())
481}
482
483pub(super) fn native_module_runtimes<D: RuntimeDriver>(
484    plan: &ResolvedAppPlan,
485    driver: &D,
486    mut generations: BTreeMap<String, PreparedNativeModule>,
487) -> BTreeMap<String, NativeModuleRuntime> {
488    let mut runtimes = BTreeMap::new();
489    for instance in plan.module_instances() {
490        let lifecycle = generations
491            .remove(instance.instance_key())
492            .map(|generation| generation.lifecycle())
493            .expect("prepared App validation requires one generation per planned Instance");
494        runtimes.insert(
495            instance.instance_key().to_owned(),
496            NativeModuleRuntime {
497                generation: RefCell::new(Some(NativeModuleGeneration {
498                    lifecycle,
499                    tasks: ManagedTaskScope::new(driver),
500                    resources: ManagedResourceScope::new(),
501                })),
502            },
503        );
504    }
505    runtimes
506}
507
508pub(super) async fn prepare_native_modules(
509    runtime: &Rc<NativeAppRuntime>,
510) -> Result<Vec<String>, RuntimeFailure> {
511    let mut prepared_instances = Vec::with_capacity(runtime.activation_order.len());
512    for instance_key in &runtime.activation_order {
513        let instance = runtime
514            .plan
515            .module_instances()
516            .iter()
517            .find(|instance| instance.instance_key() == instance_key)
518            .expect("activation order only contains planned Module Instances");
519        let module = runtime
520            .modules
521            .get(instance_key)
522            .expect("activation order only contains planned Module Instances");
523        let (lifecycle, tasks, resources) = module
524            .generation_parts()
525            .expect("every startup Module Instance has a generation");
526        let cancellation = tasks.cancellation();
527        prepared_instances.push(instance_key.clone());
528        let started_at = (runtime.driver.now)();
529        runtime
530            .diagnostics
531            .emit(super::DiagnosticSource::Lifecycle, started_at, |_| {
532                super::DiagnosticEvent::LifecycleStarted {
533                    instance: instance_key.clone(),
534                    generation: 1,
535                    phase: super::ModuleLifecyclePhase::Prepare,
536                }
537            });
538        let context = PrepareContext {
539            instance_key: instance_key.clone(),
540            entrypoint: instance.entrypoint().to_owned(),
541            configuration: instance.configuration().to_owned(),
542            dependencies: runtime
543                .dependencies
544                .get(instance_key)
545                .cloned()
546                .unwrap_or_default(),
547            resources,
548            cancellation,
549            admission: runtime.admission.clone(),
550        };
551        let result = lifecycle.prepare(context).await;
552        let outcome = result.as_ref().map_or_else(
553            |error| super::DiagnosticOutcome::RuntimeFailure(error.into()),
554            |()| super::DiagnosticOutcome::Succeeded,
555        );
556        runtime.diagnostics.emit(
557            super::DiagnosticSource::Lifecycle,
558            (runtime.driver.now)(),
559            |_| super::DiagnosticEvent::LifecycleCompleted {
560                instance: instance_key.clone(),
561                generation: 1,
562                phase: super::ModuleLifecyclePhase::Prepare,
563                outcome,
564                elapsed: (runtime.driver.now)().saturating_sub(started_at),
565            },
566        );
567        if let Err(error) = result {
568            let _ = deactivate_in_reverse(
569                &runtime.modules,
570                &runtime.dependencies,
571                &prepared_instances,
572                DeactivationReason::StartupRollback,
573                &runtime.admission,
574                &runtime.diagnostics,
575                &runtime.driver,
576            )
577            .await;
578            runtime.diagnostics.emit_runtime_failure(
579                (runtime.driver.now)(),
580                Some(instance_key),
581                &error,
582            );
583            return Err(error);
584        }
585    }
586    Ok(prepared_instances)
587}
588
589pub(super) async fn activate_native_modules(
590    runtime: &Rc<NativeAppRuntime>,
591) -> Result<(), RuntimeFailure> {
592    for instance_key in &runtime.activation_order {
593        let module = runtime
594            .modules
595            .get(instance_key)
596            .expect("activation order only contains planned Module Instances");
597        let (lifecycle, tasks, resources) = module
598            .generation_parts()
599            .expect("every startup Module Instance has a generation");
600        let cancellation = tasks.cancellation();
601        let started_at = (runtime.driver.now)();
602        runtime
603            .diagnostics
604            .emit(super::DiagnosticSource::Lifecycle, started_at, |_| {
605                super::DiagnosticEvent::LifecycleStarted {
606                    instance: instance_key.clone(),
607                    generation: 1,
608                    phase: super::ModuleLifecyclePhase::Activate,
609                }
610            });
611        let context = ActivateContext {
612            instance_key: instance_key.clone(),
613            dependencies: runtime
614                .dependencies
615                .get(instance_key)
616                .cloned()
617                .unwrap_or_default(),
618            ready_gate: runtime.ready_gate.clone(),
619            tasks,
620            resources,
621            cancellation,
622            admission: runtime.admission.clone(),
623        };
624        let result = lifecycle.activate(context).await;
625        let outcome = result.as_ref().map_or_else(
626            |error| super::DiagnosticOutcome::RuntimeFailure(error.into()),
627            |()| super::DiagnosticOutcome::Succeeded,
628        );
629        runtime.diagnostics.emit(
630            super::DiagnosticSource::Lifecycle,
631            (runtime.driver.now)(),
632            |_| super::DiagnosticEvent::LifecycleCompleted {
633                instance: instance_key.clone(),
634                generation: 1,
635                phase: super::ModuleLifecyclePhase::Activate,
636                outcome,
637                elapsed: (runtime.driver.now)().saturating_sub(started_at),
638            },
639        );
640        if let Err(error) = result {
641            runtime.diagnostics.emit_runtime_failure(
642                (runtime.driver.now)(),
643                Some(instance_key),
644                &error,
645            );
646            return Err(error);
647        }
648    }
649    Ok(())
650}
651
652pub(super) async fn open_native_readiness(runtime: &Rc<NativeAppRuntime>) {
653    runtime.ready_gate.open();
654    runtime.admission.open();
655    runtime.diagnostics.emit(
656        super::DiagnosticSource::Lifecycle,
657        (runtime.driver.now)(),
658        |_| super::DiagnosticEvent::AppReady,
659    );
660    (runtime.driver.yield_now)().await;
661}
662
663pub(super) fn native_bindings(
664    plan: &ResolvedAppPlan,
665    prepared: &[PreparedBinding],
666) -> (NativeBindingTable, NativeEndpointStateTable) {
667    let mut bindings = BTreeMap::new();
668    let mut endpoint_states = BTreeMap::new();
669    for binding in plan.capability_bindings() {
670        let Some(descriptor) =
671            plan.module_instance(binding.provider_instance())
672                .and_then(|provider| {
673                    provider
674                        .provided_capabilities()
675                        .iter()
676                        .find(|endpoint| endpoint.capability_id() == binding.capability_id())
677                })
678        else {
679            continue;
680        };
681        if descriptor.request_operations().is_empty() {
682            continue;
683        }
684        let Some(endpoint) = prepared.iter().find_map(|prepared| {
685            (prepared.consumer_instance == binding.consumer_instance()
686                && prepared.provider_instance == binding.provider_instance()
687                && prepared.endpoint.capability_id() == binding.capability_id())
688            .then_some(&prepared.endpoint)
689        }) else {
690            continue;
691        };
692        let state = endpoint_states
693            .entry((
694                binding.provider_instance().to_owned(),
695                endpoint.capability_id().to_owned(),
696            ))
697            .or_insert_with(|| Rc::new(NativeEndpointState::new(endpoint.clone(), 1)))
698            .clone();
699        let admissions = endpoint
700            .operations()
701            .iter()
702            .map(|operation| {
703                (
704                    (*operation).to_owned(),
705                    RequestAdmission::new(plan.request_admission_for(binding, operation)),
706                )
707            })
708            .collect();
709        bindings
710            .entry((
711                binding.consumer_instance().to_owned(),
712                endpoint.capability_id(),
713            ))
714            .or_insert_with(Vec::new)
715            .push(NativeEndpointBinding {
716                module_instance: binding.provider_instance().to_owned(),
717                state,
718                admissions,
719            });
720    }
721    (bindings, endpoint_states)
722}
723
724pub(super) fn native_stream_bindings(
725    plan: &ResolvedAppPlan,
726    prepared: &[PreparedStreamBinding],
727) -> (NativeStreamBindingTable, NativeStreamEndpointStateTable) {
728    let mut bindings = BTreeMap::new();
729    let mut endpoint_states = BTreeMap::new();
730    for binding in plan.capability_bindings() {
731        let Some(descriptor) =
732            plan.module_instance(binding.provider_instance())
733                .and_then(|provider| {
734                    provider
735                        .provided_capabilities()
736                        .iter()
737                        .find(|endpoint| endpoint.capability_id() == binding.capability_id())
738                })
739        else {
740            continue;
741        };
742        if descriptor.stream_operations().is_empty() {
743            continue;
744        }
745        let Some(endpoint) = prepared.iter().find_map(|prepared| {
746            (prepared.consumer_instance == binding.consumer_instance()
747                && prepared.provider_instance == binding.provider_instance()
748                && prepared.endpoint.capability_id() == binding.capability_id())
749            .then_some(&prepared.endpoint)
750        }) else {
751            continue;
752        };
753        let state = endpoint_states
754            .entry((
755                binding.provider_instance().to_owned(),
756                endpoint.capability_id().to_owned(),
757            ))
758            .or_insert_with(|| Rc::new(NativeStreamEndpointState::new(endpoint.clone(), 1)))
759            .clone();
760        let admissions = endpoint
761            .operations()
762            .iter()
763            .map(|operation| {
764                (
765                    (*operation).to_owned(),
766                    RequestAdmission::new(plan.request_admission_for(binding, operation)),
767                )
768            })
769            .collect();
770        bindings
771            .entry((
772                binding.consumer_instance().to_owned(),
773                endpoint.capability_id(),
774            ))
775            .or_insert_with(Vec::new)
776            .push(NativeStreamEndpointBinding {
777                module_instance: binding.provider_instance().to_owned(),
778                state,
779                admissions,
780            });
781    }
782    (bindings, endpoint_states)
783}
784
785pub(super) fn native_event_bindings(
786    plan: &ResolvedAppPlan,
787    prepared: &[PreparedEventBinding],
788) -> (NativeEventBindingTable, NativeEventEndpointStateTable) {
789    let mut bindings = BTreeMap::new();
790    let mut endpoint_states = BTreeMap::new();
791    for binding in plan.capability_bindings() {
792        let Some(descriptor) =
793            plan.module_instance(binding.provider_instance())
794                .and_then(|provider| {
795                    provider
796                        .provided_capabilities()
797                        .iter()
798                        .find(|endpoint| endpoint.capability_id() == binding.capability_id())
799                })
800        else {
801            continue;
802        };
803        if descriptor.event_operations().is_empty() {
804            continue;
805        }
806        let Some(endpoint) = prepared.iter().find_map(|prepared| {
807            (prepared.consumer_instance == binding.consumer_instance()
808                && prepared.provider_instance == binding.provider_instance()
809                && prepared.endpoint.capability_id() == binding.capability_id())
810            .then_some(&prepared.endpoint)
811        }) else {
812            continue;
813        };
814        let state = endpoint_states
815            .entry((
816                binding.provider_instance().to_owned(),
817                endpoint.capability_id().to_owned(),
818            ))
819            .or_insert_with(|| Rc::new(event::NativeEventEndpointState::new(endpoint.clone(), 1)))
820            .clone();
821        let queue = event::NativeEventQueue::new(plan.event_admission_for(binding));
822        state.register_queue(&queue);
823        bindings
824            .entry((
825                binding.consumer_instance().to_owned(),
826                endpoint.capability_id(),
827            ))
828            .or_insert_with(Vec::new)
829            .push(event::NativeEventEndpointBinding {
830                module_instance: binding.provider_instance().to_owned(),
831                state,
832                queue,
833            });
834    }
835    (bindings, endpoint_states)
836}
837
838pub(super) fn module_dependencies(
839    plan: &ResolvedAppPlan,
840    endpoints: &BTreeMap<(String, &'static str), Vec<NativeEndpointBinding>>,
841    stream_endpoints: &NativeStreamBindingTable,
842    event_endpoints: &NativeEventBindingTable,
843    runtime: &Rc<RefCell<Weak<NativeAppRuntime>>>,
844) -> BTreeMap<String, ModuleDependencies> {
845    let mut dependencies: BTreeMap<String, ModuleDependencies> = plan
846        .module_instances()
847        .iter()
848        .map(|instance| {
849            (
850                instance.instance_key().to_owned(),
851                ModuleDependencies::new(instance.instance_key(), runtime.clone()),
852            )
853        })
854        .collect();
855    for binding in plan.capability_bindings() {
856        dependencies
857            .get_mut(binding.consumer_instance())
858            .expect("every resolved binding consumer has Module dependencies")
859            .bindings
860            .push(ModuleDependency::new(
861                binding.capability_id(),
862                binding.provider_instance(),
863                binding.provider_order(),
864                endpoints
865                    .iter()
866                    .find(|((consumer, capability), _)| {
867                        consumer == binding.consumer_instance()
868                            && *capability == binding.capability_id()
869                    })
870                    .and_then(|(_, endpoints)| endpoints.get(binding.provider_order()))
871                    .map(|endpoint| ModuleDependencyHandle {
872                        binding: endpoint.clone(),
873                        caller_instance: binding.consumer_instance().to_owned(),
874                        runtime: runtime.clone(),
875                    }),
876                stream_endpoints
877                    .iter()
878                    .find(|((consumer, capability), _)| {
879                        consumer == binding.consumer_instance()
880                            && *capability == binding.capability_id()
881                    })
882                    .and_then(|(_, endpoints)| endpoints.get(binding.provider_order()))
883                    .map(|endpoint| ModuleStreamDependencyHandle {
884                        binding: endpoint.clone(),
885                        caller_instance: binding.consumer_instance().to_owned(),
886                        runtime: runtime.clone(),
887                    }),
888                event_endpoints
889                    .iter()
890                    .find(|((consumer, capability), _)| {
891                        consumer == binding.consumer_instance()
892                            && *capability == binding.capability_id()
893                    })
894                    .and_then(|(_, endpoints)| endpoints.get(binding.provider_order()))
895                    .map(|endpoint| ModuleEventDependencyHandle {
896                        binding: endpoint.clone(),
897                        caller_instance: binding.consumer_instance().to_owned(),
898                        runtime: runtime.clone(),
899                    }),
900            ));
901    }
902    dependencies
903}