1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
7#[serde(rename_all = "lowercase")]
8pub enum ParameterType {
9 #[default]
11 String,
12 Integer,
14 Number,
16 Boolean,
18 Array,
20 Object,
22}
23
24impl ParameterType {
25 pub fn as_str(&self) -> &'static str {
27 match self {
28 Self::String => "string",
29 Self::Integer => "integer",
30 Self::Number => "number",
31 Self::Boolean => "boolean",
32 Self::Array => "array",
33 Self::Object => "object",
34 }
35 }
36
37 pub fn matches(&self, value: &serde_json::Value) -> bool {
39 match (self, value) {
40 (Self::String, serde_json::Value::String(_)) => true,
41 (Self::Integer, serde_json::Value::Number(n)) => n.is_i64() || n.is_u64(),
42 (Self::Number, serde_json::Value::Number(_)) => true,
43 (Self::Boolean, serde_json::Value::Bool(_)) => true,
44 (Self::Array, serde_json::Value::Array(_)) => true,
45 (Self::Object, serde_json::Value::Object(_)) => true,
46 _ => false,
47 }
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub struct Parameter {
54 pub name: String,
56
57 #[serde(rename = "type", default)]
59 pub param_type: ParameterType,
60
61 #[serde(default)]
63 pub required: bool,
64
65 #[serde(default)]
67 pub description: String,
68
69 #[serde(default, skip_serializing_if = "Option::is_none")]
71 pub default: Option<serde_json::Value>,
72
73 #[serde(default, skip_serializing_if = "Vec::is_empty")]
75 pub enum_values: Vec<serde_json::Value>,
76}
77
78impl Parameter {
79 pub fn new(name: impl Into<String>) -> Self {
81 Self {
82 name: name.into(),
83 param_type: ParameterType::default(),
84 required: false,
85 description: String::new(),
86 default: None,
87 enum_values: Vec::new(),
88 }
89 }
90
91 pub fn builder(name: impl Into<String>) -> ParameterBuilder {
93 ParameterBuilder::new(name)
94 }
95
96 pub fn required_string(name: impl Into<String>) -> Self {
98 Self {
99 name: name.into(),
100 param_type: ParameterType::String,
101 required: true,
102 description: String::new(),
103 default: None,
104 enum_values: Vec::new(),
105 }
106 }
107
108 pub fn optional_string(name: impl Into<String>) -> Self {
110 Self {
111 name: name.into(),
112 param_type: ParameterType::String,
113 required: false,
114 description: String::new(),
115 default: None,
116 enum_values: Vec::new(),
117 }
118 }
119}
120
121#[derive(Debug, Default)]
123pub struct ParameterBuilder {
124 name: String,
125 param_type: ParameterType,
126 required: bool,
127 description: String,
128 default: Option<serde_json::Value>,
129 enum_values: Vec<serde_json::Value>,
130}
131
132impl ParameterBuilder {
133 pub fn new(name: impl Into<String>) -> Self {
135 Self {
136 name: name.into(),
137 ..Default::default()
138 }
139 }
140
141 pub fn param_type(mut self, param_type: ParameterType) -> Self {
143 self.param_type = param_type;
144 self
145 }
146
147 pub fn required(mut self, required: bool) -> Self {
149 self.required = required;
150 self
151 }
152
153 pub fn description(mut self, description: impl Into<String>) -> Self {
155 self.description = description.into();
156 self
157 }
158
159 pub fn default(mut self, default: serde_json::Value) -> Self {
161 self.default = Some(default);
162 self
163 }
164
165 pub fn enum_value(mut self, value: serde_json::Value) -> Self {
167 self.enum_values.push(value);
168 self
169 }
170
171 pub fn build(self) -> Parameter {
173 Parameter {
174 name: self.name,
175 param_type: self.param_type,
176 required: self.required,
177 description: self.description,
178 default: self.default,
179 enum_values: self.enum_values,
180 }
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use serde_json::json;
188
189 #[test]
190 fn parameter_type_as_str() {
191 assert_eq!(ParameterType::String.as_str(), "string");
192 assert_eq!(ParameterType::Integer.as_str(), "integer");
193 assert_eq!(ParameterType::Number.as_str(), "number");
194 assert_eq!(ParameterType::Boolean.as_str(), "boolean");
195 assert_eq!(ParameterType::Array.as_str(), "array");
196 assert_eq!(ParameterType::Object.as_str(), "object");
197 }
198
199 #[test]
200 fn parameter_type_matches_string() {
201 assert!(ParameterType::String.matches(&json!("hello")));
202 assert!(!ParameterType::String.matches(&json!(123)));
203 assert!(!ParameterType::String.matches(&json!(true)));
204 }
205
206 #[test]
207 fn parameter_type_matches_integer() {
208 assert!(ParameterType::Integer.matches(&json!(123)));
209 assert!(ParameterType::Integer.matches(&json!(-456)));
210 assert!(!ParameterType::Integer.matches(&json!(1.5)));
211 assert!(!ParameterType::Integer.matches(&json!("123")));
212 }
213
214 #[test]
215 fn parameter_type_matches_number() {
216 assert!(ParameterType::Number.matches(&json!(123)));
217 assert!(ParameterType::Number.matches(&json!(1.5)));
218 assert!(ParameterType::Number.matches(&json!(-3.14)));
219 assert!(!ParameterType::Number.matches(&json!("1.5")));
220 }
221
222 #[test]
223 fn parameter_type_matches_boolean() {
224 assert!(ParameterType::Boolean.matches(&json!(true)));
225 assert!(ParameterType::Boolean.matches(&json!(false)));
226 assert!(!ParameterType::Boolean.matches(&json!("true")));
227 assert!(!ParameterType::Boolean.matches(&json!(1)));
228 }
229
230 #[test]
231 fn parameter_type_matches_array() {
232 assert!(ParameterType::Array.matches(&json!([1, 2, 3])));
233 assert!(ParameterType::Array.matches(&json!([])));
234 assert!(!ParameterType::Array.matches(&json!({"a": 1})));
235 }
236
237 #[test]
238 fn parameter_type_matches_object() {
239 assert!(ParameterType::Object.matches(&json!({"a": 1})));
240 assert!(ParameterType::Object.matches(&json!({})));
241 assert!(!ParameterType::Object.matches(&json!([1, 2])));
242 }
243
244 #[test]
245 fn parameter_new() {
246 let param = Parameter::new("test");
247 assert_eq!(param.name, "test");
248 assert_eq!(param.param_type, ParameterType::String);
249 assert!(!param.required);
250 assert!(param.description.is_empty());
251 assert!(param.default.is_none());
252 }
253
254 #[test]
255 fn parameter_required_string() {
256 let param = Parameter::required_string("username");
257 assert_eq!(param.name, "username");
258 assert_eq!(param.param_type, ParameterType::String);
259 assert!(param.required);
260 }
261
262 #[test]
263 fn parameter_optional_string() {
264 let param = Parameter::optional_string("limit");
265 assert_eq!(param.name, "limit");
266 assert_eq!(param.param_type, ParameterType::String);
267 assert!(!param.required);
268 }
269
270 #[test]
271 fn parameter_builder() {
272 let param = Parameter::builder("count")
273 .param_type(ParameterType::Integer)
274 .required(true)
275 .description("Number of items")
276 .default(json!(10))
277 .build();
278
279 assert_eq!(param.name, "count");
280 assert_eq!(param.param_type, ParameterType::Integer);
281 assert!(param.required);
282 assert_eq!(param.description, "Number of items");
283 assert_eq!(param.default, Some(json!(10)));
284 }
285
286 #[test]
287 fn parameter_builder_with_enum() {
288 let param = Parameter::builder("format")
289 .param_type(ParameterType::String)
290 .enum_value(json!("json"))
291 .enum_value(json!("yaml"))
292 .enum_value(json!("toml"))
293 .build();
294
295 assert_eq!(param.enum_values.len(), 3);
296 assert!(param.enum_values.contains(&json!("json")));
297 }
298
299 #[test]
300 fn parameter_serialization() {
301 let param = Parameter::builder("path")
302 .param_type(ParameterType::String)
303 .required(true)
304 .description("File path")
305 .build();
306
307 let json = serde_json::to_string(¶m).unwrap();
308 let parsed: Parameter = serde_json::from_str(&json).unwrap();
309
310 assert_eq!(param, parsed);
311 }
312
313 #[test]
314 fn parameter_deserialization_with_defaults() {
315 let json = r#"{"name": "test"}"#;
316 let param: Parameter = serde_json::from_str(json).unwrap();
317
318 assert_eq!(param.name, "test");
319 assert_eq!(param.param_type, ParameterType::String);
320 assert!(!param.required);
321 }
322
323 #[test]
324 fn parameter_type_matches_edge_cases() {
325 let param_type = ParameterType::Integer;
327 assert!(param_type.matches(&json!(42)));
328 assert!(param_type.matches(&json!(-42)));
329 assert!(param_type.matches(&json!(9223372036854775807i64)));
330 assert!(!param_type.matches(&json!(3.14)));
331 assert!(!param_type.matches(&json!("42")));
332
333 let param_type = ParameterType::Number;
335 assert!(param_type.matches(&json!(42)));
336 assert!(param_type.matches(&json!(3.14)));
337 assert!(param_type.matches(&json!(-42)));
338 assert!(!param_type.matches(&json!("42")));
339
340 let param_type = ParameterType::Boolean;
342 assert!(param_type.matches(&json!(true)));
343 assert!(param_type.matches(&json!(false)));
344 assert!(!param_type.matches(&json!(1)));
345 assert!(!param_type.matches(&json!("true")));
346
347 let param_type = ParameterType::Array;
349 assert!(param_type.matches(&json!([])));
350 assert!(param_type.matches(&json!([1, 2, 3])));
351 assert!(!param_type.matches(&json!({})));
352 assert!(!param_type.matches(&json!("[]")));
353
354 let param_type = ParameterType::Object;
356 assert!(param_type.matches(&json!({})));
357 assert!(param_type.matches(&json!({"key": "value"})));
358 assert!(!param_type.matches(&json!([])));
359 assert!(!param_type.matches(&json!("{}")));
360 }
361
362 #[test]
363 fn parameter_enum_validation() {
364 let param = Parameter::builder("status")
365 .param_type(ParameterType::String)
366 .enum_value(json!("active"))
367 .enum_value(json!("inactive"))
368 .enum_value(json!("pending"))
369 .build();
370
371 assert_eq!(param.enum_values.len(), 3);
372 assert!(param.enum_values.contains(&json!("active")));
373 assert!(param.enum_values.contains(&json!("inactive")));
374 assert!(param.enum_values.contains(&json!("pending")));
375 }
376
377 #[test]
378 fn parameter_builder_complex() {
379 let param = Parameter::builder("data")
380 .param_type(ParameterType::Object)
381 .required(true)
382 .description("Complex data structure")
383 .default(json!({"nested": {"key": "value"}}))
384 .enum_value(json!({"type": "default"}))
385 .enum_value(json!({"type": "custom"}))
386 .build();
387
388 assert_eq!(param.name, "data");
389 assert_eq!(param.param_type, ParameterType::Object);
390 assert!(param.required);
391 assert_eq!(param.description, "Complex data structure");
392 assert_eq!(param.default, Some(json!({"nested": {"key": "value"}})));
393 assert_eq!(param.enum_values.len(), 2);
394 }
395
396 #[test]
397 fn parameter_serialization_roundtrip() {
398 let original = Parameter::builder("test_param")
399 .param_type(ParameterType::Integer)
400 .required(true)
401 .description("Test description")
402 .default(json!(42))
403 .build();
404
405 let serialized = serde_json::to_string(&original).unwrap();
406 let deserialized: Parameter = serde_json::from_str(&serialized).unwrap();
407
408 assert_eq!(original.name, deserialized.name);
409 assert_eq!(original.param_type, deserialized.param_type);
410 assert_eq!(original.required, deserialized.required);
411 assert_eq!(original.description, deserialized.description);
412 assert_eq!(original.default, deserialized.default);
413 }
414}