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