Skip to main content

nestforge_core/
resource_service.rs

1use 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}