Skip to main content

pywatt_sdk/data/cache/
proxy_service.rs

1use async_trait::async_trait;
2use std::collections::HashMap;
3use std::time::Duration;
4
5use crate::data::cache::{CacheConfig, CacheError, CacheResult, CacheService, CacheStats, CacheType};
6use crate::ipc::send_request;
7use crate::ipc_types::{
8    ServiceOperation, ServiceOperationResult, ServiceRequest, ServiceResponse, ServiceType,
9};
10use base64::{Engine as _, engine::general_purpose::STANDARD};
11
12// ProxyCacheService for handling cache operations via IPC
13pub struct ProxyCacheService {
14    connection_id: String,
15    cache_type: CacheType,
16    default_ttl: Duration,
17}
18
19impl ProxyCacheService {
20    pub async fn connect(config: &CacheConfig) -> CacheResult<Self> {
21        // Create a unique ID for this connection request
22        let request_id = format!("cache_request_{}", uuid::Uuid::new_v4());
23
24        // Create a service request
25        let request = ServiceRequest {
26            id: request_id.clone(),
27            service_type: ServiceType::Cache,
28            config: Some(serde_json::to_value(config).map_err(|e| {
29                CacheError::Configuration(format!("Failed to serialize cache config: {}", e))
30            })?),
31        };
32
33        // Send the request to the orchestrator
34        let response = send_request(&request)
35            .await
36            .map_err(|e| CacheError::Connection(format!("Failed to send request: {}", e)))?;
37
38        // Deserialize the response
39        let service_response: ServiceResponse = serde_json::from_str(&response)
40            .map_err(|e| CacheError::Connection(format!("Failed to parse response: {}", e)))?;
41
42        // Check if the request was successful
43        if !service_response.success {
44            return Err(CacheError::Connection(
45                service_response
46                    .error
47                    .unwrap_or_else(|| "Unknown connection error".to_string()),
48            ));
49        }
50
51        // Get the connection ID
52        let connection_id = service_response
53            .connection_id
54            .ok_or_else(|| CacheError::Connection("No connection ID returned".to_string()))?;
55
56        Ok(Self {
57            connection_id,
58            cache_type: match config.cache_type {
59                CacheType::Redis => CacheType::Redis,
60                CacheType::Memcached => CacheType::Memcached,
61                CacheType::InMemory => CacheType::InMemory,
62                CacheType::File => CacheType::File,
63            },
64            default_ttl: config.get_default_ttl(),
65        })
66    }
67}
68
69#[async_trait]
70impl CacheService for ProxyCacheService {
71    async fn get(&self, key: &str) -> CacheResult<Option<Vec<u8>>> {
72        let operation = ServiceOperation {
73            connection_id: self.connection_id.clone(),
74            service_type: ServiceType::Cache,
75            operation: "get".to_string(),
76            params: serde_json::json!({
77                "key": key,
78            }),
79        };
80
81        let result = send_operation(operation).await?;
82        match result.result {
83            Some(value) => {
84                if value.is_null() {
85                    return Ok(None);
86                }
87
88                if let Some(s) = value.as_str() {
89                    // Value is base64 encoded
90                    match STANDARD.decode(s) {
91                        Ok(bytes) => Ok(Some(bytes)),
92                        Err(e) => Err(CacheError::Serialization(format!("Invalid base64: {}", e))),
93                    }
94                } else if let Some(array) = value.as_array() {
95                    // Value is a byte array
96                    let bytes: Result<Vec<u8>, _> = array
97                        .iter()
98                        .map(|v| {
99                            if let Some(n) = v.as_u64() {
100                                if n <= 255 {
101                                    Ok(n as u8)
102                                } else {
103                                    Err(CacheError::Serialization(format!(
104                                        "Invalid byte value: {}",
105                                        n
106                                    )))
107                                }
108                            } else {
109                                Err(CacheError::Serialization("Invalid byte value".to_string()))
110                            }
111                        })
112                        .collect();
113
114                    Ok(Some(bytes?))
115                } else {
116                    Err(CacheError::Serialization(
117                        "Invalid value format".to_string(),
118                    ))
119                }
120            }
121            None => Ok(None),
122        }
123    }
124
125    async fn set(&self, key: &str, value: &[u8], ttl: Option<Duration>) -> CacheResult<()> {
126        // Convert value to base64 for transport
127        let value_base64 = STANDARD.encode(value);
128
129        // Convert ttl to seconds
130        let ttl_seconds = ttl.map(|d| d.as_secs());
131
132        let operation = ServiceOperation {
133            connection_id: self.connection_id.clone(),
134            service_type: ServiceType::Cache,
135            operation: "set".to_string(),
136            params: serde_json::json!({
137                "key": key,
138                "value": value_base64,
139                "ttl_seconds": ttl_seconds,
140            }),
141        };
142
143        let result = send_operation(operation).await?;
144        if result.success {
145            Ok(())
146        } else {
147            Err(CacheError::Operation(
148                result
149                    .error
150                    .unwrap_or_else(|| "Set operation failed".to_string()),
151            ))
152        }
153    }
154
155    async fn delete(&self, key: &str) -> CacheResult<bool> {
156        let operation = ServiceOperation {
157            connection_id: self.connection_id.clone(),
158            service_type: ServiceType::Cache,
159            operation: "delete".to_string(),
160            params: serde_json::json!({
161                "key": key,
162            }),
163        };
164
165        let result = send_operation(operation).await?;
166        match result.result {
167            Some(value) => {
168                if let Some(success) = value.as_bool() {
169                    Ok(success)
170                } else {
171                    Err(CacheError::Operation("Invalid delete result".to_string()))
172                }
173            }
174            None => Ok(false), // Assume not found
175        }
176    }
177
178    async fn exists(&self, key: &str) -> CacheResult<bool> {
179        let operation = ServiceOperation {
180            connection_id: self.connection_id.clone(),
181            service_type: ServiceType::Cache,
182            operation: "exists".to_string(),
183            params: serde_json::json!({
184                "key": key,
185            }),
186        };
187
188        let result = send_operation(operation).await?;
189        match result.result {
190            Some(value) => {
191                if let Some(exists) = value.as_bool() {
192                    Ok(exists)
193                } else {
194                    Err(CacheError::Operation("Invalid exists result".to_string()))
195                }
196            }
197            None => Ok(false), // Assume not found
198        }
199    }
200
201    async fn set_nx(&self, key: &str, value: &[u8], ttl: Option<Duration>) -> CacheResult<bool> {
202        // Convert value to base64 for transport
203        let value_base64 = STANDARD.encode(value);
204
205        // Convert ttl to seconds
206        let ttl_seconds = ttl.map(|d| d.as_secs());
207
208        let operation = ServiceOperation {
209            connection_id: self.connection_id.clone(),
210            service_type: ServiceType::Cache,
211            operation: "set_nx".to_string(),
212            params: serde_json::json!({
213                "key": key,
214                "value": value_base64,
215                "ttl_seconds": ttl_seconds,
216            }),
217        };
218
219        let result = send_operation(operation).await?;
220        match result.result {
221            Some(value) => {
222                if let Some(success) = value.as_bool() {
223                    Ok(success)
224                } else {
225                    Err(CacheError::Operation("Invalid set_nx result".to_string()))
226                }
227            }
228            None => Ok(false), // Assume not set
229        }
230    }
231
232    async fn get_set(&self, key: &str, value: &[u8]) -> CacheResult<Option<Vec<u8>>> {
233        // Convert value to base64 for transport
234        let value_base64 = STANDARD.encode(value);
235
236        let operation = ServiceOperation {
237            connection_id: self.connection_id.clone(),
238            service_type: ServiceType::Cache,
239            operation: "get_set".to_string(),
240            params: serde_json::json!({
241                "key": key,
242                "value": value_base64,
243            }),
244        };
245
246        let result = send_operation(operation).await?;
247        match result.result {
248            Some(value) => {
249                if value.is_null() {
250                    return Ok(None);
251                }
252
253                if let Some(s) = value.as_str() {
254                    // Value is base64 encoded
255                    match STANDARD.decode(s) {
256                        Ok(bytes) => Ok(Some(bytes)),
257                        Err(e) => Err(CacheError::Serialization(format!("Invalid base64: {}", e))),
258                    }
259                } else {
260                    Err(CacheError::Serialization(
261                        "Invalid value format".to_string(),
262                    ))
263                }
264            }
265            None => Ok(None),
266        }
267    }
268
269    async fn increment(&self, key: &str, delta: i64) -> CacheResult<i64> {
270        let operation = ServiceOperation {
271            connection_id: self.connection_id.clone(),
272            service_type: ServiceType::Cache,
273            operation: "increment".to_string(),
274            params: serde_json::json!({
275                "key": key,
276                "delta": delta,
277            }),
278        };
279
280        let result = send_operation(operation).await?;
281        match result.result {
282            Some(value) => {
283                if let Some(n) = value.as_i64() {
284                    Ok(n)
285                } else {
286                    Err(CacheError::Operation(
287                        "Invalid increment result".to_string(),
288                    ))
289                }
290            }
291            None => Err(CacheError::Operation(
292                "No result from increment operation".to_string(),
293            )),
294        }
295    }
296
297    async fn set_many(
298        &self,
299        items: &HashMap<String, Vec<u8>>,
300        ttl: Option<Duration>,
301    ) -> CacheResult<()> {
302        // Convert values to base64 for transport
303        let mut items_base64 = HashMap::new();
304        for (key, value) in items {
305            items_base64.insert(key.clone(), STANDARD.encode(value));
306        }
307
308        // Convert ttl to seconds
309        let ttl_seconds = ttl.map(|d| d.as_secs());
310
311        let operation = ServiceOperation {
312            connection_id: self.connection_id.clone(),
313            service_type: ServiceType::Cache,
314            operation: "set_many".to_string(),
315            params: serde_json::json!({
316                "items": items_base64,
317                "ttl_seconds": ttl_seconds,
318            }),
319        };
320
321        let result = send_operation(operation).await?;
322        if result.success {
323            Ok(())
324        } else {
325            Err(CacheError::Operation(
326                result
327                    .error
328                    .unwrap_or_else(|| "Set many operation failed".to_string()),
329            ))
330        }
331    }
332
333    async fn get_many(&self, keys: &[String]) -> CacheResult<HashMap<String, Vec<u8>>> {
334        let operation = ServiceOperation {
335            connection_id: self.connection_id.clone(),
336            service_type: ServiceType::Cache,
337            operation: "get_many".to_string(),
338            params: serde_json::json!({
339                "keys": keys,
340            }),
341        };
342
343        let result = send_operation(operation).await?;
344        match result.result {
345            Some(value) => {
346                let map: HashMap<String, String> = serde_json::from_value(value).map_err(|e| {
347                    CacheError::Serialization(format!("Failed to deserialize result: {}", e))
348                })?;
349
350                let mut result_map = HashMap::new();
351                for (key, value_base64) in map {
352                    match STANDARD.decode(&value_base64) {
353                        Ok(bytes) => {
354                            result_map.insert(key, bytes);
355                        }
356                        Err(e) => {
357                            return Err(CacheError::Serialization(format!(
358                                "Invalid base64: {}",
359                                e
360                            )));
361                        }
362                    }
363                }
364
365                Ok(result_map)
366            }
367            None => Ok(HashMap::new()), // Return empty map if no results
368        }
369    }
370
371    async fn delete_many(&self, keys: &[String]) -> CacheResult<u64> {
372        let operation = ServiceOperation {
373            connection_id: self.connection_id.clone(),
374            service_type: ServiceType::Cache,
375            operation: "delete_many".to_string(),
376            params: serde_json::json!({
377                "keys": keys,
378            }),
379        };
380
381        let result = send_operation(operation).await?;
382        match result.result {
383            Some(value) => {
384                if let Some(n) = value.as_u64() {
385                    Ok(n)
386                } else {
387                    Err(CacheError::Operation(
388                        "Invalid delete_many result".to_string(),
389                    ))
390                }
391            }
392            None => Ok(0), // Assume none deleted
393        }
394    }
395
396    async fn clear(&self, namespace: Option<&str>) -> CacheResult<()> {
397        let operation = ServiceOperation {
398            connection_id: self.connection_id.clone(),
399            service_type: ServiceType::Cache,
400            operation: "clear".to_string(),
401            params: serde_json::json!({
402                "namespace": namespace,
403            }),
404        };
405
406        let result = send_operation(operation).await?;
407        if result.success {
408            Ok(())
409        } else {
410            Err(CacheError::Operation(
411                result
412                    .error
413                    .unwrap_or_else(|| "Clear operation failed".to_string()),
414            ))
415        }
416    }
417
418    async fn lock(&self, key: &str, ttl: Duration) -> CacheResult<Option<String>> {
419        let operation = ServiceOperation {
420            connection_id: self.connection_id.clone(),
421            service_type: ServiceType::Cache,
422            operation: "lock".to_string(),
423            params: serde_json::json!({
424                "key": key,
425                "ttl_seconds": ttl.as_secs(),
426            }),
427        };
428
429        let result = send_operation(operation).await?;
430        match result.result {
431            Some(value) => {
432                if value.is_null() {
433                    return Ok(None);
434                }
435
436                if let Some(s) = value.as_str() {
437                    Ok(Some(s.to_string()))
438                } else {
439                    Err(CacheError::Operation(
440                        "Invalid lock token format".to_string(),
441                    ))
442                }
443            }
444            None => Ok(None), // Lock not acquired
445        }
446    }
447
448    async fn unlock(&self, key: &str, lock_token: &str) -> CacheResult<bool> {
449        let operation = ServiceOperation {
450            connection_id: self.connection_id.clone(),
451            service_type: ServiceType::Cache,
452            operation: "unlock".to_string(),
453            params: serde_json::json!({
454                "key": key,
455                "lock_token": lock_token,
456            }),
457        };
458
459        let result = send_operation(operation).await?;
460        match result.result {
461            Some(value) => {
462                if let Some(success) = value.as_bool() {
463                    Ok(success)
464                } else {
465                    Err(CacheError::Operation("Invalid unlock result".to_string()))
466                }
467            }
468            None => Ok(false), // Assume unlock failed
469        }
470    }
471
472    fn get_cache_type(&self) -> CacheType {
473        self.cache_type
474    }
475
476    async fn ping(&self) -> CacheResult<()> {
477        let operation = ServiceOperation {
478            connection_id: self.connection_id.clone(),
479            service_type: ServiceType::Cache,
480            operation: "ping".to_string(),
481            params: serde_json::json!({}),
482        };
483
484        let result = send_operation(operation).await?;
485        if result.success {
486            Ok(())
487        } else {
488            Err(CacheError::Connection(
489                result.error.unwrap_or_else(|| "Ping failed".to_string()),
490            ))
491        }
492    }
493
494    async fn close(&self) -> CacheResult<()> {
495        let operation = ServiceOperation {
496            connection_id: self.connection_id.clone(),
497            service_type: ServiceType::Cache,
498            operation: "close".to_string(),
499            params: serde_json::json!({}),
500        };
501
502        let result = send_operation(operation).await?;
503        if result.success {
504            Ok(())
505        } else {
506            Err(CacheError::Connection(
507                result.error.unwrap_or_else(|| "Close failed".to_string()),
508            ))
509        }
510    }
511
512    fn get_default_ttl(&self) -> Duration {
513        self.default_ttl
514    }
515
516    async fn stats(&self) -> CacheResult<CacheStats> {
517        let operation = ServiceOperation {
518            connection_id: self.connection_id.clone(),
519            service_type: ServiceType::Cache,
520            operation: "stats".to_string(),
521            params: serde_json::json!({}),
522        };
523
524        let result = send_operation(operation).await?;
525        match result.result {
526            Some(value) => serde_json::from_value(value).map_err(|e| {
527                CacheError::Serialization(format!("Failed to deserialize stats: {}", e))
528            }),
529            None => Err(CacheError::Operation("No stats available".to_string())),
530        }
531    }
532
533    async fn flush(&self) -> CacheResult<()> {
534        let operation = ServiceOperation {
535            connection_id: self.connection_id.clone(),
536            service_type: ServiceType::Cache,
537            operation: "flush".to_string(),
538            params: serde_json::json!({}),
539        };
540
541        let result = send_operation(operation).await?;
542        if result.success {
543            Ok(())
544        } else {
545            Err(CacheError::Operation(
546                result
547                    .error
548                    .unwrap_or_else(|| "Flush operation failed".to_string()),
549            ))
550        }
551    }
552}
553
554// Helper function to send an operation and receive the result
555async fn send_operation(operation: ServiceOperation) -> CacheResult<ServiceOperationResult> {
556    let response = send_request(&operation)
557        .await
558        .map_err(|e| CacheError::Operation(format!("Failed to send operation: {}", e)))?;
559
560    let result: ServiceOperationResult = serde_json::from_str(&response)
561        .map_err(|e| CacheError::Operation(format!("Failed to parse response: {}", e)))?;
562
563    if !result.success {
564        let error_msg = result.error.unwrap_or_else(|| "Unknown error".to_string());
565        return Err(CacheError::Operation(error_msg));
566    }
567
568    Ok(result)
569}
570
571// Add IPC error to the CacheError enum
572impl From<String> for CacheError {
573    fn from(error: String) -> Self {
574        CacheError::Connection(format!("IPC error: {}", error))
575    }
576}