1use crate::ValkeyFuture;
19use crate::ValkeyResult;
20use crate::cmd::cmd;
21use crate::commands::core::AsyncCommands;
22use crate::value::FromValkeyValue;
23use crate::value::ValkeyValue;
24use crate::write::ToValkeyArgs;
25use glide_core::scripts_container::add_script;
26use glide_core::scripts_container::remove_script;
27
28#[doc(hidden)]
31#[sealed::sealed(pub(crate))]
32pub trait ScriptInvoke {
33 fn glide_invoke_script<'a>(
34 &'a self,
35 hash: &'a str,
36 keys: &'a [Vec<u8>],
37 args: &'a [Vec<u8>],
38 ) -> ValkeyFuture<'a, ValkeyValue>;
39}
40
41#[cfg(feature = "sync")]
45#[doc(hidden)]
46#[sealed::sealed(pub(crate))]
47pub trait ScriptInvokeSync {
48 fn glide_invoke_script(
49 &self,
50 hash: &str,
51 keys: &[Vec<u8>],
52 args: &[Vec<u8>],
53 ) -> ValkeyResult<ValkeyValue>;
54}
55
56#[derive(Debug)]
61pub struct Script {
62 code: String,
63 hash: String,
64}
65
66impl Script {
67 pub fn new(code: &str) -> Script {
69 let hash = add_script(code.as_bytes());
70 Script {
71 code: code.to_string(),
72 hash,
73 }
74 }
75
76 pub fn get_hash(&self) -> &str {
78 &self.hash
79 }
80
81 #[must_use]
83 pub fn arg<'a, T: ToValkeyArgs>(&'a self, arg: T) -> ScriptInvocation<'a> {
84 let mut invocation = self.prepare_invoke();
85 invocation.arg(arg);
86 invocation
87 }
88
89 #[must_use]
91 pub fn key<'a, T: ToValkeyArgs>(&'a self, key: T) -> ScriptInvocation<'a> {
92 let mut invocation = self.prepare_invoke();
93 invocation.key(key);
94 invocation
95 }
96
97 #[must_use]
99 pub fn prepare_invoke(&self) -> ScriptInvocation<'_> {
100 ScriptInvocation {
101 script: self,
102 args: Vec::new(),
103 keys: Vec::new(),
104 }
105 }
106
107 pub async fn invoke_async<C: ScriptInvoke, T: FromValkeyValue>(
109 &self,
110 con: &C,
111 ) -> ValkeyResult<T> {
112 self.prepare_invoke().invoke_async(con).await
113 }
114
115 #[cfg(feature = "sync")]
118 pub fn invoke<C: ScriptInvokeSync, T: FromValkeyValue>(&self, con: &C) -> ValkeyResult<T> {
119 self.prepare_invoke().invoke(con)
120 }
121
122 pub async fn load_async<C: AsyncCommands>(&self, con: &C) -> ValkeyResult<String> {
125 let mut load = cmd("SCRIPT");
126 load.arg("LOAD").arg(self.code.as_bytes());
127 String::from_owned_valkey_value(con.glide_send_command(load).await?)
128 }
129
130 #[cfg(feature = "sync")]
133 pub fn load<C: crate::commands::core::Commands>(&self, con: &C) -> ValkeyResult<String> {
134 let mut load = cmd("SCRIPT");
135 load.arg("LOAD").arg(self.code.as_bytes());
136 String::from_owned_valkey_value(con.glide_send_command(load)?)
137 }
138}
139
140impl Clone for Script {
141 fn clone(&self) -> Self {
142 add_script(self.code.as_bytes());
144 Script {
145 code: self.code.clone(),
146 hash: self.hash.clone(),
147 }
148 }
149}
150
151impl Drop for Script {
152 fn drop(&mut self) {
153 remove_script(&self.hash);
154 }
155}
156
157#[derive(Debug, Clone)]
159pub struct ScriptInvocation<'a> {
160 script: &'a Script,
161 args: Vec<Vec<u8>>,
162 keys: Vec<Vec<u8>>,
163}
164
165impl ScriptInvocation<'_> {
166 pub fn arg<T: ToValkeyArgs>(&mut self, arg: T) -> &mut Self {
168 arg.write_valkey_args(&mut self.args);
169 self
170 }
171
172 pub fn key<T: ToValkeyArgs>(&mut self, key: T) -> &mut Self {
174 key.write_valkey_args(&mut self.keys);
175 self
176 }
177
178 pub async fn invoke_async<C: ScriptInvoke, T: FromValkeyValue>(
184 &self,
185 con: &C,
186 ) -> ValkeyResult<T> {
187 let value = con
188 .glide_invoke_script(&self.script.hash, &self.keys, &self.args)
189 .await?;
190 T::from_owned_valkey_value(value)
191 }
192
193 #[cfg(feature = "sync")]
199 pub fn invoke<C: ScriptInvokeSync, T: FromValkeyValue>(&self, con: &C) -> ValkeyResult<T> {
200 let value = con.glide_invoke_script(&self.script.hash, &self.keys, &self.args)?;
201 T::from_owned_valkey_value(value)
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn sha1_matches_server_semantics() {
211 let script = Script::new("return 1");
214 assert_eq!(
215 script.get_hash(),
216 "e0e1f9fabfc9d4800c877a703b823ac0578ff8db"
217 );
218 }
219
220 #[test]
221 fn invocation_collects_keys_and_args() {
222 let script = Script::new("return KEYS[1]");
223 let mut invocation = script.prepare_invoke();
224 invocation.key("k1").key("k2").arg("a1").arg(2);
225 assert_eq!(invocation.keys, vec![b"k1".to_vec(), b"k2".to_vec()]);
226 assert_eq!(invocation.args, vec![b"a1".to_vec(), b"2".to_vec()]);
227 }
228}