use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolFunction {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub parameters: serde_json::Value,
}
impl ToolFunction {
pub fn new(name: impl Into<String>, parameters: serde_json::Value) -> Self {
Self {
name: name.into(),
description: None,
parameters,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn no_params(name: impl Into<String>) -> Self {
Self::new(
name,
serde_json::json!({
"type": "object",
"properties": {}
}),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_tool_function_new() {
let params = json!({
"type": "object",
"properties": {
"x": {"type": "number"}
}
});
let func = ToolFunction::new("test", params.clone());
assert_eq!(func.name, "test");
assert!(func.description.is_none());
assert_eq!(func.parameters, params);
}
#[test]
fn test_tool_function_with_description() {
let func = ToolFunction::new("test", json!({})).with_description("A test function");
assert_eq!(func.description, Some("A test function".to_string()));
}
#[test]
fn test_tool_function_no_params() {
let func = ToolFunction::no_params("get_time");
assert_eq!(func.name, "get_time");
assert_eq!(func.parameters["type"], "object");
assert!(func.parameters["properties"].is_object());
}
#[test]
fn test_tool_function_serialize() {
let func = ToolFunction::new(
"get_weather",
json!({
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}),
)
.with_description("Get weather");
let json = serde_json::to_value(&func).unwrap();
assert_eq!(json["name"], "get_weather");
assert_eq!(json["description"], "Get weather");
assert_eq!(json["parameters"]["type"], "object");
assert_eq!(
json["parameters"]["properties"]["location"]["type"],
"string"
);
}
#[test]
fn test_tool_function_serialize_no_description() {
let func = ToolFunction::new("test", json!({}));
let json = serde_json::to_value(&func).unwrap();
assert!(json.get("description").is_none()); }
#[test]
fn test_tool_function_deserialize() {
let json = r#"{
"name": "calculate",
"description": "Evaluate math",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string"}
}
}
}"#;
let func: ToolFunction = serde_json::from_str(json).unwrap();
assert_eq!(func.name, "calculate");
assert_eq!(func.description, Some("Evaluate math".to_string()));
}
#[test]
fn test_tool_function_deserialize_minimal() {
let json = r#"{
"name": "simple",
"parameters": {}
}"#;
let func: ToolFunction = serde_json::from_str(json).unwrap();
assert_eq!(func.name, "simple");
assert!(func.description.is_none());
}
#[test]
fn test_tool_function_clone() {
let func = ToolFunction::new("test", json!({"a": 1})).with_description("desc");
let cloned = func.clone();
assert_eq!(func, cloned);
}
#[test]
fn test_tool_function_equality() {
let func1 = ToolFunction::new("a", json!({}));
let func2 = ToolFunction::new("a", json!({}));
let func3 = ToolFunction::new("b", json!({}));
assert_eq!(func1, func2);
assert_ne!(func1, func3);
}
#[test]
fn test_tool_function_complex_params() {
let func = ToolFunction::new(
"create_user",
json!({
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1},
"age": {"type": "integer", "minimum": 0, "maximum": 150},
"email": {"type": "string", "format": "email"},
"roles": {
"type": "array",
"items": {"type": "string", "enum": ["admin", "user", "guest"]}
}
},
"required": ["name", "email"]
}),
);
assert_eq!(func.parameters["properties"]["name"]["type"], "string");
assert_eq!(func.parameters["properties"]["age"]["minimum"], 0);
assert_eq!(func.parameters["required"][0], "name");
}
#[test]
fn test_tool_function_into_string() {
let func = ToolFunction::new(String::from("owned"), json!({}));
assert_eq!(func.name, "owned");
let func = ToolFunction::new("borrowed", json!({}));
assert_eq!(func.name, "borrowed");
}
}