use std::future::Future;
use std::pin::Pin;
use async_trait::async_trait;
use rmcp::model::CallToolResult;
use super::ParamStruct;
use super::handler_context::HandlerContext;
use super::response_builder::ResponseBuilder;
use crate::error::Result;
pub type HandlerResult<'a, T> = Pin<Box<dyn Future<Output = Result<T>> + Send + 'a>>;
#[derive(Debug)]
pub struct ToolResult<T, P = ()> {
pub result: Result<T>,
pub params: Option<P>,
}
pub(super) fn call_with_typed_params<O, P, F, Fut>(
context: HandlerContext,
f: F,
) -> HandlerResult<'static, ToolResult<O, P>>
where
O: ResultStruct + Send + Sync + 'static,
P: ParamStruct + Clone + for<'de> serde::Deserialize<'de> + Send + 'static,
F: FnOnce(HandlerContext, P) -> Fut + Send + 'static,
Fut: Future<Output = Result<O>> + Send + 'static,
{
Box::pin(async move {
let params: P = super::extract_parameter_values(&context)?;
let result = f(context, params.clone()).await;
Ok(ToolResult {
result,
params: Some(params),
})
})
}
#[async_trait]
pub trait ToolFn: Send + Sync {
type Output: ResultStruct + Send + Sync;
type Params: ParamStruct;
async fn handle_impl(&self, _: Self::Params) -> Result<Self::Output> {
unimplemented!("Must implement handle_impl")
}
fn call(
&self,
context: HandlerContext,
) -> HandlerResult<'_, ToolResult<Self::Output, Self::Params>> {
Box::pin(async move {
let params: Self::Params = super::extract_parameter_values(&context)?;
let result = self.handle_impl(params).await;
Ok(ToolResult {
result,
params: None, })
})
}
}
pub trait ErasedToolFn: Send + Sync {
fn call_erased<'a>(
&'a self,
context: HandlerContext,
) -> Pin<Box<dyn Future<Output = CallToolResult> + Send + 'a>>;
}
impl<T: ToolFn> ErasedToolFn for T {
fn call_erased<'a>(
&'a self,
context: HandlerContext,
) -> Pin<Box<dyn Future<Output = CallToolResult> + Send + 'a>> {
Box::pin(async move {
let result = self.call(context.clone()).await;
result.map_or_else(
|error| context.format_framework_error(error),
|tool_result| context.format_result(tool_result),
)
})
}
}
pub trait ResultStruct: Send + Sync {
fn add_response_fields(&self, builder: ResponseBuilder) -> Result<ResponseBuilder>;
fn get_message_template(&self) -> Result<&str>;
}