1mod managed_tasks;
4
5use std::{collections::BTreeMap, rc::Rc};
6
7#[doc(hidden)]
8pub use inventory as __inventory;
9use lenso_app_plan::{ExecutionClassId, ResolvedAppPlan};
10use lenso_kernel::{ActivateContext, DeactivateContext, PrepareContext};
11pub use lenso_kernel::{CancellationToken, RuntimeFailure};
12pub use lenso_native_adapter_macros::{ModuleConfig, PluginConfig, module, plugin, provides};
13pub use managed_tasks::{ManagedTasks, ManagedTasksError};
14
15#[allow(async_fn_in_trait)]
21pub trait Lifecycle: Clone + 'static {
22 async fn prepare(&self, _context: PrepareContext) -> Result<(), RuntimeFailure> {
23 Ok(())
24 }
25
26 async fn activate(&self, _context: ActivateContext) -> Result<(), RuntimeFailure> {
27 Ok(())
28 }
29
30 async fn deactivate(&self, _context: DeactivateContext) -> Result<(), RuntimeFailure> {
31 Ok(())
32 }
33}
34
35#[doc(hidden)]
37pub mod __private {
38 pub use crate::{
39 __inventory, Lifecycle, LinkedNativeModuleFactory, NativeModuleFactory,
40 NativeModuleFactoryContext, NativeModuleInstance, RuntimeFailure,
41 };
42 pub use futures;
43 pub use futures::future::LocalBoxFuture;
44 pub use lenso_kernel::{
45 ActivateContext, DeactivateContext, InvocationContext, ModuleFuture, ModuleLifecycle,
46 NativeEventEndpoint, NativeRequestEndpoint, NativeRequestFuture, NativeStreamEndpoint,
47 NativeStreamSession, PrepareContext,
48 };
49 pub use serde_json;
50}
51
52use lenso_kernel::{
53 ModuleLifecycle, NativeEndpointSet, NativeEventEndpoint, NativeExecutionAdapter,
54 NativeRequestEndpoint, NativeStreamEndpoint, NoopModuleLifecycle, PreparedBinding,
55 PreparedEventBinding, PreparedNativeApp, PreparedNativeModule, PreparedStreamBinding,
56};
57
58#[derive(Clone, Copy, Debug)]
60#[doc(hidden)]
61pub struct LinkedNativeModuleFactory {
62 constructor: fn() -> Rc<dyn NativeModuleFactory>,
63}
64
65impl LinkedNativeModuleFactory {
66 #[doc(hidden)]
68 pub const fn new(constructor: fn() -> Rc<dyn NativeModuleFactory>) -> Self {
69 Self { constructor }
70 }
71}
72
73inventory::collect!(LinkedNativeModuleFactory);
74
75#[derive(Debug)]
77pub struct NativeModuleInstance {
78 endpoints: NativeEndpointSet,
79 lifecycle: Rc<dyn ModuleLifecycle>,
80}
81
82impl NativeModuleInstance {
83 pub fn new(endpoints: Vec<Rc<dyn NativeRequestEndpoint>>) -> Self {
85 Self::with_lifecycle(endpoints, NoopModuleLifecycle)
86 }
87
88 pub fn with_lifecycle(
90 endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
91 lifecycle: impl ModuleLifecycle,
92 ) -> Self {
93 Self {
94 endpoints: NativeEndpointSet::new(endpoints, Vec::new(), Vec::new()),
95 lifecycle: Rc::new(lifecycle),
96 }
97 }
98
99 pub fn with_endpoints(
101 endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
102 stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
103 lifecycle: impl ModuleLifecycle,
104 ) -> Self {
105 Self {
106 endpoints: NativeEndpointSet::new(endpoints, stream_endpoints, Vec::new()),
107 lifecycle: Rc::new(lifecycle),
108 }
109 }
110
111 pub fn with_stream_endpoints(
113 stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
114 lifecycle: impl ModuleLifecycle,
115 ) -> Self {
116 Self::with_endpoints(Vec::new(), stream_endpoints, lifecycle)
117 }
118
119 pub fn with_event_endpoints(
121 event_endpoints: Vec<Rc<dyn NativeEventEndpoint>>,
122 lifecycle: impl ModuleLifecycle,
123 ) -> Self {
124 Self {
125 endpoints: NativeEndpointSet::new(Vec::new(), Vec::new(), event_endpoints),
126 lifecycle: Rc::new(lifecycle),
127 }
128 }
129
130 pub fn with_all_endpoints(
132 endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
133 stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
134 event_endpoints: Vec<Rc<dyn NativeEventEndpoint>>,
135 lifecycle: impl ModuleLifecycle,
136 ) -> Self {
137 Self {
138 endpoints: NativeEndpointSet::new(endpoints, stream_endpoints, event_endpoints),
139 lifecycle: Rc::new(lifecycle),
140 }
141 }
142
143 pub fn lifecycle(&self) -> Rc<dyn ModuleLifecycle> {
145 self.lifecycle.clone()
146 }
147
148 pub fn endpoints(&self) -> &[Rc<dyn NativeRequestEndpoint>] {
150 self.endpoints.request()
151 }
152
153 pub fn stream_endpoints(&self) -> &[Rc<dyn NativeStreamEndpoint>] {
155 self.endpoints.stream()
156 }
157
158 pub fn event_endpoints(&self) -> &[Rc<dyn NativeEventEndpoint>] {
160 self.endpoints.event()
161 }
162}
163
164impl Default for NativeModuleInstance {
165 fn default() -> Self {
166 Self::new(Vec::new())
167 }
168}
169
170pub trait NativeModuleFactory: std::fmt::Debug + 'static {
172 fn package_id(&self) -> &'static str;
174 fn package_version(&self) -> &'static str {
176 ""
177 }
178 fn factory_identity(&self) -> String {
185 let version = self.package_version();
186 if version.is_empty() {
187 self.package_id().to_owned()
188 } else {
189 format!("{}@{version}", self.package_id())
190 }
191 }
192 fn instantiate(
194 &self,
195 context: NativeModuleFactoryContext<'_>,
196 ) -> Result<NativeModuleInstance, RuntimeFailure>;
197}
198
199#[derive(Clone, Copy, Debug)]
201pub struct NativeModuleFactoryContext<'a> {
202 instance_key: &'a str,
203 entrypoint: &'a str,
204 configuration: &'a str,
205}
206
207impl<'a> NativeModuleFactoryContext<'a> {
208 fn from_plan(instance: &'a lenso_app_plan::ModuleInstancePlan) -> Self {
209 Self {
210 instance_key: instance.instance_key(),
211 entrypoint: instance.entrypoint(),
212 configuration: instance.configuration(),
213 }
214 }
215
216 pub const fn instance_key(self) -> &'a str {
218 self.instance_key
219 }
220
221 pub const fn entrypoint(self) -> &'a str {
223 self.entrypoint
224 }
225
226 pub const fn configuration(self) -> &'a str {
228 self.configuration
229 }
230}
231
232#[derive(Debug, Default)]
234pub struct NativeModuleRegistry {
235 factories: Vec<Rc<dyn NativeModuleFactory>>,
236}
237
238type NativeInstances = BTreeMap<String, NativeModuleInstance>;
239type PreparedGenerations = BTreeMap<String, PreparedNativeModule>;
240type NativeBindings = (
241 Vec<PreparedBinding>,
242 Vec<PreparedStreamBinding>,
243 Vec<PreparedEventBinding>,
244);
245
246fn factory_matches(
247 factory: &dyn NativeModuleFactory,
248 expected: &lenso_app_plan::ModuleInstancePlan,
249) -> bool {
250 factory.package_id() == expected.package_id()
251 && (expected.package_revision().is_empty()
252 || factory.package_version() == expected.package_revision()
253 || factory.factory_identity() == expected.package_revision())
254}
255
256impl NativeModuleRegistry {
257 pub fn new() -> Self {
259 Self::default()
260 }
261
262 #[must_use]
267 pub fn with_linked_factories(mut self) -> Self {
268 self.factories.extend(
269 inventory::iter::<LinkedNativeModuleFactory>
270 .into_iter()
271 .map(|linked| (linked.constructor)()),
272 );
273 self.factories
274 .sort_by_key(|factory| factory.factory_identity());
275 self
276 }
277
278 pub fn factories(&self) -> impl Iterator<Item = &dyn NativeModuleFactory> {
280 self.factories.iter().map(std::convert::AsRef::as_ref)
281 }
282 #[must_use]
284 pub fn with_factory(mut self, factory: impl NativeModuleFactory) -> Self {
285 self.factories.push(Rc::new(factory));
286 self
287 }
288
289 fn prepare_instances(
290 &self,
291 plan: &ResolvedAppPlan,
292 ) -> Result<(NativeInstances, PreparedGenerations), RuntimeFailure> {
293 let mut instances = BTreeMap::new();
294 let mut generations = BTreeMap::new();
295 for expected in plan
296 .module_instances()
297 .iter()
298 .filter(|instance| instance.execution_class() == &ExecutionClassId::native_rust())
299 {
300 let matching_factories: Vec<_> = self
301 .factories
302 .iter()
303 .filter(|factory| factory_matches(factory.as_ref(), expected))
304 .collect();
305 let factory = match matching_factories.as_slice() {
306 [] => {
307 return Err(RuntimeFailure::MissingModuleFactory {
308 instance: expected.instance_key().to_owned(),
309 package_id: expected.package_id().to_owned(),
310 });
311 }
312 [factory] => *factory,
313 _ => {
314 return invalid(format!(
315 "multiple statically linked factories declare package `{}`",
316 expected.package_id()
317 ));
318 }
319 };
320 let generation =
321 factory.instantiate(NativeModuleFactoryContext::from_plan(expected))?;
322 generations.insert(
323 expected.instance_key().to_owned(),
324 PreparedNativeModule::with_endpoint_set_lifecycle(
325 generation.endpoints.clone(),
326 generation.lifecycle(),
327 ),
328 );
329 if instances
330 .insert(expected.instance_key().to_owned(), generation)
331 .is_some()
332 {
333 return invalid(format!(
334 "duplicate Module Instance `{}`",
335 expected.instance_key()
336 ));
337 }
338 }
339 Ok((instances, generations))
340 }
341}
342
343impl NativeExecutionAdapter for NativeModuleRegistry {
344 fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure> {
345 plan.validate()
346 .map_err(|error| RuntimeFailure::InvalidResolvedPlan {
347 detail: error.to_string(),
348 })?;
349
350 let (instances, generations) = self.prepare_instances(plan)?;
351 let (bindings, stream_bindings, event_bindings) = prepare_bindings(plan, &instances)?;
352 Ok(PreparedNativeApp::new(bindings, generations)
353 .with_stream_bindings(stream_bindings)
354 .with_event_bindings(event_bindings))
355 }
356
357 fn recreate(
358 &self,
359 plan: &ResolvedAppPlan,
360 instance_key: &str,
361 ) -> Result<PreparedNativeModule, RuntimeFailure> {
362 let expected = plan
363 .module_instances()
364 .iter()
365 .find(|instance| instance.instance_key() == instance_key)
366 .ok_or_else(|| RuntimeFailure::InvalidResolvedPlan {
367 detail: format!("unknown Module Instance `{instance_key}`"),
368 })?;
369 let matching_factories: Vec<_> = self
370 .factories
371 .iter()
372 .filter(|factory| factory_matches(factory.as_ref(), expected))
373 .collect();
374 let factory = match matching_factories.as_slice() {
375 [] => {
376 return Err(RuntimeFailure::MissingModuleFactory {
377 instance: expected.instance_key().to_owned(),
378 package_id: expected.package_id().to_owned(),
379 });
380 }
381 [factory] => *factory,
382 _ => {
383 return invalid(format!(
384 "multiple statically linked factories declare package `{}`",
385 expected.package_id()
386 ));
387 }
388 };
389 let generation = factory.instantiate(NativeModuleFactoryContext::from_plan(expected))?;
390 Ok(PreparedNativeModule::with_endpoint_set_lifecycle(
391 generation.endpoints.clone(),
392 generation.lifecycle(),
393 ))
394 }
395}
396
397fn prepare_bindings(
398 plan: &ResolvedAppPlan,
399 instances: &NativeInstances,
400) -> Result<NativeBindings, RuntimeFailure> {
401 let mut bindings = Vec::new();
402 let mut stream_bindings = Vec::new();
403 let mut event_bindings = Vec::new();
404 for binding in plan.capability_bindings() {
405 if !instances.contains_key(binding.provider_instance()) {
406 continue;
407 }
408 let provider = plan
409 .module_instance(binding.provider_instance())
410 .expect("validated binding provider should exist");
411 let descriptor = provider
412 .provided_capabilities()
413 .iter()
414 .find(|descriptor| descriptor.capability_id() == binding.capability_id())
415 .expect("validated binding descriptor should exist");
416 if !descriptor.request_operations().is_empty() {
417 let endpoint = instances
418 .get(binding.provider_instance())
419 .and_then(|instance| {
420 instance.endpoints.request().iter().find(|endpoint| {
421 endpoint.capability_id() == binding.capability_id()
422 && endpoint.descriptor_version() == binding.descriptor_version()
423 })
424 })
425 .cloned()
426 .ok_or_else(|| RuntimeFailure::InvalidResolvedPlan {
427 detail: format!(
428 "Capability `{}` version `{}` has no request endpoint on provider `{}`",
429 binding.capability_id(),
430 binding.descriptor_version(),
431 binding.provider_instance()
432 ),
433 })?;
434 bindings.push(PreparedBinding::new(
435 binding.consumer_instance(),
436 binding.provider_instance(),
437 endpoint,
438 ));
439 }
440 if !descriptor.stream_operations().is_empty() {
441 let endpoint = instances
442 .get(binding.provider_instance())
443 .and_then(|instance| {
444 instance.endpoints.stream().iter().find(|endpoint| {
445 endpoint.capability_id() == binding.capability_id()
446 && endpoint.descriptor_version() == binding.descriptor_version()
447 })
448 })
449 .cloned()
450 .ok_or_else(|| RuntimeFailure::InvalidResolvedPlan {
451 detail: format!(
452 "Capability `{}` version `{}` has no stream endpoint on provider `{}`",
453 binding.capability_id(),
454 binding.descriptor_version(),
455 binding.provider_instance()
456 ),
457 })?;
458 stream_bindings.push(PreparedStreamBinding::new(
459 binding.consumer_instance(),
460 binding.provider_instance(),
461 endpoint,
462 ));
463 }
464 if !descriptor.event_operations().is_empty() {
465 let endpoint = instances
466 .get(binding.provider_instance())
467 .and_then(|instance| {
468 instance.endpoints.event().iter().find(|endpoint| {
469 endpoint.capability_id() == binding.capability_id()
470 && endpoint.descriptor_version() == binding.descriptor_version()
471 })
472 })
473 .cloned()
474 .ok_or_else(|| RuntimeFailure::InvalidResolvedPlan {
475 detail: format!(
476 "Capability `{}` version `{}` has no Event endpoint on provider `{}`",
477 binding.capability_id(),
478 binding.descriptor_version(),
479 binding.provider_instance()
480 ),
481 })?;
482 event_bindings.push(PreparedEventBinding::new(
483 binding.consumer_instance(),
484 binding.provider_instance(),
485 endpoint,
486 ));
487 }
488 }
489 Ok((bindings, stream_bindings, event_bindings))
490}
491
492fn invalid<T>(detail: String) -> Result<T, RuntimeFailure> {
493 Err(RuntimeFailure::InvalidResolvedPlan { detail })
494}