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}