use std::future::Future;
use serde::de::DeserializeOwned;
use turbomcp_core::{MaybeSend, MaybeSync};
use super::response::{IntoToolResponse, ToolError};
use super::types::{PromptResult, ResourceResult};
pub trait ToolHandlerFn: MaybeSend + MaybeSync + 'static {
type Args: DeserializeOwned + schemars::JsonSchema + 'static;
type Output: IntoToolResponse;
type Future: Future<Output = Self::Output> + MaybeSend + 'static;
fn name(&self) -> &str;
fn description(&self) -> &str;
fn call(&self, args: Self::Args) -> Self::Future;
}
pub trait ResourceHandlerFn: MaybeSend + MaybeSync + 'static {
type Output: IntoResourceResponse;
type Future: Future<Output = Self::Output> + MaybeSend + 'static;
fn uri(&self) -> &str;
fn name(&self) -> &str;
fn description(&self) -> &str;
fn read(&self, uri: String) -> Self::Future;
}
pub trait PromptHandlerFn: MaybeSend + MaybeSync + 'static {
type Args: DeserializeOwned + schemars::JsonSchema + 'static;
type Output: IntoPromptResponse;
type Future: Future<Output = Self::Output> + MaybeSend + 'static;
fn name(&self) -> &str;
fn description(&self) -> &str;
fn get(&self, args: Option<Self::Args>) -> Self::Future;
}
pub trait IntoResourceResponse {
fn into_resource_response(self) -> Result<ResourceResult, String>;
}
impl IntoResourceResponse for ResourceResult {
#[inline]
fn into_resource_response(self) -> Result<ResourceResult, String> {
Ok(self)
}
}
impl<E: std::fmt::Display> IntoResourceResponse for Result<ResourceResult, E> {
fn into_resource_response(self) -> Result<ResourceResult, String> {
self.map_err(|e| e.to_string())
}
}
pub trait IntoPromptResponse {
fn into_prompt_response(self) -> Result<PromptResult, String>;
}
impl IntoPromptResponse for PromptResult {
#[inline]
fn into_prompt_response(self) -> Result<PromptResult, String> {
Ok(self)
}
}
impl<E: std::fmt::Display> IntoPromptResponse for Result<PromptResult, E> {
fn into_prompt_response(self) -> Result<PromptResult, String> {
self.map_err(|e| e.to_string())
}
}
pub trait ResultExt<T, E> {
fn map_tool_err(self, context: &str) -> Result<T, ToolError>;
}
impl<T, E: std::fmt::Display> ResultExt<T, E> for Result<T, E> {
fn map_tool_err(self, context: &str) -> Result<T, ToolError> {
self.map_err(|e| ToolError::new(format!("{}: {}", context, e)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_result_ext_map_tool_err() {
let result: Result<(), &str> = Err("original error");
let mapped = result.map_tool_err("context");
assert!(mapped.is_err());
let err = mapped.unwrap_err();
assert!(err.to_string().contains("context"));
assert!(err.to_string().contains("original error"));
}
#[test]
fn test_into_resource_response_ok() {
let result = ResourceResult::text("uri", "content");
let response = result.into_resource_response();
assert!(response.is_ok());
}
#[test]
fn test_into_resource_response_result_ok() {
let result: Result<ResourceResult, String> = Ok(ResourceResult::text("uri", "content"));
let response = result.into_resource_response();
assert!(response.is_ok());
}
#[test]
fn test_into_resource_response_result_err() {
let result: Result<ResourceResult, String> = Err("failed".into());
let response = result.into_resource_response();
assert!(response.is_err());
assert_eq!(response.unwrap_err(), "failed");
}
#[test]
fn test_into_prompt_response_ok() {
let result = PromptResult::user("hello");
let response = result.into_prompt_response();
assert!(response.is_ok());
}
#[test]
fn test_into_prompt_response_result_err() {
let result: Result<PromptResult, ToolError> = Err(ToolError::new("failed"));
let response = result.into_prompt_response();
assert!(response.is_err());
}
}