use sim_kernel::Expr;
use sim_lib_view::SurfaceCaps;
pub fn render_scene(scene: &Expr, caps: &SurfaceCaps) -> String {
let reduced = sim_lib_view::codec::reduce_for_caps(scene, caps);
render_node(&reduced).join("\n")
}
fn render_node(node: &Expr) -> Vec<String> {
let Some(kind) = sim_lib_scene::node_kind(node) else {
return vec![atom_text(node)];
};
match &*kind.name {
"text" => vec![text_content(node)],
"stack" | "box" | "overlay" => render_children(node),
"grid" | "table" => render_rows(node),
"field" => vec![render_field(node)],
"button" => vec![format!("[{}]", field_text(node, "label"))],
"badge" => vec![format!(
"<{}: {}>",
field_text(node, "status"),
field_text(node, "label")
)],
other => vec![format!("[{other}]")],
}
}
fn render_children(node: &Expr) -> Vec<String> {
let mut lines = Vec::new();
if let Some(Expr::List(items) | Expr::Vector(items)) =
sim_value::access::field(node, "children")
{
for child in items {
lines.extend(render_node(child));
}
}
lines
}
fn render_rows(node: &Expr) -> Vec<String> {
let mut lines = Vec::new();
if let Some(Expr::List(cols) | Expr::Vector(cols)) = sim_value::access::field(node, "columns") {
lines.push(join_cells(cols));
}
if let Some(Expr::List(rows) | Expr::Vector(rows)) = sim_value::access::field(node, "rows") {
for row in rows {
lines.push(render_row(row));
}
}
lines
}
fn render_row(row: &Expr) -> String {
match row {
Expr::List(cells) | Expr::Vector(cells) => join_cells(cells),
Expr::Map(entries) => entries
.iter()
.map(|(_, value)| atom_text(value))
.collect::<Vec<_>>()
.join(" | "),
atom => atom_text(atom),
}
}
fn join_cells(cells: &[Expr]) -> String {
cells.iter().map(atom_text).collect::<Vec<_>>().join(" | ")
}
fn render_field(node: &Expr) -> String {
let value = field_text(node, "value");
match sim_value::access::field_str(node, "label") {
Some(label) => format!("{label}: {value}"),
None => value,
}
}
fn text_content(node: &Expr) -> String {
for key in ["text", "content"] {
if let Some(value) = sim_value::access::field(node, key) {
return atom_text(value);
}
}
String::new()
}
fn field_text(node: &Expr, name: &str) -> String {
sim_value::access::field(node, name)
.map(atom_text)
.unwrap_or_default()
}
fn atom_text(value: &Expr) -> String {
match value {
Expr::Nil => "nil".to_owned(),
Expr::Bool(flag) => flag.to_string(),
Expr::Number(number) => number.canonical.clone(),
Expr::String(text) => text.clone(),
Expr::Symbol(symbol) | Expr::Local(symbol) => symbol.as_qualified_str(),
Expr::Bytes(bytes) => format!("#bytes({})", bytes.len()),
Expr::List(items) | Expr::Vector(items) | Expr::Set(items) => {
items.iter().map(atom_text).collect::<Vec<_>>().join(" ")
}
Expr::Map(entries) => entries
.iter()
.map(|(key, value)| format!("{}={}", atom_text(key), atom_text(value)))
.collect::<Vec<_>>()
.join(" "),
other => format!("<{}>", sim_value::kind::expr_kind(other)),
}
}