1use crate::client::SynapClient;
4use crate::error::Result;
5use serde::de::DeserializeOwned;
6use serde::{Deserialize, Serialize};
7use serde_json::{Value, json};
8
9#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11pub struct ScriptEvalOptions {
12 #[serde(default)]
13 pub keys: Vec<String>,
14 #[serde(default)]
15 pub args: Vec<Value>,
16 #[serde(rename = "timeout_ms")]
17 pub timeout_ms: Option<u64>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ScriptEvalResponse<T> {
23 pub result: T,
24 pub sha1: String,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ScriptExistsResponse {
30 pub exists: Vec<bool>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ScriptFlushResponse {
36 pub cleared: u64,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct ScriptKillResponse {
42 pub terminated: bool,
43}
44
45#[derive(Clone)]
47pub struct ScriptManager {
48 client: SynapClient,
49}
50
51impl ScriptManager {
52 pub(crate) fn new(client: SynapClient) -> Self {
53 Self { client }
54 }
55
56 pub async fn eval<T>(
58 &self,
59 script: &str,
60 options: ScriptEvalOptions,
61 ) -> Result<ScriptEvalResponse<T>>
62 where
63 T: DeserializeOwned,
64 {
65 let payload = json!({
66 "script": script,
67 "keys": options.keys,
68 "args": options.args,
69 "timeout_ms": options.timeout_ms,
70 });
71
72 let response = self.client.send_command("script.eval", payload).await?;
73 self.parse_eval_response(response)
74 }
75
76 pub async fn evalsha<T>(
78 &self,
79 sha1: &str,
80 options: ScriptEvalOptions,
81 ) -> Result<ScriptEvalResponse<T>>
82 where
83 T: DeserializeOwned,
84 {
85 let payload = json!({
86 "sha1": sha1,
87 "keys": options.keys,
88 "args": options.args,
89 "timeout_ms": options.timeout_ms,
90 });
91
92 let response = self.client.send_command("script.evalsha", payload).await?;
93 self.parse_eval_response(response)
94 }
95
96 pub async fn load(&self, script: &str) -> Result<String> {
98 let payload = json!({ "script": script });
99 let response = self.client.send_command("script.load", payload).await?;
100 Ok(response["sha1"].as_str().unwrap_or_default().to_string())
101 }
102
103 pub async fn exists(&self, hashes: &[impl AsRef<str>]) -> Result<Vec<bool>> {
105 let payload = json!({
106 "hashes": hashes.iter().map(|h| h.as_ref()).collect::<Vec<_>>()
107 });
108 let response = self.client.send_command("script.exists", payload).await?;
109 let parsed: ScriptExistsResponse = serde_json::from_value(response)?;
110 Ok(parsed.exists)
111 }
112
113 pub async fn flush(&self) -> Result<u64> {
115 let response = self.client.send_command("script.flush", json!({})).await?;
116 let parsed: ScriptFlushResponse = serde_json::from_value(response)?;
117 Ok(parsed.cleared)
118 }
119
120 pub async fn kill(&self) -> Result<bool> {
122 let response = self.client.send_command("script.kill", json!({})).await?;
123 let parsed: ScriptKillResponse = serde_json::from_value(response)?;
124 Ok(parsed.terminated)
125 }
126
127 fn parse_eval_response<T>(&self, response: Value) -> Result<ScriptEvalResponse<T>>
128 where
129 T: DeserializeOwned,
130 {
131 let sha1 = response["sha1"].as_str().unwrap_or_default().to_string();
132 let result_value = response.get("result").cloned().unwrap_or(Value::Null);
133 let result: T = serde_json::from_value(result_value)?;
134 Ok(ScriptEvalResponse { result, sha1 })
135 }
136}