1use std::cell::UnsafeCell;
2use std::collections::{BTreeMap, HashMap};
3use std::marker::PhantomData;
4
5use dfir_lang::graph::{DfirGraph, eliminate_extra_unions_tees, partition_graph};
6
7use super::compiled::CompiledFlow;
8use super::deploy::{DeployFlow, DeployResult};
9use super::deploy_provider::{ClusterSpec, Deploy, ExternalSpec, IntoProcessSpec};
10use super::ir::{HydroRoot, emit};
11use crate::location::{Cluster, External, Process};
12#[cfg(feature = "sim")]
13#[cfg(stageleft_runtime)]
14use crate::sim::{flow::SimFlow, graph::SimNode};
15use crate::staging_util::Invariant;
16#[cfg(feature = "viz")]
17use crate::viz::api::GraphApi;
18
19pub struct BuiltFlow<'a> {
20 pub(super) ir: Vec<HydroRoot>,
21 pub(super) process_id_name: Vec<(usize, String)>,
22 pub(super) cluster_id_name: Vec<(usize, String)>,
23 pub(super) external_id_name: Vec<(usize, String)>,
24
25 pub(super) _phantom: Invariant<'a>,
26}
27
28pub(crate) fn build_inner(ir: &mut Vec<HydroRoot>) -> BTreeMap<usize, DfirGraph> {
29 emit(ir)
30 .into_iter()
31 .map(|(k, v)| {
32 let (mut flat_graph, _, _) = v.build();
33 eliminate_extra_unions_tees(&mut flat_graph);
34 let partitioned_graph =
35 partition_graph(flat_graph).expect("Failed to partition (cycle detected).");
36 (k, partitioned_graph)
37 })
38 .collect()
39}
40
41impl<'a> BuiltFlow<'a> {
42 pub fn ir(&self) -> &Vec<HydroRoot> {
43 &self.ir
44 }
45
46 pub fn process_id_name(&self) -> &Vec<(usize, String)> {
47 &self.process_id_name
48 }
49
50 pub fn cluster_id_name(&self) -> &Vec<(usize, String)> {
51 &self.cluster_id_name
52 }
53
54 pub fn external_id_name(&self) -> &Vec<(usize, String)> {
55 &self.external_id_name
56 }
57
58 #[cfg(feature = "viz")]
60 pub fn graph_api(&self) -> GraphApi<'_> {
61 GraphApi::new(
62 &self.ir,
63 &self.process_id_name,
64 &self.cluster_id_name,
65 &self.external_id_name,
66 )
67 }
68
69 #[cfg(feature = "viz")]
71 pub fn mermaid_string(
72 &self,
73 show_metadata: bool,
74 show_location_groups: bool,
75 use_short_labels: bool,
76 ) -> String {
77 self.graph_api()
78 .mermaid_to_string(show_metadata, show_location_groups, use_short_labels)
79 }
80
81 #[cfg(feature = "viz")]
82 pub fn dot_string(
83 &self,
84 show_metadata: bool,
85 show_location_groups: bool,
86 use_short_labels: bool,
87 ) -> String {
88 self.graph_api()
89 .dot_to_string(show_metadata, show_location_groups, use_short_labels)
90 }
91
92 #[cfg(feature = "viz")]
93 pub fn hydroscope_string(
94 &self,
95 show_metadata: bool,
96 show_location_groups: bool,
97 use_short_labels: bool,
98 ) -> String {
99 self.graph_api()
100 .hydroscope_to_string(show_metadata, show_location_groups, use_short_labels)
101 }
102
103 #[cfg(feature = "viz")]
105 pub fn mermaid_to_file(
106 &self,
107 filename: &str,
108 show_metadata: bool,
109 show_location_groups: bool,
110 use_short_labels: bool,
111 ) -> Result<(), Box<dyn std::error::Error>> {
112 self.graph_api().mermaid_to_file(
113 filename,
114 show_metadata,
115 show_location_groups,
116 use_short_labels,
117 )
118 }
119
120 #[cfg(feature = "viz")]
121 pub fn dot_to_file(
122 &self,
123 filename: &str,
124 show_metadata: bool,
125 show_location_groups: bool,
126 use_short_labels: bool,
127 ) -> Result<(), Box<dyn std::error::Error>> {
128 self.graph_api().dot_to_file(
129 filename,
130 show_metadata,
131 show_location_groups,
132 use_short_labels,
133 )
134 }
135
136 #[cfg(feature = "viz")]
137 pub fn hydroscope_to_file(
138 &self,
139 filename: &str,
140 show_metadata: bool,
141 show_location_groups: bool,
142 use_short_labels: bool,
143 ) -> Result<(), Box<dyn std::error::Error>> {
144 self.graph_api().hydroscope_to_file(
145 filename,
146 show_metadata,
147 show_location_groups,
148 use_short_labels,
149 )
150 }
151
152 #[cfg(feature = "viz")]
154 pub fn mermaid_to_browser(
155 &self,
156 show_metadata: bool,
157 show_location_groups: bool,
158 use_short_labels: bool,
159 message_handler: Option<&dyn Fn(&str)>,
160 ) -> Result<(), Box<dyn std::error::Error>> {
161 self.graph_api().mermaid_to_browser(
162 show_metadata,
163 show_location_groups,
164 use_short_labels,
165 message_handler,
166 )
167 }
168
169 #[cfg(feature = "viz")]
170 pub fn dot_to_browser(
171 &self,
172 show_metadata: bool,
173 show_location_groups: bool,
174 use_short_labels: bool,
175 message_handler: Option<&dyn Fn(&str)>,
176 ) -> Result<(), Box<dyn std::error::Error>> {
177 self.graph_api().dot_to_browser(
178 show_metadata,
179 show_location_groups,
180 use_short_labels,
181 message_handler,
182 )
183 }
184
185 #[cfg(feature = "viz")]
186 pub fn hydroscope_to_browser(
187 &self,
188 show_metadata: bool,
189 show_location_groups: bool,
190 use_short_labels: bool,
191 message_handler: Option<&dyn Fn(&str)>,
192 ) -> Result<(), Box<dyn std::error::Error>> {
193 self.graph_api().hydroscope_to_browser(
194 show_metadata,
195 show_location_groups,
196 use_short_labels,
197 message_handler,
198 )
199 }
200
201 pub fn optimize_with(mut self, f: impl FnOnce(&mut [HydroRoot])) -> Self {
202 f(&mut self.ir);
203 BuiltFlow {
204 ir: std::mem::take(&mut self.ir),
205 process_id_name: std::mem::take(&mut self.process_id_name),
206 cluster_id_name: std::mem::take(&mut self.cluster_id_name),
207 external_id_name: std::mem::take(&mut self.external_id_name),
208 _phantom: PhantomData,
209 }
210 }
211
212 pub fn with_default_optimize<D: Deploy<'a>>(self) -> DeployFlow<'a, D> {
213 self.into_deploy()
214 }
215
216 #[cfg(feature = "sim")]
217 pub fn sim(mut self) -> SimFlow<'a> {
220 use std::cell::{Cell, RefCell};
221 use std::rc::Rc;
222
223 use crate::sim::graph::SimExternal;
224
225 let external_ports = Rc::new(RefCell::new((vec![], 0)));
226
227 let global_port_counter = Rc::new(Cell::new(0));
228 let processes = self
229 .process_id_name
230 .iter()
231 .map(|id| {
232 (
233 id.0,
234 SimNode {
235 port_counter: global_port_counter.clone(),
236 },
237 )
238 })
239 .collect();
240
241 let clusters = self
242 .cluster_id_name
243 .iter()
244 .map(|id| {
245 (
246 id.0,
247 SimNode {
248 port_counter: global_port_counter.clone(),
249 },
250 )
251 })
252 .collect();
253
254 let all_external_registered = Rc::new(RefCell::new(HashMap::new()));
255 let externals = self
256 .external_id_name
257 .iter()
258 .map(|id| {
259 (
260 id.0,
261 SimExternal {
262 external_ports: external_ports.clone(),
263 registered: all_external_registered.clone(),
264 },
265 )
266 })
267 .collect();
268
269 SimFlow {
270 ir: std::mem::take(&mut self.ir),
271 external_ports,
272 processes,
273 clusters,
274 cluster_max_sizes: HashMap::new(),
275 externals,
276 external_registered: all_external_registered.clone(),
277 _process_id_name: std::mem::take(&mut self.process_id_name),
278 _external_id_name: std::mem::take(&mut self.external_id_name),
279 _cluster_id_name: std::mem::take(&mut self.cluster_id_name),
280 _phantom: PhantomData,
281 }
282 }
283
284 pub fn into_deploy<D: Deploy<'a>>(mut self) -> DeployFlow<'a, D> {
285 let processes = if D::has_trivial_node() {
286 self.process_id_name
287 .iter()
288 .map(|id| (id.0, D::trivial_process(id.0)))
289 .collect()
290 } else {
291 HashMap::new()
292 };
293
294 let clusters = if D::has_trivial_node() {
295 self.cluster_id_name
296 .iter()
297 .map(|id| (id.0, D::trivial_cluster(id.0)))
298 .collect()
299 } else {
300 HashMap::new()
301 };
302
303 let externals = if D::has_trivial_node() {
304 self.external_id_name
305 .iter()
306 .map(|id| (id.0, D::trivial_external(id.0)))
307 .collect()
308 } else {
309 HashMap::new()
310 };
311
312 DeployFlow {
313 ir: UnsafeCell::new(std::mem::take(&mut self.ir)),
314 processes,
315 process_id_name: std::mem::take(&mut self.process_id_name),
316 clusters,
317 cluster_id_name: std::mem::take(&mut self.cluster_id_name),
318 externals,
319 external_id_name: std::mem::take(&mut self.external_id_name),
320 _phantom: PhantomData,
321 }
322 }
323
324 pub fn with_process<P, D: Deploy<'a>>(
325 self,
326 process: &Process<P>,
327 spec: impl IntoProcessSpec<'a, D>,
328 ) -> DeployFlow<'a, D> {
329 self.into_deploy().with_process(process, spec)
330 }
331
332 pub fn with_remaining_processes<D: Deploy<'a>, S: IntoProcessSpec<'a, D> + 'a>(
333 self,
334 spec: impl Fn() -> S,
335 ) -> DeployFlow<'a, D> {
336 self.into_deploy().with_remaining_processes(spec)
337 }
338
339 pub fn with_external<P, D: Deploy<'a>>(
340 self,
341 process: &External<P>,
342 spec: impl ExternalSpec<'a, D>,
343 ) -> DeployFlow<'a, D> {
344 self.into_deploy().with_external(process, spec)
345 }
346
347 pub fn with_remaining_externals<D: Deploy<'a>, S: ExternalSpec<'a, D> + 'a>(
348 self,
349 spec: impl Fn() -> S,
350 ) -> DeployFlow<'a, D> {
351 self.into_deploy().with_remaining_externals(spec)
352 }
353
354 pub fn with_cluster<C, D: Deploy<'a>>(
355 self,
356 cluster: &Cluster<C>,
357 spec: impl ClusterSpec<'a, D>,
358 ) -> DeployFlow<'a, D> {
359 self.into_deploy().with_cluster(cluster, spec)
360 }
361
362 pub fn with_remaining_clusters<D: Deploy<'a>, S: ClusterSpec<'a, D> + 'a>(
363 self,
364 spec: impl Fn() -> S,
365 ) -> DeployFlow<'a, D> {
366 self.into_deploy().with_remaining_clusters(spec)
367 }
368
369 pub fn compile<D: Deploy<'a>>(self, env: &D::CompileEnv) -> CompiledFlow<'a, D::GraphId> {
370 self.into_deploy::<D>().compile(env)
371 }
372
373 pub fn compile_no_network<D: Deploy<'a>>(self) -> CompiledFlow<'a, D::GraphId> {
374 self.into_deploy::<D>().compile_no_network()
375 }
376
377 pub fn deploy<D: Deploy<'a, CompileEnv = ()>>(
378 self,
379 env: &mut D::InstantiateEnv,
380 ) -> DeployResult<'a, D> {
381 self.into_deploy::<D>().deploy(env)
382 }
383
384 #[cfg(feature = "viz")]
385 pub fn generate_all_files(
386 &self,
387 prefix: &str,
388 show_metadata: bool,
389 show_location_groups: bool,
390 use_short_labels: bool,
391 ) -> Result<(), Box<dyn std::error::Error>> {
392 self.graph_api().generate_all_files(
393 prefix,
394 show_metadata,
395 show_location_groups,
396 use_short_labels,
397 )
398 }
399
400 #[cfg(feature = "viz")]
401 pub fn generate_graph_with_config(
402 &self,
403 config: &crate::viz::config::GraphConfig,
404 message_handler: Option<&dyn Fn(&str)>,
405 ) -> Result<(), Box<dyn std::error::Error>> {
406 self.graph_api()
407 .generate_graph_with_config(config, message_handler)
408 }
409
410 #[cfg(feature = "viz")]
411 pub fn generate_all_files_with_config(
412 &self,
413 config: &crate::viz::config::GraphConfig,
414 prefix: &str,
415 ) -> Result<(), Box<dyn std::error::Error>> {
416 self.graph_api()
417 .generate_all_files_with_config(config, prefix)
418 }
419}