1use serde::{Deserialize, Serialize};
4use serde_json::{Value, json};
5use std::collections::HashMap;
6
7#[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#[derive(Debug, Clone)]
34struct Property {
35 prop_type: PropertyType,
36 description: String,
37 required: bool,
38}
39
40#[derive(Debug, Clone, Default)]
42pub struct ToolSchema {
43 properties: HashMap<String, Property>,
44 property_order: Vec<String>,
45}
46
47impl ToolSchema {
48 pub fn builder() -> ToolSchemaBuilder {
50 ToolSchemaBuilder::default()
51 }
52
53 pub fn has_property(&self, name: &str) -> bool {
55 self.properties.contains_key(name)
56 }
57
58 pub fn is_required(&self, name: &str) -> bool {
60 self.properties.get(name).is_some_and(|p| p.required)
61 }
62
63 pub fn property_count(&self) -> usize {
65 self.properties.len()
66 }
67
68 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#[derive(Debug, Default)]
95pub struct ToolSchemaBuilder {
96 properties: HashMap<String, Property>,
97 property_order: Vec<String>,
98}
99
100impl ToolSchemaBuilder {
101 #[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 #[must_use]
125 pub fn build(self) -> ToolSchema {
126 ToolSchema {
127 properties: self.properties,
128 property_order: self.property_order,
129 }
130 }
131}