1use crate::value::VmValue;
2
3mod api;
4mod canonicalize;
5mod limits;
6mod result;
7mod transform;
8mod type_check;
9mod validate;
10
11pub(crate) use api::{
12 canonical_param_schema, schema_assert_canonical_param, schema_assert_param,
13 schema_expect_value, schema_extend_value, schema_from_json_schema_value,
14 schema_from_openapi_schema_value, schema_is_value, schema_omit_value, schema_partial_value,
15 schema_pick_value, schema_report_value, schema_result_value, schema_to_json_schema_value,
16 schema_to_openapi_schema_value, CanonicalParamSchema,
17};
18pub use canonicalize::json_to_vm_value;
19
20pub fn elicitation_validate_schema(schema: &VmValue) -> Result<VmValue, crate::value::VmError> {
25 schema_from_json_schema_value(schema)
26}
27
28pub fn elicitation_validate(
32 data: &VmValue,
33 schema: &VmValue,
34) -> Result<VmValue, crate::value::VmError> {
35 schema_expect_value(data, schema, false)
36}
37
38pub(crate) const BYTES_B64_TAG: &str = "$bytes_b64";
39
40pub(crate) fn tagged_bytes_json(bytes: &[u8]) -> serde_json::Value {
41 use base64::Engine;
42
43 serde_json::json!({
44 BYTES_B64_TAG: base64::engine::general_purpose::STANDARD.encode(bytes),
45 })
46}
47
48fn vm_value_to_serde_json(value: &VmValue) -> serde_json::Value {
49 match value {
50 VmValue::Nil => serde_json::Value::Null,
51 VmValue::Bool(value) => serde_json::Value::Bool(*value),
52 VmValue::Int(value) => serde_json::json!(value),
53 VmValue::Float(value) => serde_json::json!(value),
54 VmValue::String(value) => serde_json::Value::String(value.to_string()),
55 VmValue::Bytes(bytes) => tagged_bytes_json(bytes),
56 VmValue::List(items) => {
57 serde_json::Value::Array(items.iter().map(vm_value_to_serde_json).collect())
58 }
59 VmValue::Set(set) => {
60 serde_json::Value::Array(set.iter().map(vm_value_to_serde_json).collect())
61 }
62 VmValue::Dict(items) => serde_json::Value::Object(
63 items
64 .iter()
65 .map(|(key, value)| (key.to_string(), vm_value_to_serde_json(value)))
66 .collect(),
67 ),
68 _ => serde_json::Value::String(value.display()),
69 }
70}
71
72fn schema_bool(schema: &crate::value::DictMap, key: &str) -> bool {
73 matches!(schema.get(key), Some(VmValue::Bool(true)))
74}
75
76fn schema_i64(schema: &crate::value::DictMap, key: &str) -> Option<i64> {
77 match schema.get(key) {
78 Some(VmValue::Int(value)) => Some(*value),
79 _ => None,
80 }
81}
82
83fn schema_number(schema: &crate::value::DictMap, key: &str) -> Option<f64> {
84 match schema.get(key) {
85 Some(VmValue::Int(value)) => Some(*value as f64),
86 Some(VmValue::Float(value)) => Some(*value),
87 _ => None,
88 }
89}
90
91fn child_path(path: &str, key: &str) -> String {
92 if path.is_empty() {
93 key.to_string()
94 } else {
95 format!("{path}.{key}")
96 }
97}
98
99fn index_path(path: &str, index: usize) -> String {
100 if path.is_empty() {
101 format!("[{index}]")
102 } else {
103 format!("{path}[{index}]")
104 }
105}
106
107#[cfg(test)]
108mod tests;