#[cfg(feature = "server")]
use crate::app::handler::{FromHandlerParams, GenericHandler, Handler, HandlerParams};
#[cfg(feature = "server")]
use crate::error::Error;
#[cfg(feature = "server")]
use futures_util::future::BoxFuture;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt::Debug;
#[cfg(feature = "server")]
use std::sync::Arc;
use crate::types::{
Annotations, Cursor, Icon, IntoResponse, Page, RequestId, Response, resource::Uri,
};
#[cfg(feature = "server")]
use crate::types::{FromRequest, ReadResourceRequestParams, ReadResourceResult, Request};
#[derive(Clone, Serialize, Deserialize)]
pub struct ResourceTemplate {
#[serde(rename = "uriTemplate")]
pub uri_template: Uri,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
pub descr: Option<String>,
#[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
pub mime: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub annotations: Option<Annotations>,
#[serde(skip_serializing_if = "Option::is_none")]
pub icons: Option<Vec<Icon>>,
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub meta: Option<Value>,
#[serde(skip)]
#[cfg(feature = "http-server")]
pub(crate) roles: Option<Vec<String>>,
#[serde(skip)]
#[cfg(feature = "http-server")]
pub(crate) permissions: Option<Vec<String>>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ListResourceTemplatesRequestParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<Cursor>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ListResourceTemplatesResult {
#[serde(rename = "resourceTemplates")]
pub templates: Vec<ResourceTemplate>,
#[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
}
impl IntoResponse for ListResourceTemplatesResult {
#[inline]
fn into_response(self, req_id: RequestId) -> Response {
match serde_json::to_value(self) {
Ok(v) => Response::success(req_id, v),
Err(err) => Response::error(req_id, err.into()),
}
}
}
impl From<Vec<ResourceTemplate>> for ListResourceTemplatesResult {
#[inline]
fn from(templates: Vec<ResourceTemplate>) -> Self {
Self {
next_cursor: None,
templates,
}
}
}
impl From<Page<'_, ResourceTemplate>> for ListResourceTemplatesResult {
#[inline]
fn from(page: Page<'_, ResourceTemplate>) -> Self {
Self {
next_cursor: page.next_cursor,
templates: page.items.to_vec(),
}
}
}
#[cfg(feature = "server")]
impl FromHandlerParams for ListResourceTemplatesRequestParams {
#[inline]
fn from_params(params: &HandlerParams) -> Result<Self, Error> {
let req = Request::from_params(params)?;
Self::from_request(req)
}
}
impl ListResourceTemplatesResult {
#[inline]
pub fn new() -> Self {
Default::default()
}
}
#[cfg(feature = "server")]
pub(crate) struct ResourceFunc<F, R, Args>
where
F: GenericHandler<Args, Output = R>,
R: TryInto<ReadResourceResult>,
Args: TryFrom<ReadResourceRequestParams, Error = Error>,
{
func: F,
_marker: std::marker::PhantomData<Args>,
}
#[cfg(feature = "server")]
impl<F, R, Args> ResourceFunc<F, R, Args>
where
F: GenericHandler<Args, Output = R>,
R: TryInto<ReadResourceResult>,
Args: TryFrom<ReadResourceRequestParams, Error = Error>,
{
pub(crate) fn new(func: F) -> Arc<Self> {
let func = Self {
func,
_marker: std::marker::PhantomData,
};
Arc::new(func)
}
}
#[cfg(feature = "server")]
impl<F, R, Args> Handler<ReadResourceResult> for ResourceFunc<F, R, Args>
where
F: GenericHandler<Args, Output = R>,
R: TryInto<ReadResourceResult>,
R::Error: Into<Error>,
Args: TryFrom<ReadResourceRequestParams, Error = Error> + Send + Sync,
{
#[inline]
fn call(&self, params: HandlerParams) -> BoxFuture<'_, Result<ReadResourceResult, Error>> {
let HandlerParams::Resource(params) = params else {
unreachable!()
};
Box::pin(async move {
let args = Args::try_from(params)?;
self.func.call(args).await.try_into().map_err(Into::into)
})
}
}
impl Debug for ResourceTemplate {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ResourceTemplate")
.field("uri_template", &self.uri_template)
.field("name", &self.name)
.field("title", &self.title)
.field("descr", &self.descr)
.field("mime", &self.mime)
.field("annotations", &self.annotations)
.field("meta", &self.meta)
.finish()
}
}
#[cfg(feature = "server")]
impl ResourceTemplate {
#[inline]
pub fn new<U: Into<Uri>, S: Into<String>>(uri: U, name: S) -> Self {
Self {
uri_template: uri.into(),
name: name.into(),
title: None,
mime: None,
descr: None,
annotations: None,
meta: None,
icons: None,
#[cfg(feature = "http-server")]
roles: None,
#[cfg(feature = "http-server")]
permissions: None,
}
}
pub fn with_title(&mut self, title: impl Into<String>) -> &mut Self {
self.title = Some(title.into());
self
}
pub fn with_description(&mut self, description: &str) -> &mut Self {
self.descr = Some(description.into());
self
}
pub fn with_mime(&mut self, mime: &str) -> &mut Self {
self.mime = Some(mime.into());
self
}
pub fn with_annotations<F>(&mut self, config: F) -> &mut Self
where
F: FnOnce(Annotations) -> Annotations,
{
self.annotations = Some(config(Default::default()));
self
}
#[cfg(feature = "http-server")]
pub fn with_roles<T, I>(&mut self, roles: T) -> &mut Self
where
T: IntoIterator<Item = I>,
I: Into<String>,
{
self.roles = Some(roles.into_iter().map(Into::into).collect());
self
}
#[cfg(feature = "http-server")]
pub fn with_permissions<T, I>(&mut self, permissions: T) -> &mut Self
where
T: IntoIterator<Item = I>,
I: Into<String>,
{
self.permissions = Some(permissions.into_iter().map(Into::into).collect());
self
}
pub fn with_icons(&mut self, icons: impl IntoIterator<Item = Icon>) -> &mut Self {
self.icons = Some(icons.into_iter().collect());
self
}
}
#[cfg(test)]
mod tests {}