use a2ui::core::message_processor::MessageProcessor;
use a2ui::core::validate::{
analyze_topology, parse_and_fix, validate_component_integrity, RefFieldSpec, RELAXED_VALIDATION,
ROOT_ID, STRICT_VALIDATION, ValidationConfig, ValidationReport,
};
use serde_json::{json, Value};
fn main() {
println!("A2UI validate — examples\n");
let valid = json!([
{ "id": "root", "component": "Column", "children": ["title", "body"] },
{ "id": "title", "component": "Text", "text": "Hi" },
{ "id": "body", "component": "Text", "text": "World" }
]);
print_section("1. Valid payload (STRICT)");
run_checks(&valid, STRICT_VALIDATION);
let messy = json!([
{ "id": "root", "component": "Column", "child": "root" }, { "id": "dup", "component": "Text", "text": "first" },
{ "id": "dup", "component": "Text", "text": "second" }, { "id": "orphan","component": "Text", "text": "lost" } ]);
print_section("2. Messy payload (STRICT) — duplicate / self-ref / orphan");
run_checks(&messy, STRICT_VALIDATION);
let partial = json!([
{ "id": "root", "component": "Column", "children": ["already_here"] }
]);
print_section("3. Partial update under STRICT (dangling flagged)");
run_checks(&partial, STRICT_VALIDATION);
print_section("4. Same partial update under RELAXED (tolerated)");
run_checks(&partial, RELAXED_VALIDATION);
print_section("5. parse_and_fix — smart quotes + trailing comma");
let malformed = "[{\u{201C}id\u{201D}: \u{201C}root\u{201D}, \u{201C}component\u{201D}: \u{201C}Text\u{201D},}]";
match parse_and_fix(malformed) {
Ok(items) => println!(" healed → {} item(s): {:#}", items.len(), items[0]),
Err(e) => println!(" still broken: {e}"),
}
print_section("6. MessageProcessor opt-in validation");
let mut proc = MessageProcessor::new(vec![]).with_validation(STRICT_VALIDATION);
let create = json!({
"version": "v1.0",
"createSurface": { "surfaceId": "demo", "catalogId": "demo" }
});
let update = json!({
"version": "v1.0",
"updateComponents": {
"surfaceId": "demo",
"components": [
{ "id": "root", "component": "Column", "children": ["missing_child"] }
]
}
});
proc.process_message(MessageProcessor::parse_message(&create.to_string()).unwrap())
.unwrap();
proc.process_message(MessageProcessor::parse_message(&update.to_string()).unwrap())
.unwrap();
let loaded = proc.model.get_surface("demo").is_some();
println!(" surface loaded despite bad ref? {loaded}");
let report = proc.drain_validation();
print_report(&report, " ");
println!("\nDone.");
}
fn run_checks(components: &Value, cfg: ValidationConfig) {
let arr: &[Value] = components.as_array().expect("components is an array");
let spec = RefFieldSpec::DEFAULT;
let mut report = validate_component_integrity(
arr,
&spec,
ROOT_ID,
cfg.allow_dangling_references,
cfg.allow_missing_root,
);
let (_visited, topo) = analyze_topology(
arr,
&spec,
ROOT_ID,
cfg.allow_orphan_components,
cfg.allow_missing_root,
);
report.extend(topo);
print_report(&report, " ");
}
fn print_report(report: &ValidationReport, indent: &str) {
if report.is_empty() {
println!("{indent}✓ no problems found");
return;
}
for err in &report.errors {
let where_ = err.component_id.as_deref().unwrap_or("—");
println!("{indent}• {:?} [{}]", err.code, where_);
println!("{indent} {}", err.message);
}
}
fn print_section(title: &str) {
println!("── {title} ──");
}