use std::collections::HashSet;
use std::sync::Arc;
use indexmap::IndexMap;
use mumu::parser::types::Value;
use super::label::leaf_preview;
#[derive(Debug, Clone)]
pub struct Node {
pub name: String,
pub leaf: Option<String>,
pub children: Vec<Node>,
}
impl Node {
pub fn leaf(name: String, text: String) -> Self {
Self { name, leaf: Some(text), children: Vec::new() }
}
pub fn container(name: String, children: Vec<Node>) -> Self {
Self { name, leaf: None, children }
}
}
#[derive(Debug, Clone)]
pub struct WalkOpts {
pub quote_strings: bool,
pub show_types: bool,
pub index_labels: bool,
pub max_depth: Option<usize>, }
pub fn build_tree(root_label: &str, v: &Value, opts: &WalkOpts) -> Node {
let mut seen: HashSet<usize> = HashSet::new();
let children = descend(v, opts, 0, &mut seen);
Node::container(root_label.to_string(), children)
}
fn descend(v: &Value, opts: &WalkOpts, depth: usize, seen: &mut HashSet<usize>) -> Vec<Node> {
if let Some(limit) = opts.max_depth {
if depth >= limit {
return vec![Node::leaf("…".to_string(), String::new())];
}
}
match v {
Value::KeyedArray(map) => keyed_children(map, opts, depth, seen),
Value::MixedArray(items) => list_children(items, opts, depth, seen),
Value::IntArray(xs) => {
let items = xs.iter().map(|&n| Value::Int(n)).collect::<Vec<_>>();
list_children(&items, opts, depth, seen)
}
Value::FloatArray(xs) => {
let items = xs.iter().map(|&n| Value::Float(n)).collect::<Vec<_>>();
list_children(&items, opts, depth, seen)
}
Value::BoolArray(xs) => {
let items = xs.iter().map(|&b| Value::Bool(b)).collect::<Vec<_>>();
list_children(&items, opts, depth, seen)
}
Value::StrArray(xs) => {
let items = xs.iter().map(|s| Value::SingleString(s.clone())).collect::<Vec<_>>();
list_children(&items, opts, depth, seen)
}
Value::Int2DArray(rows) => {
let mut out = Vec::new();
for (ri, row) in rows.iter().enumerate() {
let label = if opts.index_labels { format!("[{}]", ri) } else { String::new() };
let row_items = row.iter().map(|&n| Value::Int(n)).collect::<Vec<_>>();
let kids = list_children(&row_items, opts, depth + 1, seen);
out.push(Node::container(label, kids));
}
out
}
Value::Float2DArray(rows) => {
let mut out = Vec::new();
for (ri, row) in rows.iter().enumerate() {
let label = if opts.index_labels { format!("[{}]", ri) } else { String::new() };
let row_items = row.iter().map(|&n| Value::Float(n)).collect::<Vec<_>>();
let kids = list_children(&row_items, opts, depth + 1, seen);
out.push(Node::container(label, kids));
}
out
}
Value::Ref(cell) => {
let raw: usize = Arc::as_ptr(cell) as usize;
if !seen.insert(raw) {
return vec![Node::leaf(String::new(), "↻".to_string())];
}
let inner = cell.lock().unwrap().clone();
let mut nodes = descend(&inner, opts, depth, seen);
seen.remove(&raw);
if nodes.is_empty() {
nodes.push(Node::leaf(String::new(), "↻".to_string()));
}
nodes
}
other => {
let text = leaf_preview(other, opts.quote_strings, opts.show_types);
vec![Node::leaf(String::new(), text)]
}
}
}
fn keyed_children(map: &IndexMap<String, Value>, opts: &WalkOpts, depth: usize, seen: &mut HashSet<usize>) -> Vec<Node> {
let mut out = Vec::new();
for (k, val) in map.iter() {
match val {
Value::KeyedArray(_)
| Value::MixedArray(_)
| Value::IntArray(_) | Value::FloatArray(_) | Value::BoolArray(_) | Value::StrArray(_)
| Value::Int2DArray(_) | Value::Float2DArray(_) => {
let kids = descend(val, opts, depth + 1, seen);
out.push(Node::container(k.clone(), kids));
}
Value::Ref(cell) => {
let raw: usize = Arc::as_ptr(cell) as usize;
if !seen.insert(raw) {
out.push(Node::leaf(k.clone(), "↻".to_string()));
continue;
}
let inner = cell.lock().unwrap().clone();
let kids = match &inner {
Value::KeyedArray(_)
| Value::MixedArray(_)
| Value::IntArray(_) | Value::FloatArray(_) | Value::BoolArray(_) | Value::StrArray(_)
| Value::Int2DArray(_) | Value::Float2DArray(_) => descend(&inner, opts, depth + 1, seen),
_ => {
vec![Node::leaf(String::new(), leaf_preview(&inner, opts.quote_strings, opts.show_types))]
}
};
seen.remove(&raw);
if kids.len() == 1 && kids[0].children.is_empty() && kids[0].name.is_empty() && kids[0].leaf.is_some() {
out.push(Node::leaf(k.clone(), kids[0].leaf.clone().unwrap()));
} else {
out.push(Node::container(k.clone(), kids));
}
}
_ => {
out.push(Node::leaf(k.clone(), leaf_preview(val, opts.quote_strings, opts.show_types)));
}
}
}
out
}
fn list_children(items: &[Value], opts: &WalkOpts, depth: usize, seen: &mut HashSet<usize>) -> Vec<Node> {
let mut out = Vec::new();
for (i, val) in items.iter().enumerate() {
let name = if opts.index_labels { format!("[{}]", i) } else { String::new() };
match val {
Value::KeyedArray(_)
| Value::MixedArray(_)
| Value::IntArray(_) | Value::FloatArray(_) | Value::BoolArray(_) | Value::StrArray(_)
| Value::Int2DArray(_) | Value::Float2DArray(_) => {
let kids = descend(val, opts, depth + 1, seen);
out.push(Node::container(name, kids));
}
Value::Ref(cell) => {
let raw: usize = Arc::as_ptr(cell) as usize;
if !seen.insert(raw) {
out.push(Node::leaf(name, "↻".to_string()));
continue;
}
let inner = cell.lock().unwrap().clone();
let kids = match &inner {
Value::KeyedArray(_)
| Value::MixedArray(_)
| Value::IntArray(_) | Value::FloatArray(_) | Value::BoolArray(_) | Value::StrArray(_)
| Value::Int2DArray(_) | Value::Float2DArray(_) => descend(&inner, opts, depth + 1, seen),
_ => vec![Node::leaf(String::new(), leaf_preview(&inner, opts.quote_strings, opts.show_types))],
};
seen.remove(&raw);
if kids.len() == 1 && kids[0].children.is_empty() && kids[0].name.is_empty() && kids[0].leaf.is_some() {
out.push(Node::leaf(name, kids[0].leaf.clone().unwrap()));
} else {
out.push(Node::container(name, kids));
}
}
_ => {
out.push(Node::leaf(name, leaf_preview(val, opts.quote_strings, opts.show_types)));
}
}
}
out
}