1use crate::core::{LinkService, link::LinkEntity};
4use anyhow::{Result, anyhow};
5use async_trait::async_trait;
6use std::collections::HashMap;
7use std::sync::{Arc, RwLock};
8use uuid::Uuid;
9
10#[derive(Clone)]
14pub struct InMemoryLinkService {
15 links: Arc<RwLock<HashMap<Uuid, LinkEntity>>>,
16}
17
18impl InMemoryLinkService {
19 pub fn new() -> Self {
21 Self {
22 links: Arc::new(RwLock::new(HashMap::new())),
23 }
24 }
25}
26
27impl Default for InMemoryLinkService {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33#[async_trait]
34impl LinkService for InMemoryLinkService {
35 async fn create(&self, link: LinkEntity) -> Result<LinkEntity> {
36 let mut links = self
37 .links
38 .write()
39 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
40
41 links.insert(link.id, link.clone());
42
43 Ok(link)
44 }
45
46 async fn get(&self, id: &Uuid) -> Result<Option<LinkEntity>> {
47 let links = self
48 .links
49 .read()
50 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
51
52 Ok(links.get(id).cloned())
53 }
54
55 async fn list(&self) -> Result<Vec<LinkEntity>> {
56 let links = self
57 .links
58 .read()
59 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
60
61 Ok(links.values().cloned().collect())
62 }
63
64 async fn find_by_source(
65 &self,
66 source_id: &Uuid,
67 link_type: Option<&str>,
68 target_type: Option<&str>,
69 ) -> Result<Vec<LinkEntity>> {
70 let links = self
71 .links
72 .read()
73 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
74
75 Ok(links
76 .values()
77 .filter(|link| {
78 &link.source_id == source_id
79 && link_type.is_none_or(|lt| link.link_type == lt)
80 && target_type.is_none_or(|_tt| true) })
82 .cloned()
83 .collect())
84 }
85
86 async fn find_by_target(
87 &self,
88 target_id: &Uuid,
89 link_type: Option<&str>,
90 source_type: Option<&str>,
91 ) -> Result<Vec<LinkEntity>> {
92 let links = self
93 .links
94 .read()
95 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
96
97 Ok(links
98 .values()
99 .filter(|link| {
100 &link.target_id == target_id
101 && link_type.is_none_or(|lt| link.link_type == lt)
102 && source_type.is_none_or(|_st| true) })
104 .cloned()
105 .collect())
106 }
107
108 async fn update(&self, id: &Uuid, updated_link: LinkEntity) -> Result<LinkEntity> {
109 let mut links = self
110 .links
111 .write()
112 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
113
114 links.get_mut(id).ok_or_else(|| anyhow!("Link not found"))?;
115
116 links.insert(*id, updated_link.clone());
117
118 Ok(updated_link)
119 }
120
121 async fn delete(&self, id: &Uuid) -> Result<()> {
122 let mut links = self
123 .links
124 .write()
125 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
126
127 links.remove(id);
128
129 Ok(())
130 }
131
132 async fn delete_by_entity(&self, entity_id: &Uuid) -> Result<()> {
133 let mut links = self
134 .links
135 .write()
136 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
137
138 links.retain(|_, link| &link.source_id != entity_id && &link.target_id != entity_id);
139
140 Ok(())
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[tokio::test]
149 async fn test_create_link() {
150 let service = InMemoryLinkService::new();
151 let user_id = Uuid::new_v4();
152 let car_id = Uuid::new_v4();
153
154 let link = LinkEntity::new("owner", user_id, car_id, None);
155
156 let created = service.create(link.clone()).await.unwrap();
157
158 assert_eq!(created.link_type, "owner");
159 assert_eq!(created.source_id, user_id);
160 assert_eq!(created.target_id, car_id);
161 }
162
163 #[tokio::test]
164 async fn test_get_link() {
165 let service = InMemoryLinkService::new();
166 let link = LinkEntity::new("owner", Uuid::new_v4(), Uuid::new_v4(), None);
167
168 service.create(link.clone()).await.unwrap();
169
170 let retrieved = service.get(&link.id).await.unwrap();
171 assert!(retrieved.is_some());
172 assert_eq!(retrieved.unwrap().id, link.id);
173 }
174
175 #[tokio::test]
176 async fn test_list_links() {
177 let service = InMemoryLinkService::new();
178
179 let link1 = LinkEntity::new("owner", Uuid::new_v4(), Uuid::new_v4(), None);
180 let link2 = LinkEntity::new("driver", Uuid::new_v4(), Uuid::new_v4(), None);
181
182 service.create(link1).await.unwrap();
183 service.create(link2).await.unwrap();
184
185 let links = service.list().await.unwrap();
186 assert_eq!(links.len(), 2);
187 }
188
189 #[tokio::test]
190 async fn test_find_by_source() {
191 let service = InMemoryLinkService::new();
192 let user_id = Uuid::new_v4();
193 let car1_id = Uuid::new_v4();
194 let car2_id = Uuid::new_v4();
195
196 service
198 .create(LinkEntity::new("owner", user_id, car1_id, None))
199 .await
200 .unwrap();
201
202 service
204 .create(LinkEntity::new("driver", user_id, car2_id, None))
205 .await
206 .unwrap();
207
208 let links = service.find_by_source(&user_id, None, None).await.unwrap();
210 assert_eq!(links.len(), 2);
211
212 let owner_links = service
214 .find_by_source(&user_id, Some("owner"), None)
215 .await
216 .unwrap();
217 assert_eq!(owner_links.len(), 1);
218 assert_eq!(owner_links[0].link_type, "owner");
219 }
220
221 #[tokio::test]
222 async fn test_find_by_target() {
223 let service = InMemoryLinkService::new();
224 let user1_id = Uuid::new_v4();
225 let user2_id = Uuid::new_v4();
226 let car_id = Uuid::new_v4();
227
228 service
230 .create(LinkEntity::new("owner", user1_id, car_id, None))
231 .await
232 .unwrap();
233
234 service
236 .create(LinkEntity::new("driver", user2_id, car_id, None))
237 .await
238 .unwrap();
239
240 let links = service.find_by_target(&car_id, None, None).await.unwrap();
242 assert_eq!(links.len(), 2);
243
244 let driver_links = service
246 .find_by_target(&car_id, Some("driver"), None)
247 .await
248 .unwrap();
249 assert_eq!(driver_links.len(), 1);
250 assert_eq!(driver_links[0].link_type, "driver");
251 }
252
253 #[tokio::test]
254 async fn test_update_link() {
255 let service = InMemoryLinkService::new();
256 let user_id = Uuid::new_v4();
257 let company_id = Uuid::new_v4();
258
259 let mut link = LinkEntity::new(
260 "worker",
261 user_id,
262 company_id,
263 Some(serde_json::json!({"role": "Developer"})),
264 );
265
266 service.create(link.clone()).await.unwrap();
267
268 link.metadata = Some(serde_json::json!({"role": "Senior Developer"}));
270 link.touch();
271
272 let updated = service.update(&link.id, link.clone()).await.unwrap();
273 assert_eq!(
274 updated.metadata,
275 Some(serde_json::json!({"role": "Senior Developer"}))
276 );
277 }
278
279 #[tokio::test]
280 async fn test_delete_link() {
281 let service = InMemoryLinkService::new();
282 let link = LinkEntity::new("owner", Uuid::new_v4(), Uuid::new_v4(), None);
283
284 service.create(link.clone()).await.unwrap();
285
286 let retrieved = service.get(&link.id).await.unwrap();
287 assert!(retrieved.is_some());
288
289 service.delete(&link.id).await.unwrap();
290
291 let retrieved = service.get(&link.id).await.unwrap();
292 assert!(retrieved.is_none());
293 }
294
295 #[tokio::test]
296 async fn test_delete_by_entity() {
297 let service = InMemoryLinkService::new();
298 let user_id = Uuid::new_v4();
299 let car1_id = Uuid::new_v4();
300 let car2_id = Uuid::new_v4();
301
302 service
303 .create(LinkEntity::new("owner", user_id, car1_id, None))
304 .await
305 .unwrap();
306 service
307 .create(LinkEntity::new("driver", user_id, car2_id, None))
308 .await
309 .unwrap();
310 service
311 .create(LinkEntity::new("owner", Uuid::new_v4(), car1_id, None))
312 .await
313 .unwrap();
314
315 let links = service.list().await.unwrap();
316 assert_eq!(links.len(), 3);
317
318 service.delete_by_entity(&user_id).await.unwrap();
320
321 let remaining = service.list().await.unwrap();
322 assert_eq!(remaining.len(), 1);
323 assert_ne!(remaining[0].source_id, user_id);
324 assert_ne!(remaining[0].target_id, user_id);
325 }
326}