openai_tools/common/
parameters.rs1use serde::{de::Error, Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4
5pub type Name = String;
6
7#[derive(Debug, Clone, Default, Deserialize, Serialize)]
8pub struct ParameterProperty {
9 #[serde(rename = "type")]
10 pub type_name: String,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub description: Option<String>,
13 #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
14 pub enum_values: Option<Vec<String>>,
15}
16
17impl TryFrom<Value> for ParameterProperty {
18 type Error = serde_json::Error;
19 fn try_from(value: Value) -> Result<Self, serde_json::Error> {
20 let props = value.as_object().ok_or_else(|| serde_json::Error::custom("Expected an object"))?;
21 let type_name = {
22 if let Some(type_name) = props.get("type") {
23 if let Some(type_name) = type_name.as_str() {
24 type_name.to_string()
25 } else {
26 return Err(serde_json::Error::custom("Expected 'type' to be a string"));
27 }
28 } else {
29 return Err(serde_json::Error::custom("Missing 'type' field"));
30 }
31 };
32 let description = props.get("description").and_then(Value::as_str).map(|s| s.to_string());
33 let enum_values = {
34 if let Some(enum_value) = props.get("enum") {
35 if let Some(enum_array) = enum_value.as_array() {
36 Some(enum_array.iter().filter_map(Value::as_str).map(|s| s.to_string()).collect::<Vec<String>>())
37 } else {
38 return Err(serde_json::Error::custom("Expected 'enum' to be an array"));
39 }
40 } else {
41 None
42 }
43 };
44 Ok(Self { type_name, description, enum_values })
45 }
46}
47
48impl From<ParameterProperty> for Value {
49 fn from(prop: ParameterProperty) -> Self {
50 let mut map = serde_json::Map::new();
51
52 map.insert("type".to_string(), Value::String(prop.type_name.clone()));
54
55 if let Some(desc) = &prop.description {
57 map.insert("description".to_string(), Value::String(desc.clone()));
58 }
59
60 if let Some(enum_values) = prop.enum_values {
62 map.insert("enum".to_string(), Value::Array(enum_values.iter().map(|s| Value::String(s.clone())).collect()));
63 }
64 Value::Object(map)
65 }
66}
67
68impl ParameterProperty {
69 pub fn from_string<T: AsRef<str>>(description: T) -> Self {
70 Self { type_name: "string".into(), description: Some(description.as_ref().to_string()), enum_values: None }
71 }
72 pub fn from_number<T: AsRef<str>>(description: T) -> Self {
73 Self { type_name: "number".into(), description: Some(description.as_ref().to_string()), enum_values: None }
74 }
75 pub fn from_boolean<T: AsRef<str>>(description: T) -> Self {
76 Self { type_name: "boolean".into(), description: Some(description.as_ref().to_string()), enum_values: None }
77 }
78 pub fn from_integer<T: AsRef<str>>(description: T) -> Self {
79 Self { type_name: "integer".into(), description: Some(description.as_ref().to_string()), enum_values: None }
80 }
81 pub fn add_enum_values<T: AsRef<str>>(&mut self, values: Vec<T>) -> Self {
82 self.enum_values = Some(values.into_iter().map(|v| v.as_ref().to_string()).collect());
83 self.clone()
84 }
85}
86
87#[derive(Debug, Clone, Default, Deserialize, Serialize)]
88pub struct Parameters {
89 #[serde(rename = "type")]
90 pub type_name: String,
91 pub properties: HashMap<Name, ParameterProperty>,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub required: Option<Vec<Name>>,
94 #[serde(rename = "additionalProperties", skip_serializing_if = "Option::is_none")]
95 pub additional_properties: Option<bool>,
96}
97
98impl Parameters {
99 pub fn new<T: AsRef<str>>(properties: Vec<(T, ParameterProperty)>, additional_properties: Option<bool>) -> Self {
100 let props = properties.iter().map(|(k, v)| (k.as_ref().to_string(), v.clone())).collect::<HashMap<String, ParameterProperty>>();
101 let required = properties.iter().map(|(k, _)| k.as_ref().to_string()).collect::<Vec<_>>();
102 Self { type_name: "object".into(), properties: props, required: Some(required), additional_properties }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn test_parameter_property_serialization() {
112 let prop = ParameterProperty::from_string("A string parameter");
113 let serialized = serde_json::to_string(&prop).unwrap();
114 let deserialized = serde_json::from_str::<ParameterProperty>(&serialized).unwrap();
115 assert_eq!(deserialized.type_name, "string");
116 assert_eq!(deserialized.description, Some("A string parameter".to_string()));
117
118 let prop = ParameterProperty::from_integer("An integer parameter");
119 let serialized = serde_json::to_string(&prop).unwrap();
120 let deserialized = serde_json::from_str::<ParameterProperty>(&serialized).unwrap();
121 assert_eq!(deserialized.type_name, "integer");
122 assert_eq!(deserialized.description, Some("An integer parameter".to_string()));
123
124 let prop = ParameterProperty::from_boolean("A boolean parameter");
125 let serialized = serde_json::to_string(&prop).unwrap();
126 let deserialized = serde_json::from_str::<ParameterProperty>(&serialized).unwrap();
127 assert_eq!(deserialized.type_name, "boolean");
128 assert_eq!(deserialized.description, Some("A boolean parameter".to_string()));
129
130 let prop = ParameterProperty::from_number("A number parameter");
131 let serialized = serde_json::to_string(&prop).unwrap();
132 let deserialized = serde_json::from_str::<ParameterProperty>(&serialized).unwrap();
133 assert_eq!(deserialized.type_name, "number");
134 assert_eq!(deserialized.description, Some("A number parameter".to_string()));
135 }
136
137 #[test]
138 fn test_parameter_property_enum_serialization() {
139 let prop = ParameterProperty::from_string("An enum parameter").add_enum_values(vec!["value1", "value2", "value3"]);
140 let serialized = serde_json::to_string(&prop).unwrap();
141 let deserialized = serde_json::from_str::<ParameterProperty>(&serialized).unwrap();
142 assert_eq!(deserialized.type_name, "string");
143 assert_eq!(deserialized.enum_values, Some(vec!["value1".to_string(), "value2".to_string(), "value3".to_string()]));
144 assert_eq!(deserialized.description, Some("An enum parameter".to_string()));
145 }
146}