1use std::collections::{BTreeMap, BTreeSet};
12
13use snafu::{OptionExt, ResultExt, Snafu};
14use uuid::Uuid;
15
16use crate::{EdgeStore, Graph, Meta, MetadataFilter, Node, NodeStore};
17
18#[derive(Clone, Debug, Snafu)]
20pub enum MdmError {
21 NodeNotOnAxis { id: Uuid },
23 NodeStore { source: crate::node::NodeStoreError },
25 EdgeStore { source: crate::edge::EdgeStoreError },
27}
28
29#[derive(Clone, Debug, Default, PartialEq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub enum NodeSelectionMode {
34 #[default]
35 Independent,
36 Dependent(BTreeSet<String>),
37}
38
39#[derive(Copy, Clone, Default, Debug, PartialEq)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub enum EdgeDisplayMode {
43 #[default]
44 Binary,
45 Kinds,
46 Labels,
47 Weights,
48}
49
50#[derive(Copy, Clone, Default, Debug, PartialEq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53pub enum EdgeDivisionMode {
54 Absolute,
55 #[default]
56 Equal,
57 Relative,
58}
59
60pub fn mdm_axis_nodes(
62 graph: &Graph,
63 node_filter: &MetadataFilter,
64 edge_filter: &MetadataFilter,
65 root_ids: Option<&Vec<Uuid>>,
66 node_depth: Option<usize>,
67 node_selection_mode: &NodeSelectionMode,
68) -> Result<Vec<Uuid>, MdmError> {
69 let root_ids = root_ids
70 .cloned()
71 .unwrap_or_else(|| graph.root_ids().map(|id| id.to_owned()).collect());
72
73 let node_kinds_vec = node_filter.kinds.clone().unwrap_or_else(|| {
74 graph
75 .node_aggregate()
76 .kinds
77 .keys()
78 .map(|x| x.to_owned())
79 .collect::<Vec<_>>()
80 });
81 let node_kind_order = node_kinds_vec
82 .into_iter()
83 .enumerate()
84 .map(|(i, kind)| (kind, i))
85 .collect::<BTreeMap<String, usize>>();
86
87 let (mut ind_roots, mut dep_roots) =
89 split_dependent_roots(graph, root_ids, node_selection_mode)?;
90
91 ind_roots.sort_by_key(|id| {
92 graph
93 .get_node(id)
94 .map(|n| node_kind_order.get(&n.get_meta().kind))
95 });
96 dep_roots.sort_by_key(|id| {
97 graph
98 .get_node(id)
99 .map(|n| node_kind_order.get(&n.get_meta().kind))
100 });
101
102 let ind_leaf_vec: Vec<Uuid> =
104 independent_leafs(graph, &ind_roots, node_filter, node_depth).collect();
105
106 if node_selection_mode == &NodeSelectionMode::Independent {
108 return Ok(ind_leaf_vec);
109 }
110 let mut result = ind_leaf_vec.clone();
111
112 let ind_leaf_set: BTreeSet<Uuid> = ind_leaf_vec.iter().copied().collect();
114
115 let dep_leaf_iter =
117 independent_leafs(graph, &dep_roots, node_filter, node_depth).filter(|&leaf_id| {
118 let this = BTreeSet::from([leaf_id]);
119 graph
120 .edges_between_all(&this, &ind_leaf_set)
121 .any(|e| edge_filter.satisfies(e))
122 || graph
123 .edges_between_all(&ind_leaf_set, &this)
124 .any(|e| edge_filter.satisfies(e))
125 });
126
127 result.extend(dep_leaf_iter);
128
129 Ok(result)
130}
131
132pub fn mdm_axis_expand(
134 axis: &mut Vec<Uuid>,
135 node_id: &Uuid,
136 graph: &Graph,
137 node_filter: &MetadataFilter,
138 edge_filter: &MetadataFilter,
139 node_selection_mode: &NodeSelectionMode,
140) -> Result<(), MdmError> {
141 let position = axis
142 .iter()
143 .position(|id| id == node_id)
144 .with_context(|| NodeNotOnAxisSnafu { id: *node_id })?;
145
146 let children = mdm_axis_children(
147 axis,
148 node_id,
149 graph,
150 node_filter,
151 edge_filter,
152 node_selection_mode,
153 )?;
154
155 axis.splice(position..position + 1, children);
156
157 Ok(())
158}
159
160pub fn mdm_axis_children<'a>(
161 axis: &[Uuid],
162 node_id: &Uuid,
163 graph: &'a Graph,
164 node_filter: &'a MetadataFilter,
165 edge_filter: &'a MetadataFilter,
166 node_selection_mode: &NodeSelectionMode,
167) -> Result<impl Iterator<Item = Uuid> + 'a, MdmError> {
168 let children = graph
169 .get_node_err(node_id)
170 .with_context(|_| NodeStoreSnafu)?
171 .children
172 .iter()
173 .filter(|&child_id| {
174 graph
175 .get_node(child_id)
176 .map(|c| node_filter.satisfies(c))
177 .unwrap_or_default()
178 });
179 let children: Box<dyn Iterator<Item = &Uuid>> = match node_selection_mode {
180 NodeSelectionMode::Independent => Box::new(children),
181 NodeSelectionMode::Dependent(kinds) => {
182 let ind_leaf_set = axis
183 .iter()
184 .filter(|&id| {
185 graph
186 .get_node(id)
187 .map(|n| kinds.contains(&n.get_meta().kind))
188 .unwrap_or_default()
189 })
190 .copied()
191 .collect::<BTreeSet<_>>();
192 Box::new(children.filter(move |&child_id| {
193 let this = BTreeSet::from([*child_id]);
194 graph
195 .edges_between_all(&this, &ind_leaf_set)
196 .any(|e| edge_filter.satisfies(e))
197 || graph
198 .edges_between_all(&ind_leaf_set, &this)
199 .any(|e| edge_filter.satisfies(e))
200 }))
201 }
202 };
203 Ok(children.copied())
204}
205
206pub fn mdm_axis_collapse(
208 axis: &mut Vec<Uuid>,
209 node_id: &Uuid,
210 graph: &Graph,
211) -> Result<(), MdmError> {
212 let leaf_set = axis.iter().copied().collect::<BTreeSet<_>>();
213
214 let mut to_replace = graph
215 .descendant_ids(node_id, true, true, Some(&leaf_set), None)
216 .filter_map(|id| id.ok())
217 .filter(|&id| leaf_set.contains(id))
218 .filter_map(|id| axis.iter().position(|e| e == id));
219
220 if let Some(first) = to_replace.next() {
221 let (start, end) =
222 to_replace.fold((first, first), |(start, end), c| (start.min(c), end.max(c)));
223 axis.splice(start..end + 1, [*node_id]);
224 }
225
226 Ok(())
227}
228
229fn split_dependent_roots(
231 graph: &Graph,
232 root_ids: Vec<Uuid>,
233 mode: &NodeSelectionMode,
234) -> Result<(Vec<Uuid>, Vec<Uuid>), MdmError> {
235 Ok(match mode {
236 NodeSelectionMode::Independent => (root_ids, vec![]),
237 NodeSelectionMode::Dependent(lead_kinds) => {
238 let mut ind_roots = vec![];
239 let mut dep_roots = vec![];
240 for root_id in root_ids.into_iter() {
241 if lead_kinds.contains(
242 graph
243 .get_node_err(&root_id)
244 .with_context(|_| NodeStoreSnafu)?
245 .kind(),
246 ) {
247 ind_roots.push(root_id);
248 } else {
249 dep_roots.push(root_id);
250 }
251 }
252 (ind_roots, dep_roots)
253 }
254 })
255}
256
257fn independent_leafs<'a>(
259 graph: &'a Graph,
260 root_ids: &'a [Uuid],
261 node_filter: &'a MetadataFilter,
262 node_depth: Option<usize>,
263) -> impl Iterator<Item = Uuid> + 'a {
264 root_ids.iter().flat_map(move |root_id| {
265 let descendants: Box<dyn Iterator<Item = &Uuid>> = if node_depth == Some(0)
266 || graph
267 .get_node(root_id)
268 .map(|r| r.is_leaf())
269 .unwrap_or_default()
270 {
271 Box::new(Some(root_id).into_iter())
272 } else {
273 Box::new(
274 graph
275 .descendant_ids(root_id, true, true, None, node_depth)
276 .filter_map(|id| id.ok()),
277 )
278 };
279 descendants.filter_map(|id| {
280 graph.get_node(id).and_then(|node| {
281 if node_filter.satisfies(node) {
282 Some(*id)
283 } else {
284 None
285 }
286 })
287 })
288 })
289}
290
291pub fn display_sort(
293 graph: &Graph,
294 node_ids: &[Uuid],
295 node_kind_order: Option<&BTreeMap<String, usize>>,
296 leaf_ids: Option<&BTreeSet<Uuid>>,
297) -> Vec<usize> {
298 let mut nodes: Vec<(usize, &Node)> = node_ids
299 .iter()
300 .filter_map(|x| graph.get_node(x))
301 .enumerate()
302 .collect();
303 nodes.sort_unstable_by_key(|&(_, node)| {
304 (
305 node_kind_order.map(|x| x.get(node.kind())),
306 graph.node_width(node.id(), false, leaf_ids, None).unwrap(),
307 node.name(),
308 node.id(),
309 )
310 });
311 nodes.into_iter().map(|x| x.0).collect()
312}