Skip to main content

surreal_client/engines/
debug.rs

1//! Debug wrapper for Engine trait that logs all RPC operations
2
3use async_trait::async_trait;
4use ciborium::Value as CborValue;
5use serde_json::Value;
6
7use crate::{Engine, Result};
8
9/// Wrapper around an Engine that logs all RPC operations
10pub struct DebugEngine {
11    inner: Box<dyn Engine>,
12}
13
14impl DebugEngine {
15    /// Create a new DebugEngine wrapping an existing engine
16    pub fn wrap(engine: Box<dyn Engine>) -> Box<dyn Engine> {
17        Box::new(Self { inner: engine })
18    }
19
20    /// Log an RPC method call
21    fn log_request(&self, method: &str, params: &Value) {
22        let params_str = serde_json::to_string(params).unwrap_or_default();
23        println!("🔍 Surreal RPC: {} {}", method, params_str);
24    }
25
26    /// Log an RPC response
27    fn log_response(&self, response: &Value) {
28        // Check if response contains error
29        let icon = if let Value::Array(results) = response {
30            if results
31                .iter()
32                .any(|r| r.get("status").and_then(|s| s.as_str()) == Some("ERR"))
33            {
34                "❌"
35            } else {
36                "✅"
37            }
38        } else if response.get("error").is_some() {
39            "❌"
40        } else {
41            "✅"
42        };
43
44        let response_str = serde_json::to_string(response).unwrap_or_default();
45        println!("{} {}", icon, response_str);
46    }
47}
48
49#[async_trait]
50impl Engine for DebugEngine {
51    async fn send_message(&mut self, method: &str, params: Value) -> Result<Value> {
52        self.log_request(method, &params);
53        let response = self.inner.send_message(method, params).await?;
54        self.log_response(&response);
55        Ok(response)
56    }
57
58    async fn send_message_cbor(&mut self, method: &str, params: CborValue) -> Result<CborValue> {
59        println!("🔍 Surreal CBOR RPC: {} {:?}", method, params);
60        let response = self.inner.send_message_cbor(method, params).await?;
61        println!("✅ CBOR Response: {:?}", response);
62        Ok(response)
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    struct MockEngine;
71
72    #[async_trait]
73    impl Engine for MockEngine {
74        async fn send_message(&mut self, _method: &str, _params: Value) -> Result<Value> {
75            Ok(serde_json::json!({
76                "status": "OK",
77                "result": []
78            }))
79        }
80
81        async fn send_message_cbor(
82            &mut self,
83            _method: &str,
84            _params: CborValue,
85        ) -> Result<CborValue> {
86            Ok(CborValue::Map(vec![
87                (
88                    CborValue::Text("status".to_string()),
89                    CborValue::Text("OK".to_string()),
90                ),
91                (
92                    CborValue::Text("result".to_string()),
93                    CborValue::Array(vec![]),
94                ),
95            ]))
96        }
97    }
98
99    #[tokio::test]
100    async fn test_debug_engine_wraps_correctly() {
101        let mock = Box::new(MockEngine);
102        let mut debug_engine = DebugEngine::wrap(mock);
103
104        let result = debug_engine
105            .send_message("test", serde_json::json!(["param1"]))
106            .await;
107
108        assert!(result.is_ok());
109    }
110}