1use anyhow::Result;
4use async_trait::async_trait;
5use serde::{Serialize, Deserialize};
6use uuid::Uuid;
7use std::collections::HashMap;
8use std::marker::PhantomData;
9use std::sync::Arc;
10
11use crate::service::{ServiceError, ServiceResult};
12
13#[derive(Debug, Serialize, Deserialize)]
15pub struct GrpcResponse<T> {
16 pub success: bool,
17 pub data: Option<T>,
18 pub error: Option<String>,
19}
20
21impl<T> GrpcResponse<T> {
22 pub fn success(data: T) -> Self {
23 Self {
24 success: true,
25 data: Some(data),
26 error: None,
27 }
28 }
29
30 pub fn error(error: String) -> Self {
31 Self {
32 success: false,
33 data: None,
34 error: Some(error),
35 }
36 }
37}
38
39#[derive(Debug, Serialize, Deserialize)]
41pub struct GrpcListRequest {
42 pub page: u32,
43 pub limit: u32,
44 pub sort_by: Option<String>,
45 pub sort_order: Option<String>,
46 pub filters: Option<HashMap<String, String>>,
47}
48
49#[derive(Debug, Serialize, Deserialize)]
51pub struct GrpcListResponse<T> {
52 pub items: Vec<T>,
53 pub total: u64,
54 pub page: u32,
55 pub limit: u32,
56 pub total_pages: u32,
57}
58
59#[derive(Debug, Serialize, Deserialize)]
61pub struct GrpcBulkCreateRequest<T> {
62 pub items: Vec<T>,
63}
64
65#[derive(Debug, Serialize, Deserialize)]
67pub struct GrpcBulkResponse<T> {
68 pub items: Vec<T>,
69 pub total: usize,
70 pub failed: usize,
71 pub errors: Vec<String>,
72}
73
74#[derive(Debug, Serialize, Deserialize)]
76pub struct GrpcUpsertRequest<T> {
77 pub entity: T,
78 pub create_if_not_exists: bool,
79}
80
81#[derive(Debug, Serialize, Deserialize)]
83pub struct GrpcPartialUpdateRequest {
84 pub id: Uuid,
85 pub fields: HashMap<String, String>,
86}
87
88pub trait BackboneGrpcService<T> {
90 fn list(&self, request: GrpcListRequest) -> Result<GrpcResponse<GrpcListResponse<T>>, anyhow::Error>;
92
93 fn create(&self, request: T) -> Result<GrpcResponse<T>, anyhow::Error>;
95
96 fn get_by_id(&self, request: Uuid) -> Result<GrpcResponse<T>, anyhow::Error>;
98
99 fn update(&self, request: T) -> Result<GrpcResponse<T>, anyhow::Error>;
101
102 fn partial_update(&self, request: GrpcPartialUpdateRequest) -> Result<GrpcResponse<T>, anyhow::Error>;
104
105 fn soft_delete(&self, request: Uuid) -> Result<GrpcResponse<()>, anyhow::Error>;
107
108 fn bulk_create(&self, request: GrpcBulkCreateRequest<T>) -> Result<GrpcResponse<GrpcBulkResponse<T>>, anyhow::Error>;
110
111 fn upsert(&self, request: GrpcUpsertRequest<T>) -> Result<GrpcResponse<T>, anyhow::Error>;
113
114 fn list_deleted(&self, request: GrpcListRequest) -> Result<GrpcResponse<GrpcListResponse<T>>, anyhow::Error>;
116
117 fn restore(&self, request: Uuid) -> Result<GrpcResponse<T>, anyhow::Error>;
119
120 fn empty_trash(&self, request: ()) -> Result<GrpcResponse<()>, anyhow::Error>;
122}
123
124pub trait GrpcService<T> {
126 fn create(&self, request: T) -> Result<T, anyhow::Error>;
127 fn get(&self, request: T) -> Result<T, anyhow::Error>;
128 fn update(&self, request: T) -> Result<T, anyhow::Error>;
129 fn delete(&self, request: T) -> Result<T, anyhow::Error>;
130 fn list(&self, request: T) -> Result<T, anyhow::Error>;
131}
132
133#[async_trait]
140pub trait GrpcCapableService<E, C, U>: Send + Sync + 'static
141where
142 E: Clone + Send + Sync + 'static,
143 C: Send + Sync + 'static,
144 U: Send + Sync + 'static,
145{
146 async fn list(&self, page: u32, limit: u32, filters: HashMap<String, String>) -> ServiceResult<(Vec<E>, u64)>;
147 async fn create(&self, dto: C) -> ServiceResult<E>;
148 async fn get_by_id(&self, id: &str) -> ServiceResult<Option<E>>;
149 async fn update(&self, id: &str, dto: U) -> ServiceResult<Option<E>>;
150 async fn soft_delete(&self, id: &str) -> ServiceResult<bool>;
151 async fn restore(&self, id: &str) -> ServiceResult<Option<E>>;
152 async fn list_deleted(&self, page: u32, limit: u32) -> ServiceResult<(Vec<E>, u64)>;
153 async fn empty_trash(&self) -> ServiceResult<u64>;
154}
155
156pub struct GenericGrpcService<E, C, U, S>
169where
170 E: Clone + Send + Sync + 'static,
171 C: Send + Sync + 'static,
172 U: Send + Sync + 'static,
173 S: GrpcCapableService<E, C, U>,
174{
175 service: Arc<S>,
176 _phantom: PhantomData<(E, C, U)>,
177}
178
179impl<E, C, U, S> GenericGrpcService<E, C, U, S>
180where
181 E: Clone + Send + Sync + 'static,
182 C: Send + Sync + 'static,
183 U: Send + Sync + 'static,
184 S: GrpcCapableService<E, C, U>,
185{
186 pub fn new(service: Arc<S>) -> Self {
187 Self {
188 service,
189 _phantom: PhantomData,
190 }
191 }
192
193 pub fn service(&self) -> &Arc<S> {
194 &self.service
195 }
196
197 pub async fn rpc_list(
200 &self,
201 page: u32,
202 limit: u32,
203 filters: HashMap<String, String>,
204 ) -> GrpcResponse<GrpcListResponse<E>>
205 where
206 E: Serialize + for<'de> serde::Deserialize<'de>,
207 {
208 match self.service.list(page, limit, filters).await {
209 Ok((items, total)) => {
210 let total_pages = if limit == 0 { 0 } else { ((total as f64) / (limit as f64)).ceil() as u32 };
211 GrpcResponse::success(GrpcListResponse {
212 items,
213 total,
214 page,
215 limit,
216 total_pages,
217 })
218 }
219 Err(e) => GrpcResponse::error(e.to_string()),
220 }
221 }
222
223 pub async fn rpc_create(&self, dto: C) -> GrpcResponse<E>
224 where
225 E: Serialize + for<'de> serde::Deserialize<'de>,
226 {
227 match self.service.create(dto).await {
228 Ok(entity) => GrpcResponse::success(entity),
229 Err(e) => GrpcResponse::error(e.to_string()),
230 }
231 }
232
233 pub async fn rpc_get_by_id(&self, id: &str) -> GrpcResponse<Option<E>>
234 where
235 E: Serialize + for<'de> serde::Deserialize<'de>,
236 {
237 match self.service.get_by_id(id).await {
238 Ok(entity) => GrpcResponse::success(entity),
239 Err(e) => GrpcResponse::error(e.to_string()),
240 }
241 }
242
243 pub async fn rpc_update(&self, id: &str, dto: U) -> GrpcResponse<Option<E>>
244 where
245 E: Serialize + for<'de> serde::Deserialize<'de>,
246 {
247 match self.service.update(id, dto).await {
248 Ok(entity) => GrpcResponse::success(entity),
249 Err(e) => GrpcResponse::error(e.to_string()),
250 }
251 }
252
253 pub async fn rpc_soft_delete(&self, id: &str) -> GrpcResponse<bool> {
254 match self.service.soft_delete(id).await {
255 Ok(deleted) => GrpcResponse::success(deleted),
256 Err(e) => GrpcResponse::error(e.to_string()),
257 }
258 }
259
260 pub async fn rpc_restore(&self, id: &str) -> GrpcResponse<Option<E>>
261 where
262 E: Serialize + for<'de> serde::Deserialize<'de>,
263 {
264 match self.service.restore(id).await {
265 Ok(entity) => GrpcResponse::success(entity),
266 Err(e) => GrpcResponse::error(e.to_string()),
267 }
268 }
269
270 pub async fn rpc_list_deleted(
271 &self,
272 page: u32,
273 limit: u32,
274 ) -> GrpcResponse<GrpcListResponse<E>>
275 where
276 E: Serialize + for<'de> serde::Deserialize<'de>,
277 {
278 match self.service.list_deleted(page, limit).await {
279 Ok((items, total)) => {
280 let total_pages = if limit == 0 { 0 } else { ((total as f64) / (limit as f64)).ceil() as u32 };
281 GrpcResponse::success(GrpcListResponse {
282 items,
283 total,
284 page,
285 limit,
286 total_pages,
287 })
288 }
289 Err(e) => GrpcResponse::error(e.to_string()),
290 }
291 }
292
293 pub async fn rpc_empty_trash(&self) -> GrpcResponse<u64> {
294 match self.service.empty_trash().await {
295 Ok(count) => GrpcResponse::success(count),
296 Err(e) => GrpcResponse::error(e.to_string()),
297 }
298 }
299}
300
301#[derive(Debug, Clone)]
303pub struct GrpcConfig {
304 pub host: String,
305 pub port: u16,
306 pub max_message_size: usize,
307}
308
309impl Default for GrpcConfig {
310 fn default() -> Self {
311 Self {
312 host: "127.0.0.1".to_string(),
313 port: 50051,
314 max_message_size: 4 * 1024 * 1024, }
316 }
317}