use std::collections::HashMap; #[derive(serde::Deserialize)] struct Margin { top: isize, bottom: isize, left: isize, right: isize, } #[derive(serde::Deserialize)] struct Node { id: usize, behaviour: String, child_arrangement: String, width_policy: [usize; 3], height_policy: [usize; 3], halign: String, valign: String, margin: Margin, table_cell: Option<[usize; 2]>, render_area: Option<[[usize; 2]; 2]>, children: Vec, } fn main() { let mut args = std::env::args(); let execname = args.next().unwrap(); if args.len() != 1 { println!("usage: {} path-to-json", execname); return; } let fname = args.next().unwrap(); let indata = std::fs::read(fname).expect("couldn't open file"); let nodetree: Node = serde_json::from_slice(&indata).expect("couldn't parse file"); let mut x_pos: HashMap = HashMap::new(); let mut y_pos: HashMap = HashMap::new(); let mut parents: HashMap = HashMap::new(); let mut siblings: HashMap = HashMap::new(); let mut dfs = vec![&nodetree]; let mut max_x = 0; while let Some(node) = dfs.pop() { println!("visiting node {}", node.id); // X coordinate: // case 1: no sibling, so same x as parent // case 2: sibling, so use max_x + 1 // Y coordinate: // always use parent's y pos + 1 let x = match siblings.get(&node.id) { None => parents .get(&node.id) .and_then(|p| x_pos.get(p)) .cloned() .unwrap_or(0), Some(_sib) => max_x + 1, }; let y = parents .get(&node.id) .and_then(|p| y_pos.get(p)) .map(|v| v + 1) .unwrap_or(0); max_x = max_x.max(x); x_pos.insert(node.id, x); y_pos.insert(node.id, y); let mut sibling: Option = None; for ch in node.children.iter() { parents.insert(ch.id, node.id); if let Some(sibling) = sibling { siblings.insert(ch.id, sibling); } sibling = Some(ch.id); dfs.push(ch); } } let mut yvalues = y_pos.values().collect::>(); yvalues.sort(); yvalues.dedup(); for y in yvalues { let mut line = y_pos .iter() .filter(|(_, v)| *v == y) .map(|(k, _)| (x_pos.get(k).unwrap(), k)) .collect::>(); line.sort(); println!("{line:?}"); } }