1use std::future::Future;
43use std::io::Error;
44use std::pin::Pin;
45
46use bytes::{Bytes, BytesMut};
47use dfir_lang::diagnostic::Diagnostics;
48use dfir_lang::graph::{AsCodeOptions, DfirGraph};
49use futures::{Sink, Stream};
50use proc_macro2::Span;
51use quote::quote;
52use serde::Serialize;
53use serde::de::DeserializeOwned;
54use slotmap::SparseSecondaryMap;
55
56use super::deploy_provider::{ClusterSpec, Deploy, ExternalSpec, Node, ProcessSpec, RegisterPort};
57use crate::compile::builder::ExternalPortId;
58use crate::location::dynamic::LocationId;
59use crate::location::{LocationKey, NetworkHint};
60
61pub enum EmbeddedDeploy {}
65
66#[derive(Clone)]
68pub struct EmbeddedNode {
69 pub fn_name: String,
71 pub location_key: LocationKey,
73}
74
75impl Node for EmbeddedNode {
76 type Port = ();
77 type Meta = ();
78 type InstantiateEnv = EmbeddedInstantiateEnv;
79
80 fn next_port(&self) -> Self::Port {}
81
82 fn update_meta(&self, _meta: &Self::Meta) {}
83
84 fn instantiate(
85 &self,
86 _env: &mut Self::InstantiateEnv,
87 _meta: &mut Self::Meta,
88 _graph: DfirGraph,
89 _extra_stmts: &[syn::Stmt],
90 _sidecars: &[syn::Expr],
91 _as_code_options: &AsCodeOptions,
92 ) {
93 }
95}
96
97impl<'a> RegisterPort<'a, EmbeddedDeploy> for EmbeddedNode {
98 fn register(&self, _external_port_id: ExternalPortId, _port: Self::Port) {
99 panic!("EmbeddedDeploy does not support external ports");
100 }
101
102 #[expect(clippy::manual_async_fn, reason = "false positive, involves lifetimes")]
103 fn as_bytes_bidi(
104 &self,
105 _external_port_id: ExternalPortId,
106 ) -> impl Future<
107 Output = super::deploy_provider::DynSourceSink<Result<BytesMut, Error>, Bytes, Error>,
108 > + 'a {
109 async { panic!("EmbeddedDeploy does not support external ports") }
110 }
111
112 #[expect(clippy::manual_async_fn, reason = "false positive, involves lifetimes")]
113 fn as_bincode_bidi<InT, OutT>(
114 &self,
115 _external_port_id: ExternalPortId,
116 ) -> impl Future<Output = super::deploy_provider::DynSourceSink<OutT, InT, Error>> + 'a
117 where
118 InT: Serialize + 'static,
119 OutT: DeserializeOwned + 'static,
120 {
121 async { panic!("EmbeddedDeploy does not support external ports") }
122 }
123
124 #[expect(clippy::manual_async_fn, reason = "false positive, involves lifetimes")]
125 fn as_bincode_sink<T>(
126 &self,
127 _external_port_id: ExternalPortId,
128 ) -> impl Future<Output = Pin<Box<dyn Sink<T, Error = Error>>>> + 'a
129 where
130 T: Serialize + 'static,
131 {
132 async { panic!("EmbeddedDeploy does not support external ports") }
133 }
134
135 #[expect(clippy::manual_async_fn, reason = "false positive, involves lifetimes")]
136 fn as_bincode_source<T>(
137 &self,
138 _external_port_id: ExternalPortId,
139 ) -> impl Future<Output = Pin<Box<dyn Stream<Item = T>>>> + 'a
140 where
141 T: DeserializeOwned + 'static,
142 {
143 async { panic!("EmbeddedDeploy does not support external ports") }
144 }
145}
146
147impl<S: Into<String>> ProcessSpec<'_, EmbeddedDeploy> for S {
148 fn build(self, location_key: LocationKey, _name_hint: &str) -> EmbeddedNode {
149 EmbeddedNode {
150 fn_name: self.into(),
151 location_key,
152 }
153 }
154}
155
156impl<S: Into<String>> ClusterSpec<'_, EmbeddedDeploy> for S {
157 fn build(self, location_key: LocationKey, _name_hint: &str) -> EmbeddedNode {
158 EmbeddedNode {
159 fn_name: self.into(),
160 location_key,
161 }
162 }
163}
164
165impl<S: Into<String>> ExternalSpec<'_, EmbeddedDeploy> for S {
166 fn build(self, location_key: LocationKey, _name_hint: &str) -> EmbeddedNode {
167 EmbeddedNode {
168 fn_name: self.into(),
169 location_key,
170 }
171 }
172}
173
174#[derive(Clone)]
176struct EmbeddedNetworkPort {
177 name: String,
178 is_tagged: bool,
180 external_type: Option<syn::Type>,
182 peer: LocationKey,
184 transport: syn::Type,
186 serialization: syn::Type,
187 config: String,
188}
189
190impl EmbeddedNetworkPort {
191 fn new(
192 name: &str,
193 is_tagged: bool,
194 external_type: Option<&syn::Type>,
195 peer: LocationKey,
196 networking_info: &crate::networking::NetworkingInfo,
197 ) -> Self {
198 let (transport, serialization, config) =
199 channel_config_types(networking_info, external_type.is_some());
200 EmbeddedNetworkPort {
201 name: name.to_owned(),
202 is_tagged,
203 external_type: external_type.cloned(),
204 peer,
205 transport,
206 serialization,
207 config,
208 }
209 }
210}
211
212fn channel_config_types(
215 networking_info: &crate::networking::NetworkingInfo,
216 external: bool,
217) -> (syn::Type, syn::Type, String) {
218 use crate::compile::ir::{StreamOrder, StreamRetry};
219 use crate::networking::{EmbeddedFault, NetworkingInfo, TcpFault, UdpFault};
220 let root = crate::staging_util::get_this_crate();
221 let fault = |name: &str| -> syn::Type {
222 let ident = syn::Ident::new(name, Span::call_site());
223 syn::parse_quote!(#root::networking::#ident)
224 };
225 let transport: syn::Type = match networking_info {
226 NetworkingInfo::Tcp { fault: f } => {
227 let f = fault(match f {
228 TcpFault::FailStop => "FailStop",
229 TcpFault::Lossy => "Lossy",
230 TcpFault::LossyDelayedForever => "LossyDelayedForever",
231 });
232 syn::parse_quote!(#root::networking::Tcp<#f>)
233 }
234 NetworkingInfo::Udp { fault: f } => {
235 let f = fault(match f {
236 UdpFault::Lossy => "Lossy",
237 UdpFault::LossyDelayedForever => "LossyDelayedForever",
238 });
239 syn::parse_quote!(#root::networking::Udp<#f>)
240 }
241 NetworkingInfo::Embedded {
242 retries,
243 ordering,
244 fault: f,
245 } => {
246 let retries: syn::Type = match retries {
247 StreamRetry::ExactlyOnce => {
248 syn::parse_quote!(#root::live_collections::stream::ExactlyOnce)
249 }
250 StreamRetry::AtLeastOnce => {
251 syn::parse_quote!(#root::live_collections::stream::AtLeastOnce)
252 }
253 };
254 let ordering: syn::Type = match ordering {
255 StreamOrder::TotalOrder => {
256 syn::parse_quote!(#root::live_collections::stream::TotalOrder)
257 }
258 StreamOrder::NoOrder => syn::parse_quote!(#root::live_collections::stream::NoOrder),
259 };
260 let f = fault(match f {
261 EmbeddedFault::FailStop => "FailStop",
262 EmbeddedFault::Lossy => "Lossy",
263 EmbeddedFault::LossyDelayedForever => "LossyDelayedForever",
264 });
265 syn::parse_quote!(#root::networking::EmbeddedTransport<#retries, #ordering, #f>)
266 }
267 };
268 let serialization: syn::Type = if external {
269 syn::parse_quote!(#root::networking::Embedded)
270 } else {
271 syn::parse_quote!(#root::networking::Bincode)
272 };
273 (
274 transport,
275 serialization,
276 config_doc(networking_info, external),
277 )
278}
279
280fn config_doc(networking_info: &crate::networking::NetworkingInfo, external: bool) -> String {
282 use crate::compile::ir::{StreamOrder, StreamRetry};
283 use crate::networking::{EmbeddedFault, NetworkingInfo, TcpFault, UdpFault};
284 let serialization = if external { "" } else { ".bincode()" };
285 match networking_info {
286 NetworkingInfo::Tcp { fault } => {
287 let fault = match fault {
288 TcpFault::FailStop => ".fail_stop()",
289 TcpFault::Lossy => ".lossy(nondet)",
290 TcpFault::LossyDelayedForever => ".lossy_delayed_forever()",
291 };
292 format!("TCP{fault}{serialization}")
293 }
294 NetworkingInfo::Udp { fault } => {
295 let fault = match fault {
296 UdpFault::Lossy => ".lossy(nondet)",
297 UdpFault::LossyDelayedForever => ".lossy_delayed_forever()",
298 };
299 format!("UDP{fault}{serialization}")
300 }
301 NetworkingInfo::Embedded {
302 retries,
303 ordering,
304 fault,
305 } => {
306 let reorder = match ordering {
308 StreamOrder::TotalOrder => ".never_reorders()",
309 StreamOrder::NoOrder => ".may_reorder()",
310 };
311 let duplicate = match retries {
312 StreamRetry::ExactlyOnce => ".never_duplicates()",
313 StreamRetry::AtLeastOnce => ".may_duplicate()",
314 };
315 let loss = match fault {
316 EmbeddedFault::FailStop => ".never_drops()",
317 EmbeddedFault::Lossy => ".may_drop(nondet)",
318 EmbeddedFault::LossyDelayedForever => ".may_delay_forever()",
319 };
320 format!("EMBEDDED{reorder}{duplicate}{loss}")
321 }
322 }
323}
324
325#[derive(Clone)]
327struct EmbeddedStreamPort {
328 ident: syn::Ident,
329 element_type: syn::Type,
330}
331
332#[derive(Default)]
339pub struct EmbeddedInstantiateEnv {
340 inputs: SparseSecondaryMap<LocationKey, Vec<EmbeddedStreamPort>>,
341 singleton_inputs: SparseSecondaryMap<LocationKey, Vec<(syn::Ident, syn::Type)>>,
342 outputs: SparseSecondaryMap<LocationKey, Vec<EmbeddedStreamPort>>,
343 network_outputs: SparseSecondaryMap<LocationKey, Vec<EmbeddedNetworkPort>>,
345 network_inputs: SparseSecondaryMap<LocationKey, Vec<EmbeddedNetworkPort>>,
347 membership_streams: SparseSecondaryMap<LocationKey, Vec<LocationKey>>,
349 boundary: BoundaryGuarantees,
350}
351
352#[derive(Clone, Debug)]
354struct EmbeddedStreamGuarantees {
355 order: crate::compile::ir::StreamOrder,
356 retry: crate::compile::ir::StreamRetry,
357}
358
359impl EmbeddedStreamGuarantees {
360 fn of(metadata: &crate::compile::ir::HydroIrMetadata, what: &str) -> Self {
361 use crate::compile::ir::CollectionKind;
362 match &metadata.collection_kind {
363 CollectionKind::Stream { order, retry, .. }
364 | CollectionKind::KeyedStream {
365 value_order: order,
366 value_retry: retry,
367 ..
368 } => EmbeddedStreamGuarantees {
369 order: order.clone(),
370 retry: retry.clone(),
371 },
372 kind => panic!("{what} must be a stream or keyed stream, not {kind:?}"),
373 }
374 }
375}
376
377#[derive(Clone, Debug)]
379struct EmbeddedNetworkGuarantees {
380 sent: EmbeddedStreamGuarantees,
381 received: EmbeddedStreamGuarantees,
382}
383
384#[derive(Default)]
387struct BoundaryGuarantees {
388 inputs: std::collections::HashMap<(LocationKey, String), EmbeddedStreamGuarantees>,
389 outputs: std::collections::HashMap<(LocationKey, String), EmbeddedStreamGuarantees>,
390 networks: std::collections::HashMap<String, EmbeddedNetworkGuarantees>,
391}
392
393impl BoundaryGuarantees {
394 fn collect(ir: &mut [crate::compile::ir::HydroRoot]) -> Self {
395 use crate::compile::ir::{HydroNode, HydroRoot, HydroSource};
396 let location_key = |id: &LocationId, what: &str| match id.root() {
397 LocationId::Process(key) | LocationId::Cluster(key) => *key,
398 _ => panic!("{what} must be on a process or cluster"),
399 };
400 let mut boundary = BoundaryGuarantees::default();
401 let cell = std::cell::RefCell::new(&mut boundary);
402 crate::compile::ir::transform_bottom_up(
403 ir,
404 &mut |root| {
405 if let HydroRoot::EmbeddedOutput { ident, input, .. } = root {
406 let what = "an embedded output";
407 cell.borrow_mut().outputs.insert(
408 (
409 location_key(&input.metadata().location_id, what),
410 ident.to_string(),
411 ),
412 EmbeddedStreamGuarantees::of(input.metadata(), what),
413 );
414 }
415 },
416 &mut |node| match node {
417 HydroNode::Source {
418 source: HydroSource::Embedded(ident),
419 metadata,
420 } => {
421 let what = "an embedded input";
422 cell.borrow_mut().inputs.insert(
423 (location_key(&metadata.location_id, what), ident.to_string()),
424 EmbeddedStreamGuarantees::of(metadata, what),
425 );
426 }
427 HydroNode::Network {
428 name: Some(name),
429 input,
430 metadata,
431 ..
432 } => {
433 let what = "a network channel's stream";
434 cell.borrow_mut().networks.insert(
435 name.clone(),
436 EmbeddedNetworkGuarantees {
437 sent: EmbeddedStreamGuarantees::of(input.metadata(), what),
438 received: EmbeddedStreamGuarantees::of(metadata, what),
439 },
440 );
441 }
442 _ => {}
443 },
444 false,
445 );
446 boundary
447 }
448
449 fn input(&self, location_key: LocationKey, ident: &syn::Ident) -> EmbeddedStreamGuarantees {
450 self.inputs[&(location_key, ident.to_string())].clone()
451 }
452
453 fn output(&self, location_key: LocationKey, ident: &syn::Ident) -> EmbeddedStreamGuarantees {
454 self.outputs[&(location_key, ident.to_string())].clone()
455 }
456
457 fn network(&self, name: &str) -> EmbeddedNetworkGuarantees {
458 self.networks[name].clone()
459 }
460}
461
462impl<'a> Deploy<'a> for EmbeddedDeploy {
463 type Meta = ();
464 type InstantiateEnv = EmbeddedInstantiateEnv;
465
466 type Process = EmbeddedNode;
467 type Cluster = EmbeddedNode;
468 type External = EmbeddedNode;
469
470 const SUPPORTS_EXTERNAL_SERIALIZATION: bool = true;
471
472 fn o2o_sink_source(
473 env: &mut Self::InstantiateEnv,
474 p1: &Self::Process,
475 _p1_port: &(),
476 p2: &Self::Process,
477 _p2_port: &(),
478 name: Option<&str>,
479 networking_info: &crate::networking::NetworkingInfo,
480 external_types: Option<(&syn::Type, &syn::Type)>,
481 ) -> (syn::Expr, syn::Expr) {
482 let name = name.expect(
483 "EmbeddedDeploy o2o networking requires a channel name. Use `TCP.name(\"my_channel\")` to provide one.",
484 );
485
486 let sink_ident = syn::Ident::new(&format!("__network_out_{name}"), Span::call_site());
487 let source_ident = syn::Ident::new(&format!("__network_in_{name}"), Span::call_site());
488
489 env.network_outputs
490 .entry(p1.location_key)
491 .unwrap()
492 .or_default()
493 .push(EmbeddedNetworkPort::new(
494 name,
495 false,
496 external_types.map(|(i, _)| i),
497 p2.location_key,
498 networking_info,
499 ));
500 env.network_inputs
501 .entry(p2.location_key)
502 .unwrap()
503 .or_default()
504 .push(EmbeddedNetworkPort::new(
505 name,
506 false,
507 external_types.map(|(_, o)| o),
508 p1.location_key,
509 networking_info,
510 ));
511
512 (
513 syn::parse_quote!(__root_dfir_rs::sinktools::for_each(#sink_ident)),
514 syn::parse_quote!(#source_ident),
515 )
516 }
517
518 fn o2o_connect(
519 _p1: &Self::Process,
520 _p1_port: &(),
521 _p2: &Self::Process,
522 _p2_port: &(),
523 ) -> Box<dyn FnOnce()> {
524 Box::new(|| {})
525 }
526
527 fn o2m_sink_source(
528 env: &mut Self::InstantiateEnv,
529 p1: &Self::Process,
530 _p1_port: &(),
531 c2: &Self::Cluster,
532 _c2_port: &(),
533 name: Option<&str>,
534 networking_info: &crate::networking::NetworkingInfo,
535 external_types: Option<(&syn::Type, &syn::Type)>,
536 ) -> (syn::Expr, syn::Expr) {
537 let name = name.expect("EmbeddedDeploy o2m networking requires a channel name.");
538 let sink_ident = syn::Ident::new(&format!("__network_out_{name}"), Span::call_site());
539 let source_ident = syn::Ident::new(&format!("__network_in_{name}"), Span::call_site());
540 env.network_outputs
541 .entry(p1.location_key)
542 .unwrap()
543 .or_default()
544 .push(EmbeddedNetworkPort::new(
545 name,
546 true,
547 external_types.map(|(i, _)| i),
548 c2.location_key,
549 networking_info,
550 ));
551 env.network_inputs
552 .entry(c2.location_key)
553 .unwrap()
554 .or_default()
555 .push(EmbeddedNetworkPort::new(
556 name,
557 false,
558 external_types.map(|(_, o)| o),
559 p1.location_key,
560 networking_info,
561 ));
562 (
563 syn::parse_quote!(__root_dfir_rs::sinktools::for_each(#sink_ident)),
564 syn::parse_quote!(#source_ident),
565 )
566 }
567
568 fn o2m_connect(
569 _p1: &Self::Process,
570 _p1_port: &(),
571 _c2: &Self::Cluster,
572 _c2_port: &(),
573 ) -> Box<dyn FnOnce()> {
574 Box::new(|| {})
575 }
576
577 fn m2o_sink_source(
578 env: &mut Self::InstantiateEnv,
579 c1: &Self::Cluster,
580 _c1_port: &(),
581 p2: &Self::Process,
582 _p2_port: &(),
583 name: Option<&str>,
584 networking_info: &crate::networking::NetworkingInfo,
585 external_types: Option<(&syn::Type, &syn::Type)>,
586 ) -> (syn::Expr, syn::Expr) {
587 let name = name.expect("EmbeddedDeploy m2o networking requires a channel name.");
588 let sink_ident = syn::Ident::new(&format!("__network_out_{name}"), Span::call_site());
589 let source_ident = syn::Ident::new(&format!("__network_in_{name}"), Span::call_site());
590 env.network_outputs
591 .entry(c1.location_key)
592 .unwrap()
593 .or_default()
594 .push(EmbeddedNetworkPort::new(
595 name,
596 false,
597 external_types.map(|(i, _)| i),
598 p2.location_key,
599 networking_info,
600 ));
601 env.network_inputs
602 .entry(p2.location_key)
603 .unwrap()
604 .or_default()
605 .push(EmbeddedNetworkPort::new(
606 name,
607 true,
608 external_types.map(|(_, o)| o),
609 c1.location_key,
610 networking_info,
611 ));
612 (
613 syn::parse_quote!(__root_dfir_rs::sinktools::for_each(#sink_ident)),
614 syn::parse_quote!(#source_ident),
615 )
616 }
617
618 fn m2o_connect(
619 _c1: &Self::Cluster,
620 _c1_port: &(),
621 _p2: &Self::Process,
622 _p2_port: &(),
623 ) -> Box<dyn FnOnce()> {
624 Box::new(|| {})
625 }
626
627 fn m2m_sink_source(
628 env: &mut Self::InstantiateEnv,
629 c1: &Self::Cluster,
630 _c1_port: &(),
631 c2: &Self::Cluster,
632 _c2_port: &(),
633 name: Option<&str>,
634 networking_info: &crate::networking::NetworkingInfo,
635 external_types: Option<(&syn::Type, &syn::Type)>,
636 ) -> (syn::Expr, syn::Expr) {
637 let name = name.expect("EmbeddedDeploy m2m networking requires a channel name.");
638 let sink_ident = syn::Ident::new(&format!("__network_out_{name}"), Span::call_site());
639 let source_ident = syn::Ident::new(&format!("__network_in_{name}"), Span::call_site());
640 env.network_outputs
641 .entry(c1.location_key)
642 .unwrap()
643 .or_default()
644 .push(EmbeddedNetworkPort::new(
645 name,
646 true,
647 external_types.map(|(i, _)| i),
648 c2.location_key,
649 networking_info,
650 ));
651 env.network_inputs
652 .entry(c2.location_key)
653 .unwrap()
654 .or_default()
655 .push(EmbeddedNetworkPort::new(
656 name,
657 true,
658 external_types.map(|(_, o)| o),
659 c1.location_key,
660 networking_info,
661 ));
662 (
663 syn::parse_quote!(__root_dfir_rs::sinktools::for_each(#sink_ident)),
664 syn::parse_quote!(#source_ident),
665 )
666 }
667
668 fn m2m_connect(
669 _c1: &Self::Cluster,
670 _c1_port: &(),
671 _c2: &Self::Cluster,
672 _c2_port: &(),
673 ) -> Box<dyn FnOnce()> {
674 Box::new(|| {})
675 }
676
677 fn e2o_many_source(
678 _extra_stmts: &mut Vec<syn::Stmt>,
679 _p2: &Self::Process,
680 _p2_port: &(),
681 _codec_type: &syn::Type,
682 _shared_handle: String,
683 ) -> syn::Expr {
684 panic!("EmbeddedDeploy does not support networking (e2o)")
685 }
686
687 fn e2o_many_sink(_shared_handle: String) -> syn::Expr {
688 panic!("EmbeddedDeploy does not support networking (e2o)")
689 }
690
691 fn e2o_source(
692 _extra_stmts: &mut Vec<syn::Stmt>,
693 _p1: &Self::External,
694 _p1_port: &(),
695 _p2: &Self::Process,
696 _p2_port: &(),
697 _codec_type: &syn::Type,
698 _shared_handle: String,
699 ) -> syn::Expr {
700 panic!("EmbeddedDeploy does not support networking (e2o)")
701 }
702
703 fn e2o_connect(
704 _p1: &Self::External,
705 _p1_port: &(),
706 _p2: &Self::Process,
707 _p2_port: &(),
708 _many: bool,
709 _server_hint: NetworkHint,
710 ) -> Box<dyn FnOnce()> {
711 panic!("EmbeddedDeploy does not support networking (e2o)")
712 }
713
714 fn o2e_sink(
715 _p1: &Self::Process,
716 _p1_port: &(),
717 _p2: &Self::External,
718 _p2_port: &(),
719 _shared_handle: String,
720 ) -> syn::Expr {
721 panic!("EmbeddedDeploy does not support networking (o2e)")
722 }
723
724 fn cluster_ids(_of_cluster: LocationKey) -> syn::Expr {
725 panic!("EmbeddedDeploy does not support cluster IDs");
726 }
727
728 fn cluster_self_id() -> syn::Expr {
729 syn::parse_quote!(__cluster_self_id)
730 }
731
732 fn cluster_membership_stream(
733 env: &mut Self::InstantiateEnv,
734 at_location: &LocationId,
735 location_id: &LocationId,
736 ) -> syn::Expr {
737 let at_key = match at_location {
738 LocationId::Process(key) | LocationId::Cluster(key) => *key,
739 _ => panic!("cluster_membership_stream must be called from a process or cluster"),
740 };
741 let cluster_key = match location_id {
742 LocationId::Cluster(key) => *key,
743 _ => panic!("cluster_membership_stream target must be a cluster"),
744 };
745 let vec = env.membership_streams.entry(at_key).unwrap().or_default();
746 let idx = if let Some(pos) = vec.iter().position(|k| *k == cluster_key) {
747 pos
748 } else {
749 vec.push(cluster_key);
750 vec.len() - 1
751 };
752
753 let ident = quote::format_ident!("__membership_{}", idx);
754 syn::parse_quote!(#ident)
755 }
756
757 fn register_embedded_stream_input(
758 env: &mut Self::InstantiateEnv,
759 location_key: LocationKey,
760 ident: &syn::Ident,
761 element_type: &syn::Type,
762 ) {
763 env.inputs
764 .entry(location_key)
765 .unwrap()
766 .or_default()
767 .push(EmbeddedStreamPort {
768 ident: ident.clone(),
769 element_type: element_type.clone(),
770 });
771 }
772
773 fn register_embedded_singleton_input(
774 env: &mut Self::InstantiateEnv,
775 location_key: LocationKey,
776 ident: &syn::Ident,
777 element_type: &syn::Type,
778 ) {
779 env.singleton_inputs
780 .entry(location_key)
781 .unwrap()
782 .or_default()
783 .push((ident.clone(), element_type.clone()));
784 }
785
786 fn register_embedded_output(
787 env: &mut Self::InstantiateEnv,
788 location_key: LocationKey,
789 ident: &syn::Ident,
790 element_type: &syn::Type,
791 ) {
792 env.outputs
793 .entry(location_key)
794 .unwrap()
795 .or_default()
796 .push(EmbeddedStreamPort {
797 ident: ident.clone(),
798 element_type: element_type.clone(),
799 });
800 }
801}
802
803struct NetworkPortTokens {
807 fields: Vec<syn::Ident>,
808 field_docs: Vec<String>,
809 field_types: Vec<proc_macro2::TokenStream>,
813 generics: Vec<syn::Ident>,
814 items: Vec<proc_macro2::TokenStream>,
815 marker_defs: Vec<proc_macro2::TokenStream>,
816}
817
818impl NetworkPortTokens {
819 fn new(
820 ports: &[EmbeddedNetworkPort],
821 boundary: &BoundaryGuarantees,
822 fn_names: &SparseSecondaryMap<LocationKey, &str>,
823 generic_prefix: &str,
824 sending: bool,
825 item: impl Fn(&EmbeddedNetworkPort) -> proc_macro2::TokenStream,
826 ) -> Self {
827 let root = crate::staging_util::get_this_crate();
828 let items: Vec<proc_macro2::TokenStream> = ports.iter().map(&item).collect();
829 let fields: Vec<syn::Ident> = ports
831 .iter()
832 .map(|port| syn::Ident::new(&port.name, Span::call_site()))
833 .collect();
834 let mut marker_defs = Vec::with_capacity(ports.len());
835 let mut field_docs = Vec::with_capacity(ports.len());
836 for (port, marker) in ports.iter().zip(&fields) {
837 let name = &port.name;
838 let peer = fn_names[port.peer];
839 let channel = boundary.network(name);
840 let guarantees = if sending {
841 &channel.sent
842 } else {
843 &channel.received
844 };
845 let doc = format!(
846 "The `{name}` channel's end at this location; its other end is at `{peer}`.\n\n{}",
847 network_end_doc(port, guarantees, sending)
848 );
849 let marker_def = marker_def(&root, marker, &doc, guarantees);
850 let (transport, serialization) = (&port.transport, &port.serialization);
851 marker_defs.push(quote! {
852 #marker_def
853
854 impl #root::compile::embedded_runtime::EmbeddedNetworkChannel for #marker {
855 type Transport = #transport;
856 type Serialization = #serialization;
857 }
858 });
859 let (action, module) = if sending {
860 (format!("Sends on `{name}` to `{peer}`"), "outgoing")
861 } else {
862 (format!("Receives `{name}` from `{peer}`"), "incoming")
863 };
864 field_docs.push(format!("{action} (marker type: [`{module}::{marker}`])."));
865 }
866 let generics: Vec<syn::Ident> = (0..ports.len())
867 .map(|i| quote::format_ident!("{}{}", generic_prefix, i))
868 .collect();
869 let module = syn::Ident::new(
870 if sending { "outgoing" } else { "incoming" },
871 Span::call_site(),
872 );
873 let wrapper = if sending {
877 quote! { #root::compile::embedded_runtime::EmbeddedNetworkOutputChannel }
878 } else {
879 quote! { #root::compile::embedded_runtime::EmbeddedNetworkInputChannel }
880 };
881 let field_types = fields
882 .iter()
883 .zip(&generics)
884 .map(|(marker, generic)| quote! { #wrapper<#module::#marker, #generic> })
885 .collect();
886 NetworkPortTokens {
887 fields,
888 field_docs,
889 field_types,
890 generics,
891 items,
892 marker_defs,
893 }
894 }
895}
896
897fn marker_def(
901 root: &proc_macro2::TokenStream,
902 marker: &syn::Ident,
903 doc: &str,
904 guarantees: &EmbeddedStreamGuarantees,
905) -> proc_macro2::TokenStream {
906 let ordering = guarantees.order.type_tokens();
907 let retries = guarantees.retry.type_tokens();
908 quote! {
909 #[doc = #doc]
910 #[allow(non_camel_case_types, reason = "named after its field")]
911 pub struct #marker;
912
913 impl #root::compile::embedded_runtime::EmbeddedChannel for #marker {
914 type Ordering = #ordering;
915 type Retries = #retries;
916 }
917 }
918}
919
920fn promise_doc(guarantees: &EmbeddedStreamGuarantees, wrapper: &str) -> String {
922 format!(
923 "Promise its guarantees with `{wrapper}::new(.., Promises::new(){})`.",
924 promises_chain(guarantees)
925 )
926}
927
928fn promises_chain(guarantees: &EmbeddedStreamGuarantees) -> String {
930 use crate::compile::ir::{StreamOrder, StreamRetry};
931 [
932 (guarantees.order == StreamOrder::TotalOrder).then_some(".total_order(proof)"),
933 (guarantees.retry == StreamRetry::ExactlyOnce).then_some(".exactly_once(proof)"),
934 ]
935 .into_iter()
936 .flatten()
937 .collect()
938}
939
940fn acknowledge_doc(guarantees: &EmbeddedStreamGuarantees) -> String {
942 format!(
943 "Acknowledge what it may do with \
944 `EmbeddedOutputChannel::new(.., Acknowledgements::new(){})`.",
945 acknowledgements_chain(guarantees)
946 )
947}
948
949fn acknowledgements_chain(guarantees: &EmbeddedStreamGuarantees) -> String {
951 use crate::compile::ir::{StreamOrder, StreamRetry};
952 [
953 (guarantees.order == StreamOrder::NoOrder).then_some(".no_order()"),
954 (guarantees.retry == StreamRetry::AtLeastOnce).then_some(".at_least_once()"),
955 ]
956 .into_iter()
957 .flatten()
958 .collect()
959}
960
961fn network_end_doc(
963 port: &EmbeddedNetworkPort,
964 guarantees: &EmbeddedStreamGuarantees,
965 sending: bool,
966) -> String {
967 let config = &port.config;
968 if sending {
969 format!(
970 "Acknowledge the channel's configuration and what the stream sent on it may do with \
971 `EmbeddedNetworkOutputChannel::new(.., ChannelConfiguration::new({config}), \
972 Acknowledgements::new(){})`.",
973 acknowledgements_chain(guarantees)
974 )
975 } else {
976 format!(
977 "Acknowledge the channel's configuration and promise the guarantees of the stream it \
978 delivers with `EmbeddedNetworkInputChannel::new(.., ChannelConfiguration::new({config}), \
979 Promises::new(){})`.",
980 promises_chain(guarantees)
981 )
982 }
983}
984
985#[derive(Clone, Debug, Default)]
996#[non_exhaustive]
997pub struct EmbeddedCodegenOptions {
998 pub require_flame_attribution: bool,
1005}
1006
1007impl EmbeddedCodegenOptions {
1008 pub fn with_flame_attribution() -> Self {
1010 Self {
1011 require_flame_attribution: true,
1012 }
1013 }
1014}
1015
1016impl super::deploy::DeployFlow<'_, EmbeddedDeploy> {
1017 pub fn generate_embedded(self, crate_name: &str) -> syn::File {
1051 self.generate_embedded_with_options(crate_name, &EmbeddedCodegenOptions::default())
1052 }
1053
1054 pub fn generate_embedded_with_options(
1056 mut self,
1057 crate_name: &str,
1058 codegen_options: &EmbeddedCodegenOptions,
1059 ) -> syn::File {
1060 let mut env = EmbeddedInstantiateEnv {
1061 boundary: BoundaryGuarantees::collect(&mut self.ir),
1062 ..Default::default()
1063 };
1064 let compiled = self.compile_internal(&mut env);
1065
1066 let root = crate::staging_util::get_this_crate();
1067 let orig_crate_name = quote::format_ident!("{}", crate_name.replace('-', "_"));
1068
1069 let mut items: Vec<syn::Item> = Vec::new();
1070
1071 let mut location_keys: Vec<_> = compiled.all_dfir().keys().collect();
1073 location_keys.sort();
1074
1075 let fn_names: SparseSecondaryMap<LocationKey, &str> = location_keys
1077 .iter()
1078 .map(|&k| {
1079 let name = self
1080 .processes
1081 .get(k)
1082 .map(|n| n.fn_name.as_str())
1083 .or_else(|| self.clusters.get(k).map(|n| n.fn_name.as_str()))
1084 .or_else(|| self.externals.get(k).map(|n| n.fn_name.as_str()))
1085 .expect("location key not found in any node map");
1086 (k, name)
1087 })
1088 .collect();
1089
1090 for location_key in location_keys {
1091 let graph = compiled.all_dfir()[location_key]
1092 .as_ref()
1093 .unwrap_or_else(|err| {
1094 panic!(
1095 "Failed to partition DFIR graph for location {location_key}: {}",
1096 err.diagnostic
1097 )
1098 });
1099
1100 let fn_name = fn_names[location_key];
1102 let fn_ident = syn::Ident::new(fn_name, Span::call_site());
1103
1104 let mut loc_inputs = env.inputs.get(location_key).cloned().unwrap_or_default();
1106 loc_inputs.sort_by(|a, b| a.ident.cmp(&b.ident));
1107
1108 let mut loc_outputs = env.outputs.get(location_key).cloned().unwrap_or_default();
1110 loc_outputs.sort_by(|a, b| a.ident.cmp(&b.ident));
1111
1112 let mut diagnostics = Diagnostics::new();
1113 let mut as_code_options = compiled
1116 .as_code_options
1117 .get(location_key)
1118 .cloned()
1119 .unwrap_or_default();
1120 as_code_options.exclude_type_guards = true;
1125 as_code_options.require_flame_attribution = codegen_options.require_flame_attribution;
1126 as_code_options.exclude_meta = true;
1130 let dfir_tokens = graph
1131 .as_code_with_options(
1132 "e! { __root_dfir_rs },
1133 &as_code_options,
1134 quote!(),
1135 &mut diagnostics,
1136 )
1137 .expect("DFIR inline code generation failed with diagnostics.");
1138
1139 let mut mod_items: Vec<proc_macro2::TokenStream> = Vec::new();
1141 let mut extra_fn_generics: Vec<proc_macro2::TokenStream> = Vec::new();
1142 let mut cluster_params: Vec<proc_macro2::TokenStream> = Vec::new();
1143 let mut output_params: Vec<proc_macro2::TokenStream> = Vec::new();
1144 let mut net_out_params: Vec<proc_macro2::TokenStream> = Vec::new();
1145 let mut net_in_params: Vec<proc_macro2::TokenStream> = Vec::new();
1146 let mut extra_destructure: Vec<proc_macro2::TokenStream> = Vec::new();
1147
1148 if self.clusters.contains_key(location_key)
1150 && self.clusters_reading_self_id.contains(&location_key)
1151 {
1152 cluster_params.push(quote! {
1153 __cluster_self_id: #root::location::member_id::TaglessMemberId
1154 });
1155 let self_id_ident = syn::Ident::new(
1157 &format!("__hydro_lang_cluster_self_id_{}", location_key),
1158 Span::call_site(),
1159 );
1160 extra_destructure.push(quote! {
1161 let #self_id_ident = __cluster_self_id;
1162 });
1163 }
1164
1165 if let Some(loc_memberships) = env.membership_streams.get(location_key) {
1167 let membership_struct_ident =
1168 syn::Ident::new("EmbeddedMembershipStreams", Span::call_site());
1169
1170 let mem_generic_idents: Vec<syn::Ident> = loc_memberships
1171 .iter()
1172 .enumerate()
1173 .map(|(i, _)| quote::format_ident!("__Mem{}", i))
1174 .collect();
1175
1176 let mem_field_names: Vec<syn::Ident> = loc_memberships
1177 .iter()
1178 .map(|k| {
1179 let cluster_fn_name = fn_names[*k];
1180 syn::Ident::new(cluster_fn_name, Span::call_site())
1181 })
1182 .collect();
1183
1184 let mem_item = quote! {
1185 (#root::location::member_id::TaglessMemberId, #root::location::MembershipEvent)
1186 };
1187 let mem_guarantees = EmbeddedStreamGuarantees {
1189 order: crate::compile::ir::StreamOrder::TotalOrder,
1190 retry: crate::compile::ir::StreamRetry::ExactlyOnce,
1191 };
1192 let mem_marker_defs = mem_field_names.iter().map(|field| {
1193 marker_def(
1194 &root,
1195 field,
1196 &format!(
1197 "The membership stream of the `{field}` cluster.\n\n{}",
1198 promise_doc(&mem_guarantees, "EmbeddedInputChannel")
1199 ),
1200 &mem_guarantees,
1201 )
1202 });
1203
1204 let struct_fields: Vec<proc_macro2::TokenStream> = mem_field_names
1205 .iter()
1206 .zip(mem_generic_idents.iter())
1207 .map(|(field, generic)| {
1208 let doc = format!(
1209 "Each member's `Joined` / `Left` events (marker type: [`membership::{field}`])."
1210 );
1211 quote! {
1212 #[doc = #doc]
1213 pub #field: #root::compile::embedded_runtime::EmbeddedInputChannel<membership::#field, #generic>
1214 }
1215 })
1216 .collect();
1217
1218 let struct_generics: Vec<proc_macro2::TokenStream> = mem_generic_idents
1219 .iter()
1220 .map(|generic| quote! { #generic: __root_dfir_rs::futures::Stream<Item = #mem_item> + Unpin })
1221 .collect();
1222
1223 for generic in &mem_generic_idents {
1224 extra_fn_generics.push(
1225 quote! { #generic: __root_dfir_rs::futures::Stream<Item = #mem_item> + Unpin + 'a },
1226 );
1227 }
1228
1229 cluster_params.push(quote! {
1230 __membership: #fn_ident::#membership_struct_ident<#(#mem_generic_idents),*>
1231 });
1232
1233 for (i, field) in mem_field_names.iter().enumerate() {
1234 let var_ident =
1235 syn::Ident::new(&format!("__membership_{}", i), Span::call_site());
1236 extra_destructure.push(quote! {
1237 let #var_ident = __membership.#field.into_inner();
1238 });
1239 }
1240
1241 mod_items.push(quote! {
1242 pub struct #membership_struct_ident<#(#struct_generics),*> {
1243 #(#struct_fields),*
1244 }
1245
1246 pub mod membership {
1249 use super::*;
1250 #(#mem_marker_defs)*
1251 }
1252 });
1253 }
1254
1255 let mut loc_singleton_inputs = env
1258 .singleton_inputs
1259 .get(location_key)
1260 .cloned()
1261 .unwrap_or_default();
1262 loc_singleton_inputs.sort_by(|a, b| a.0.cmp(&b.0));
1263
1264 let mut input_params: Vec<proc_macro2::TokenStream> = Vec::new();
1265 if !loc_inputs.is_empty() || !loc_singleton_inputs.is_empty() {
1266 let input_struct_ident = syn::Ident::new("EmbeddedInputs", Span::call_site());
1267
1268 let input_generic_idents: Vec<syn::Ident> = loc_inputs
1269 .iter()
1270 .enumerate()
1271 .map(|(i, _)| quote::format_ident!("__In{}", i))
1272 .collect();
1273
1274 let input_marker_defs: Vec<proc_macro2::TokenStream> = loc_inputs
1277 .iter()
1278 .map(|input| {
1279 let guarantees = env.boundary.input(location_key, &input.ident);
1280 marker_def(
1281 &root,
1282 &input.ident,
1283 &format!(
1284 "The `{}` stream input.\n\n{}",
1285 input.ident,
1286 promise_doc(&guarantees, "EmbeddedInputChannel")
1287 ),
1288 &guarantees,
1289 )
1290 })
1291 .collect();
1292
1293 let struct_fields: Vec<proc_macro2::TokenStream> = loc_inputs
1294 .iter()
1295 .zip(input_generic_idents.iter())
1296 .map(|(input, generic)| {
1297 let ident = &input.ident;
1298 let doc =
1299 format!("The `{ident}` stream input (marker type: [`inputs::{ident}`]).");
1300 quote! {
1301 #[doc = #doc]
1302 pub #ident: #root::compile::embedded_runtime::EmbeddedInputChannel<inputs::#ident, #generic>
1303 }
1304 })
1305 .chain(
1306 loc_singleton_inputs
1307 .iter()
1308 .map(|(ident, element_type)| quote! { pub #ident: #element_type }),
1309 )
1310 .collect();
1311
1312 let struct_generics: Vec<proc_macro2::TokenStream> = loc_inputs
1313 .iter()
1314 .zip(input_generic_idents.iter())
1315 .map(|(input, generic)| {
1316 let element_type = &input.element_type;
1317 quote! { #generic: __root_dfir_rs::futures::Stream<Item = #element_type> + Unpin }
1318 })
1319 .collect();
1320
1321 for (input, generic) in loc_inputs.iter().zip(input_generic_idents.iter()) {
1322 let element_type = &input.element_type;
1323 extra_fn_generics.push(
1324 quote! { #generic: __root_dfir_rs::futures::Stream<Item = #element_type> + Unpin + 'a },
1325 );
1326 }
1327
1328 input_params.push(quote! {
1329 __inputs: #fn_ident::#input_struct_ident<#(#input_generic_idents),*>
1330 });
1331
1332 for input in &loc_inputs {
1333 let ident = &input.ident;
1334 extra_destructure.push(quote! { let #ident = __inputs.#ident.into_inner(); });
1335 }
1336 for (ident, _) in &loc_singleton_inputs {
1337 extra_destructure.push(quote! { let #ident = __inputs.#ident; });
1338 }
1339
1340 let input_markers_mod = (!input_marker_defs.is_empty()).then(|| {
1341 quote! {
1342 pub mod inputs {
1345 use super::*;
1346 #(#input_marker_defs)*
1347 }
1348 }
1349 });
1350 mod_items.push(quote! {
1351 pub struct #input_struct_ident<#(#struct_generics),*> {
1352 #(#struct_fields),*
1353 }
1354
1355 #input_markers_mod
1356 });
1357 }
1358
1359 if !loc_outputs.is_empty() {
1362 let output_struct_ident = syn::Ident::new("EmbeddedOutputs", Span::call_site());
1363
1364 let mut struct_generics: Vec<proc_macro2::TokenStream> = Vec::new();
1365 let mut struct_fields: Vec<proc_macro2::TokenStream> = Vec::new();
1366 let mut generics: Vec<syn::Ident> = Vec::new();
1367 let mut marker_defs: Vec<proc_macro2::TokenStream> = Vec::new();
1368 for (i, output) in loc_outputs.iter().enumerate() {
1369 let ident = &output.ident;
1370 let element_type = &output.element_type;
1371 let generic = quote::format_ident!("__Out{}", i);
1372 let guarantees = env.boundary.output(location_key, ident);
1373 marker_defs.push(marker_def(
1374 &root,
1375 ident,
1376 &format!("The `{ident}` output.\n\n{}", acknowledge_doc(&guarantees)),
1377 &guarantees,
1378 ));
1379 let doc = format!("The `{ident}` output (marker type: [`outputs::{ident}`]).");
1380 let sink_bound = quote! { FnMut(#element_type) };
1381 struct_generics.push(quote! { #generic: #sink_bound });
1382 extra_fn_generics.push(quote! { #generic: #sink_bound + 'a });
1383 struct_fields.push(quote! {
1384 #[doc = #doc]
1385 pub #ident: #root::compile::embedded_runtime::EmbeddedOutputChannel<outputs::#ident, #generic>
1386 });
1387 generics.push(generic);
1388 extra_destructure
1389 .push(quote! { let mut #ident = __outputs.#ident.into_inner(); });
1390 }
1391
1392 output_params.push(quote! {
1393 __outputs: #fn_ident::#output_struct_ident<#(#generics),*>
1394 });
1395
1396 mod_items.push(quote! {
1397 pub struct #output_struct_ident<#(#struct_generics),*> {
1398 #(#struct_fields),*
1399 }
1400
1401 pub mod outputs {
1403 use super::*;
1404 #(#marker_defs)*
1405 }
1406 });
1407 }
1408
1409 if let Some(mut ports) = env.network_outputs.remove(location_key) {
1411 ports.sort_by(|a, b| a.name.cmp(&b.name));
1412 let channels = NetworkPortTokens::new(
1413 &ports,
1414 &env.boundary,
1415 &fn_names,
1416 "__NetOut",
1417 true,
1418 |port| {
1419 let payload = if let Some(ty) = &port.external_type {
1420 quote! { #ty }
1421 } else {
1422 quote! { #root::runtime_support::dfir_rs::bytes::Bytes }
1423 };
1424 if port.is_tagged {
1425 quote! { (#root::location::member_id::TaglessMemberId, #payload) }
1426 } else {
1427 payload
1428 }
1429 },
1430 );
1431 let NetworkPortTokens {
1432 fields,
1433 field_docs,
1434 field_types,
1435 generics,
1436 items,
1437 marker_defs,
1438 } = &channels;
1439
1440 for (generic, item) in generics.iter().zip(items) {
1441 extra_fn_generics.push(quote! { #generic: FnMut(#item) + 'a });
1442 }
1443
1444 net_out_params.push(quote! {
1445 __network_out: #fn_ident::EmbeddedNetworkOut<#(#generics),*>
1446 });
1447
1448 for (port, field) in ports.iter().zip(fields) {
1449 let var_ident =
1450 syn::Ident::new(&format!("__network_out_{}", port.name), Span::call_site());
1451 extra_destructure
1452 .push(quote! { let mut #var_ident = __network_out.#field.into_inner(); });
1453 }
1454
1455 mod_items.push(quote! {
1456 pub struct EmbeddedNetworkOut<#(#generics: FnMut(#items)),*> {
1457 #(#[doc = #field_docs] pub #fields: #field_types),*
1458 }
1459
1460 pub mod outgoing {
1463 use super::*;
1464 #(#marker_defs)*
1465 }
1466 });
1467 }
1468
1469 if let Some(mut ports) = env.network_inputs.remove(location_key) {
1471 ports.sort_by(|a, b| a.name.cmp(&b.name));
1472 let channels = NetworkPortTokens::new(
1473 &ports,
1474 &env.boundary,
1475 &fn_names,
1476 "__NetIn",
1477 false,
1478 |port| {
1479 match (&port.external_type, port.is_tagged) {
1482 (Some(ty), true) => {
1483 quote! { (#root::location::member_id::TaglessMemberId, #ty) }
1484 }
1485 (Some(ty), false) => quote! { #ty },
1486 (None, true) => {
1487 quote! { Result<(#root::location::member_id::TaglessMemberId, __root_dfir_rs::bytes::BytesMut), std::io::Error> }
1488 }
1489 (None, false) => {
1490 quote! { Result<__root_dfir_rs::bytes::BytesMut, std::io::Error> }
1491 }
1492 }
1493 },
1494 );
1495 let NetworkPortTokens {
1496 fields,
1497 field_docs,
1498 field_types,
1499 generics,
1500 items,
1501 marker_defs,
1502 } = &channels;
1503
1504 for (generic, item) in generics.iter().zip(items) {
1505 extra_fn_generics.push(
1506 quote! { #generic: __root_dfir_rs::futures::Stream<Item = #item> + Unpin + 'a },
1507 );
1508 }
1509
1510 net_in_params.push(quote! {
1511 __network_in: #fn_ident::EmbeddedNetworkIn<#(#generics),*>
1512 });
1513
1514 for (port, field) in ports.iter().zip(fields) {
1515 let var_ident =
1516 syn::Ident::new(&format!("__network_in_{}", port.name), Span::call_site());
1517 extra_destructure
1518 .push(quote! { let #var_ident = __network_in.#field.into_inner(); });
1519 }
1520
1521 mod_items.push(quote! {
1522 pub struct EmbeddedNetworkIn<#(#generics: __root_dfir_rs::futures::Stream<Item = #items> + Unpin),*> {
1523 #(#[doc = #field_docs] pub #fields: #field_types),*
1524 }
1525
1526 pub mod incoming {
1529 use super::*;
1530 #(#marker_defs)*
1531 }
1532 });
1533 }
1534
1535 let output_mod: syn::Item = syn::parse_quote! {
1537 pub mod #fn_ident {
1538 use super::*;
1539
1540 #(#mod_items)*
1541 }
1542 };
1543 items.push(output_mod);
1544
1545 let all_params: Vec<proc_macro2::TokenStream> = cluster_params
1547 .into_iter()
1548 .chain(input_params)
1549 .chain(output_params)
1550 .chain(net_in_params)
1551 .chain(net_out_params)
1552 .collect();
1553
1554 let dfir_metrics_ty = crate::compile::dfir_metrics_state_type(&as_code_options, &root);
1555 let ret_type: syn::Type = syn::parse_quote! { #root::runtime_support::dfir_rs::scheduled::context::Dfir<impl #root::runtime_support::dfir_rs::scheduled::context::TickClosure<Metrics = #dfir_metrics_ty> + 'a> };
1556
1557 let func = syn::parse_quote! {
1558 #[allow(unused, non_snake_case, clippy::suspicious_else_formatting)]
1559 pub fn #fn_ident<'a, #(#extra_fn_generics),*>(#(#all_params),*) -> #ret_type {
1560 #(#extra_destructure)*
1561 #dfir_tokens
1562 }
1563 };
1564
1565 items.push(func);
1566 }
1567
1568 syn::parse_quote! {
1569 use #orig_crate_name::__staged::__deps::*;
1570 use #root::prelude::*;
1571 use #root::runtime_support::dfir_rs as __root_dfir_rs;
1572 pub use #orig_crate_name::__staged;
1573
1574 #( #items )*
1575 }
1576 }
1577}