1use crate::Result;
10use std::collections::HashMap;
11use std::sync::RwLock;
12use thulp_core::{
13 Resource, ResourceContents, ResourceListResult, ResourceTemplate, ResourceTemplateListResult,
14};
15
16pub struct ResourcesClient {
18 cache: RwLock<HashMap<String, Resource>>,
20 templates_cache: RwLock<Vec<ResourceTemplate>>,
22 subscriptions: RwLock<Vec<String>>,
24}
25
26impl ResourcesClient {
27 pub fn new() -> Self {
29 Self {
30 cache: RwLock::new(HashMap::new()),
31 templates_cache: RwLock::new(Vec::new()),
32 subscriptions: RwLock::new(Vec::new()),
33 }
34 }
35
36 pub async fn list(&self) -> Result<ResourceListResult> {
40 let cache = self.cache.read().unwrap();
41 Ok(ResourceListResult {
42 resources: cache.values().cloned().collect(),
43 next_cursor: None,
44 })
45 }
46
47 pub async fn read(&self, uri: &str) -> Result<ResourceContents> {
51 Ok(ResourceContents::text(uri, format!("Content of {}", uri)))
53 }
54
55 pub async fn list_templates(&self) -> Result<ResourceTemplateListResult> {
59 let cache = self.templates_cache.read().unwrap();
60 Ok(ResourceTemplateListResult {
61 resource_templates: cache.clone(),
62 next_cursor: None,
63 })
64 }
65
66 pub async fn subscribe(&self, uri: &str) -> Result<()> {
68 let mut subs = self.subscriptions.write().unwrap();
69 if !subs.contains(&uri.to_string()) {
70 subs.push(uri.to_string());
71 }
72 Ok(())
73 }
74
75 pub async fn unsubscribe(&self, uri: &str) -> Result<()> {
77 let mut subs = self.subscriptions.write().unwrap();
78 subs.retain(|s| s != uri);
79 Ok(())
80 }
81
82 pub fn subscriptions(&self) -> Vec<String> {
84 self.subscriptions.read().unwrap().clone()
85 }
86
87 pub fn register(&self, resource: Resource) {
89 let mut cache = self.cache.write().unwrap();
90 cache.insert(resource.uri.clone(), resource);
91 }
92
93 pub fn register_template(&self, template: ResourceTemplate) {
95 let mut cache = self.templates_cache.write().unwrap();
96 cache.push(template);
97 }
98
99 pub fn clear(&self) {
101 self.cache.write().unwrap().clear();
102 self.templates_cache.write().unwrap().clear();
103 self.subscriptions.write().unwrap().clear();
104 }
105
106 pub fn get(&self, uri: &str) -> Option<Resource> {
108 self.cache.read().unwrap().get(uri).cloned()
109 }
110}
111
112impl Default for ResourcesClient {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[tokio::test]
123 async fn test_resources_client_creation() {
124 let client = ResourcesClient::new();
125 let result = client.list().await.unwrap();
126 assert!(result.resources.is_empty());
127 }
128
129 #[tokio::test]
130 async fn test_register_resource() {
131 let client = ResourcesClient::new();
132 let resource = Resource::new("file:///test.txt", "test.txt");
133 client.register(resource);
134
135 let result = client.list().await.unwrap();
136 assert_eq!(result.resources.len(), 1);
137 }
138
139 #[tokio::test]
140 async fn test_get_resource() {
141 let client = ResourcesClient::new();
142 client.register(Resource::new("file:///test.txt", "test.txt"));
143
144 let resource = client.get("file:///test.txt");
145 assert!(resource.is_some());
146 assert_eq!(resource.unwrap().name, "test.txt");
147 }
148
149 #[tokio::test]
150 async fn test_read_resource() {
151 let client = ResourcesClient::new();
152 let contents = client.read("file:///test.txt").await.unwrap();
153 assert!(contents.text.is_some());
154 }
155
156 #[tokio::test]
157 async fn test_subscribe_unsubscribe() {
158 let client = ResourcesClient::new();
159
160 client.subscribe("file:///test.txt").await.unwrap();
161 assert_eq!(client.subscriptions().len(), 1);
162
163 client.unsubscribe("file:///test.txt").await.unwrap();
164 assert!(client.subscriptions().is_empty());
165 }
166
167 #[tokio::test]
168 async fn test_list_templates() {
169 let client = ResourcesClient::new();
170 client.register_template(ResourceTemplate::new("file:///{path}", "file"));
171
172 let result = client.list_templates().await.unwrap();
173 assert_eq!(result.resource_templates.len(), 1);
174 }
175
176 #[tokio::test]
177 async fn test_clear() {
178 let client = ResourcesClient::new();
179 client.register(Resource::new("file:///test.txt", "test.txt"));
180 client.subscribe("file:///test.txt").await.unwrap();
181
182 client.clear();
183 assert!(client.list().await.unwrap().resources.is_empty());
184 assert!(client.subscriptions().is_empty());
185 }
186}