1use std::error::Error;
9use std::fmt;
10use std::sync::Arc;
11
12use indexmap::IndexMap;
13use serde_json::Value;
14
15use super::flatten::FlatNode;
16
17#[derive(Debug, Clone, Default, PartialEq)]
25pub struct ComposedNode {
26 pub path: String,
31 pub attributes: IndexMap<String, Arc<Value>>,
33 pub children: IndexMap<String, Arc<ComposedNode>>,
34}
35
36impl ComposedNode {
37 pub fn descendant(self: &Arc<Self>, path: &str) -> Option<&Arc<ComposedNode>> {
40 let mut node = self;
41 let mut rest = path;
42 loop {
43 if rest.is_empty() {
44 return Some(node);
45 }
46 let (name, tail) = match rest.split_once('/') {
47 Some((name, tail)) => (name, Some(tail)),
48 None => (rest, None),
49 };
50 node = node.children.get(name)?;
51 match tail {
52 Some(tail) => rest = tail,
53 None => return Some(node),
54 }
55 }
56 }
57}
58
59impl Drop for ComposedNode {
60 fn drop(&mut self) {
63 let mut stack: Vec<Arc<ComposedNode>> =
64 self.children.drain(..).map(|(_, child)| child).collect();
65 while let Some(child) = stack.pop() {
66 if let Some(mut child) = Arc::into_inner(child) {
67 stack.extend(child.children.drain(..).map(|(_, c)| c));
68 }
69 }
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75#[non_exhaustive]
76pub enum ComposeError {
77 Cycle { cycle: Vec<String> },
81 UnknownReference { path: String, reference: String },
84}
85
86impl fmt::Display for ComposeError {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 match self {
89 Self::Cycle { cycle } => {
90 write!(f, "reference cycle: ")?;
91 for path in cycle {
92 write!(f, "{path} -> ")?;
93 }
94 write!(f, "{}", cycle.first().map_or("", String::as_str))
95 }
96 Self::UnknownReference { path, reference } => {
97 write!(f, "node {path} references unknown node {reference}")
98 }
99 }
100 }
101}
102
103impl Error for ComposeError {}
104
105#[derive(Debug, Clone)]
107pub struct Composition {
108 heads: IndexMap<String, Arc<ComposedNode>>,
110 roots: Vec<String>,
111}
112
113impl Composition {
114 pub fn roots(&self) -> &[String] {
117 &self.roots
118 }
119
120 pub fn get(&self, path: &str) -> Option<&Arc<ComposedNode>> {
123 let (head, tail) = split_head(path);
124 self.heads.get(head)?.descendant(tail)
125 }
126
127 pub fn root(&self) -> ComposedNode {
130 ComposedNode {
131 path: String::new(),
132 attributes: IndexMap::new(),
133 children: self
134 .roots
135 .iter()
136 .map(|root| (root.clone(), self.heads[root.as_str()].clone()))
137 .collect(),
138 }
139 }
140}
141
142fn split_head(path: &str) -> (&str, &str) {
144 path.split_once('/').unwrap_or((path, ""))
145}
146
147struct Head<'a> {
149 node: Option<&'a FlatNode>,
151 edits: Vec<(&'a str, &'a FlatNode)>,
153 deps: Vec<usize>,
155}
156
157impl<'a> Head<'a> {
158 fn nodes(&self) -> impl Iterator<Item = &'a FlatNode> + '_ {
159 self.node.into_iter().chain(self.edits.iter().map(|e| e.1))
160 }
161}
162
163fn references(node: &FlatNode) -> impl Iterator<Item = &str> {
164 node.inherits
165 .values()
166 .chain(node.children.values().flatten())
167 .map(String::as_str)
168}
169
170pub fn compose(flat: &IndexMap<String, FlatNode>) -> Result<Composition, ComposeError> {
224 let mut heads: IndexMap<&str, Head<'_>> = IndexMap::new();
225 for (path, node) in flat {
226 let (head, _) = split_head(path);
227 let entry = heads.entry(head).or_insert_with(|| Head {
228 node: None,
229 edits: Vec::new(),
230 deps: Vec::new(),
231 });
232 if head.len() == path.len() {
233 entry.node = Some(node);
234 } else {
235 entry.edits.push((path.as_str(), node));
236 }
237 }
238
239 let mut referenced = vec![false; heads.len()];
240 for index in 0..heads.len() {
241 let mut deps: Vec<usize> = heads[index]
242 .nodes()
243 .flat_map(references)
244 .filter_map(|reference| heads.get_index_of(split_head(reference).0))
245 .collect();
246 deps.sort_unstable();
247 deps.dedup();
248 for &dep in &deps {
249 referenced[dep] = true;
250 }
251 heads[index].deps = deps;
252 }
253
254 let order = topological_order(&heads)?;
255
256 let mut composed: Vec<Option<Arc<ComposedNode>>> = vec![None; heads.len()];
257 for index in order {
258 let (path, head) = heads.get_index(index).expect("index from heads");
259 let node = compose_head(path, head, &heads, &composed)?;
260 composed[index] = Some(Arc::new(node));
261 }
262
263 let roots = heads
264 .iter()
265 .enumerate()
266 .filter(|(index, (_, head))| head.node.is_some() && !referenced[*index])
267 .map(|(_, (path, _))| (*path).to_owned())
268 .collect();
269 let heads = heads
270 .keys()
271 .zip(composed)
272 .map(|(path, node)| ((*path).to_owned(), node.expect("every head composed")))
273 .collect();
274 Ok(Composition { heads, roots })
275}
276
277fn topological_order(heads: &IndexMap<&str, Head<'_>>) -> Result<Vec<usize>, ComposeError> {
280 const NEW: u8 = 0;
281 const ACTIVE: u8 = 1;
282 const DONE: u8 = 2;
283 let mut state = vec![NEW; heads.len()];
284 let mut order = Vec::with_capacity(heads.len());
285 let mut stack: Vec<(usize, usize)> = Vec::new();
287 for start in 0..heads.len() {
288 if state[start] != NEW {
289 continue;
290 }
291 state[start] = ACTIVE;
292 stack.push((start, 0));
293 while let Some((index, next)) = stack.last_mut() {
294 let deps = &heads[*index].deps;
295 if let Some(&dep) = deps.get(*next) {
296 *next += 1;
297 match state[dep] {
298 NEW => {
299 state[dep] = ACTIVE;
300 stack.push((dep, 0));
301 }
302 ACTIVE => {
303 let from = stack.iter().position(|(i, _)| *i == dep).expect("active");
304 let cycle = stack[from..]
305 .iter()
306 .map(|(i, _)| (*heads.get_index(*i).expect("index").0).to_owned())
307 .collect();
308 return Err(ComposeError::Cycle { cycle });
309 }
310 _ => {}
311 }
312 } else {
313 state[*index] = DONE;
314 order.push(*index);
315 stack.pop();
316 }
317 }
318 }
319 Ok(order)
320}
321
322fn compose_head(
323 path: &str,
324 head: &Head<'_>,
325 heads: &IndexMap<&str, Head<'_>>,
326 composed: &[Option<Arc<ComposedNode>>],
327) -> Result<ComposedNode, ComposeError> {
328 let mut node = ComposedNode {
329 path: path.to_owned(),
330 attributes: IndexMap::new(),
331 children: IndexMap::new(),
332 };
333 if let Some(flat) = head.node {
334 add_data(&mut node, path, flat, heads, composed)?;
335 }
336
337 let mut edits: Vec<&(&str, &FlatNode)> = head.edits.iter().collect();
341 edits.sort_by_key(|(edit_path, _)| edit_path.matches('/').count());
342 for (edit_path, flat) in edits {
343 let names = split_head(edit_path).1;
344 let mut target = &node;
345 let mut found = true;
346 for name in names.split('/') {
347 match target.children.get(name) {
348 Some(child) => target = child,
349 None => {
350 found = false;
351 break;
352 }
353 }
354 }
355 if !found {
356 continue;
357 }
358 let mut target = &mut node;
359 for name in names.split('/') {
360 let child = target.children.get_mut(name).expect("checked above");
361 target = Arc::make_mut(child);
362 }
363 add_data(target, edit_path, flat, heads, composed)?;
364 }
365 Ok(node)
366}
367
368fn add_data(
370 node: &mut ComposedNode,
371 path: &str,
372 flat: &FlatNode,
373 heads: &IndexMap<&str, Head<'_>>,
374 composed: &[Option<Arc<ComposedNode>>],
375) -> Result<(), ComposeError> {
376 let resolve = |reference: &str| -> Result<&Arc<ComposedNode>, ComposeError> {
377 let (head, tail) = split_head(reference);
378 heads
379 .get_index_of(head)
380 .and_then(|index| composed[index].as_ref())
381 .and_then(|head| head.descendant(tail))
382 .ok_or_else(|| ComposeError::UnknownReference {
383 path: path.to_owned(),
384 reference: reference.to_owned(),
385 })
386 };
387
388 for reference in flat.inherits.values() {
389 let inherited = resolve(reference)?;
390 for (name, child) in &inherited.children {
391 node.children.insert(name.clone(), child.clone());
392 }
393 for (name, value) in &inherited.attributes {
394 node.attributes.insert(name.clone(), value.clone());
395 }
396 }
397 for (name, child) in &flat.children {
398 match child {
399 Some(reference) => {
400 let child = resolve(reference)?.clone();
401 node.children.insert(name.clone(), child);
402 }
403 None => {
406 node.children.shift_remove(name);
407 }
408 }
409 }
410 for (name, value) in &flat.attributes {
411 node.attributes.insert(name.clone(), value.clone());
412 }
413 Ok(())
414}