seda_sdk_rs/bytes.rs
1//! Module for handling byte arrays in a oracle program compatible way.
2//!
3//! Creating a standardized way to handle byte arrays is important for
4//! oracle programs, as they are expected to return promises in a specific
5//! format.
6//!
7//! This module provides a [`Bytes`] type that wraps a vector of bytes
8//! and implements the [`ToBytes`] and [`FromBytes`] traits for various types.
9
10use std::ops::Deref;
11
12use serde::{Deserialize, Serialize};
13
14use crate::{errors, errors::Result};
15
16/// A wrapper around a vector of bytes that provides convenience methods for
17/// the format that oracle promises are expected to be in.
18#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
19pub struct Bytes(Vec<u8>);
20
21impl Bytes {
22 /// Get the inner vector of bytes.
23 ///
24 /// # Examples
25 ///
26 /// ```
27 /// use seda_sdk_rs::bytes::{Bytes, ToBytes};
28 /// let bytes: Bytes = "Hello, world!".to_bytes();
29 /// let inner: Vec<u8> = bytes.eject();
30 /// assert_eq!(inner, b"Hello, world!");
31 /// ```
32 pub fn eject(self) -> Vec<u8> {
33 self.0
34 }
35}
36
37impl Deref for Bytes {
38 type Target = [u8];
39
40 fn deref(&self) -> &Self::Target {
41 self.0.as_slice()
42 }
43}
44
45/// A trait for types that can be converted to the [`Bytes`] type.
46///
47/// This trait is implemented for various types such as `Vec<u8>`, `String`, `&str`, and primitive types like `u8`, `i32`, etc.
48///
49/// Additionally it supports an JSON serializable type via the [`serde`] and [`serde_json`] crates.
50pub trait ToBytes {
51 /// Converts the implementing type to a [`Bytes`] instance.
52 ///
53 /// # Examples
54 ///
55 /// ```
56 /// use seda_sdk_rs::bytes::{Bytes, ToBytes};
57 /// let bytes: Bytes = "Hello, world!".to_bytes();
58 /// assert_eq!(bytes.eject(), b"Hello, world!");
59 /// ```
60 fn to_bytes(self) -> Bytes;
61}
62
63impl ToBytes for Bytes {
64 fn to_bytes(self) -> Bytes {
65 self
66 }
67}
68
69impl ToBytes for Vec<u8> {
70 fn to_bytes(self) -> Bytes {
71 Bytes(self)
72 }
73}
74
75impl ToBytes for String {
76 fn to_bytes(self) -> Bytes {
77 Bytes(self.as_bytes().to_vec())
78 }
79}
80
81impl ToBytes for &str {
82 fn to_bytes(self) -> Bytes {
83 Bytes(self.as_bytes().to_vec())
84 }
85}
86
87impl ToBytes for bool {
88 fn to_bytes(self) -> Bytes {
89 Bytes(vec![self as u8])
90 }
91}
92
93impl ToBytes for () {
94 fn to_bytes(self) -> Bytes {
95 Bytes::default()
96 }
97}
98
99/// A trait to convert [`Bytes`] into a specific type.
100///
101/// This trait is implemented for various types such as `Vec<u8>`, `String`, `&str`, and primitive types like `u8`, `i32`, etc.
102///
103/// Additionally it supports an JSON deserializable type via the [`serde`] and [`serde_json`] crates.
104pub trait FromBytes
105where
106 Self: Sized,
107{
108 /// A way to convert the [`Bytes`] into the implementing type without consuming the original bytes.
109 ///
110 /// # Errors
111 ///
112 /// There are several reasons why this conversion might fail returning an [`errors::SDKError`]:
113 ///
114 /// - [`errors::SDKError::FromUtf8Error`] if the bytes are not valid UTF-8.
115 /// - [`errors::SDKError::NumBytesConversion`] if the bytes are not of the expected length for the type of number primitive.
116 /// - [`errors::SDKError::InvalidValue`] if the bytes do not represent a valid value for the type.
117 /// - [`errors::SDKError::Serde`] if the bytes cannot be deserialized into the type.
118 ///
119 /// # Examples
120 ///
121 /// ```
122 /// use seda_sdk_rs::bytes::{FromBytes, ToBytes};
123 /// let bytes = "Hello, world!".to_bytes();
124 /// let string: String = String::from_bytes(&bytes).expect("Should be valid UTF-8");
125 /// assert_eq!(string, "Hello, world!");
126 /// ```
127 fn from_bytes(bytes: &[u8]) -> Result<Self>;
128
129 /// A way to convert the [`Bytes`] into the implementing type and consume the original bytes.
130 ///
131 /// # Errors
132 ///
133 /// There are several reasons why this conversion might fail returning an [`errors::SDKError`]:
134 ///
135 /// - [`errors::SDKError::FromUtf8Error`] if the bytes are not valid UTF-8.
136 /// - [`errors::SDKError::NumBytesConversion`] if the bytes are not of the expected length for the type of number primitive.
137 /// - [`errors::SDKError::InvalidValue`] if the bytes do not represent a valid value for the type.
138 /// - [`errors::SDKError::Serde`] if the bytes cannot be deserialized into the type.
139 ///
140 /// # Examples
141 ///
142 /// ```
143 /// use seda_sdk_rs::bytes::{FromBytes, ToBytes};
144 /// let bytes = "Hello, world!".to_bytes();
145 /// let string: String = String::from_bytes_vec(bytes.eject()).expect("Should be valid UTF-8");
146 /// assert_eq!(string, "Hello, world!");
147 /// ```
148 fn from_bytes_vec(bytes: Vec<u8>) -> Result<Self>;
149}
150
151impl FromBytes for Vec<u8> {
152 fn from_bytes(bytes: &[u8]) -> Result<Self> {
153 Ok(bytes.to_vec())
154 }
155
156 fn from_bytes_vec(bytes: Vec<u8>) -> Result<Self> {
157 Ok(bytes)
158 }
159}
160
161impl FromBytes for String {
162 fn from_bytes(bytes: &[u8]) -> Result<Self> {
163 Ok(std::str::from_utf8(bytes)?.into())
164 }
165
166 fn from_bytes_vec(bytes: Vec<u8>) -> Result<Self> {
167 Self::from_bytes(bytes.as_slice())
168 }
169}
170
171impl FromBytes for bool {
172 fn from_bytes(bytes: &[u8]) -> Result<Self> {
173 match bytes[0] {
174 0 => Ok(false),
175 1 => Ok(true),
176 _ => Err(errors::SDKError::InvalidValue),
177 }
178 }
179
180 fn from_bytes_vec(bytes: Vec<u8>) -> Result<Self> {
181 if bytes.len() != 1 {
182 Err(errors::SDKError::InvalidValue)
183 } else {
184 Self::from_bytes(bytes.as_slice())
185 }
186 }
187}
188
189macro_rules! bytes_impls_le_bytes {
190 ($type_:ty, $num_bytes:expr) => {
191 impl ToBytes for $type_ {
192 fn to_bytes(self) -> Bytes {
193 Bytes(self.to_le_bytes().to_vec())
194 }
195 }
196
197 impl FromBytes for $type_ {
198 fn from_bytes(bytes: &[u8]) -> Result<Self> {
199 let bytes: [u8; $num_bytes] = bytes.try_into()?;
200 Ok(<$type_>::from_le_bytes(bytes))
201 }
202
203 fn from_bytes_vec(bytes: Vec<u8>) -> Result<Self> {
204 Self::from_bytes(bytes.as_slice())
205 }
206 }
207 };
208}
209
210/// Implements `ToBytes` and `FromBytes` via JSON serialization/deserialization for the given type.
211///
212/// This macro generates:
213/// - a `ToBytes` impl that serializes the type to a `Vec<u8>` using `serde_json::to_vec`
214/// and wraps it in `seda_sdk_rs::Bytes`.
215/// - a `FromBytes` impl that deserializes from a `&[u8]` or `Vec<u8>` using `serde_json::from_slice`.
216///
217/// # Example
218///
219/// # Example
220///
221/// ```
222/// use serde::{Serialize, Deserialize};
223/// use seda_sdk_rs::bytes::{Bytes, ToBytes, FromBytes};
224/// use seda_sdk_rs::bytes_serde_json;
225///
226/// #[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
227/// struct MyType { foo: String, bar: i32 }
228///
229/// // Generate the ToBytes/FromBytes impls
230/// bytes_serde_json!(MyType);
231///
232/// let original = MyType { foo: "hi".into(), bar: 123 };
233/// let bytes = original.clone().to_bytes();
234/// let decoded = MyType::from_bytes(&bytes).unwrap();
235/// assert_eq!(original, decoded);
236/// ```
237#[macro_export]
238macro_rules! bytes_serde_json {
239 ($type_:ty) => {
240 impl $crate::bytes::ToBytes for $type_ {
241 fn to_bytes(self) -> $crate::bytes::Bytes {
242 serde_json::to_vec(&self).unwrap().to_bytes()
243 }
244 }
245
246 impl $crate::bytes::FromBytes for $type_ {
247 fn from_bytes(bytes: &[u8]) -> $crate::errors::Result<Self> {
248 Ok(serde_json::from_slice(bytes)?)
249 }
250
251 fn from_bytes_vec(bytes: Vec<u8>) -> $crate::errors::Result<Self> {
252 Self::from_bytes(&bytes)
253 }
254 }
255 };
256}
257
258bytes_impls_le_bytes!(u8, 1);
259bytes_impls_le_bytes!(u32, 4);
260bytes_impls_le_bytes!(u64, 8);
261bytes_impls_le_bytes!(u128, 16);
262bytes_impls_le_bytes!(i8, 1);
263bytes_impls_le_bytes!(i32, 4);
264bytes_impls_le_bytes!(i64, 8);
265bytes_impls_le_bytes!(i128, 16);
266bytes_impls_le_bytes!(f32, 4);
267bytes_impls_le_bytes!(f64, 8);