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