use super::{McpContent, McpError, McpManager, McpResourceDefinition, McpResult};
pub struct ResourceManager {
manager: std::sync::Arc<McpManager>,
}
impl ResourceManager {
pub fn new(manager: std::sync::Arc<McpManager>) -> Self {
Self { manager }
}
pub async fn list_all(&self) -> Vec<(String, McpResourceDefinition)> {
self.manager.list_resources().await
}
#[cfg(feature = "mcp")]
pub async fn list_from_server(
&self,
server_name: &str,
) -> McpResult<Vec<McpResourceDefinition>> {
let all_resources = self.manager.list_resources().await;
let resources: Vec<_> = all_resources
.into_iter()
.filter(|(name, _)| name == server_name)
.map(|(_, r)| r)
.collect();
if resources.is_empty() {
let servers = self.manager.list_servers().await;
if !servers.contains(&server_name.to_string()) {
return Err(McpError::ServerNotFound {
name: server_name.to_string(),
});
}
}
Ok(resources)
}
#[cfg(not(feature = "mcp"))]
pub async fn list_from_server(
&self,
_server_name: &str,
) -> McpResult<Vec<McpResourceDefinition>> {
Ok(Vec::new())
}
pub async fn read(&self, server_name: &str, uri: &str) -> McpResult<Vec<McpContent>> {
self.manager.read_resource(server_name, uri).await
}
pub async fn read_text(&self, server_name: &str, uri: &str) -> McpResult<String> {
let contents = self.read(server_name, uri).await?;
let text: Vec<String> = contents
.iter()
.filter_map(|c| c.as_text().map(|s| s.to_string()))
.collect();
if text.is_empty() {
Err(McpError::ResourceNotFound {
uri: format!("{} (no text content)", uri),
})
} else {
Ok(text.join("\n"))
}
}
pub async fn find_by_pattern(&self, pattern: &str) -> Vec<(String, McpResourceDefinition)> {
let all_resources = self.manager.list_resources().await;
let is_prefix_match = pattern.ends_with('*');
let pattern = pattern.trim_end_matches('*');
all_resources
.into_iter()
.filter(|(_, r)| {
if is_prefix_match {
r.uri.starts_with(pattern)
} else {
r.uri == pattern
}
})
.collect()
}
}
pub struct ResourceQuery {
server: Option<String>,
pattern: Option<String>,
mime_type: Option<String>,
}
impl ResourceQuery {
pub fn new() -> Self {
Self {
server: None,
pattern: None,
mime_type: None,
}
}
pub fn server(mut self, name: impl Into<String>) -> Self {
self.server = Some(name.into());
self
}
pub fn pattern(mut self, pattern: impl Into<String>) -> Self {
self.pattern = Some(pattern.into());
self
}
pub fn mime_type(mut self, mime_type: impl Into<String>) -> Self {
self.mime_type = Some(mime_type.into());
self
}
pub async fn execute(&self, manager: &ResourceManager) -> Vec<(String, McpResourceDefinition)> {
let mut results = manager.list_all().await;
if let Some(ref server) = self.server {
results.retain(|(s, _)| s == server);
}
if let Some(ref pattern) = self.pattern {
let is_prefix = pattern.ends_with('*');
let pattern = pattern.trim_end_matches('*');
results.retain(|(_, r)| {
if is_prefix {
r.uri.starts_with(pattern)
} else {
r.uri == pattern
}
});
}
if let Some(ref mime_type) = self.mime_type {
results.retain(|(_, r)| {
r.mime_type
.as_ref()
.map(|m| m == mime_type)
.unwrap_or(false)
});
}
results
}
}
impl Default for ResourceQuery {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resource_query_builder() {
let query = ResourceQuery::new()
.server("filesystem")
.pattern("file://*")
.mime_type("text/plain");
assert_eq!(query.server, Some("filesystem".to_string()));
assert_eq!(query.pattern, Some("file://*".to_string()));
assert_eq!(query.mime_type, Some("text/plain".to_string()));
}
}