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:?}"); } }