Skip to main content

erio_tools/
schema.rs

1//! Tool schema types for JSON Schema generation.
2
3use serde::{Deserialize, Serialize};
4use serde_json::{Value, json};
5use std::collections::HashMap;
6
7/// JSON Schema property types.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "lowercase")]
10pub enum PropertyType {
11    String,
12    Integer,
13    Number,
14    Boolean,
15    Array,
16    Object,
17}
18
19impl PropertyType {
20    fn as_str(self) -> &'static str {
21        match self {
22            Self::String => "string",
23            Self::Integer => "integer",
24            Self::Number => "number",
25            Self::Boolean => "boolean",
26            Self::Array => "array",
27            Self::Object => "object",
28        }
29    }
30}
31
32/// A property definition in a tool schema.
33#[derive(Debug, Clone)]
34struct Property {
35    prop_type: PropertyType,
36    description: String,
37    required: bool,
38}
39
40/// Schema describing tool parameters as JSON Schema.
41#[derive(Debug, Clone, Default)]
42pub struct ToolSchema {
43    properties: HashMap<String, Property>,
44    property_order: Vec<String>,
45}
46
47impl ToolSchema {
48    /// Creates a new schema builder.
49    pub fn builder() -> ToolSchemaBuilder {
50        ToolSchemaBuilder::default()
51    }
52
53    /// Returns `true` if the schema has the given property.
54    pub fn has_property(&self, name: &str) -> bool {
55        self.properties.contains_key(name)
56    }
57
58    /// Returns `true` if the given property is required.
59    pub fn is_required(&self, name: &str) -> bool {
60        self.properties.get(name).is_some_and(|p| p.required)
61    }
62
63    /// Returns the number of properties in the schema.
64    pub fn property_count(&self) -> usize {
65        self.properties.len()
66    }
67
68    /// Converts the schema to a JSON Schema object.
69    pub fn to_json_schema(&self) -> Value {
70        let mut properties = json!({});
71        let mut required = Vec::new();
72
73        for name in &self.property_order {
74            if let Some(prop) = self.properties.get(name) {
75                properties[name] = json!({
76                    "type": prop.prop_type.as_str(),
77                    "description": prop.description,
78                });
79                if prop.required {
80                    required.push(name.clone());
81                }
82            }
83        }
84
85        json!({
86            "type": "object",
87            "properties": properties,
88            "required": required,
89        })
90    }
91}
92
93/// Builder for constructing tool schemas.
94#[derive(Debug, Default)]
95pub struct ToolSchemaBuilder {
96    properties: HashMap<String, Property>,
97    property_order: Vec<String>,
98}
99
100impl ToolSchemaBuilder {
101    /// Adds a property to the schema.
102    #[must_use]
103    pub fn property(
104        mut self,
105        name: impl Into<String>,
106        prop_type: PropertyType,
107        description: impl Into<String>,
108        required: bool,
109    ) -> Self {
110        let name = name.into();
111        self.property_order.push(name.clone());
112        self.properties.insert(
113            name,
114            Property {
115                prop_type,
116                description: description.into(),
117                required,
118            },
119        );
120        self
121    }
122
123    /// Builds the schema.
124    #[must_use]
125    pub fn build(self) -> ToolSchema {
126        ToolSchema {
127            properties: self.properties,
128            property_order: self.property_order,
129        }
130    }
131}