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