88 pyo3:: {
99 exceptions:: PyAssertionError ,
1010 types:: { PyBytes , PyMapping , PyModule } ,
11- Py , PyErr , PyObject , PyResult , Python ,
11+ Py , PyAny , PyErr , PyObject , PyResult , Python , ToPyObject ,
1212 } ,
1313 spin_sdk:: {
1414 config,
1515 http:: { Request , Response } ,
16- outbound_http, redis,
16+ key_value, outbound_http,
17+ redis:: { self , RedisParameter , RedisResult } ,
1718 } ,
1819 std:: { env, ops:: Deref , str} ,
1920} ;
@@ -87,6 +88,40 @@ impl HttpResponse {
8788 }
8889}
8990
91+ #[ derive( Clone ) ]
92+ #[ pyo3:: pyclass]
93+ #[ pyo3( name = "Store" ) ]
94+ struct Store {
95+ inner : key_value:: Store ,
96+ }
97+
98+ #[ pyo3:: pymethods]
99+ impl Store {
100+ fn get ( & self , py : Python < ' _ > , key : String ) -> PyResult < PyObject > {
101+ match self . inner . get ( key) {
102+ Ok ( v) => bytes ( py, & v) . map ( PyObject :: from) ,
103+ Err ( key_value:: Error :: NoSuchKey ) => Ok ( py. None ( ) ) ,
104+ Err ( e) => Err ( PyErr :: from ( Anyhow :: from ( e) ) ) ,
105+ }
106+ }
107+
108+ fn set ( & self , key : String , value : & PyBytes ) -> Result < ( ) , Anyhow > {
109+ self . inner . set ( key, value. as_bytes ( ) ) . map_err ( Anyhow :: from)
110+ }
111+
112+ fn delete ( & self , key : String ) -> Result < ( ) , Anyhow > {
113+ self . inner . delete ( key) . map_err ( Anyhow :: from)
114+ }
115+
116+ fn exists ( & self , key : String ) -> Result < bool , Anyhow > {
117+ self . inner . exists ( key) . map_err ( Anyhow :: from)
118+ }
119+
120+ fn get_keys ( & self ) -> Result < Vec < String > , Anyhow > {
121+ self . inner . get_keys ( ) . map_err ( Anyhow :: from)
122+ }
123+ }
124+
90125struct Anyhow ( Error ) ;
91126
92127impl From < Anyhow > for PyErr {
@@ -213,6 +248,45 @@ fn redis_srem(address: String, key: String, values: Vec<String>) -> PyResult<i64
213248 . map_err ( |_| PyErr :: from ( Anyhow ( anyhow ! ( "Error executing Redis set command" ) ) ) )
214249}
215250
251+ #[ pyo3:: pyfunction]
252+ #[ pyo3( pass_module) ]
253+ fn redis_execute (
254+ module : & PyModule ,
255+ address : String ,
256+ command : String ,
257+ arguments : Vec < & PyAny > ,
258+ ) -> PyResult < Vec < PyObject > > {
259+ let arguments = arguments
260+ . iter ( )
261+ . map ( |v| {
262+ if let Ok ( v) = v. extract :: < i64 > ( ) {
263+ Ok ( RedisParameter :: Int64 ( v) )
264+ } else if let Ok ( v) = v. downcast :: < PyBytes > ( ) {
265+ Ok ( RedisParameter :: Binary ( v. as_bytes ( ) ) )
266+ } else {
267+ Err ( PyErr :: from ( Anyhow ( anyhow ! (
268+ "Unable to use {v:?} as a Redis `execute` argument \
269+ -- expected `int` or `bytes`"
270+ ) ) ) )
271+ }
272+ } )
273+ . collect :: < PyResult < Vec < _ > > > ( ) ?;
274+
275+ redis:: execute ( & address, & command, & arguments)
276+ . map_err ( |_| PyErr :: from ( Anyhow ( anyhow ! ( "Error executing Redis set command" ) ) ) )
277+ . and_then ( |results| {
278+ results
279+ . into_iter ( )
280+ . map ( |v| match v {
281+ RedisResult :: Nil => Ok ( module. py ( ) . None ( ) ) ,
282+ RedisResult :: Status ( v) => Ok ( v. to_object ( module. py ( ) ) ) ,
283+ RedisResult :: Int64 ( v) => Ok ( v. to_object ( module. py ( ) ) ) ,
284+ RedisResult :: Binary ( v) => bytes ( module. py ( ) , & v) . map ( PyObject :: from) ,
285+ } )
286+ . collect :: < PyResult < _ > > ( )
287+ } )
288+ }
289+
216290#[ pyo3:: pymodule]
217291#[ pyo3( name = "spin_redis" ) ]
218292fn spin_redis_module ( _py : Python < ' _ > , module : & PyModule ) -> PyResult < ( ) > {
@@ -223,7 +297,29 @@ fn spin_redis_module(_py: Python<'_>, module: &PyModule) -> PyResult<()> {
223297 module. add_function ( pyo3:: wrap_pyfunction!( redis_sadd, module) ?) ?;
224298 module. add_function ( pyo3:: wrap_pyfunction!( redis_set, module) ?) ?;
225299 module. add_function ( pyo3:: wrap_pyfunction!( redis_smembers, module) ?) ?;
226- module. add_function ( pyo3:: wrap_pyfunction!( redis_srem, module) ?)
300+ module. add_function ( pyo3:: wrap_pyfunction!( redis_srem, module) ?) ?;
301+ module. add_function ( pyo3:: wrap_pyfunction!( redis_execute, module) ?)
302+ }
303+
304+ #[ pyo3:: pyfunction]
305+ fn kv_open ( name : String ) -> Result < Store , Anyhow > {
306+ Ok ( Store {
307+ inner : key_value:: Store :: open ( name) . map_err ( Anyhow :: from) ?,
308+ } )
309+ }
310+
311+ #[ pyo3:: pyfunction]
312+ fn kv_open_default ( ) -> Result < Store , Anyhow > {
313+ Ok ( Store {
314+ inner : key_value:: Store :: open_default ( ) . map_err ( Anyhow :: from) ?,
315+ } )
316+ }
317+
318+ #[ pyo3:: pymodule]
319+ #[ pyo3( name = "spin_key_value" ) ]
320+ fn spin_key_value_module ( _py : Python < ' _ > , module : & PyModule ) -> PyResult < ( ) > {
321+ module. add_function ( pyo3:: wrap_pyfunction!( kv_open, module) ?) ?;
322+ module. add_function ( pyo3:: wrap_pyfunction!( kv_open_default, module) ?)
227323}
228324
229325#[ pyo3:: pyfunction]
@@ -241,6 +337,7 @@ fn do_init() -> Result<()> {
241337 pyo3:: append_to_inittab!( spin_http_module) ;
242338 pyo3:: append_to_inittab!( spin_redis_module) ;
243339 pyo3:: append_to_inittab!( spin_config_module) ;
340+ pyo3:: append_to_inittab!( spin_key_value_module) ;
244341
245342 pyo3:: prepare_freethreaded_python ( ) ;
246343
0 commit comments