1use std::any::Any;
2
3use super::{
4 BTreeMap, BTreeSet, ErasedDomainResult, ErasedValue, ExecutionClassId, InvocationContext,
5 LocalBoxFuture, ModuleLifecycle, NativeEventEndpoint, NativeStreamEndpoint, Rc,
6 ResolvedAppPlan, RuntimeFailure,
7};
8
9pub trait NativeRequestEndpoint: std::fmt::Debug {
11 fn capability_id(&self) -> &'static str;
13 fn descriptor_version(&self) -> &'static str;
15 fn operations(&self) -> &'static [&'static str];
17 #[doc(hidden)]
21 fn typed_endpoint(&self) -> Option<&dyn Any> {
22 None
23 }
24 fn invoke(
26 &self,
27 operation: &str,
28 request: ErasedValue,
29 context: InvocationContext,
30 ) -> LocalBoxFuture<'static, Result<ErasedDomainResult, RuntimeFailure>>;
31}
32
33#[derive(Clone, Debug, Default)]
35pub struct NativeEndpointSet {
36 request: Vec<Rc<dyn NativeRequestEndpoint>>,
37 stream: Vec<Rc<dyn NativeStreamEndpoint>>,
38 event: Vec<Rc<dyn NativeEventEndpoint>>,
39}
40
41impl NativeEndpointSet {
42 pub fn new(
44 request: Vec<Rc<dyn NativeRequestEndpoint>>,
45 stream: Vec<Rc<dyn NativeStreamEndpoint>>,
46 event: Vec<Rc<dyn NativeEventEndpoint>>,
47 ) -> Self {
48 Self {
49 request,
50 stream,
51 event,
52 }
53 }
54
55 pub fn request(&self) -> &[Rc<dyn NativeRequestEndpoint>] {
57 &self.request
58 }
59
60 pub fn stream(&self) -> &[Rc<dyn NativeStreamEndpoint>] {
62 &self.stream
63 }
64
65 pub fn event(&self) -> &[Rc<dyn NativeEventEndpoint>] {
67 &self.event
68 }
69}
70
71#[derive(Debug)]
73pub struct PreparedNativeModule {
74 pub(super) endpoints: NativeEndpointSet,
75 pub(super) lifecycle: Rc<dyn ModuleLifecycle>,
76}
77
78impl PreparedNativeModule {
79 pub fn new(
81 endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
82 lifecycle: impl ModuleLifecycle,
83 ) -> Self {
84 Self {
85 endpoints: NativeEndpointSet::new(endpoints, Vec::new(), Vec::new()),
86 lifecycle: Rc::new(lifecycle),
87 }
88 }
89
90 pub fn with_lifecycle(
92 endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
93 lifecycle: Rc<dyn ModuleLifecycle>,
94 ) -> Self {
95 Self {
96 endpoints: NativeEndpointSet::new(endpoints, Vec::new(), Vec::new()),
97 lifecycle,
98 }
99 }
100
101 pub fn with_endpoints(
103 endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
104 stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
105 lifecycle: impl ModuleLifecycle,
106 ) -> Self {
107 Self {
108 endpoints: NativeEndpointSet::new(endpoints, stream_endpoints, Vec::new()),
109 lifecycle: Rc::new(lifecycle),
110 }
111 }
112
113 pub fn with_endpoint_set_lifecycle(
115 endpoints: NativeEndpointSet,
116 lifecycle: Rc<dyn ModuleLifecycle>,
117 ) -> Self {
118 Self {
119 endpoints,
120 lifecycle,
121 }
122 }
123
124 pub fn with_stream_endpoints(
126 stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
127 lifecycle: impl ModuleLifecycle,
128 ) -> Self {
129 Self::with_endpoints(Vec::new(), stream_endpoints, lifecycle)
130 }
131
132 pub fn with_event_endpoints(
134 event_endpoints: Vec<Rc<dyn NativeEventEndpoint>>,
135 lifecycle: impl ModuleLifecycle,
136 ) -> Self {
137 Self::with_all_endpoints(Vec::new(), Vec::new(), event_endpoints, lifecycle)
138 }
139
140 pub fn with_all_endpoints(
142 endpoints: Vec<Rc<dyn NativeRequestEndpoint>>,
143 stream_endpoints: Vec<Rc<dyn NativeStreamEndpoint>>,
144 event_endpoints: Vec<Rc<dyn NativeEventEndpoint>>,
145 lifecycle: impl ModuleLifecycle,
146 ) -> Self {
147 Self {
148 endpoints: NativeEndpointSet::new(endpoints, stream_endpoints, event_endpoints),
149 lifecycle: Rc::new(lifecycle),
150 }
151 }
152
153 pub fn endpoints(&self) -> &[Rc<dyn NativeRequestEndpoint>] {
155 self.endpoints.request()
156 }
157
158 pub fn stream_endpoints(&self) -> &[Rc<dyn NativeStreamEndpoint>] {
160 self.endpoints.stream()
161 }
162
163 pub fn event_endpoints(&self) -> &[Rc<dyn NativeEventEndpoint>] {
165 self.endpoints.event()
166 }
167
168 pub fn lifecycle(&self) -> Rc<dyn ModuleLifecycle> {
170 self.lifecycle.clone()
171 }
172
173 pub(super) fn into_parts(self) -> (NativeEndpointSet, Rc<dyn ModuleLifecycle>) {
174 (self.endpoints, self.lifecycle)
175 }
176}
177
178#[derive(Clone, Debug)]
180pub struct PreparedBinding {
181 pub(super) consumer_instance: String,
182 pub(super) provider_instance: String,
183 pub(super) endpoint: Rc<dyn NativeRequestEndpoint>,
184}
185
186#[derive(Clone, Debug)]
188pub struct PreparedStreamBinding {
189 pub(super) consumer_instance: String,
190 pub(super) provider_instance: String,
191 pub(super) endpoint: Rc<dyn NativeStreamEndpoint>,
192}
193
194#[derive(Clone, Debug)]
196pub struct PreparedEventBinding {
197 pub(super) consumer_instance: String,
198 pub(super) provider_instance: String,
199 pub(super) endpoint: Rc<dyn NativeEventEndpoint>,
200}
201
202impl PreparedEventBinding {
203 pub fn new(
205 consumer_instance: impl Into<String>,
206 provider_instance: impl Into<String>,
207 endpoint: Rc<dyn NativeEventEndpoint>,
208 ) -> Self {
209 Self {
210 consumer_instance: consumer_instance.into(),
211 provider_instance: provider_instance.into(),
212 endpoint,
213 }
214 }
215
216 pub fn consumer_instance(&self) -> &str {
218 &self.consumer_instance
219 }
220
221 pub fn provider_instance(&self) -> &str {
223 &self.provider_instance
224 }
225
226 pub fn endpoint(&self) -> Rc<dyn NativeEventEndpoint> {
228 self.endpoint.clone()
229 }
230
231 pub(super) fn same_identity(&self, other: &Self) -> bool {
232 self.consumer_instance == other.consumer_instance
233 && self.provider_instance == other.provider_instance
234 && self.endpoint.capability_id() == other.endpoint.capability_id()
235 }
236}
237
238impl PreparedStreamBinding {
239 pub fn new(
241 consumer_instance: impl Into<String>,
242 provider_instance: impl Into<String>,
243 endpoint: Rc<dyn NativeStreamEndpoint>,
244 ) -> Self {
245 Self {
246 consumer_instance: consumer_instance.into(),
247 provider_instance: provider_instance.into(),
248 endpoint,
249 }
250 }
251
252 pub fn consumer_instance(&self) -> &str {
254 &self.consumer_instance
255 }
256
257 pub fn provider_instance(&self) -> &str {
259 &self.provider_instance
260 }
261
262 pub fn endpoint(&self) -> Rc<dyn NativeStreamEndpoint> {
264 self.endpoint.clone()
265 }
266
267 pub(super) fn same_identity(&self, other: &Self) -> bool {
268 self.consumer_instance == other.consumer_instance
269 && self.provider_instance == other.provider_instance
270 && self.endpoint.capability_id() == other.endpoint.capability_id()
271 }
272}
273
274impl PreparedBinding {
275 pub fn new(
277 consumer_instance: impl Into<String>,
278 provider_instance: impl Into<String>,
279 endpoint: Rc<dyn NativeRequestEndpoint>,
280 ) -> Self {
281 Self {
282 consumer_instance: consumer_instance.into(),
283 provider_instance: provider_instance.into(),
284 endpoint,
285 }
286 }
287
288 pub fn consumer_instance(&self) -> &str {
290 &self.consumer_instance
291 }
292
293 pub fn provider_instance(&self) -> &str {
295 &self.provider_instance
296 }
297
298 pub fn endpoint(&self) -> Rc<dyn NativeRequestEndpoint> {
300 self.endpoint.clone()
301 }
302
303 pub(super) fn same_identity(&self, other: &Self) -> bool {
304 self.consumer_instance == other.consumer_instance
305 && self.provider_instance == other.provider_instance
306 && self.endpoint.capability_id() == other.endpoint.capability_id()
307 }
308}
309
310#[derive(Debug)]
312pub struct PreparedNativeApp {
313 pub(super) bindings: Vec<PreparedBinding>,
314 pub(super) stream_bindings: Vec<PreparedStreamBinding>,
315 pub(super) event_bindings: Vec<PreparedEventBinding>,
316 pub(super) generations: BTreeMap<String, PreparedNativeModule>,
317}
318
319impl PreparedNativeApp {
320 pub fn new(
322 bindings: Vec<PreparedBinding>,
323 generations: BTreeMap<String, PreparedNativeModule>,
324 ) -> Self {
325 Self {
326 bindings,
327 stream_bindings: Vec::new(),
328 event_bindings: Vec::new(),
329 generations,
330 }
331 }
332
333 pub fn empty() -> Self {
335 Self::new(Vec::new(), BTreeMap::new())
336 }
337
338 #[must_use]
340 pub fn with_stream_bindings(mut self, stream_bindings: Vec<PreparedStreamBinding>) -> Self {
341 self.stream_bindings = stream_bindings;
342 self
343 }
344
345 #[must_use]
347 pub fn with_event_bindings(mut self, event_bindings: Vec<PreparedEventBinding>) -> Self {
348 self.event_bindings = event_bindings;
349 self
350 }
351
352 pub(super) fn merge(&mut self, other: Self) -> Result<(), RuntimeFailure> {
353 for binding in other.bindings {
354 if self
355 .bindings
356 .iter()
357 .any(|existing| existing.same_identity(&binding))
358 {
359 return Err(RuntimeFailure::InvalidResolvedPlan {
360 detail: format!(
361 "multiple Execution Adapters prepared binding `{}:{}:{}`",
362 binding.consumer_instance,
363 binding.endpoint.capability_id(),
364 binding.provider_instance
365 ),
366 });
367 }
368 self.bindings.push(binding);
369 }
370 for binding in other.stream_bindings {
371 if self
372 .stream_bindings
373 .iter()
374 .any(|existing| existing.same_identity(&binding))
375 {
376 return Err(RuntimeFailure::InvalidResolvedPlan {
377 detail: format!(
378 "multiple Execution Adapters prepared stream binding `{}:{}:{}`",
379 binding.consumer_instance,
380 binding.endpoint.capability_id(),
381 binding.provider_instance
382 ),
383 });
384 }
385 self.stream_bindings.push(binding);
386 }
387 for binding in other.event_bindings {
388 if self
389 .event_bindings
390 .iter()
391 .any(|existing| existing.same_identity(&binding))
392 {
393 return Err(RuntimeFailure::InvalidResolvedPlan {
394 detail: format!(
395 "multiple Execution Adapters prepared Event binding `{}:{}:{}`",
396 binding.consumer_instance,
397 binding.endpoint.capability_id(),
398 binding.provider_instance
399 ),
400 });
401 }
402 self.event_bindings.push(binding);
403 }
404 for (instance_key, generation) in other.generations {
405 if self
406 .generations
407 .insert(instance_key.clone(), generation)
408 .is_some()
409 {
410 return Err(RuntimeFailure::InvalidResolvedPlan {
411 detail: format!(
412 "multiple Execution Adapters prepared Module Instance generation `{instance_key}`"
413 ),
414 });
415 }
416 }
417 Ok(())
418 }
419}
420
421pub trait ExecutionAdapter: std::fmt::Debug + 'static {
423 fn execution_class(&self) -> ExecutionClassId;
425
426 fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure>;
428
429 fn recreate(
435 &self,
436 _plan: &ResolvedAppPlan,
437 instance_key: &str,
438 ) -> Result<PreparedNativeModule, RuntimeFailure> {
439 Err(RuntimeFailure::Internal {
440 detail: format!("Execution Adapter cannot recreate Module Instance `{instance_key}`"),
441 })
442 }
443}
444
445pub trait NativeExecutionAdapter: std::fmt::Debug + 'static {
450 fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure>;
452
453 fn recreate(
455 &self,
456 _plan: &ResolvedAppPlan,
457 instance_key: &str,
458 ) -> Result<PreparedNativeModule, RuntimeFailure> {
459 Err(RuntimeFailure::Internal {
460 detail: format!("Execution Adapter cannot recreate Module Instance `{instance_key}`"),
461 })
462 }
463}
464
465impl<T: NativeExecutionAdapter> ExecutionAdapter for T {
466 fn execution_class(&self) -> ExecutionClassId {
467 ExecutionClassId::native_rust()
468 }
469
470 fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure> {
471 NativeExecutionAdapter::prepare(self, plan)
472 }
473
474 fn recreate(
475 &self,
476 plan: &ResolvedAppPlan,
477 instance_key: &str,
478 ) -> Result<PreparedNativeModule, RuntimeFailure> {
479 NativeExecutionAdapter::recreate(self, plan, instance_key)
480 }
481}
482
483#[derive(Clone, Debug, Default, Eq, PartialEq)]
485pub struct ExecutionClassSet(BTreeSet<ExecutionClassId>);
486
487impl ExecutionClassSet {
488 pub fn contains(&self, execution_class: &ExecutionClassId) -> bool {
490 self.0.contains(execution_class)
491 }
492
493 pub fn iter(&self) -> impl Iterator<Item = &ExecutionClassId> {
495 self.0.iter()
496 }
497}
498
499#[derive(Clone, Debug, Eq, PartialEq)]
501pub enum ExecutionAdapterCatalogError {
502 DuplicateExecutionClass { execution_class: String },
504}
505
506impl std::fmt::Display for ExecutionAdapterCatalogError {
507 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
508 match self {
509 Self::DuplicateExecutionClass { execution_class } => write!(
510 formatter,
511 "multiple Execution Adapters provide class `{execution_class}`"
512 ),
513 }
514 }
515}
516
517impl std::error::Error for ExecutionAdapterCatalogError {}
518
519#[derive(Debug, Default)]
521pub struct ExecutionAdapterCatalog {
522 pub(super) adapters: BTreeMap<ExecutionClassId, Rc<dyn ExecutionAdapter>>,
523}
524
525impl ExecutionAdapterCatalog {
526 pub fn new() -> Self {
528 Self::default()
529 }
530
531 pub fn single(adapter: impl ExecutionAdapter) -> Self {
533 Self::new()
534 .with_adapter(adapter)
535 .expect("a new catalog cannot contain a duplicate execution class")
536 }
537
538 pub fn with_adapter(
540 self,
541 adapter: impl ExecutionAdapter,
542 ) -> Result<Self, ExecutionAdapterCatalogError> {
543 self.with_shared_adapter(Rc::new(adapter))
544 }
545
546 pub fn with_shared_adapter(
548 mut self,
549 adapter: Rc<dyn ExecutionAdapter>,
550 ) -> Result<Self, ExecutionAdapterCatalogError> {
551 let execution_class = adapter.execution_class();
552 if self.adapters.contains_key(&execution_class) {
553 return Err(ExecutionAdapterCatalogError::DuplicateExecutionClass {
554 execution_class: execution_class.to_string(),
555 });
556 }
557 self.adapters.insert(execution_class, adapter);
558 Ok(self)
559 }
560
561 pub fn execution_classes(&self) -> ExecutionClassSet {
563 ExecutionClassSet(self.adapters.keys().cloned().collect())
564 }
565
566 pub(super) fn adapter(
567 &self,
568 execution_class: &ExecutionClassId,
569 ) -> Option<Rc<dyn ExecutionAdapter>> {
570 self.adapters.get(execution_class).cloned()
571 }
572
573 pub(super) fn prepare(
574 &self,
575 plan: &ResolvedAppPlan,
576 ) -> Result<PreparedNativeApp, RuntimeFailure> {
577 let mut required_classes = BTreeSet::new();
578 for instance in plan.module_instances() {
579 if !self.adapters.contains_key(instance.execution_class()) {
580 return Err(RuntimeFailure::UnavailableExecutionClass {
581 instance_key: instance.instance_key().to_owned(),
582 execution_class: instance.execution_class().to_string(),
583 });
584 }
585 required_classes.insert(instance.execution_class().clone());
586 }
587
588 let mut prepared = PreparedNativeApp::empty();
589 for execution_class in required_classes {
590 let adapter = self
591 .adapters
592 .get(&execution_class)
593 .expect("required execution classes were validated");
594 prepared.merge(adapter.prepare(plan)?)?;
595 }
596 Ok(prepared)
597 }
598}