gent/runtime/
validation.rs1use crate::interpreter::OutputSchema;
4use crate::parser::ast::FieldType;
5use serde_json::Value as JsonValue;
6
7pub fn validate_output(json: &JsonValue, schema: &OutputSchema) -> Result<(), String> {
9 let obj = json.as_object().ok_or("Expected JSON object")?;
10
11 for field in &schema.fields {
12 let value = obj
13 .get(&field.name)
14 .ok_or_else(|| format!("missing required field: '{}'", field.name))?;
15
16 validate_field_type(value, &field.field_type, &field.name)?;
17 }
18
19 Ok(())
20}
21
22fn validate_field_type(value: &JsonValue, expected: &FieldType, path: &str) -> Result<(), String> {
23 match expected {
24 FieldType::String => {
25 if !value.is_string() {
26 return Err(format!(
27 "'{}': expected string, got {}",
28 path,
29 json_type_name(value)
30 ));
31 }
32 }
33 FieldType::Number => {
34 if !value.is_number() {
35 return Err(format!(
36 "'{}': expected number, got {}",
37 path,
38 json_type_name(value)
39 ));
40 }
41 }
42 FieldType::Boolean => {
43 if !value.is_boolean() {
44 return Err(format!(
45 "'{}': expected boolean, got {}",
46 path,
47 json_type_name(value)
48 ));
49 }
50 }
51 FieldType::Array(inner) => {
52 let arr = value.as_array().ok_or_else(|| {
53 format!("'{}': expected array, got {}", path, json_type_name(value))
54 })?;
55 for (i, item) in arr.iter().enumerate() {
56 validate_field_type(item, inner, &format!("{}[{}]", path, i))?;
57 }
58 }
59 FieldType::Object(fields) => {
60 let obj = value.as_object().ok_or_else(|| {
61 format!("'{}': expected object, got {}", path, json_type_name(value))
62 })?;
63 for field in fields {
64 let field_value = obj
65 .get(&field.name)
66 .ok_or_else(|| format!("'{}.{}': missing required field", path, field.name))?;
67 validate_field_type(
68 field_value,
69 &field.field_type,
70 &format!("{}.{}", path, field.name),
71 )?;
72 }
73 }
74 FieldType::Named(_) => {
75 if !value.is_object() {
78 return Err(format!(
79 "'{}': expected object, got {}",
80 path,
81 json_type_name(value)
82 ));
83 }
84 }
85 }
86 Ok(())
87}
88
89fn json_type_name(value: &JsonValue) -> &'static str {
90 match value {
91 JsonValue::Null => "null",
92 JsonValue::Bool(_) => "boolean",
93 JsonValue::Number(_) => "number",
94 JsonValue::String(_) => "string",
95 JsonValue::Array(_) => "array",
96 JsonValue::Object(_) => "object",
97 }
98}