use rsllm::tool;
use rsllm::tools::{ToolCall as ToolCallExec, ToolRegistry};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::error::Error;
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct CalcParams {
pub op: String, pub a: f64,
pub b: f64,
}
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct CalcResult {
pub result: f64,
}
#[tool(description = "Calculator: add, sub, mul, div")]
fn calc(p: CalcParams) -> Result<CalcResult, Box<dyn Error + Send + Sync>> {
let r = match p.op.as_str() {
"add" => p.a + p.b,
"sub" => p.a - p.b,
"mul" => p.a * p.b,
"div" if p.b != 0.0 => p.a / p.b,
"div" => return Err("Division by zero".into()),
_ => return Err("Unknown operation".into()),
};
Ok(CalcResult { result: r })
}
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct TaskParams {
pub action: String, pub desc: Option<String>,
pub id: Option<u32>,
}
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct TaskResult {
pub ok: bool,
pub msg: String,
pub data: Option<serde_json::Value>,
}
#[tool(description = "Manage tasks: create, list, done, delete")]
fn tasks(p: TaskParams) -> Result<TaskResult, Box<dyn Error + Send + Sync>> {
match p.action.as_str() {
"create" => Ok(TaskResult {
ok: true,
msg: format!("Created: {}", p.desc.unwrap_or_default()),
data: Some(json!({"id": 1})),
}),
"list" => Ok(TaskResult {
ok: true,
msg: "All tasks".into(),
data: Some(json!({"tasks": [], "count": 0})),
}),
"done" => Ok(TaskResult {
ok: true,
msg: format!("Completed task {}", p.id.unwrap_or(0)),
data: None,
}),
"delete" => Ok(TaskResult {
ok: true,
msg: format!("Deleted task {}", p.id.unwrap_or(0)),
data: None,
}),
_ => Err("Unknown action".into()),
}
}
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct TextParams {
pub text: String,
}
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct TextResult {
pub words: usize,
pub chars: usize,
pub lines: usize,
}
#[tool(description = "Analyze text statistics")]
fn analyze(p: TextParams) -> Result<TextResult, Box<dyn Error + Send + Sync>> {
Ok(TextResult {
words: p.text.split_whitespace().count(),
chars: p.text.len(),
lines: p.text.lines().count(),
})
}
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct ConvertParams {
pub data: String,
pub from: String,
pub to: String,
}
#[derive(JsonSchema, Serialize, Deserialize)]
pub struct ConvertResult {
pub output: String,
}
#[tool(description = "Convert data between formats")]
fn convert(p: ConvertParams) -> Result<ConvertResult, Box<dyn Error + Send + Sync>> {
Ok(ConvertResult {
output: format!("Converted {} from {} to {}", p.data, p.from, p.to),
})
}
fn main() -> Result<(), Box<dyn Error>> {
tracing::debug!("🚀 Simple Complete Application");
tracing::debug!("===============================\n");
let mut registry = ToolRegistry::new();
registry.register(Box::new(CalcTool))?;
registry.register(Box::new(TasksTool))?;
registry.register(Box::new(AnalyzeTool))?;
registry.register(Box::new(ConvertTool))?;
tracing::debug!("✅ Registered {} tools\n", registry.len());
tracing::debug!("🔧 Tool Executions:\n");
let r1 = registry.execute(&ToolCallExec::new(
"1",
"calc",
json!({"op": "mul", "a": 15, "b": 8}),
));
tracing::debug!(" Calculator: {}", r1.content);
let r2 = registry.execute(&ToolCallExec::new(
"2",
"tasks",
json!({"action": "create", "desc": "Review PR"}),
));
tracing::debug!(" Tasks: {}", r2.content);
let r3 = registry.execute(&ToolCallExec::new(
"3",
"analyze",
json!({"text": "Hello World!"}),
));
tracing::debug!(" Analyze: {}", r3.content);
let r4 = registry.execute(&ToolCallExec::new(
"4",
"convert",
json!({"data": "test", "from": "json", "to": "csv"}),
));
tracing::debug!(" Convert: {}", r4.content);
tracing::debug!("\n🔄 Batch Execution:\n");
let batch = vec![
ToolCallExec::new("b1", "calc", json!({"op": "add", "a": 100, "b": 50})),
ToolCallExec::new("b2", "analyze", json!({"text": "Batch test!"})),
];
for r in registry.execute_batch(&batch) {
tracing::debug!(
" {} → {}",
r.tool_name,
if r.success { "✅" } else { "❌" }
);
}
tracing::debug!("\n⚠️ Error Handling:\n");
let err = registry.execute(&ToolCallExec::new(
"e1",
"calc",
json!({"op": "div", "a": 10, "b": 0}),
));
tracing::debug!(" Division by zero: {}", err.error.unwrap_or_default());
tracing::debug!("\n📊 Summary:");
tracing::debug!(" Total code per tool: ~15-20 lines");
tracing::debug!(" Total code for 4 tools: ~80 lines");
tracing::debug!(" vs Old approach: ~300 lines");
tracing::debug!(" Savings: 75% less code!");
tracing::debug!("\n🎉 Simple, type-safe, automatic schema generation!");
Ok(())
}