///|
/// Layout JSON serialization helpers.
///|
/// Sanitize a number for JSON output (handle Infinity and NaN)
fn safe_number(n : Double) -> Double {
// Check for Infinity or very large numbers (>= 1e10 is likely a placeholder infinity)
if n >= 1.0e10 || n <= -1.0e10 {
return 0.0
}
// Check for NaN (NaN != NaN)
if n != n {
return 0.0
}
n
}
///|
/// Convert a number to JSON string safely
fn number_to_json(n : Double) -> String {
safe_number(n).to_string()
}
///|
/// Write a number to the JSON buffer safely.
fn write_number_json(buf : StringBuilder, n : Double) -> Unit {
buf.write_string(number_to_json(n))
}
///|
/// Write Rect JSON directly to the output buffer to avoid temporary strings.
fn write_rect_json(buf : StringBuilder, rect : @types.Rect[Double]) -> Unit {
buf.write_string("{\"top\":")
write_number_json(buf, rect.top)
buf.write_string(",\"right\":")
write_number_json(buf, rect.right)
buf.write_string(",\"bottom\":")
write_number_json(buf, rect.bottom)
buf.write_string(",\"left\":")
write_number_json(buf, rect.left)
buf.write_string("}")
}
///|
/// Convert layout to JSON string (with Box Model)
pub fn layout_to_json(layout : @layout_types.Layout) -> String {
let buf = StringBuilder::new(size_hint=estimate_layout_json_size(layout))
layout_to_json_impl(layout, buf)
buf.to_string()
}
///|
fn estimate_layout_json_size(layout : @layout_types.Layout) -> Int {
let mut estimated = 120 + layout.id.length() * 2
for child in layout.children {
estimated = estimated + estimate_layout_json_size(child) + 2
}
estimated
}
///|
fn layout_to_json_impl(
layout : @layout_types.Layout,
buf : StringBuilder,
) -> Unit {
buf.write_string("{\"id\":\"")
buf.write_string(escape_json_string(layout.id))
buf.write_string("\",\"x\":")
write_number_json(buf, layout.x)
buf.write_string(",\"y\":")
write_number_json(buf, layout.y)
buf.write_string(",\"width\":")
write_number_json(buf, layout.width)
buf.write_string(",\"height\":")
write_number_json(buf, layout.height)
buf.write_string(",\"margin\":")
write_rect_json(buf, layout.margin)
buf.write_string(",\"padding\":")
write_rect_json(buf, layout.padding)
buf.write_string(",\"border\":")
write_rect_json(buf, layout.border)
buf.write_string(",\"children\":[")
for i, child in layout.children {
if i > 0 {
buf.write_string(",")
}
layout_to_json_impl(child, buf)
}
buf.write_string("]}")
}
///|
/// Escape special characters in JSON string
fn escape_json_string(s : String) -> String {
let buf = StringBuilder::new()
for c in s {
match c {
'"' => buf.write_string("\\\"")
'\\' => buf.write_string("\\\\")
'\n' => buf.write_string("\\n")
'\r' => buf.write_string("\\r")
'\t' => buf.write_string("\\t")
_ => buf.write_char(c)
}
}
buf.to_string()
}