use serde_json::Value;
use crate::types::UiNode;
pub const READ_ONLY_FIELDS: &[&str] = crate::layout_tree::FORM_FIELDS;
pub const READ_ONLY_WRITE_PREFIXES: &[&str] = &["records.", "actions."];
pub fn call_writes(name: &str) -> bool {
READ_ONLY_WRITE_PREFIXES.iter().any(|p| name.starts_with(p))
}
pub fn action_writes(action: &Value) -> bool {
action.get("functionCall").and_then(|f| f.get("call")).and_then(Value::as_str).is_some_and(call_writes)
}
pub fn disables(component: &str, on: Option<&indexmap::IndexMap<String, Value>>) -> bool {
match on {
Some(on) if !on.is_empty() => on.values().any(action_writes),
_ => READ_ONLY_FIELDS.contains(&component),
}
}
fn already_disabled(node: &UiNode) -> bool {
node.props.get("disabled") == Some(&Value::Bool(true))
}
pub fn read_only_tree(node: &UiNode) -> UiNode {
let mut out = node.clone();
apply(&mut out);
out
}
fn apply(node: &mut UiNode) {
if disables(&node.component, node.on.as_ref()) && !already_disabled(node) {
node.props.insert("disabled".into(), Value::Bool(true));
}
if let Some(slots) = node.slots.as_mut() {
slots.values_mut().for_each(apply);
}
node.children.iter_mut().for_each(apply);
}
pub fn disabled_ids(node: &UiNode) -> Vec<String> {
let mut out = Vec::new();
collect(node, &mut out);
out
}
fn collect(node: &UiNode, out: &mut Vec<String>) {
if disables(&node.component, node.on.as_ref()) && !already_disabled(node) {
out.push(node.id.clone());
}
for s in node.slots.iter().flat_map(|s| s.values()) {
collect(s, out);
}
for c in &node.children {
collect(c, out);
}
}