1use super::{
2 BTreeMap, BTreeSet, ErasedDomainResult, ErasedValue, ExecutionClassId, InvocationContext,
3 LocalBoxFuture, ModuleLifecycle, NativeEventEndpoint, NativeStreamEndpoint, Rc,
4 ResolvedAppPlan, RuntimeFailure,
5};
6
7pub trait NativeRequestEndpoint: std::fmt::Debug {
9 fn capability_id(&self) -> &'static str;
11 fn descriptor_version(&self) -> &'static str;
13 fn operations(&self) -> &'static [&'static str];
15 fn invoke(
17 &self,
18 operation: &str,
19 request: ErasedValue,
20 context: InvocationContext,
21 ) -> LocalBoxFuture<'static, Result<ErasedDomainResult, RuntimeFailure>>;
22}
23
24#[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 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 pub fn request(&self) -> &[Rc<dyn NativeRequestEndpoint>] {
48 &self.request
49 }
50
51 pub fn stream(&self) -> &[Rc<dyn NativeStreamEndpoint>] {
53 &self.stream
54 }
55
56 pub fn event(&self) -> &[Rc<dyn NativeEventEndpoint>] {
58 &self.event
59 }
60}
61
62#[derive(Debug)]
64pub struct PreparedNativeModule {
65 pub(super) endpoints: NativeEndpointSet,
66 pub(super) lifecycle: Rc<dyn ModuleLifecycle>,
67}
68
69impl PreparedNativeModule {
70 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 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 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 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 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 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 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 pub fn endpoints(&self) -> &[Rc<dyn NativeRequestEndpoint>] {
146 self.endpoints.request()
147 }
148
149 pub fn stream_endpoints(&self) -> &[Rc<dyn NativeStreamEndpoint>] {
151 self.endpoints.stream()
152 }
153
154 pub fn event_endpoints(&self) -> &[Rc<dyn NativeEventEndpoint>] {
156 self.endpoints.event()
157 }
158
159 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#[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#[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#[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 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 pub fn consumer_instance(&self) -> &str {
209 &self.consumer_instance
210 }
211
212 pub fn provider_instance(&self) -> &str {
214 &self.provider_instance
215 }
216
217 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 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 pub fn consumer_instance(&self) -> &str {
245 &self.consumer_instance
246 }
247
248 pub fn provider_instance(&self) -> &str {
250 &self.provider_instance
251 }
252
253 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 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 pub fn consumer_instance(&self) -> &str {
281 &self.consumer_instance
282 }
283
284 pub fn provider_instance(&self) -> &str {
286 &self.provider_instance
287 }
288
289 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#[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 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 pub fn empty() -> Self {
326 Self::new(Vec::new(), BTreeMap::new())
327 }
328
329 #[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 #[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
412pub trait ExecutionAdapter: std::fmt::Debug + 'static {
414 fn execution_class(&self) -> ExecutionClassId;
416
417 fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure>;
419
420 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
436pub trait NativeExecutionAdapter: std::fmt::Debug + 'static {
441 fn prepare(&self, plan: &ResolvedAppPlan) -> Result<PreparedNativeApp, RuntimeFailure>;
443
444 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#[derive(Clone, Debug, Default, Eq, PartialEq)]
476pub struct ExecutionClassSet(BTreeSet<ExecutionClassId>);
477
478impl ExecutionClassSet {
479 pub fn contains(&self, execution_class: &ExecutionClassId) -> bool {
481 self.0.contains(execution_class)
482 }
483
484 pub fn iter(&self) -> impl Iterator<Item = &ExecutionClassId> {
486 self.0.iter()
487 }
488}
489
490#[derive(Clone, Debug, Eq, PartialEq)]
492pub enum ExecutionAdapterCatalogError {
493 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#[derive(Debug, Default)]
512pub struct ExecutionAdapterCatalog {
513 pub(super) adapters: BTreeMap<ExecutionClassId, Rc<dyn ExecutionAdapter>>,
514}
515
516impl ExecutionAdapterCatalog {
517 pub fn new() -> Self {
519 Self::default()
520 }
521
522 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 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 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 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}