iridis_layout/
layout.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fmt,
4    sync::Arc,
5};
6
7use crate::prelude::*;
8
9/// Represents a flattened dataflow layout
10pub struct DataflowLayout {
11    /// Inputs present in the dataflow
12    pub inputs: HashSet<Uuid>,
13    /// Outputs present in the dataflow
14    pub outputs: HashSet<Uuid>,
15    /// Queries present in the dataflow
16    pub queries: HashSet<Uuid>,
17    /// Queryables present in the dataflow
18    pub queryables: HashSet<Uuid>,
19
20    /// Labels for nodes and IO, useful for debugging and visualization
21    pub labels: HashMap<Uuid, String>,
22    /// Nodes representation with their IO, useful for debugging and visualization
23    pub nodes: HashMap<Uuid, HashSet<Uuid>>,
24}
25
26impl Default for DataflowLayout {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl DataflowLayout {
33    /// Creates a new empty dataflow layout
34    pub fn new() -> Self {
35        Self {
36            inputs: HashSet::new(),
37            outputs: HashSet::new(),
38            queryables: HashSet::new(),
39            queries: HashSet::new(),
40
41            labels: HashMap::new(),
42            nodes: HashMap::new(),
43        }
44    }
45
46    /// Adds a new node to the dataflow layout, providing its label and a builder function for its IO.
47    /// For convenience, the node layout is returned, together with the result of the builder function.
48    pub async fn node<T>(
49        &mut self,
50        label: impl Into<String>,
51        builder_function: impl AsyncFnOnce(&mut NodeIOBuilder) -> T,
52    ) -> (NodeLayout, T) {
53        let label = label.into();
54        let layout = NodeLayout::new(&label);
55        let mut io = NodeIOBuilder::new(&layout);
56
57        let result = builder_function(&mut io).await;
58
59        self.nodes.insert(
60            layout.uuid,
61            io.inputs
62                .union(&io.outputs)
63                .chain(io.queries.union(&io.queryables))
64                .cloned()
65                .collect(),
66        );
67
68        self.inputs.extend(io.inputs);
69        self.outputs.extend(io.outputs);
70        self.queries.extend(io.queries);
71        self.queryables.extend(io.queryables);
72        self.labels.extend(io.labels);
73
74        self.labels.insert(layout.uuid, label.clone());
75
76        tracing::debug!("Node '{}' (uuid: {}) created", label, layout.uuid);
77
78        (layout, result)
79    }
80
81    /// Access the label of an entity in the dataflow layout (node, input, output, query, queryable)
82    pub fn label(&self, uuid: impl Into<Uuid>) -> String {
83        let uuid = uuid.into();
84
85        self.labels.get(&uuid).cloned().unwrap_or_default()
86    }
87
88    /// Build the dataflow layout by making it immutable and returning an Arc
89    pub fn build(self) -> Arc<Self> {
90        Arc::new(self)
91    }
92}
93
94impl fmt::Debug for DataflowLayout {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        #[derive(Debug)]
97        struct Layout {
98            #[allow(dead_code)]
99            id: (String, Uuid),
100
101            inputs: HashSet<(String, Uuid)>,
102            outputs: HashSet<(String, Uuid)>,
103            queryables: HashSet<(String, Uuid)>,
104            queries: HashSet<(String, Uuid)>,
105        }
106
107        let mut nodes = Vec::new();
108
109        for (&node, io) in &self.nodes {
110            let mut layout = Layout {
111                id: (self.label(node), node),
112
113                inputs: HashSet::new(),
114                outputs: HashSet::new(),
115                queryables: HashSet::new(),
116                queries: HashSet::new(),
117            };
118
119            for &io in io {
120                if self.inputs.contains(&io) {
121                    layout.inputs.insert((self.label(io), io));
122                }
123                if self.outputs.contains(&io) {
124                    layout.outputs.insert((self.label(io), io));
125                }
126                if self.queryables.contains(&io) {
127                    layout.queryables.insert((self.label(io), io));
128                }
129                if self.queries.contains(&io) {
130                    layout.queries.insert((self.label(io), io));
131                }
132            }
133
134            nodes.push(layout);
135        }
136
137        writeln!(f, "{:#?}", nodes)
138    }
139}