use std::fmt::Write;
use crate::ir::ast::{Node, Object, Array, Value, ValueData};
const INDENT_UNIT: &str = " ";
pub fn to_string(node: &Node) -> String {
let mut buf = String::new();
write_node(&mut buf, node, 0);
buf
}
fn write_node(buf: &mut String, node: &Node, indent: usize) {
match node {
Node::Value(v) => write_value(buf, v),
Node::Object(o) => write_object(buf, o, indent),
Node::Array(a) => write_array(buf, a, indent),
}
}
fn write_value(buf: &mut String, val: &Value) {
match &val.data {
ValueData::Bool(b) => {
buf.push_str(if *b { "true" } else { "false" });
}
ValueData::String(s) => {
let needs_quotes = val.tag.as_ref()
.map(|t| t.value_type.needs_quotes())
.unwrap_or(false);
if needs_quotes {
write_quoted_string(buf, s);
} else {
buf.push_str(s);
}
}
ValueData::Int(i) => {
write!(buf, "{}", i).unwrap();
}
ValueData::Uint(u) => {
write!(buf, "{}", u).unwrap();
}
ValueData::Float(f) => {
write!(buf, "{}", f).unwrap();
}
ValueData::Bytes(b) => {
write_quoted_string(buf, &format!("{:?}", b));
}
ValueData::Null => {
buf.push_str("null");
}
}
}
fn write_quoted_string(buf: &mut String, s: &str) {
buf.push('"');
for c in s.chars() {
match c {
'"' => buf.push_str("\\\""),
'\\' => buf.push_str("\\\\"),
'\n' => buf.push_str("\\n"),
'\r' => buf.push_str("\\r"),
'\t' => buf.push_str("\\t"),
_ => buf.push(c),
}
}
buf.push('"');
}
fn write_object(buf: &mut String, obj: &Object, indent: usize) {
buf.push_str("{\n");
for (i, field) in obj.fields.iter().enumerate() {
write_indent(buf, indent + 1);
if let Some(tag) = field.value.get_tag() {
let tag_str = tag.to_string();
if !tag_str.is_empty() {
buf.push_str("// ");
buf.push_str(&tag_str);
buf.push('\n');
write_indent(buf, indent + 1);
}
}
write_quoted_string(buf, &field.key);
buf.push_str(": ");
write_node(buf, &field.value, indent + 1);
if i < obj.fields.len() - 1 {
buf.push(',');
}
buf.push('\n');
}
write_indent(buf, indent);
buf.push('}');
}
fn write_array(buf: &mut String, arr: &Array, indent: usize) {
buf.push_str("[\n");
for (i, item) in arr.items.iter().enumerate() {
write_indent(buf, indent + 1);
write_node(buf, item, indent + 1);
if i < arr.items.len() - 1 {
buf.push(',');
}
buf.push('\n');
}
write_indent(buf, indent);
buf.push(']');
}
fn write_indent(buf: &mut String, indent: usize) {
for _ in 0..indent {
buf.push_str(INDENT_UNIT);
}
}