use serde::{Deserialize, Serialize, Serializer};
use std::collections::HashMap;
use std::sync::Arc;
use serde_json::Value;
use log::warn;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Resource {
pub uri: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub annotations: Option<HashMap<String, Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<HashMap<String, Value>>,
}
#[derive(Clone)]
pub struct FunctionResource {
pub function: Arc<dyn Fn() -> Result<Value, String> + Send + Sync>,
pub uri: String,
pub name: String,
pub description: String,
pub mime_type: String,
pub tags: Vec<String>,
pub annotations: HashMap<String, Value>,
pub meta: Option<HashMap<String, Value>>,
}
impl FunctionResource {
#[allow(clippy::too_many_arguments)]
pub fn from_function<F>(
function: F,
uri: String,
name: Option<String>,
description: Option<String>,
mime_type: Option<String>,
tags: Option<Vec<String>>,
annotations: Option<HashMap<String, Value>>,
meta: Option<HashMap<String, Value>>,
) -> Self
where
F: Fn() -> Result<Value, String> + Send + Sync + 'static,
{
Self {
function: Arc::new(function),
uri: uri.clone(),
name: name.unwrap_or_else(|| "unnamed_resource".to_string()),
description: description.unwrap_or_default(),
mime_type: mime_type.unwrap_or_else(|| "text/plain".to_string()),
tags: tags.unwrap_or_default(),
annotations: annotations.unwrap_or_default(),
meta,
}
}
pub fn read(&self) -> Result<Value, String> {
(self.function)()
}
}
impl Serialize for FunctionResource {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let resource = Resource {
uri: self.uri.clone(),
name: self.name.clone(),
description: if self.description.is_empty() { None } else { Some(self.description.clone()) },
mime_type: if self.mime_type.is_empty() { None } else { Some(self.mime_type.clone()) },
tags: if self.tags.is_empty() { None } else { Some(self.tags.clone()) },
annotations: if self.annotations.is_empty() { None } else { Some(self.annotations.clone()) },
meta: self.meta.clone(),
};
resource.serialize(serializer)
}
}
impl std::fmt::Debug for FunctionResource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FunctionResource")
.field("uri", &self.uri)
.field("name", &self.name)
.field("description", &self.description)
.field("mime_type", &self.mime_type)
.field("tags", &self.tags)
.field("annotations", &self.annotations)
.field("meta", &self.meta)
.finish()
}
}
#[derive(Debug, Clone)]
pub enum DuplicateBehavior {
Warn,
Error,
Replace,
Ignore,
}
#[derive(Debug, Clone)]
pub struct ResourceManager {
resources: HashMap<String, FunctionResource>,
duplicate_behavior: DuplicateBehavior,
}
impl ResourceManager {
pub fn new() -> Self {
Self {
resources: HashMap::new(),
duplicate_behavior: DuplicateBehavior::Warn,
}
}
pub fn with_behavior(duplicate_behavior: DuplicateBehavior) -> Self {
Self {
resources: HashMap::new(),
duplicate_behavior,
}
}
}
impl Default for ResourceManager {
fn default() -> Self {
Self::new()
}
}
impl ResourceManager {
pub fn add_resource(&mut self, resource: FunctionResource) {
if self.resources.contains_key(&resource.uri) {
match self.duplicate_behavior {
DuplicateBehavior::Warn => {
warn!("Resource '{}' already exists, replacing", resource.uri);
self.resources.insert(resource.uri.clone(), resource);
}
DuplicateBehavior::Error => {
panic!("Resource '{}' already exists", resource.uri);
}
DuplicateBehavior::Replace => {
self.resources.insert(resource.uri.clone(), resource);
}
DuplicateBehavior::Ignore => {
}
}
} else {
self.resources.insert(resource.uri.clone(), resource);
}
}
pub fn list_resources(&self) -> Vec<Resource> {
self.resources.values().map(|r| {
Resource {
uri: r.uri.clone(),
name: r.name.clone(),
description: if r.description.is_empty() { None } else { Some(r.description.clone()) },
mime_type: if r.mime_type.is_empty() { None } else { Some(r.mime_type.clone()) },
tags: if r.tags.is_empty() { None } else { Some(r.tags.clone()) },
annotations: if r.annotations.is_empty() { None } else { Some(r.annotations.clone()) },
meta: r.meta.clone(),
}
}).collect()
}
pub fn read_resource(&self, uri: &str) -> Result<Value, String> {
if let Some(resource) = self.resources.get(uri) {
resource.read()
} else {
Err(format!("Resource not found: {}", uri))
}
}
}