use crate::value::Concrete;
use crate::Value;
pub const MAX_RENDER_DEPTH: usize = 128;
pub const DEEP_SENTINEL: &str = "<...>";
#[must_use]
pub fn escape_str(s: &str) -> String {
s.replace('\\', "\\\\").replace('"', "\\\"")
}
pub fn render_tree(v: &Value) -> Result<String, String> {
render_tree_at(v, 0)
}
fn render_tree_at(v: &Value, depth: usize) -> Result<String, String> {
if depth >= MAX_RENDER_DEPTH {
return Ok(DEEP_SENTINEL.to_string());
}
let c = crate::eval::force_concrete(v).map_err(|e| e.to_string())?;
Ok(match c {
Concrete::Null => "null".to_string(),
Concrete::Bool(b) => b.to_string(),
Concrete::Int(n) => n.to_string(),
Concrete::Float(f) => sui_compat::versions::cppnix_format_float(f),
Concrete::String(s) => {
let mut out = String::from("\"");
out.push_str(&escape_str(&s));
out.push('"');
out
}
Concrete::Path(p) => p.to_string(),
Concrete::List(items) => {
let mut out = String::from("[ ");
for item in items.iter() {
out.push_str(&render_tree_at(item, depth + 1)?);
out.push(' ');
}
out.push(']');
out
}
Concrete::Attrs(attrs) => {
let mut out = String::from("{ ");
for (k, v) in attrs.iter() {
out.push_str(&k);
out.push_str(" = ");
out.push_str(&render_tree_at(v, depth + 1)?);
out.push_str("; ");
}
out.push('}');
out
}
Concrete::Lambda(_) => "<<lambda>>".to_string(),
Concrete::Builtin(b) => {
let mut out = String::from("<<builtin ");
out.push_str(b.name);
out.push_str(">>");
out
}
})
}