Skip to main content

lenso_kernel/
prepared.rs

1use super::{
2    BTreeMap, BTreeSet, ErasedDomainResult, ErasedValue, ExecutionClassId, InvocationContext,
3    LocalBoxFuture, ModuleLifecycle, NativeEventEndpoint, NativeStreamEndpoint, Rc,
4    ResolvedAppPlan, RuntimeFailure,
5};
6
7/// Type-erased native endpoint used only while Kernel constructs and dispatches the graph.
8pub trait NativeRequestEndpoint: std::fmt::Debug {
9    /// Stable Capability series identity.
10    fn capability_id(&self) -> &'static str;
11    /// Exact Descriptor version implemented by this endpoint.
12    fn descriptor_version(&self) -> &'static str;
13    /// Exact stable Operation names implemented by this endpoint.
14    fn operations(&self) -> &'static [&'static str];
15    /// Dispatches one operation without serializing its typed Rust payload.
16    fn invoke(
17        &self,
18        operation: &str,
19        request: ErasedValue,
20        context: InvocationContext,
21    ) -> LocalBoxFuture<'static, Result<ErasedDomainResult, RuntimeFailure>>;
22}
23
24/// The complete native endpoint set owned by one Module generation.
25#[derive(Clone, Debug, Default)]
26pub struct NativeEndpointSet {
27    request: Vec<Rc<dyn NativeRequestEndpoint>>,
28    stream: Vec<Rc<dyn NativeStreamEndpoint>>,
29    event: Vec<Rc<dyn NativeEventEndpoint>>,
30}
31
32impl NativeEndpointSet {
33    /// Creates an endpoint set containing every native interaction kind.
34    pub fn new(
35        request: Vec<Rc<dyn NativeRequestEndpoint>>,
36        stream: Vec<Rc<dyn NativeStreamEndpoint>>,
37        event: Vec<Rc<dyn NativeEventEndpoint>>,
38    ) -> Self {
39        Self {
40            request,
41            stream,
42            event,
43        }
44    }
45
46    /// Returns the request endpoints in this generation.
47    pub fn request(&self) -> &[Rc<dyn NativeRequestEndpoint>] {
48        &self.request
49    }
50
51    /// Returns the stream endpoints in this generation.
52    pub fn stream(&self) -> &[Rc<dyn NativeStreamEndpoint>] {
53        &self.stream
54    }
55
56    /// Returns the Event endpoints in this generation.
57    pub fn event(&self) -> &[Rc<dyn NativeEventEndpoint>] {
58        &self.event
59    }
60}
61
62/// One freshly prepared Module Instance generation returned by an Execution Adapter.
63#[derive(Debug)]
64pub struct PreparedNativeModule {
65    pub(super) endpoints: NativeEndpointSet,
66    pub(super) lifecycle: Rc<dyn ModuleLifecycle>,
67}
68
69impl PreparedNativeModule {
70    /// Creates one generation from its exact endpoint set and lifecycle Interface.
71    pub fn new(
72        endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
73        lifecycle: impl ModuleLifecycle,
74    ) -> Self {
75        Self {
76            endpoints: NativeEndpointSet::new(endpoints, Vec::new(), Vec::new()),
77            lifecycle: Rc::new(lifecycle),
78        }
79    }
80
81    /// Creates one generation from an already shared lifecycle implementation.
82    pub fn with_lifecycle(
83        endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
84        lifecycle: Rc<dyn ModuleLifecycle>,
85    ) -> Self {
86        Self {
87            endpoints: NativeEndpointSet::new(endpoints, Vec::new(), Vec::new()),
88            lifecycle,
89        }
90    }
91
92    /// Creates one generation with request and bidirectional stream endpoints.
93    pub fn with_endpoints(
94        endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
95        stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
96        lifecycle: impl ModuleLifecycle,
97    ) -> Self {
98        Self {
99            endpoints: NativeEndpointSet::new(endpoints, stream_endpoints, Vec::new()),
100            lifecycle: Rc::new(lifecycle),
101        }
102    }
103
104    /// Creates a generation from one complete endpoint set and shared lifecycle.
105    pub fn with_endpoint_set_lifecycle(
106        endpoints: NativeEndpointSet,
107        lifecycle: Rc<dyn ModuleLifecycle>,
108    ) -> Self {
109        Self {
110            endpoints,
111            lifecycle,
112        }
113    }
114
115    /// Creates one generation containing only bidirectional stream endpoints.
116    pub fn with_stream_endpoints(
117        stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
118        lifecycle: impl ModuleLifecycle,
119    ) -> Self {
120        Self::with_endpoints(Vec::new(), stream_endpoints, lifecycle)
121    }
122
123    /// Creates one generation containing only ephemeral Event endpoints.
124    pub fn with_event_endpoints(
125        event_endpoints: Vec<Rc<dyn NativeEventEndpoint>>,
126        lifecycle: impl ModuleLifecycle,
127    ) -> Self {
128        Self::with_all_endpoints(Vec::new(), Vec::new(), event_endpoints, lifecycle)
129    }
130
131    /// Creates one generation with request, stream, and ephemeral Event endpoints.
132    pub fn with_all_endpoints(
133        endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
134        stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
135        event_endpoints: Vec<Rc<dyn NativeEventEndpoint>>,
136        lifecycle: impl ModuleLifecycle,
137    ) -> Self {
138        Self {
139            endpoints: NativeEndpointSet::new(endpoints, stream_endpoints, event_endpoints),
140            lifecycle: Rc::new(lifecycle),
141        }
142    }
143
144    /// Returns the exact endpoints prepared for this generation.
145    pub fn endpoints(&self) -> &[Rc<dyn NativeRequestEndpoint>] {
146        self.endpoints.request()
147    }
148
149    /// Returns the exact stream endpoints prepared for this generation.
150    pub fn stream_endpoints(&self) -> &[Rc<dyn NativeStreamEndpoint>] {
151        self.endpoints.stream()
152    }
153
154    /// Returns the exact Event endpoints prepared for this generation.
155    pub fn event_endpoints(&self) -> &[Rc<dyn NativeEventEndpoint>] {
156        self.endpoints.event()
157    }
158
159    /// Returns the lifecycle Interface prepared for this generation.
160    pub fn lifecycle(&self) -> Rc<dyn ModuleLifecycle> {
161        self.lifecycle.clone()
162    }
163
164    pub(super) fn into_parts(self) -> (NativeEndpointSet, Rc<dyn ModuleLifecycle>) {
165        (self.endpoints, self.lifecycle)
166    }
167}
168
169/// One provider-specific binding prepared by an Execution Adapter.
170#[derive(Clone, Debug)]
171pub struct PreparedBinding {
172    pub(super) consumer_instance: String,
173    pub(super) provider_instance: String,
174    pub(super) endpoint: Rc<dyn NativeRequestEndpoint>,
175}
176
177/// One provider-specific bidirectional stream binding prepared by an Adapter.
178#[derive(Clone, Debug)]
179pub struct PreparedStreamBinding {
180    pub(super) consumer_instance: String,
181    pub(super) provider_instance: String,
182    pub(super) endpoint: Rc<dyn NativeStreamEndpoint>,
183}
184
185/// One provider-specific ephemeral Event binding prepared by an Adapter.
186#[derive(Clone, Debug)]
187pub struct PreparedEventBinding {
188    pub(super) consumer_instance: String,
189    pub(super) provider_instance: String,
190    pub(super) endpoint: Rc<dyn NativeEventEndpoint>,
191}
192
193impl PreparedEventBinding {
194    /// Binds one consumer to one exact Event endpoint and provider Instance.
195    pub fn new(
196        consumer_instance: impl Into<String>,
197        provider_instance: impl Into<String>,
198        endpoint: Rc<dyn NativeEventEndpoint>,
199    ) -> Self {
200        Self {
201            consumer_instance: consumer_instance.into(),
202            provider_instance: provider_instance.into(),
203            endpoint,
204        }
205    }
206
207    /// Returns the App-local consumer Instance selected by the Plan.
208    pub fn consumer_instance(&self) -> &str {
209        &self.consumer_instance
210    }
211
212    /// Returns the App-local provider Instance selected by the Plan.
213    pub fn provider_instance(&self) -> &str {
214        &self.provider_instance
215    }
216
217    /// Returns the exact prepared Event endpoint referenced by this binding.
218    pub fn endpoint(&self) -> Rc<dyn NativeEventEndpoint> {
219        self.endpoint.clone()
220    }
221
222    pub(super) fn same_identity(&self, other: &Self) -> bool {
223        self.consumer_instance == other.consumer_instance
224            && self.provider_instance == other.provider_instance
225            && self.endpoint.capability_id() == other.endpoint.capability_id()
226    }
227}
228
229impl PreparedStreamBinding {
230    /// Binds one consumer to one exact stream endpoint and provider Instance.
231    pub fn new(
232        consumer_instance: impl Into<String>,
233        provider_instance: impl Into<String>,
234        endpoint: Rc<dyn NativeStreamEndpoint>,
235    ) -> Self {
236        Self {
237            consumer_instance: consumer_instance.into(),
238            provider_instance: provider_instance.into(),
239            endpoint,
240        }
241    }
242
243    /// Returns the App-local consumer Instance selected by the Plan.
244    pub fn consumer_instance(&self) -> &str {
245        &self.consumer_instance
246    }
247
248    /// Returns the App-local provider Instance selected by the Plan.
249    pub fn provider_instance(&self) -> &str {
250        &self.provider_instance
251    }
252
253    /// Returns the exact prepared stream endpoint referenced by this binding.
254    pub fn endpoint(&self) -> Rc<dyn NativeStreamEndpoint> {
255        self.endpoint.clone()
256    }
257
258    pub(super) fn same_identity(&self, other: &Self) -> bool {
259        self.consumer_instance == other.consumer_instance
260            && self.provider_instance == other.provider_instance
261            && self.endpoint.capability_id() == other.endpoint.capability_id()
262    }
263}
264
265impl PreparedBinding {
266    /// Binds one consumer to the endpoint prepared for one exact provider Instance.
267    pub fn new(
268        consumer_instance: impl Into<String>,
269        provider_instance: impl Into<String>,
270        endpoint: Rc<dyn NativeRequestEndpoint>,
271    ) -> Self {
272        Self {
273            consumer_instance: consumer_instance.into(),
274            provider_instance: provider_instance.into(),
275            endpoint,
276        }
277    }
278
279    /// Returns the App-local consumer Instance selected by the Plan.
280    pub fn consumer_instance(&self) -> &str {
281        &self.consumer_instance
282    }
283
284    /// Returns the App-local provider Instance selected by the Plan.
285    pub fn provider_instance(&self) -> &str {
286        &self.provider_instance
287    }
288
289    /// Returns the exact prepared endpoint referenced by this binding.
290    pub fn endpoint(&self) -> Rc<dyn NativeRequestEndpoint> {
291        self.endpoint.clone()
292    }
293
294    pub(super) fn same_identity(&self, other: &Self) -> bool {
295        self.consumer_instance == other.consumer_instance
296            && self.provider_instance == other.provider_instance
297            && self.endpoint.capability_id() == other.endpoint.capability_id()
298    }
299}
300
301/// Prepared native bindings returned by an Execution Adapter to Kernel.
302#[derive(Debug)]
303pub struct PreparedNativeApp {
304    pub(super) bindings: Vec<PreparedBinding>,
305    pub(super) stream_bindings: Vec<PreparedStreamBinding>,
306    pub(super) event_bindings: Vec<PreparedEventBinding>,
307    pub(super) generations: BTreeMap<String, PreparedNativeModule>,
308}
309
310impl PreparedNativeApp {
311    /// Completes Adapter preparation with the full generation and binding tables.
312    pub fn new(
313        bindings: Vec<PreparedBinding>,
314        generations: BTreeMap<String, PreparedNativeModule>,
315    ) -> Self {
316        Self {
317            bindings,
318            stream_bindings: Vec::new(),
319            event_bindings: Vec::new(),
320            generations,
321        }
322    }
323
324    /// Creates the complete Adapter result for an empty Plan.
325    pub fn empty() -> Self {
326        Self::new(Vec::new(), BTreeMap::new())
327    }
328
329    /// Adds the exact bidirectional stream bindings prepared by an Adapter.
330    #[must_use]
331    pub fn with_stream_bindings(mut self, stream_bindings: Vec<PreparedStreamBinding>) -> Self {
332        self.stream_bindings = stream_bindings;
333        self
334    }
335
336    /// Adds the exact ephemeral Event bindings prepared by an Adapter.
337    #[must_use]
338    pub fn with_event_bindings(mut self, event_bindings: Vec<PreparedEventBinding>) -> Self {
339        self.event_bindings = event_bindings;
340        self
341    }
342
343    pub(super) fn merge(&mut self, other: Self) -> Result<(), RuntimeFailure> {
344        for binding in other.bindings {
345            if self
346                .bindings
347                .iter()
348                .any(|existing| existing.same_identity(&binding))
349            {
350                return Err(RuntimeFailure::InvalidResolvedPlan {
351                    detail: format!(
352                        "multiple Execution Adapters prepared binding `{}:{}:{}`",
353                        binding.consumer_instance,
354                        binding.endpoint.capability_id(),
355                        binding.provider_instance
356                    ),
357                });
358            }
359            self.bindings.push(binding);
360        }
361        for binding in other.stream_bindings {
362            if self
363                .stream_bindings
364                .iter()
365                .any(|existing| existing.same_identity(&binding))
366            {
367                return Err(RuntimeFailure::InvalidResolvedPlan {
368                    detail: format!(
369                        "multiple Execution Adapters prepared stream binding `{}:{}:{}`",
370                        binding.consumer_instance,
371                        binding.endpoint.capability_id(),
372                        binding.provider_instance
373                    ),
374                });
375            }
376            self.stream_bindings.push(binding);
377        }
378        for binding in other.event_bindings {
379            if self
380                .event_bindings
381                .iter()
382                .any(|existing| existing.same_identity(&binding))
383            {
384                return Err(RuntimeFailure::InvalidResolvedPlan {
385                    detail: format!(
386                        "multiple Execution Adapters prepared Event binding `{}:{}:{}`",
387                        binding.consumer_instance,
388                        binding.endpoint.capability_id(),
389                        binding.provider_instance
390                    ),
391                });
392            }
393            self.event_bindings.push(binding);
394        }
395        for (instance_key, generation) in other.generations {
396            if self
397                .generations
398                .insert(instance_key.clone(), generation)
399                .is_some()
400            {
401                return Err(RuntimeFailure::InvalidResolvedPlan {
402                    detail: format!(
403                        "multiple Execution Adapters prepared Module Instance generation `{instance_key}`"
404                    ),
405                });
406            }
407        }
408        Ok(())
409    }
410}
411
412/// Host-specific seam that instantiates Module generations and prepares endpoints.
413pub trait ExecutionAdapter: std::fmt::Debug + 'static {
414    /// Returns the open execution class implemented by this Adapter package.
415    fn execution_class(&self) -> ExecutionClassId;
416
417    /// Instantiates the exact Plan and confirms its endpoint and binding tables.
418    fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure>;
419
420    /// Creates a fresh generation for one selected Module Instance.
421    ///
422    /// Adapters that cannot truthfully recreate a generation retain the default
423    /// failure, which lets Kernel apply the selected finite policy without
424    /// pretending that an in-process fault boundary is recoverable.
425    fn recreate(
426        &self,
427        _plan: &ResolvedAppPlan,
428        instance_key: &str,
429    ) -> Result<PreparedNativeModule, RuntimeFailure> {
430        Err(RuntimeFailure::Internal {
431            detail: format!("Execution Adapter cannot recreate Module Instance `{instance_key}`"),
432        })
433    }
434}
435
436/// Native Rust Adapter Interface for statically linked Module packages.
437///
438/// The blanket implementation below contributes every native Adapter to the
439/// open catalog under the official native execution-class identity.
440pub trait NativeExecutionAdapter: std::fmt::Debug + 'static {
441    /// Instantiates the exact Plan and confirms its endpoint and binding tables.
442    fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure>;
443
444    /// Creates a fresh generation for one selected native Module Instance.
445    fn recreate(
446        &self,
447        _plan: &ResolvedAppPlan,
448        instance_key: &str,
449    ) -> Result<PreparedNativeModule, RuntimeFailure> {
450        Err(RuntimeFailure::Internal {
451            detail: format!("Execution Adapter cannot recreate Module Instance `{instance_key}`"),
452        })
453    }
454}
455
456impl<T: NativeExecutionAdapter> ExecutionAdapter for T {
457    fn execution_class(&self) -> ExecutionClassId {
458        ExecutionClassId::native_rust()
459    }
460
461    fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure> {
462        NativeExecutionAdapter::prepare(self, plan)
463    }
464
465    fn recreate(
466        &self,
467        plan: &ResolvedAppPlan,
468        instance_key: &str,
469    ) -> Result<PreparedNativeModule, RuntimeFailure> {
470        NativeExecutionAdapter::recreate(self, plan, instance_key)
471    }
472}
473
474/// The execution classes contributed by installed Adapter packages.
475#[derive(Clone, Debug, Default, Eq, PartialEq)]
476pub struct ExecutionClassSet(BTreeSet<ExecutionClassId>);
477
478impl ExecutionClassSet {
479    /// Returns whether an installed Adapter provides this execution class.
480    pub fn contains(&self, execution_class: &ExecutionClassId) -> bool {
481        self.0.contains(execution_class)
482    }
483
484    /// Iterates the execution classes in deterministic identity order.
485    pub fn iter(&self) -> impl Iterator<Item = &ExecutionClassId> {
486        self.0.iter()
487    }
488}
489
490/// A Runner could not assemble one unambiguous Adapter catalog.
491#[derive(Clone, Debug, Eq, PartialEq)]
492pub enum ExecutionAdapterCatalogError {
493    /// More than one installed Adapter claimed the same execution class.
494    DuplicateExecutionClass { execution_class: String },
495}
496
497impl std::fmt::Display for ExecutionAdapterCatalogError {
498    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
499        match self {
500            Self::DuplicateExecutionClass { execution_class } => write!(
501                formatter,
502                "multiple Execution Adapters provide class `{execution_class}`"
503            ),
504        }
505    }
506}
507
508impl std::error::Error for ExecutionAdapterCatalogError {}
509
510/// Immutable Adapter catalog assembled by a Runner before Kernel boot.
511#[derive(Debug, Default)]
512pub struct ExecutionAdapterCatalog {
513    pub(super) adapters: BTreeMap<ExecutionClassId, Rc<dyn ExecutionAdapter>>,
514}
515
516impl ExecutionAdapterCatalog {
517    /// Creates an empty catalog for an App with no Module Instances.
518    pub fn new() -> Self {
519        Self::default()
520    }
521
522    /// Creates a catalog containing one Adapter package.
523    pub fn single(adapter: impl ExecutionAdapter) -> Self {
524        Self::new()
525            .with_adapter(adapter)
526            .expect("a new catalog cannot contain a duplicate execution class")
527    }
528
529    /// Installs one Adapter package under its open execution-class identity.
530    pub fn with_adapter(
531        self,
532        adapter: impl ExecutionAdapter,
533    ) -> Result<Self, ExecutionAdapterCatalogError> {
534        self.with_shared_adapter(Rc::new(adapter))
535    }
536
537    /// Installs an Adapter package discovered as a runtime trait object.
538    pub fn with_shared_adapter(
539        mut self,
540        adapter: Rc<dyn ExecutionAdapter>,
541    ) -> Result<Self, ExecutionAdapterCatalogError> {
542        let execution_class = adapter.execution_class();
543        if self.adapters.contains_key(&execution_class) {
544            return Err(ExecutionAdapterCatalogError::DuplicateExecutionClass {
545                execution_class: execution_class.to_string(),
546            });
547        }
548        self.adapters.insert(execution_class, adapter);
549        Ok(self)
550    }
551
552    /// Returns the effective execution classes contributed by installed packages.
553    pub fn execution_classes(&self) -> ExecutionClassSet {
554        ExecutionClassSet(self.adapters.keys().cloned().collect())
555    }
556
557    pub(super) fn adapter(
558        &self,
559        execution_class: &ExecutionClassId,
560    ) -> Option<Rc<dyn ExecutionAdapter>> {
561        self.adapters.get(execution_class).cloned()
562    }
563
564    pub(super) fn prepare(
565        &self,
566        plan: &ResolvedAppPlan,
567    ) -> Result<PreparedNativeApp, RuntimeFailure> {
568        let mut required_classes = BTreeSet::new();
569        for instance in plan.module_instances() {
570            if !self.adapters.contains_key(instance.execution_class()) {
571                return Err(RuntimeFailure::UnavailableExecutionClass {
572                    instance_key: instance.instance_key().to_owned(),
573                    execution_class: instance.execution_class().to_string(),
574                });
575            }
576            required_classes.insert(instance.execution_class().clone());
577        }
578
579        let mut prepared = PreparedNativeApp::empty();
580        for execution_class in required_classes {
581            let adapter = self
582                .adapters
583                .get(&execution_class)
584                .expect("required execution classes were validated");
585            prepared.merge(adapter.prepare(plan)?)?;
586        }
587        Ok(prepared)
588    }
589}