solders_traits_core/
lib.rs1use pyo3::{
2 exceptions::{PyTypeError, PyValueError},
3 prelude::*,
4 pyclass::CompareOp,
5 types::PyBytes,
6};
7use serde::{Deserialize, Serialize};
8use std::{
9 collections::hash_map::DefaultHasher,
10 fmt,
11 hash::{Hash, Hasher},
12};
13
14pub fn richcmp_type_error(op: &str) -> PyErr {
15 let msg = format!("{op} not supported.");
16 PyTypeError::new_err(msg)
17}
18
19fn calculate_hash<T>(t: &T) -> u64
20where
21 T: Hash + ?Sized,
22{
23 let mut s = DefaultHasher::new();
24 t.hash(&mut s);
25 s.finish()
26}
27
28pub trait RichcmpEqualityOnly: PartialEq {
29 fn richcmp(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
30 match op {
31 CompareOp::Eq => Ok(self == other),
32 CompareOp::Ne => Ok(self != other),
33 CompareOp::Lt => Err(richcmp_type_error("<")),
34 CompareOp::Gt => Err(richcmp_type_error(">")),
35 CompareOp::Le => Err(richcmp_type_error("<=")),
36 CompareOp::Ge => Err(richcmp_type_error(">=")),
37 }
38 }
39}
40pub trait RichcmpFull: PartialEq + PartialOrd {
41 fn richcmp(&self, other: &Self, op: CompareOp) -> bool {
42 match op {
43 CompareOp::Eq => self == other,
44 CompareOp::Ne => self != other,
45 CompareOp::Lt => self < other,
46 CompareOp::Gt => self > other,
47 CompareOp::Le => self <= other,
48 CompareOp::Ge => self >= other,
49 }
50 }
51}
52
53#[macro_export]
54macro_rules! impl_display {
55 ($ident:ident) => {
56 impl std::fmt::Display for $ident {
57 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
58 write!(f, "{:?}", self.0)
59 }
60 }
61 };
62}
63
64pub trait PyHash: Hash {
65 fn pyhash(&self) -> u64 {
66 calculate_hash(self)
67 }
68}
69
70pub trait PyBytesSlice: AsRef<[u8]> {
71 fn pybytes_slice<'a>(&self, py: Python<'a>) -> &'a PyBytes {
72 PyBytes::new(py, AsRef::<[u8]>::as_ref(self))
73 }
74}
75
76#[macro_export]
77macro_rules! pybytes_general_for_pybytes_slice {
78 ($ident:ident) => {
79 impl $crate::PyBytesGeneral for $ident {
80 fn pybytes_general<'a>(
81 &self,
82 py: pyo3::prelude::Python<'a>,
83 ) -> &'a pyo3::types::PyBytes {
84 $crate::PyBytesSlice::pybytes_slice(self, py)
85 }
86 }
87 };
88}
89
90#[macro_export]
91macro_rules! pybytes_general_for_pybytes_bincode {
92 ($ident:ident) => {
93 impl $crate::PyBytesGeneral for $ident {
94 fn pybytes_general<'a>(
95 &self,
96 py: pyo3::prelude::Python<'a>,
97 ) -> &'a pyo3::types::PyBytes {
98 $crate::PyBytesBincode::pybytes_bincode(self, py)
99 }
100 }
101 };
102}
103
104#[macro_export]
105macro_rules! pybytes_general_for_pybytes_cbor {
106 ($ident:ident) => {
107 impl $crate::PyBytesGeneral for $ident {
108 fn pybytes_general<'a>(
109 &self,
110 py: pyo3::prelude::Python<'a>,
111 ) -> &'a pyo3::types::PyBytes {
112 $crate::PyBytesCbor::pybytes_cbor(self, py)
113 }
114 }
115 };
116}
117
118pub trait PyBytesBincode: Serialize {
119 fn pybytes_bincode<'a>(&self, py: Python<'a>) -> &'a PyBytes {
120 PyBytes::new(py, &bincode::serialize(self).unwrap())
121 }
122}
123
124pub trait PyBytesCbor: Serialize + std::marker::Sized {
125 fn pybytes_cbor<'a>(&self, py: Python<'a>) -> &'a PyBytes {
126 PyBytes::new(py, &serde_cbor::to_vec(self).unwrap())
127 }
128}
129
130pub trait PyBytesGeneral {
131 fn pybytes_general<'a>(&self, py: Python<'a>) -> &'a PyBytes;
132}
133
134#[macro_export]
135macro_rules! pybytes_general_via_slice {
136 ($ident:ident) => {
137 impl $crate::PyBytesSlice for $ident {}
138 $crate::pybytes_general_for_pybytes_slice!($ident);
139 };
140}
141
142#[macro_export]
143macro_rules! pybytes_general_via_bincode {
144 ($ident:ident) => {
145 impl $crate::PyBytesBincode for $ident {}
146 $crate::pybytes_general_for_pybytes_bincode!($ident);
147 };
148}
149
150#[macro_export]
151macro_rules! pybytes_general_via_cbor {
152 ($ident:ident) => {
153 impl $crate::PyBytesCbor for $ident {}
154 $crate::pybytes_general_for_pybytes_cbor!($ident);
155 };
156}
157
158#[macro_export]
159macro_rules! py_from_bytes_general_for_py_from_bytes_bincode {
160 ($ident:ident) => {
161 impl $crate::PyFromBytesGeneral for $ident {
162 fn py_from_bytes_general(raw: &[u8]) -> PyResult<Self> {
163 <Self as $crate::PyFromBytesBincode>::py_from_bytes_bincode(raw)
164 }
165 }
166 };
167}
168
169#[macro_export]
170macro_rules! py_from_bytes_general_for_py_from_bytes_cbor {
171 ($ident:ident) => {
172 impl $crate::PyFromBytesGeneral for $ident {
173 fn py_from_bytes_general(raw: &[u8]) -> PyResult<Self> {
174 <Self as $crate::PyFromBytesCbor>::py_from_bytes_cbor(raw)
175 }
176 }
177 };
178}
179
180#[macro_export]
181macro_rules! py_from_bytes_general_via_bincode {
182 ($ident:ident) => {
183 impl $crate::PyFromBytesBincode<'_> for $ident {}
184 $crate::py_from_bytes_general_for_py_from_bytes_bincode!($ident);
185 };
186}
187
188pub fn to_py_value_err(err: &impl ToString) -> PyErr {
189 PyValueError::new_err(err.to_string())
190}
191
192pub fn handle_py_value_err<T: Into<P>, E: ToString, P>(res: Result<T, E>) -> PyResult<P> {
193 res.map_or_else(|e| Err(to_py_value_err(&e)), |v| Ok(v.into()))
194}
195
196pub trait PyFromBytesBincode<'b>: Deserialize<'b> {
197 fn py_from_bytes_bincode(raw: &'b [u8]) -> PyResult<Self> {
198 let deser = bincode::deserialize::<Self>(raw);
199 handle_py_value_err(deser)
200 }
201}
202
203#[macro_export]
204macro_rules! py_from_bytes_general_via_cbor {
205 ($ident:ident) => {
206 impl $crate::PyFromBytesCbor<'_> for $ident {}
207 $crate::py_from_bytes_general_for_py_from_bytes_cbor!($ident);
208 };
209}
210
211pub trait PyFromBytesCbor<'b>: Deserialize<'b> {
212 fn py_from_bytes_cbor(raw: &'b [u8]) -> PyResult<Self> {
213 let deser = serde_cbor::from_slice::<Self>(raw);
214 handle_py_value_err(deser)
215 }
216}
217
218pub trait PyFromBytesGeneral: Sized {
219 fn py_from_bytes_general(raw: &[u8]) -> PyResult<Self>;
220}
221
222pub trait CommonMethodsCore:
223 fmt::Display + fmt::Debug + PyBytesGeneral + PyFromBytesGeneral + IntoPy<PyObject> + Clone
224{
225 fn pybytes<'b>(&self, py: Python<'b>) -> &'b PyBytes {
226 PyBytesGeneral::pybytes_general(self, py)
227 }
228
229 fn pystr(&self) -> String {
230 self.to_string()
231 }
232 fn pyrepr(&self) -> String {
233 format!("{self:#?}")
234 }
235
236 fn py_from_bytes(raw: &[u8]) -> PyResult<Self> {
237 <Self as PyFromBytesGeneral>::py_from_bytes_general(raw)
238 }
239
240 fn pyreduce(&self) -> PyResult<(PyObject, PyObject)> {
241 let cloned = self.clone();
242 Python::with_gil(|py| {
243 let constructor = cloned.into_py(py).getattr(py, "from_bytes")?;
244 Ok((
245 constructor,
246 (PyBytesGeneral::pybytes_general(self, py).to_object(py),).to_object(py),
247 ))
248 })
249 }
250}
251
252pub trait CommonMethods<'a>: CommonMethodsCore + Serialize + Deserialize<'a> {
253 fn py_to_json(&self) -> String {
254 serde_json::to_string(self).unwrap()
255 }
256
257 fn py_from_json(raw: &'a str) -> PyResult<Self> {
258 serde_json::from_str(raw).map_err(|e| to_py_value_err(&e))
259 }
260}
261
262#[macro_export]
263macro_rules! common_methods_default {
264 ($ty:ty) => {
265 impl $crate::CommonMethodsCore for $ty {}
266 impl $crate::CommonMethods<'_> for $ty {}
267 };
268}
269
270#[macro_export]
271macro_rules! transaction_status_boilerplate {
272 ($name:ident) => {
273 impl $crate::RichcmpEqualityOnly for $name {}
274 impl std::fmt::Display for $name {
275 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
276 write!(f, "{:?}", self)
277 }
278 }
279 $crate::pybytes_general_via_bincode!($name);
280 $crate::py_from_bytes_general_via_bincode!($name);
281 $crate::common_methods_default!($name);
282 };
283}