openai_tools/common/
parameters.rs

1use 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        // type
53        map.insert("type".to_string(), Value::String(prop.type_name.clone()));
54
55        // description
56        if let Some(desc) = &prop.description {
57            map.insert("description".to_string(), Value::String(desc.clone()));
58        }
59
60        // enum
61        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}