nestforge_core/
resource_service.rs1use serde::{de::DeserializeOwned, Serialize};
2use serde_json::Value;
3use thiserror::Error;
4
5use crate::{Identifiable, InMemoryStore};
6
7#[derive(Clone)]
8pub struct ResourceService<T>
9where
10 T: Identifiable + Clone,
11{
12 store: InMemoryStore<T>,
13}
14
15impl<T> Default for ResourceService<T>
16where
17 T: Identifiable + Clone + Serialize + DeserializeOwned,
18{
19 fn default() -> Self {
20 Self::new()
21 }
22}
23
24#[derive(Debug, Error)]
25pub enum ResourceError {
26 #[error("Failed to serialize dto: {0}")]
27 Serialize(#[from] serde_json::Error),
28 #[error("DTO must serialize into a JSON object")]
29 DtoNotObject,
30 #[error("Entity must serialize into a JSON object")]
31 EntityNotObject,
32}
33
34impl<T> ResourceService<T>
35where
36 T: Identifiable + Clone + Serialize + DeserializeOwned,
37{
38 pub fn new() -> Self {
39 Self {
40 store: InMemoryStore::new(),
41 }
42 }
43
44 pub fn with_seed(seed: Vec<T>) -> Self {
45 Self {
46 store: InMemoryStore::with_seed(seed),
47 }
48 }
49
50 pub fn find_all(&self) -> Vec<T> {
51 self.store.find_all()
52 }
53
54 pub fn all(&self) -> Vec<T> {
55 self.find_all()
56 }
57
58 pub fn find_by_id(&self, id: u64) -> Option<T> {
59 self.store.find_by_id(id)
60 }
61
62 pub fn get(&self, id: u64) -> Option<T> {
63 self.find_by_id(id)
64 }
65
66 pub fn count(&self) -> usize {
67 self.store.count()
68 }
69
70 pub fn exists(&self, id: u64) -> bool {
71 self.find_by_id(id).is_some()
72 }
73
74 pub fn create_from<D>(&self, dto: D) -> Result<T, ResourceError>
75 where
76 D: Serialize,
77 {
78 let mut dto_value = serde_json::to_value(dto)?;
79 let Some(map) = dto_value.as_object_mut() else {
80 return Err(ResourceError::DtoNotObject);
81 };
82
83 map.insert("id".to_string(), Value::from(0_u64));
84 let entity = serde_json::from_value::<T>(dto_value)?;
85 Ok(self.store.create(entity))
86 }
87
88 pub fn create<D>(&self, dto: D) -> Result<T, ResourceError>
89 where
90 D: Serialize,
91 {
92 self.create_from(dto)
93 }
94
95 pub fn update_from<D>(&self, id: u64, dto: D) -> Result<Option<T>, ResourceError>
96 where
97 D: Serialize,
98 {
99 let dto_value = serde_json::to_value(dto)?;
100 let Some(dto_map) = dto_value.as_object() else {
101 return Err(ResourceError::DtoNotObject);
102 };
103
104 let mut dto_map = dto_map.clone();
105 dto_map.remove("id");
106
107 let Some(existing) = self.store.find_by_id(id) else {
108 return Ok(None);
109 };
110
111 let mut entity_value = serde_json::to_value(existing)?;
112 let Some(entity_map) = entity_value.as_object_mut() else {
113 return Err(ResourceError::EntityNotObject);
114 };
115
116 for (key, value) in dto_map {
117 if !value.is_null() {
118 entity_map.insert(key, value);
119 }
120 }
121
122 let merged = serde_json::from_value::<T>(entity_value)?;
123 let updated = self.store.update_by_id(id, |entity| {
124 *entity = merged.clone();
125 });
126
127 Ok(updated)
128 }
129
130 pub fn update<D>(&self, id: u64, dto: D) -> Result<Option<T>, ResourceError>
131 where
132 D: Serialize,
133 {
134 self.update_from(id, dto)
135 }
136
137 pub fn replace_from<D>(&self, id: u64, dto: D) -> Result<Option<T>, ResourceError>
138 where
139 D: Serialize,
140 {
141 let mut dto_value = serde_json::to_value(dto)?;
142 let Some(map) = dto_value.as_object_mut() else {
143 return Err(ResourceError::DtoNotObject);
144 };
145
146 map.insert("id".to_string(), Value::from(id));
147 let replacement = serde_json::from_value::<T>(dto_value)?;
148 Ok(self.store.replace_by_id(id, replacement))
149 }
150
151 pub fn replace<D>(&self, id: u64, dto: D) -> Result<Option<T>, ResourceError>
152 where
153 D: Serialize,
154 {
155 self.replace_from(id, dto)
156 }
157
158 pub fn delete_by_id(&self, id: u64) -> Option<T> {
159 self.store.delete_by_id(id)
160 }
161
162 pub fn delete(&self, id: u64) -> Option<T> {
163 self.delete_by_id(id)
164 }
165}