mod any_of;
mod array;
mod boolean;
mod enum_;
mod number;
mod object;
mod raw;
mod string;
pub use any_of::AnyOfSchema;
pub use array::ArraySchema;
pub use boolean::BooleanSchema;
pub use enum_::EnumSchema;
pub use number::NumberSchema;
pub use object::ObjectSchema;
pub use raw::RawSchema;
pub use string::StringSchema;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Schema {
String(StringSchema),
Number(NumberSchema),
Boolean(BooleanSchema),
Array(ArraySchema),
Object(ObjectSchema),
Enum(EnumSchema),
AnyOf(AnyOfSchema),
Raw(RawSchema),
}
impl Schema {
pub fn name(&self) -> &str {
match self {
Schema::String(s) => &s.name,
Schema::Number(s) => &s.name,
Schema::Boolean(s) => &s.name,
Schema::Array(s) => &s.name,
Schema::Object(s) => &s.name,
Schema::Enum(s) => &s.name,
Schema::AnyOf(s) => &s.name,
Schema::Raw(s) => &s.name,
}
}
}
pub fn to_json_schema(schema: &Schema) -> Value {
match schema {
Schema::String(s) => with_description(json!({"type": "string"}), s.description.as_deref()),
Schema::Number(s) => with_description(json!({"type": "number"}), s.description.as_deref()),
Schema::Boolean(s) => {
with_description(json!({"type": "boolean"}), s.description.as_deref())
}
Schema::Array(s) => {
let mut value = with_description(json!({"type": "array"}), s.description.as_deref());
value["items"] = to_json_schema(&s.items);
value
}
Schema::Object(s) if s.json_schema.is_some() => s
.json_schema
.clone()
.expect("just checked is_some via the match guard"),
Schema::Object(s) => {
let mut properties = serde_json::Map::new();
for prop in &s.properties {
properties.insert(prop.name().to_string(), to_json_schema(prop));
}
let mut value = with_description(
json!({
"type": "object",
"properties": properties,
"required": s.required,
}),
s.description.as_deref(),
);
value["additionalProperties"] = json!(s.allow_additional_properties);
value
}
Schema::Enum(s) => with_description(
json!({"type": "string", "enum": s.options}),
s.description.as_deref(),
),
Schema::AnyOf(s) => with_description(
json!({"anyOf": s.schemas.iter().map(to_json_schema).collect::<Vec<_>>()}),
s.description.as_deref(),
),
Schema::Raw(s) => s.json_schema.clone(),
}
}
fn with_description(mut value: Value, description: Option<&str>) -> Value {
if let Some(description) = description {
value["description"] = json!(description);
}
value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_json_schema_returns_a_raw_object_schema_unchanged() {
let raw = json!({
"type": "object",
"properties": {"anything": {"type": "string", "const": "not a Schema variant"}},
"oneOf": [{"required": ["a"]}, {"required": ["b"]}],
});
let schema = Schema::Object(ObjectSchema::from_raw_json_schema("weird", raw.clone()));
assert_eq!(to_json_schema(&schema), raw);
}
#[test]
fn from_raw_json_schema_still_sets_the_ordinary_name_field() {
let schema = ObjectSchema::from_raw_json_schema("my_tool", json!({"type": "object"}));
assert_eq!(schema.name, "my_tool");
}
}