Skip to main content

lenso_kernel/
request.rs

1use std::time::Duration;
2
3use super::{
4    CancellationToken, EventCapability, InvocationContext, ModuleEventDependencyHandle,
5    NativeAppRuntime, NativeEndpointBinding, NativeEventHandle, NativeRequestHandle,
6    NativeStreamEndpointBinding, NativeStreamHandle, Rc, RefCell, StreamCapability, Weak,
7};
8
9pub trait RequestCapability: 'static {
10    /// Typed request value.
11    type Request: 'static;
12    /// Typed success value.
13    type Response: 'static;
14    /// Typed Capability-defined error value.
15    type DomainError: 'static;
16    /// Stable Capability series identity.
17    const ID: &'static str;
18    /// Exact generated Descriptor version.
19    const DESCRIPTOR_VERSION: &'static str;
20}
21
22/// Kernel-generated identity for one logical request invocation.
23pub type RequestId = u64;
24
25/// Runtime-owned failure, kept separate from Capability-defined Domain Errors.
26#[derive(Clone, Debug, Eq, PartialEq)]
27pub enum RuntimeFailure {
28    /// The consumer has no resolved binding for this Capability.
29    Unavailable { capability: &'static str },
30    /// The bound provider does not declare the requested Operation.
31    UnknownOperation {
32        capability: &'static str,
33        operation: String,
34    },
35    /// A singular generated client was used for a requirement with many providers.
36    AmbiguousBinding {
37        capability: &'static str,
38        providers: usize,
39    },
40    /// Generated native types disagreed with the prepared endpoint.
41    ProtocolViolation { capability: &'static str },
42    /// A package selected by the Plan was not linked into the native App.
43    MissingModuleFactory {
44        instance: String,
45        package_id: String,
46    },
47    /// No installed Execution Adapter provides the class selected by one Instance.
48    UnavailableExecutionClass {
49        instance_key: String,
50        execution_class: String,
51    },
52    /// The Resolved Plan or prepared endpoint set is internally inconsistent.
53    InvalidResolvedPlan { detail: String },
54    /// New request admission was closed because the App is shutting down.
55    AdmissionClosed,
56    /// The request could not enter a full bounded admission queue.
57    ResourceExhausted {
58        capability: &'static str,
59        operation: String,
60    },
61    /// The invocation deadline expired before the request completed.
62    DeadlineExceeded { request_id: RequestId },
63    /// The caller cancelled the invocation before it completed.
64    Cancelled { request_id: RequestId },
65    /// The Runtime Driver or Adapter reported an internal execution failure.
66    Internal { detail: String },
67    /// A Module generation reported a failure that should trigger supervision.
68    ModuleFailure { detail: String },
69    /// A Module Instance exhausted its finite restart budget.
70    ModuleRestartExhausted { instance: String, attempts: usize },
71}
72
73/// The lifecycle phase represented by a Module context.
74#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub enum ModuleLifecyclePhase {
76    /// The Module may validate configuration and reserve reversible resources.
77    Prepare,
78    /// The Module may initialize against already prepared dependencies.
79    Activate,
80    /// The App Ready Gate has opened and externally triggered work may begin.
81    Ready,
82    /// The Module must release work and resources owned by this generation.
83    Deactivate,
84}
85
86/// A deterministic dependency visible to one Module Instance.
87#[derive(Clone, Debug)]
88pub struct ModuleDependency {
89    pub(super) capability_id: String,
90    pub(super) provider_instance: String,
91    pub(super) provider_order: usize,
92    pub(super) handle: Option<ModuleDependencyHandle>,
93    pub(super) stream_handle: Option<ModuleStreamDependencyHandle>,
94    pub(super) event_handle: Option<ModuleEventDependencyHandle>,
95}
96
97impl ModuleDependency {
98    pub(super) fn new(
99        capability_id: impl Into<String>,
100        provider_instance: impl Into<String>,
101        provider_order: usize,
102        handle: Option<ModuleDependencyHandle>,
103        stream_handle: Option<ModuleStreamDependencyHandle>,
104        event_handle: Option<ModuleEventDependencyHandle>,
105    ) -> Self {
106        Self {
107            capability_id: capability_id.into(),
108            provider_instance: provider_instance.into(),
109            provider_order,
110            handle,
111            stream_handle,
112            event_handle,
113        }
114    }
115
116    /// Returns the Capability required by this dependency.
117    pub fn capability_id(&self) -> &str {
118        &self.capability_id
119    }
120
121    /// Returns the App-local provider Instance key.
122    pub fn provider_instance(&self) -> &str {
123        &self.provider_instance
124    }
125
126    /// Returns the deterministic provider order for a `many` binding.
127    pub const fn provider_order(&self) -> usize {
128        self.provider_order
129    }
130
131    /// Returns the resolved native endpoint handle when the Adapter supplied one.
132    pub fn handle(&self) -> Option<ModuleDependencyHandle> {
133        self.handle.clone()
134    }
135
136    /// Returns the resolved native stream endpoint handle when the Adapter supplied one.
137    pub fn stream_handle(&self) -> Option<ModuleStreamDependencyHandle> {
138        self.stream_handle.clone()
139    }
140
141    /// Returns the resolved native Event endpoint handle when the Adapter supplied one.
142    pub fn event_handle(&self) -> Option<ModuleEventDependencyHandle> {
143        self.event_handle.clone()
144    }
145}
146
147/// An opaque, Adapter-resolved Capability endpoint passed to lifecycle code.
148#[derive(Clone, Debug)]
149pub struct ModuleDependencyHandle {
150    pub(super) binding: NativeEndpointBinding,
151    pub(super) caller_instance: String,
152    pub(super) runtime: Rc<RefCell<Weak<NativeAppRuntime>>>,
153}
154
155/// An opaque, Adapter-resolved stream Capability endpoint passed to lifecycle code.
156#[derive(Clone, Debug)]
157pub struct ModuleStreamDependencyHandle {
158    pub(super) binding: NativeStreamEndpointBinding,
159    pub(super) caller_instance: String,
160    pub(super) runtime: Rc<RefCell<Weak<NativeAppRuntime>>>,
161}
162
163impl ModuleStreamDependencyHandle {
164    /// Returns the Capability implemented by this stream handle.
165    pub fn capability_id(&self) -> &'static str {
166        self.binding.state.capability_id
167    }
168
169    /// Returns the exact Descriptor version implemented by this stream handle.
170    pub fn descriptor_version(&self) -> &'static str {
171        self.binding.state.descriptor_version
172    }
173
174    /// Returns the exact stream Operation table implemented by this handle.
175    pub fn operations(&self) -> &'static [&'static str] {
176        self.binding.state.operations
177    }
178
179    /// Converts this resolved dependency into its generated typed stream handle.
180    pub fn typed<C: StreamCapability>(&self) -> Result<NativeStreamHandle<C>, RuntimeFailure> {
181        if self.capability_id() != C::ID || self.descriptor_version() != C::DESCRIPTOR_VERSION {
182            return Err(RuntimeFailure::ProtocolViolation { capability: C::ID });
183        }
184        let runtime = self
185            .runtime
186            .borrow()
187            .upgrade()
188            .ok_or(RuntimeFailure::AdmissionClosed)?;
189        Ok(NativeStreamHandle::from_endpoints(
190            std::slice::from_ref(&self.binding),
191            runtime,
192            &self.caller_instance,
193            true,
194        ))
195    }
196}
197
198impl ModuleDependencyHandle {
199    /// Returns the Capability implemented by this handle.
200    pub fn capability_id(&self) -> &'static str {
201        self.binding.state.capability_id
202    }
203
204    /// Returns the exact Descriptor version implemented by this handle.
205    pub fn descriptor_version(&self) -> &'static str {
206        self.binding.state.descriptor_version
207    }
208
209    /// Returns the exact Operation table implemented by this handle.
210    pub fn operations(&self) -> &'static [&'static str] {
211        self.binding.state.operations
212    }
213
214    /// Converts this resolved dependency into its generated typed request handle.
215    pub fn typed<C: RequestCapability>(&self) -> Result<NativeRequestHandle<C>, RuntimeFailure> {
216        if self.capability_id() != C::ID || self.descriptor_version() != C::DESCRIPTOR_VERSION {
217            return Err(RuntimeFailure::ProtocolViolation { capability: C::ID });
218        }
219        let runtime = self
220            .runtime
221            .borrow()
222            .upgrade()
223            .ok_or(RuntimeFailure::AdmissionClosed)?;
224        Ok(NativeRequestHandle::from_endpoints(
225            std::slice::from_ref(&self.binding),
226            runtime,
227            &self.caller_instance,
228            true,
229        ))
230    }
231}
232
233/// The explicit Capability dependencies available during Module lifecycle.
234#[derive(Clone, Debug, Default)]
235pub struct ModuleDependencies {
236    pub(super) bindings: Vec<ModuleDependency>,
237    pub(super) caller_instance: String,
238    pub(super) runtime: Rc<RefCell<Weak<NativeAppRuntime>>>,
239}
240
241impl ModuleDependencies {
242    pub(super) fn new(
243        caller_instance: impl Into<String>,
244        runtime: Rc<RefCell<Weak<NativeAppRuntime>>>,
245    ) -> Self {
246        Self {
247            bindings: Vec::new(),
248            caller_instance: caller_instance.into(),
249            runtime,
250        }
251    }
252
253    /// Returns dependencies in the order materialized by the Resolved App Plan.
254    pub fn bindings(&self) -> &[ModuleDependency] {
255        &self.bindings
256    }
257
258    /// Returns the number of explicit dependencies.
259    pub fn len(&self) -> usize {
260        self.bindings.len()
261    }
262
263    /// Returns whether this Module has no explicit dependencies.
264    pub fn is_empty(&self) -> bool {
265        self.bindings.is_empty()
266    }
267
268    /// Creates a Kernel Invocation Context for work initiated by this Module.
269    ///
270    /// The request identity and monotonic deadline come from the same Runtime
271    /// Driver as the App. The context is still owned by the caller and its
272    /// cancellation token remains explicit.
273    pub fn invocation_context(
274        &self,
275        deadline: Option<Duration>,
276        cancellation: CancellationToken,
277    ) -> Result<InvocationContext, RuntimeFailure> {
278        let runtime = self
279            .runtime
280            .borrow()
281            .upgrade()
282            .ok_or(RuntimeFailure::AdmissionClosed)?;
283        let request_id = runtime.request_ids.get();
284        runtime.request_ids.set(request_id.saturating_add(1));
285        Ok(InvocationContext::new(request_id, deadline, cancellation)
286            .with_caller_instance(self.caller_instance.clone()))
287    }
288
289    /// Creates a Module Invocation Context with a Driver-relative deadline.
290    pub fn invocation_context_after(
291        &self,
292        timeout: Duration,
293        cancellation: CancellationToken,
294    ) -> Result<InvocationContext, RuntimeFailure> {
295        let runtime = self
296            .runtime
297            .borrow()
298            .upgrade()
299            .ok_or(RuntimeFailure::AdmissionClosed)?;
300        let deadline = (runtime.driver.now)().saturating_add(timeout);
301        drop(runtime);
302        self.invocation_context(Some(deadline), cancellation)
303    }
304
305    /// Returns the one explicitly bound typed dependency.
306    pub fn one<C: RequestCapability>(&self) -> Result<NativeRequestHandle<C>, RuntimeFailure> {
307        let handles: Vec<_> = self
308            .bindings
309            .iter()
310            .filter(|binding| binding.capability_id() == C::ID)
311            .filter_map(ModuleDependency::handle)
312            .collect();
313        match handles.as_slice() {
314            [handle] => handle.typed::<C>(),
315            [] => Err(RuntimeFailure::Unavailable { capability: C::ID }),
316            handles => Err(RuntimeFailure::AmbiguousBinding {
317                capability: C::ID,
318                providers: handles.len(),
319            }),
320        }
321    }
322
323    /// Returns an optional explicitly bound typed dependency.
324    pub fn optional<C: RequestCapability>(
325        &self,
326    ) -> Result<Option<NativeRequestHandle<C>>, RuntimeFailure> {
327        match self
328            .bindings
329            .iter()
330            .filter(|binding| binding.capability_id() == C::ID)
331            .filter_map(ModuleDependency::handle)
332            .collect::<Vec<_>>()
333            .as_slice()
334        {
335            [] => Ok(None),
336            [handle] => handle.typed::<C>().map(Some),
337            handles => Err(RuntimeFailure::AmbiguousBinding {
338                capability: C::ID,
339                providers: handles.len(),
340            }),
341        }
342    }
343
344    /// Returns all explicitly bound typed dependencies in resolved provider order.
345    pub fn many<C: RequestCapability>(
346        &self,
347    ) -> Result<Vec<NativeRequestHandle<C>>, RuntimeFailure> {
348        self.bindings
349            .iter()
350            .filter(|binding| binding.capability_id() == C::ID)
351            .filter_map(ModuleDependency::handle)
352            .map(|handle| handle.typed::<C>())
353            .collect()
354    }
355
356    /// Returns the one explicitly bound typed stream dependency.
357    pub fn one_stream<C: StreamCapability>(&self) -> Result<NativeStreamHandle<C>, RuntimeFailure> {
358        let handles: Vec<_> = self
359            .bindings
360            .iter()
361            .filter(|binding| binding.capability_id() == C::ID)
362            .filter_map(ModuleDependency::stream_handle)
363            .collect();
364        match handles.as_slice() {
365            [handle] => handle.typed::<C>(),
366            [] => Err(RuntimeFailure::Unavailable { capability: C::ID }),
367            handles => Err(RuntimeFailure::AmbiguousBinding {
368                capability: C::ID,
369                providers: handles.len(),
370            }),
371        }
372    }
373
374    /// Returns an optional explicitly bound typed stream dependency.
375    pub fn optional_stream<C: StreamCapability>(
376        &self,
377    ) -> Result<Option<NativeStreamHandle<C>>, RuntimeFailure> {
378        match self
379            .bindings
380            .iter()
381            .filter(|binding| binding.capability_id() == C::ID)
382            .filter_map(ModuleDependency::stream_handle)
383            .collect::<Vec<_>>()
384            .as_slice()
385        {
386            [] => Ok(None),
387            [handle] => handle.typed::<C>().map(Some),
388            handles => Err(RuntimeFailure::AmbiguousBinding {
389                capability: C::ID,
390                providers: handles.len(),
391            }),
392        }
393    }
394
395    /// Returns all explicitly bound typed stream dependencies in Plan order.
396    pub fn many_stream<C: StreamCapability>(
397        &self,
398    ) -> Result<Vec<NativeStreamHandle<C>>, RuntimeFailure> {
399        self.bindings
400            .iter()
401            .filter(|binding| binding.capability_id() == C::ID)
402            .filter_map(ModuleDependency::stream_handle)
403            .map(|handle| handle.typed::<C>())
404            .collect()
405    }
406
407    /// Returns one typed Event handle over every explicit binding in Plan order.
408    pub fn many_event<C: EventCapability>(&self) -> Result<NativeEventHandle<C>, RuntimeFailure> {
409        let handles: Vec<_> = self
410            .bindings
411            .iter()
412            .filter(|binding| binding.capability_id() == C::ID)
413            .filter_map(ModuleDependency::event_handle)
414            .collect();
415        if handles.iter().any(|handle| {
416            handle.capability_id() != C::ID || handle.descriptor_version() != C::DESCRIPTOR_VERSION
417        }) {
418            return Err(RuntimeFailure::ProtocolViolation { capability: C::ID });
419        }
420        let runtime = self
421            .runtime
422            .borrow()
423            .upgrade()
424            .ok_or(RuntimeFailure::AdmissionClosed)?;
425        let endpoints = handles
426            .iter()
427            .map(|handle| handle.binding.clone())
428            .collect::<Vec<_>>();
429        Ok(NativeEventHandle::from_endpoints(
430            &endpoints,
431            runtime,
432            &self.caller_instance,
433            true,
434        ))
435    }
436
437    /// Returns the one explicitly bound typed Event dependency.
438    pub fn one_event<C: EventCapability>(&self) -> Result<NativeEventHandle<C>, RuntimeFailure> {
439        match self
440            .bindings
441            .iter()
442            .filter(|binding| binding.capability_id() == C::ID)
443            .filter_map(ModuleDependency::event_handle)
444            .collect::<Vec<_>>()
445            .as_slice()
446        {
447            [handle] => handle.typed::<C>(),
448            [] => Err(RuntimeFailure::Unavailable { capability: C::ID }),
449            handles => Err(RuntimeFailure::AmbiguousBinding {
450                capability: C::ID,
451                providers: handles.len(),
452            }),
453        }
454    }
455
456    /// Returns an optional explicitly bound typed Event dependency.
457    pub fn optional_event<C: EventCapability>(
458        &self,
459    ) -> Result<Option<NativeEventHandle<C>>, RuntimeFailure> {
460        match self
461            .bindings
462            .iter()
463            .filter(|binding| binding.capability_id() == C::ID)
464            .filter_map(ModuleDependency::event_handle)
465            .collect::<Vec<_>>()
466            .as_slice()
467        {
468            [] => Ok(None),
469            [handle] => handle.typed::<C>().map(Some),
470            handles => Err(RuntimeFailure::AmbiguousBinding {
471                capability: C::ID,
472                providers: handles.len(),
473            }),
474        }
475    }
476}