1use crate::core::field::FieldValue;
4use crate::core::{Data, DataService, LinkService, link::LinkEntity};
5use anyhow::{Result, anyhow};
6use async_trait::async_trait;
7use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9use uuid::Uuid;
10
11pub struct InMemoryDataService<T: Data> {
28 data: Arc<RwLock<HashMap<Uuid, T>>>,
29}
30
31impl<T: Data> InMemoryDataService<T> {
32 pub fn new() -> Self {
34 Self {
35 data: Arc::new(RwLock::new(HashMap::new())),
36 }
37 }
38}
39
40impl<T: Data> Clone for InMemoryDataService<T> {
41 fn clone(&self) -> Self {
42 Self {
43 data: Arc::clone(&self.data),
44 }
45 }
46}
47
48impl<T: Data> Default for InMemoryDataService<T> {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54#[async_trait]
55impl<T: Data> DataService<T> for InMemoryDataService<T> {
56 async fn create(&self, entity: T) -> Result<T> {
57 let mut data = self
58 .data
59 .write()
60 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
61
62 data.insert(entity.id(), entity.clone());
63
64 Ok(entity)
65 }
66
67 async fn get(&self, id: &Uuid) -> Result<Option<T>> {
68 let data = self
69 .data
70 .read()
71 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
72
73 Ok(data.get(id).cloned())
74 }
75
76 async fn list(&self) -> Result<Vec<T>> {
77 let data = self
78 .data
79 .read()
80 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
81
82 Ok(data.values().cloned().collect())
83 }
84
85 async fn update(&self, id: &Uuid, entity: T) -> Result<T> {
86 let mut data = self
87 .data
88 .write()
89 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
90
91 data.get(id)
92 .ok_or_else(|| anyhow!("Entity not found: {}", id))?;
93
94 data.insert(*id, entity.clone());
95
96 Ok(entity)
97 }
98
99 async fn delete(&self, id: &Uuid) -> Result<()> {
100 let mut data = self
101 .data
102 .write()
103 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
104
105 data.remove(id);
106
107 Ok(())
108 }
109
110 async fn search(&self, field: &str, value: &str) -> Result<Vec<T>> {
111 let data = self
112 .data
113 .read()
114 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
115
116 Ok(data
117 .values()
118 .filter(|entity| {
119 entity.field_value(field).is_some_and(|fv| match &fv {
120 FieldValue::String(s) => s == value,
121 FieldValue::Integer(i) => i.to_string() == value,
122 FieldValue::Float(f) => f.to_string() == value,
123 FieldValue::Boolean(b) => b.to_string() == value,
124 FieldValue::Uuid(u) => u.to_string() == value,
125 FieldValue::DateTime(dt) => dt.to_rfc3339() == value,
126 FieldValue::Null => false,
127 })
128 })
129 .cloned()
130 .collect())
131 }
132}
133
134#[derive(Clone)]
142pub struct InMemoryLinkService {
143 links: Arc<RwLock<HashMap<Uuid, LinkEntity>>>,
144}
145
146impl InMemoryLinkService {
147 pub fn new() -> Self {
149 Self {
150 links: Arc::new(RwLock::new(HashMap::new())),
151 }
152 }
153}
154
155impl Default for InMemoryLinkService {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161#[async_trait]
162impl LinkService for InMemoryLinkService {
163 async fn create(&self, link: LinkEntity) -> Result<LinkEntity> {
164 let mut links = self
165 .links
166 .write()
167 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
168
169 links.insert(link.id, link.clone());
170
171 Ok(link)
172 }
173
174 async fn get(&self, id: &Uuid) -> Result<Option<LinkEntity>> {
175 let links = self
176 .links
177 .read()
178 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
179
180 Ok(links.get(id).cloned())
181 }
182
183 async fn list(&self) -> Result<Vec<LinkEntity>> {
184 let links = self
185 .links
186 .read()
187 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
188
189 Ok(links.values().cloned().collect())
190 }
191
192 async fn find_by_source(
193 &self,
194 source_id: &Uuid,
195 link_type: Option<&str>,
196 target_type: Option<&str>,
197 ) -> Result<Vec<LinkEntity>> {
198 let links = self
199 .links
200 .read()
201 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
202
203 Ok(links
204 .values()
205 .filter(|link| {
206 &link.source_id == source_id
207 && link_type.is_none_or(|lt| link.link_type == lt)
208 && target_type.is_none_or(|_tt| true) })
210 .cloned()
211 .collect())
212 }
213
214 async fn find_by_target(
215 &self,
216 target_id: &Uuid,
217 link_type: Option<&str>,
218 source_type: Option<&str>,
219 ) -> Result<Vec<LinkEntity>> {
220 let links = self
221 .links
222 .read()
223 .map_err(|e| anyhow!("Failed to acquire read lock: {}", e))?;
224
225 Ok(links
226 .values()
227 .filter(|link| {
228 &link.target_id == target_id
229 && link_type.is_none_or(|lt| link.link_type == lt)
230 && source_type.is_none_or(|_st| true) })
232 .cloned()
233 .collect())
234 }
235
236 async fn update(&self, id: &Uuid, updated_link: LinkEntity) -> Result<LinkEntity> {
237 let mut links = self
238 .links
239 .write()
240 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
241
242 links.get_mut(id).ok_or_else(|| anyhow!("Link not found"))?;
243
244 links.insert(*id, updated_link.clone());
245
246 Ok(updated_link)
247 }
248
249 async fn delete(&self, id: &Uuid) -> Result<()> {
250 let mut links = self
251 .links
252 .write()
253 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
254
255 links.remove(id);
256
257 Ok(())
258 }
259
260 async fn delete_by_entity(&self, entity_id: &Uuid) -> Result<()> {
261 let mut links = self
262 .links
263 .write()
264 .map_err(|e| anyhow!("Failed to acquire write lock: {}", e))?;
265
266 links.retain(|_, link| &link.source_id != entity_id && &link.target_id != entity_id);
267
268 Ok(())
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use crate::core::entity::Entity;
276 use crate::core::field::FieldValue;
277 use chrono::{DateTime, Utc};
278
279 #[derive(Clone, Debug, PartialEq)]
284 struct TestDataEntity {
285 id: Uuid,
286 entity_name: String,
287 status: String,
288 created_at: DateTime<Utc>,
289 updated_at: DateTime<Utc>,
290 }
291
292 impl TestDataEntity {
293 fn new(name: &str) -> Self {
294 let now = Utc::now();
295 Self {
296 id: Uuid::new_v4(),
297 entity_name: name.to_string(),
298 status: "active".to_string(),
299 created_at: now,
300 updated_at: now,
301 }
302 }
303 }
304
305 impl Entity for TestDataEntity {
306 type Service = ();
307
308 fn resource_name() -> &'static str {
309 "test_data_entities"
310 }
311
312 fn resource_name_singular() -> &'static str {
313 "test_data_entity"
314 }
315
316 fn service_from_host(
317 _: &std::sync::Arc<dyn std::any::Any + Send + Sync>,
318 ) -> anyhow::Result<std::sync::Arc<Self::Service>> {
319 Ok(std::sync::Arc::new(()))
320 }
321
322 fn id(&self) -> Uuid {
323 self.id
324 }
325
326 fn entity_type(&self) -> &str {
327 "test_data"
328 }
329
330 fn created_at(&self) -> DateTime<Utc> {
331 self.created_at
332 }
333
334 fn updated_at(&self) -> DateTime<Utc> {
335 self.updated_at
336 }
337
338 fn deleted_at(&self) -> Option<DateTime<Utc>> {
339 None
340 }
341
342 fn status(&self) -> &str {
343 &self.status
344 }
345 }
346
347 impl crate::core::Data for TestDataEntity {
348 fn name(&self) -> &str {
349 &self.entity_name
350 }
351
352 fn indexed_fields() -> &'static [&'static str] {
353 &["entity_name", "status"]
354 }
355
356 fn field_value(&self, field: &str) -> Option<FieldValue> {
357 match field {
358 "entity_name" => Some(FieldValue::String(self.entity_name.clone())),
359 "status" => Some(FieldValue::String(self.status.clone())),
360 _ => None,
361 }
362 }
363 }
364
365 #[tokio::test]
370 async fn test_data_create_entity() {
371 let service = InMemoryDataService::<TestDataEntity>::new();
372 let entity = TestDataEntity::new("Alice");
373
374 let created = service.create(entity.clone()).await.unwrap();
375 assert_eq!(created.id, entity.id);
376 assert_eq!(created.entity_name, "Alice");
377 }
378
379 #[tokio::test]
380 async fn test_data_get_entity() {
381 let service = InMemoryDataService::<TestDataEntity>::new();
382 let entity = TestDataEntity::new("Bob");
383
384 service.create(entity.clone()).await.unwrap();
385
386 let retrieved = service.get(&entity.id).await.unwrap();
387 assert!(retrieved.is_some());
388 assert_eq!(retrieved.unwrap().entity_name, "Bob");
389 }
390
391 #[tokio::test]
392 async fn test_data_get_nonexistent() {
393 let service = InMemoryDataService::<TestDataEntity>::new();
394
395 let retrieved = service.get(&Uuid::new_v4()).await.unwrap();
396 assert!(retrieved.is_none());
397 }
398
399 #[tokio::test]
400 async fn test_data_list_entities() {
401 let service = InMemoryDataService::<TestDataEntity>::new();
402
403 service.create(TestDataEntity::new("Alice")).await.unwrap();
404 service.create(TestDataEntity::new("Bob")).await.unwrap();
405 service
406 .create(TestDataEntity::new("Charlie"))
407 .await
408 .unwrap();
409
410 let all = service.list().await.unwrap();
411 assert_eq!(all.len(), 3);
412 }
413
414 #[tokio::test]
415 async fn test_data_list_empty() {
416 let service = InMemoryDataService::<TestDataEntity>::new();
417
418 let all = service.list().await.unwrap();
419 assert!(all.is_empty());
420 }
421
422 #[tokio::test]
423 async fn test_data_update_entity() {
424 let service = InMemoryDataService::<TestDataEntity>::new();
425 let mut entity = TestDataEntity::new("Alice");
426
427 service.create(entity.clone()).await.unwrap();
428
429 entity.entity_name = "Alice Updated".to_string();
430 let updated = service.update(&entity.id, entity.clone()).await.unwrap();
431
432 assert_eq!(updated.entity_name, "Alice Updated");
433
434 let retrieved = service.get(&entity.id).await.unwrap().unwrap();
436 assert_eq!(retrieved.entity_name, "Alice Updated");
437 }
438
439 #[tokio::test]
440 async fn test_data_update_nonexistent() {
441 let service = InMemoryDataService::<TestDataEntity>::new();
442 let entity = TestDataEntity::new("Ghost");
443 let id = entity.id;
444
445 let result = service.update(&id, entity).await;
446 assert!(result.is_err());
447 assert!(result.unwrap_err().to_string().contains("not found"));
448 }
449
450 #[tokio::test]
451 async fn test_data_delete_entity() {
452 let service = InMemoryDataService::<TestDataEntity>::new();
453 let entity = TestDataEntity::new("Alice");
454
455 service.create(entity.clone()).await.unwrap();
456 assert!(service.get(&entity.id).await.unwrap().is_some());
457
458 service.delete(&entity.id).await.unwrap();
459 assert!(service.get(&entity.id).await.unwrap().is_none());
460 }
461
462 #[tokio::test]
463 async fn test_data_delete_nonexistent() {
464 let service = InMemoryDataService::<TestDataEntity>::new();
465
466 let result = service.delete(&Uuid::new_v4()).await;
468 assert!(result.is_ok());
469 }
470
471 #[tokio::test]
476 async fn test_data_search_by_indexed_field() {
477 let service = InMemoryDataService::<TestDataEntity>::new();
478
479 service.create(TestDataEntity::new("Alice")).await.unwrap();
480 service.create(TestDataEntity::new("Bob")).await.unwrap();
481 service.create(TestDataEntity::new("Alice")).await.unwrap();
482
483 let results = service.search("entity_name", "Alice").await.unwrap();
484 assert_eq!(results.len(), 2);
485 assert!(results.iter().all(|e| e.entity_name == "Alice"));
486 }
487
488 #[tokio::test]
489 async fn test_data_search_no_results() {
490 let service = InMemoryDataService::<TestDataEntity>::new();
491
492 service.create(TestDataEntity::new("Alice")).await.unwrap();
493
494 let results = service.search("entity_name", "Zara").await.unwrap();
495 assert!(results.is_empty());
496 }
497
498 #[tokio::test]
499 async fn test_data_search_by_status() {
500 let service = InMemoryDataService::<TestDataEntity>::new();
501
502 let mut inactive = TestDataEntity::new("Inactive");
503 inactive.status = "inactive".to_string();
504
505 service.create(TestDataEntity::new("Active")).await.unwrap();
506 service.create(inactive).await.unwrap();
507
508 let results = service.search("status", "inactive").await.unwrap();
509 assert_eq!(results.len(), 1);
510 assert_eq!(results[0].entity_name, "Inactive");
511 }
512
513 #[tokio::test]
514 async fn test_data_search_unknown_field() {
515 let service = InMemoryDataService::<TestDataEntity>::new();
516
517 service.create(TestDataEntity::new("Alice")).await.unwrap();
518
519 let results = service
521 .search("nonexistent_field", "anything")
522 .await
523 .unwrap();
524 assert!(results.is_empty());
525 }
526
527 #[tokio::test]
528 async fn test_data_clone_shares_state() {
529 let service = InMemoryDataService::<TestDataEntity>::new();
530 let cloned = service.clone();
531
532 service.create(TestDataEntity::new("Alice")).await.unwrap();
533
534 let all = cloned.list().await.unwrap();
536 assert_eq!(all.len(), 1);
537 }
538
539 #[derive(Clone, Debug, PartialEq)]
544 struct ExtendedTestEntity {
545 id: Uuid,
546 entity_name: String,
547 status: String,
548 ref_id: Uuid,
549 created_at: DateTime<Utc>,
550 updated_at: DateTime<Utc>,
551 }
552
553 impl ExtendedTestEntity {
554 fn new(name: &str, ref_id: Uuid) -> Self {
555 let now = Utc::now();
556 Self {
557 id: Uuid::new_v4(),
558 entity_name: name.to_string(),
559 status: "active".to_string(),
560 ref_id,
561 created_at: now,
562 updated_at: now,
563 }
564 }
565 }
566
567 impl Entity for ExtendedTestEntity {
568 type Service = ();
569
570 fn resource_name() -> &'static str {
571 "extended_test_entities"
572 }
573
574 fn resource_name_singular() -> &'static str {
575 "extended_test_entity"
576 }
577
578 fn service_from_host(
579 _: &std::sync::Arc<dyn std::any::Any + Send + Sync>,
580 ) -> anyhow::Result<std::sync::Arc<Self::Service>> {
581 Ok(std::sync::Arc::new(()))
582 }
583
584 fn id(&self) -> Uuid {
585 self.id
586 }
587
588 fn entity_type(&self) -> &str {
589 "extended_test"
590 }
591
592 fn created_at(&self) -> DateTime<Utc> {
593 self.created_at
594 }
595
596 fn updated_at(&self) -> DateTime<Utc> {
597 self.updated_at
598 }
599
600 fn deleted_at(&self) -> Option<DateTime<Utc>> {
601 None
602 }
603
604 fn status(&self) -> &str {
605 &self.status
606 }
607 }
608
609 impl crate::core::Data for ExtendedTestEntity {
610 fn name(&self) -> &str {
611 &self.entity_name
612 }
613
614 fn indexed_fields() -> &'static [&'static str] {
615 &["entity_name", "status", "ref_id", "created_at"]
616 }
617
618 fn field_value(&self, field: &str) -> Option<FieldValue> {
619 match field {
620 "entity_name" => Some(FieldValue::String(self.entity_name.clone())),
621 "status" => Some(FieldValue::String(self.status.clone())),
622 "ref_id" => Some(FieldValue::Uuid(self.ref_id)),
623 "created_at" => Some(FieldValue::DateTime(self.created_at)),
624 _ => None,
625 }
626 }
627 }
628
629 #[tokio::test]
630 async fn test_data_search_by_uuid_field() {
631 let service = InMemoryDataService::<ExtendedTestEntity>::new();
632 let target_ref = Uuid::new_v4();
633 let other_ref = Uuid::new_v4();
634
635 service
636 .create(ExtendedTestEntity::new("Alpha", target_ref))
637 .await
638 .expect("create Alpha should succeed");
639 service
640 .create(ExtendedTestEntity::new("Beta", other_ref))
641 .await
642 .expect("create Beta should succeed");
643 service
644 .create(ExtendedTestEntity::new("Gamma", target_ref))
645 .await
646 .expect("create Gamma should succeed");
647
648 let results = service
649 .search("ref_id", &target_ref.to_string())
650 .await
651 .expect("search by ref_id should succeed");
652 assert_eq!(
653 results.len(),
654 2,
655 "should find 2 entities with matching ref_id"
656 );
657 assert!(results.iter().all(|e| e.ref_id == target_ref));
658 }
659
660 #[tokio::test]
661 async fn test_data_search_by_datetime_field() {
662 let service = InMemoryDataService::<ExtendedTestEntity>::new();
663 let entity = ExtendedTestEntity::new("Timed", Uuid::new_v4());
664 let created_rfc3339 = entity.created_at.to_rfc3339();
665
666 service.create(entity).await.expect("create should succeed");
667
668 let results = service
669 .search("created_at", &created_rfc3339)
670 .await
671 .expect("search by created_at should succeed");
672 assert_eq!(
673 results.len(),
674 1,
675 "should find entity by its created_at timestamp"
676 );
677 assert_eq!(results[0].entity_name, "Timed");
678 }
679
680 #[tokio::test]
681 async fn test_data_default_creates_empty_service() {
682 let service = InMemoryDataService::<TestDataEntity>::default();
683 let all = service
684 .list()
685 .await
686 .expect("list should succeed on default service");
687 assert!(all.is_empty(), "default service should start empty");
688 }
689
690 #[tokio::test]
695 async fn test_create_link() {
696 let service = InMemoryLinkService::new();
697 let user_id = Uuid::new_v4();
698 let car_id = Uuid::new_v4();
699
700 let link = LinkEntity::new("owner", user_id, car_id, None);
701
702 let created = service.create(link.clone()).await.unwrap();
703
704 assert_eq!(created.link_type, "owner");
705 assert_eq!(created.source_id, user_id);
706 assert_eq!(created.target_id, car_id);
707 }
708
709 #[tokio::test]
710 async fn test_get_link() {
711 let service = InMemoryLinkService::new();
712 let link = LinkEntity::new("owner", Uuid::new_v4(), Uuid::new_v4(), None);
713
714 service.create(link.clone()).await.unwrap();
715
716 let retrieved = service.get(&link.id).await.unwrap();
717 assert!(retrieved.is_some());
718 assert_eq!(retrieved.unwrap().id, link.id);
719 }
720
721 #[tokio::test]
722 async fn test_list_links() {
723 let service = InMemoryLinkService::new();
724
725 let link1 = LinkEntity::new("owner", Uuid::new_v4(), Uuid::new_v4(), None);
726 let link2 = LinkEntity::new("driver", Uuid::new_v4(), Uuid::new_v4(), None);
727
728 service.create(link1).await.unwrap();
729 service.create(link2).await.unwrap();
730
731 let links = service.list().await.unwrap();
732 assert_eq!(links.len(), 2);
733 }
734
735 #[tokio::test]
736 async fn test_find_by_source() {
737 let service = InMemoryLinkService::new();
738 let user_id = Uuid::new_v4();
739 let car1_id = Uuid::new_v4();
740 let car2_id = Uuid::new_v4();
741
742 service
744 .create(LinkEntity::new("owner", user_id, car1_id, None))
745 .await
746 .unwrap();
747
748 service
750 .create(LinkEntity::new("driver", user_id, car2_id, None))
751 .await
752 .unwrap();
753
754 let links = service.find_by_source(&user_id, None, None).await.unwrap();
756 assert_eq!(links.len(), 2);
757
758 let owner_links = service
760 .find_by_source(&user_id, Some("owner"), None)
761 .await
762 .unwrap();
763 assert_eq!(owner_links.len(), 1);
764 assert_eq!(owner_links[0].link_type, "owner");
765 }
766
767 #[tokio::test]
768 async fn test_find_by_target() {
769 let service = InMemoryLinkService::new();
770 let user1_id = Uuid::new_v4();
771 let user2_id = Uuid::new_v4();
772 let car_id = Uuid::new_v4();
773
774 service
776 .create(LinkEntity::new("owner", user1_id, car_id, None))
777 .await
778 .unwrap();
779
780 service
782 .create(LinkEntity::new("driver", user2_id, car_id, None))
783 .await
784 .unwrap();
785
786 let links = service.find_by_target(&car_id, None, None).await.unwrap();
788 assert_eq!(links.len(), 2);
789
790 let driver_links = service
792 .find_by_target(&car_id, Some("driver"), None)
793 .await
794 .unwrap();
795 assert_eq!(driver_links.len(), 1);
796 assert_eq!(driver_links[0].link_type, "driver");
797 }
798
799 #[tokio::test]
800 async fn test_update_link() {
801 let service = InMemoryLinkService::new();
802 let user_id = Uuid::new_v4();
803 let company_id = Uuid::new_v4();
804
805 let mut link = LinkEntity::new(
806 "worker",
807 user_id,
808 company_id,
809 Some(serde_json::json!({"role": "Developer"})),
810 );
811
812 service.create(link.clone()).await.unwrap();
813
814 link.metadata = Some(serde_json::json!({"role": "Senior Developer"}));
816 link.touch();
817
818 let updated = service.update(&link.id, link.clone()).await.unwrap();
819 assert_eq!(
820 updated.metadata,
821 Some(serde_json::json!({"role": "Senior Developer"}))
822 );
823 }
824
825 #[tokio::test]
826 async fn test_delete_link() {
827 let service = InMemoryLinkService::new();
828 let link = LinkEntity::new("owner", Uuid::new_v4(), Uuid::new_v4(), None);
829
830 service.create(link.clone()).await.unwrap();
831
832 let retrieved = service.get(&link.id).await.unwrap();
833 assert!(retrieved.is_some());
834
835 service.delete(&link.id).await.unwrap();
836
837 let retrieved = service.get(&link.id).await.unwrap();
838 assert!(retrieved.is_none());
839 }
840
841 #[tokio::test]
842 async fn test_delete_by_entity() {
843 let service = InMemoryLinkService::new();
844 let user_id = Uuid::new_v4();
845 let car1_id = Uuid::new_v4();
846 let car2_id = Uuid::new_v4();
847
848 service
849 .create(LinkEntity::new("owner", user_id, car1_id, None))
850 .await
851 .unwrap();
852 service
853 .create(LinkEntity::new("driver", user_id, car2_id, None))
854 .await
855 .unwrap();
856 service
857 .create(LinkEntity::new("owner", Uuid::new_v4(), car1_id, None))
858 .await
859 .unwrap();
860
861 let links = service.list().await.unwrap();
862 assert_eq!(links.len(), 3);
863
864 service.delete_by_entity(&user_id).await.unwrap();
866
867 let remaining = service.list().await.unwrap();
868 assert_eq!(remaining.len(), 1);
869 assert_ne!(remaining[0].source_id, user_id);
870 assert_ne!(remaining[0].target_id, user_id);
871 }
872}