1use std::collections::HashMap;
7
8use crate::helper;
9use crate::models::collection::{Collection, Method, Request};
10
11pub type CollectionResult<T> = Result<T, CollectionError>;
13
14#[derive(Debug)]
16pub enum CollectionError {
17 CollectionNotFound(String),
19 EndpointNotFound(String),
21 IoError(std::io::Error),
23 JsonError(serde_json::Error),
25 Other(String),
27}
28
29impl std::fmt::Display for CollectionError {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 CollectionError::CollectionNotFound(name) => {
33 write!(f, "Collection not found: {}", name)
34 }
35 CollectionError::EndpointNotFound(name) => write!(f, "Endpoint not found: {}", name),
36 CollectionError::IoError(e) => write!(f, "IO error: {}", e),
37 CollectionError::JsonError(e) => write!(f, "JSON error: {}", e),
38 CollectionError::Other(msg) => write!(f, "{}", msg),
39 }
40 }
41}
42
43impl std::error::Error for CollectionError {}
44
45impl From<std::io::Error> for CollectionError {
46 fn from(err: std::io::Error) -> Self {
47 CollectionError::IoError(err)
48 }
49}
50
51impl From<serde_json::Error> for CollectionError {
52 fn from(err: serde_json::Error) -> Self {
53 CollectionError::JsonError(err)
54 }
55}
56
57impl From<Box<dyn std::error::Error>> for CollectionError {
58 fn from(err: Box<dyn std::error::Error>) -> Self {
59 CollectionError::Other(err.to_string())
60 }
61}
62
63#[derive(Clone)]
67pub struct CollectionManager {
68 file_path: Option<String>,
69}
70
71impl Default for CollectionManager {
72 fn default() -> Self {
73 Self::new(None)
74 }
75}
76
77impl CollectionManager {
78 pub fn new(file_path: Option<String>) -> Self {
84 if let Some(ref path) = file_path {
85 std::env::set_var("COMAN_JSON", path);
86 }
87 Self { file_path }
88 }
89
90 pub fn get_file_path(&self) -> String {
92 self.file_path
93 .clone()
94 .unwrap_or_else(|| helper::get_file_path().to_string())
95 }
96
97 pub fn load_collections(&self) -> CollectionResult<Vec<Collection>> {
99 match helper::read_json_from_file() {
100 Ok(c) => Ok(c),
101 Err(e) => {
102 if let Some(io_err) = e.downcast_ref::<std::io::Error>() {
103 if io_err.kind() == std::io::ErrorKind::NotFound {
104 Ok(Vec::new())
105 } else {
106 Err(CollectionError::Other(e.to_string()))
107 }
108 } else {
109 Err(CollectionError::Other(e.to_string()))
110 }
111 }
112 }
113 }
114
115 pub fn save_collections(&self, collections: &[Collection]) -> CollectionResult<()> {
117 let vec: Vec<Collection> = collections.to_vec();
118 helper::write_json_to_file(&vec).map_err(|e| CollectionError::Other(e.to_string()))
119 }
120
121 pub fn get_collection(&self, name: &str) -> CollectionResult<Collection> {
123 let collections = self.load_collections()?;
124 collections
125 .into_iter()
126 .find(|c| c.name == name)
127 .ok_or_else(|| CollectionError::CollectionNotFound(name.to_string()))
128 }
129
130 pub fn get_endpoint(&self, collection: &str, endpoint: &str) -> CollectionResult<Request> {
132 let col = self.get_collection(collection)?;
133 col.requests
134 .and_then(|requests| requests.into_iter().find(|r| r.name == endpoint))
135 .ok_or_else(|| CollectionError::EndpointNotFound(endpoint.to_string()))
136 }
137
138 pub fn get_endpoint_url(&self, collection: &str, endpoint: &str) -> CollectionResult<String> {
140 let col = self.get_collection(collection)?;
141 let req = self.get_endpoint(collection, endpoint)?;
142 Ok(format!("{}{}", col.url, req.endpoint))
143 }
144
145 pub fn get_endpoint_headers(
147 &self,
148 collection: &str,
149 endpoint: &str,
150 ) -> CollectionResult<Vec<(String, String)>> {
151 let col = self.get_collection(collection)?;
152 let req = self.get_endpoint(collection, endpoint)?;
153
154 let mut merged: HashMap<String, String> = HashMap::new();
155 for (k, v) in &col.headers {
156 merged.insert(k.clone(), v.clone());
157 }
158 for (k, v) in &req.headers {
159 merged.insert(k.clone(), v.clone());
160 }
161
162 Ok(merged.into_iter().collect())
163 }
164
165 pub fn add_collection(
169 &self,
170 name: &str,
171 url: &str,
172 headers: Vec<(String, String)>,
173 ) -> CollectionResult<()> {
174 let mut collections = self.load_collections()?;
175
176 if let Some(col) = collections.iter_mut().find(|c| c.name == name) {
177 col.url = url.to_string();
179 col.headers = headers;
180 } else {
181 collections.push(Collection {
183 name: name.to_string(),
184 url: url.to_string(),
185 headers,
186 requests: None,
187 });
188 }
189
190 self.save_collections(&collections)
191 }
192
193 pub fn delete_collection(&self, name: &str) -> CollectionResult<()> {
195 let mut collections = self.load_collections()?;
196 let original_len = collections.len();
197 collections.retain(|c| c.name != name);
198
199 if collections.len() == original_len {
200 return Err(CollectionError::CollectionNotFound(name.to_string()));
201 }
202
203 self.save_collections(&collections)
204 }
205
206 pub fn update_collection(
208 &self,
209 name: &str,
210 url: Option<&str>,
211 headers: Option<Vec<(String, String)>>,
212 ) -> CollectionResult<()> {
213 let mut collections = self.load_collections()?;
214
215 let col = collections
216 .iter_mut()
217 .find(|c| c.name == name)
218 .ok_or_else(|| CollectionError::CollectionNotFound(name.to_string()))?;
219
220 if let Some(url) = url {
221 col.url = url.to_string();
222 }
223
224 if let Some(new_headers) = headers {
225 col.headers = Self::merge_headers(col.headers.clone(), &new_headers);
226 }
227
228 self.save_collections(&collections)
229 }
230
231 pub fn copy_collection(&self, name: &str, new_name: &str) -> CollectionResult<()> {
233 let mut collections = self.load_collections()?;
234
235 let col = collections
236 .iter()
237 .find(|c| c.name == name)
238 .ok_or_else(|| CollectionError::CollectionNotFound(name.to_string()))?;
239
240 let mut new_col = col.clone();
241 new_col.name = new_name.to_string();
242 collections.push(new_col);
243
244 self.save_collections(&collections)
245 }
246
247 pub fn add_endpoint(
251 &self,
252 collection: &str,
253 name: &str,
254 path: &str,
255 method: Method,
256 headers: Vec<(String, String)>,
257 body: Option<String>,
258 ) -> CollectionResult<()> {
259 let mut collections = self.load_collections()?;
260
261 let col = collections
262 .iter_mut()
263 .find(|c| c.name == collection)
264 .ok_or_else(|| CollectionError::CollectionNotFound(collection.to_string()))?;
265
266 let request = Request {
267 name: name.to_string(),
268 endpoint: path.to_string(),
269 method,
270 headers,
271 body,
272 };
273
274 let mut requests = col.requests.clone().unwrap_or_default();
275 requests.retain(|r| r.name != name);
276 requests.push(request);
277 col.requests = Some(requests);
278
279 self.save_collections(&collections)
280 }
281
282 pub fn delete_endpoint(&self, collection: &str, endpoint: &str) -> CollectionResult<()> {
284 let mut collections = self.load_collections()?;
285
286 let col = collections
287 .iter_mut()
288 .find(|c| c.name == collection)
289 .ok_or_else(|| CollectionError::CollectionNotFound(collection.to_string()))?;
290
291 if let Some(requests) = col.requests.as_mut() {
292 let original_len = requests.len();
293 requests.retain(|r| r.name != endpoint);
294
295 if requests.len() == original_len {
296 return Err(CollectionError::EndpointNotFound(endpoint.to_string()));
297 }
298 } else {
299 return Err(CollectionError::EndpointNotFound(endpoint.to_string()));
300 }
301
302 self.save_collections(&collections)
303 }
304
305 pub fn update_endpoint(
307 &self,
308 collection: &str,
309 endpoint: &str,
310 path: Option<&str>,
311 headers: Option<Vec<(String, String)>>,
312 body: Option<String>,
313 ) -> CollectionResult<()> {
314 let mut collections = self.load_collections()?;
315
316 let col = collections
317 .iter_mut()
318 .find(|c| c.name == collection)
319 .ok_or_else(|| CollectionError::CollectionNotFound(collection.to_string()))?;
320
321 if let Some(requests) = col.requests.as_mut() {
322 let req = requests
323 .iter_mut()
324 .find(|r| r.name == endpoint)
325 .ok_or_else(|| CollectionError::EndpointNotFound(endpoint.to_string()))?;
326
327 if let Some(path) = path {
328 req.endpoint = path.to_string();
329 }
330
331 if let Some(new_headers) = headers {
332 req.headers = Self::merge_headers(req.headers.clone(), &new_headers);
333 }
334
335 if let Some(new_body) = body {
336 req.body = if new_body.is_empty() {
337 None
338 } else {
339 Some(new_body)
340 };
341 }
342 } else {
343 return Err(CollectionError::EndpointNotFound(endpoint.to_string()));
344 }
345
346 self.save_collections(&collections)
347 }
348
349 pub fn copy_endpoint(
351 &self,
352 collection: &str,
353 endpoint: &str,
354 new_name: &str,
355 to_collection: Option<&str>,
356 ) -> CollectionResult<()> {
357 let mut collections = self.load_collections()?;
358
359 let source_col = collections
361 .iter()
362 .find(|c| c.name == collection)
363 .ok_or_else(|| CollectionError::CollectionNotFound(collection.to_string()))?;
364
365 let source_req = source_col
366 .requests
367 .as_ref()
368 .and_then(|r| r.iter().find(|r| r.name == endpoint))
369 .ok_or_else(|| CollectionError::EndpointNotFound(endpoint.to_string()))?;
370
371 let mut new_req = source_req.clone();
372
373 if let Some(target_col_name) = to_collection {
374 let target_col = collections
376 .iter_mut()
377 .find(|c| c.name == target_col_name)
378 .ok_or_else(|| CollectionError::CollectionNotFound(target_col_name.to_string()))?;
379
380 let mut requests = target_col.requests.clone().unwrap_or_default();
381 requests.push(new_req);
382 target_col.requests = Some(requests);
383 } else {
384 new_req.name = new_name.to_string();
386
387 let col = collections
388 .iter_mut()
389 .find(|c| c.name == collection)
390 .ok_or_else(|| CollectionError::CollectionNotFound(collection.to_string()))?;
391
392 let mut requests = col.requests.clone().unwrap_or_default();
393 requests.push(new_req);
394 col.requests = Some(requests);
395 }
396
397 self.save_collections(&collections)
398 }
399
400 pub fn list_collections(&self) -> CollectionResult<Vec<Collection>> {
402 self.load_collections()
403 }
404
405 fn merge_headers(
407 existing: Vec<(String, String)>,
408 new_headers: &[(String, String)],
409 ) -> Vec<(String, String)> {
410 let mut merged: HashMap<String, String> = existing.into_iter().collect();
411 for (key, value) in new_headers.iter() {
412 if merged.contains_key(key) {
413 if value.is_empty() {
414 merged.remove(key);
415 } else {
416 merged.entry(key.clone()).and_modify(|v| *v = value.clone());
417 }
418 } else {
419 merged.insert(key.clone(), value.clone());
420 }
421 }
422 merged.into_iter().collect()
423 }
424}
425
426#[cfg(test)]
427mod tests {
428 use super::*;
429 use serial_test::serial;
430
431 fn setup_test_manager() -> CollectionManager {
432 std::env::set_var("COMAN_JSON", "test.json");
433 CollectionManager::new(Some("test.json".to_string()))
434 }
435
436 #[test]
437 #[serial]
438 fn test_load_collections() {
439 let manager = setup_test_manager();
440 let result = manager.load_collections();
441 assert!(result.is_ok());
442 }
443
444 #[test]
445 #[serial]
446 fn test_get_collection() {
447 let manager = setup_test_manager();
448 let result = manager.load_collections();
450 assert!(result.is_ok());
451 }
452}