Skip to main content

oo7/
lib.rs

1#![allow(unsafe_op_in_unsafe_fn)]
2
3use std::{collections::HashMap, sync::Arc};
4
5use pyo3::{
6    exceptions::{PyRuntimeError, PyValueError},
7    prelude::*,
8    types::PyBytes,
9};
10use pyo3_async_runtimes::tokio::future_into_py;
11
12fn convert_error(err: oo7_rs::Error) -> PyErr {
13    PyRuntimeError::new_err(format!("oo7 error: {:?}", err))
14}
15
16#[derive(Clone)]
17struct SecretBytes(Vec<u8>);
18
19impl<'py> IntoPyObject<'py> for SecretBytes {
20    type Target = PyBytes;
21    type Output = Bound<'py, Self::Target>;
22    type Error = std::convert::Infallible;
23
24    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
25        Ok(PyBytes::new(py, &self.0))
26    }
27}
28
29fn python_to_secret(obj: &Bound<PyAny>) -> PyResult<oo7_rs::Secret> {
30    if let Ok(bytes) = obj.extract::<Vec<u8>>() {
31        Ok(oo7_rs::Secret::from(bytes))
32    } else if let Ok(text) = obj.extract::<String>() {
33        Ok(oo7_rs::Secret::text(&text))
34    } else {
35        Err(PyValueError::new_err("Secret must be either bytes or str"))
36    }
37}
38
39#[pyclass]
40struct Keyring {
41    inner: Arc<oo7_rs::Keyring>,
42}
43
44#[pymethods]
45impl Keyring {
46    #[staticmethod]
47    #[pyo3(name = "new")]
48    fn create(py: Python<'_>) -> PyResult<Bound<'_, PyAny>> {
49        future_into_py(py, async move {
50            let keyring = oo7_rs::Keyring::new().await.map_err(convert_error)?;
51            Ok(Keyring {
52                inner: Arc::new(keyring),
53            })
54        })
55    }
56
57    fn unlock<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
58        let inner = Arc::clone(&self.inner);
59        future_into_py(py, async move {
60            inner.unlock().await.map_err(convert_error)?;
61            Ok(())
62        })
63    }
64
65    fn lock<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
66        let inner = Arc::clone(&self.inner);
67        future_into_py(py, async move {
68            inner.lock().await.map_err(convert_error)?;
69            Ok(())
70        })
71    }
72
73    fn is_locked<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
74        let inner = Arc::clone(&self.inner);
75        future_into_py(py, async move {
76            let locked = inner.is_locked().await.map_err(convert_error)?;
77            Ok(locked)
78        })
79    }
80
81    fn delete<'p>(
82        &self,
83        py: Python<'p>,
84        attributes: HashMap<String, String>,
85    ) -> PyResult<Bound<'p, PyAny>> {
86        let inner = Arc::clone(&self.inner);
87        future_into_py(py, async move {
88            inner.delete(&attributes).await.map_err(convert_error)?;
89            Ok(())
90        })
91    }
92
93    fn items<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
94        let inner = Arc::clone(&self.inner);
95        future_into_py(py, async move {
96            let items = inner.items().await.map_err(convert_error)?;
97            let py_items: Vec<Item> = items
98                .into_iter()
99                .map(|item| Item {
100                    inner: Arc::new(item),
101                })
102                .collect();
103            Ok(py_items)
104        })
105    }
106
107    fn create_item<'p>(
108        &self,
109        py: Python<'p>,
110        label: String,
111        attributes: HashMap<String, String>,
112        secret: &Bound<'p, PyAny>,
113        replace: bool,
114    ) -> PyResult<Bound<'p, PyAny>> {
115        let inner = Arc::clone(&self.inner);
116        let secret = python_to_secret(secret)?;
117
118        future_into_py(py, async move {
119            inner
120                .create_item(&label, &attributes, secret, replace)
121                .await
122                .map_err(convert_error)?;
123            Ok(())
124        })
125    }
126
127    fn search_items<'p>(
128        &self,
129        py: Python<'p>,
130        attributes: HashMap<String, String>,
131    ) -> PyResult<Bound<'p, PyAny>> {
132        let inner = Arc::clone(&self.inner);
133        future_into_py(py, async move {
134            let items = inner
135                .search_items(&attributes)
136                .await
137                .map_err(convert_error)?;
138            let py_items: Vec<Item> = items
139                .into_iter()
140                .map(|item| Item {
141                    inner: Arc::new(item),
142                })
143                .collect();
144            Ok(py_items)
145        })
146    }
147}
148
149#[pyclass]
150struct Item {
151    inner: Arc<oo7_rs::Item>,
152}
153
154#[pymethods]
155impl Item {
156    fn label<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
157        let inner = Arc::clone(&self.inner);
158        future_into_py(py, async move {
159            let label = inner.label().await.map_err(convert_error)?;
160            Ok(label)
161        })
162    }
163
164    fn set_label<'p>(&self, py: Python<'p>, label: String) -> PyResult<Bound<'p, PyAny>> {
165        let inner = Arc::clone(&self.inner);
166        future_into_py(py, async move {
167            inner.set_label(&label).await.map_err(convert_error)?;
168            Ok(())
169        })
170    }
171
172    fn attributes<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
173        let inner = Arc::clone(&self.inner);
174        future_into_py(py, async move {
175            let attrs = inner.attributes().await.map_err(convert_error)?;
176            Ok(attrs)
177        })
178    }
179
180    fn set_attributes<'p>(
181        &self,
182        py: Python<'p>,
183        attributes: HashMap<String, String>,
184    ) -> PyResult<Bound<'p, PyAny>> {
185        let inner = Arc::clone(&self.inner);
186        future_into_py(py, async move {
187            inner
188                .set_attributes(&attributes)
189                .await
190                .map_err(convert_error)?;
191            Ok(())
192        })
193    }
194
195    fn secret<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
196        let inner = Arc::clone(&self.inner);
197        future_into_py(py, async move {
198            let secret = inner.secret().await.map_err(convert_error)?;
199            let bytes = SecretBytes(secret.as_bytes().to_vec());
200            Ok(bytes)
201        })
202    }
203
204    fn set_secret<'p>(
205        &self,
206        py: Python<'p>,
207        secret: &Bound<'p, PyAny>,
208    ) -> PyResult<Bound<'p, PyAny>> {
209        let inner = Arc::clone(&self.inner);
210        let secret = python_to_secret(secret)?;
211
212        future_into_py(py, async move {
213            inner.set_secret(secret).await.map_err(convert_error)?;
214            Ok(())
215        })
216    }
217
218    fn is_locked<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
219        let inner = Arc::clone(&self.inner);
220        future_into_py(py, async move {
221            let locked = inner.is_locked().await.map_err(convert_error)?;
222            Ok(locked)
223        })
224    }
225
226    fn lock<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
227        let inner = Arc::clone(&self.inner);
228        future_into_py(py, async move {
229            inner.lock().await.map_err(convert_error)?;
230            Ok(())
231        })
232    }
233
234    fn unlock<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
235        let inner = Arc::clone(&self.inner);
236        future_into_py(py, async move {
237            inner.unlock().await.map_err(convert_error)?;
238            Ok(())
239        })
240    }
241
242    fn delete<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
243        let inner = Arc::clone(&self.inner);
244        future_into_py(py, async move {
245            inner.delete().await.map_err(convert_error)?;
246            Ok(())
247        })
248    }
249
250    fn created<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
251        let inner = Arc::clone(&self.inner);
252        future_into_py(py, async move {
253            let duration = inner.created().await.map_err(convert_error)?;
254            Ok(duration.as_secs_f64())
255        })
256    }
257
258    fn modified<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>> {
259        let inner = Arc::clone(&self.inner);
260        future_into_py(py, async move {
261            let duration = inner.modified().await.map_err(convert_error)?;
262            Ok(duration.as_secs_f64())
263        })
264    }
265}
266
267#[pymodule]
268fn oo7(m: &Bound<'_, PyModule>) -> PyResult<()> {
269    m.add_class::<Keyring>()?;
270    m.add_class::<Item>()?;
271    Ok(())
272}