sequencer_utils/
ser.rs

1use serde::{
2    de::{DeserializeOwned, Deserializer, Error as _},
3    ser::{Error as _, Serializer},
4    Deserialize, Serialize,
5};
6
7/// Types which can be deserialized from either integers or strings.
8///
9/// Some types can be represented as an integer or a string in human-readable formats like JSON or
10/// TOML. For example, 1 GWEI might be represented by the integer `1000000000` or the string `"1
11/// gwei"`. Such types can implement `FromStringOrInteger` and then use [`impl_string_or_integer`]
12/// to derive this user-friendly serialization.
13///
14/// These types are assumed to have an efficient representation as an integral type in Rust --
15/// [`Self::Binary`] -- and will be serialized to and from this type when using a non-human-readable
16/// encoding. With human readable encodings, serialization is always to a string.
17pub trait FromStringOrInteger: Sized {
18    type Binary: Serialize + DeserializeOwned;
19    type Integer: Serialize + DeserializeOwned;
20
21    fn from_binary(b: Self::Binary) -> anyhow::Result<Self>;
22    fn from_string(s: String) -> anyhow::Result<Self>;
23    fn from_integer(i: Self::Integer) -> anyhow::Result<Self>;
24
25    fn to_binary(&self) -> anyhow::Result<Self::Binary>;
26    fn to_string(&self) -> anyhow::Result<String>;
27}
28
29/// Deserialize a type from either a string or integer in human-readable encodings.
30///
31/// This macro implements serde `Serialize` and `DeserializeOwned` traits with a friendly
32/// deserialization mechanism that can handle strings and integers when using human-readable
33/// formats. It works with any [`FromStringOrInteger`] type.
34#[macro_export]
35macro_rules! impl_serde_from_string_or_integer {
36    ($t:ty) => {
37        impl serde::Serialize for $t {
38            fn serialize<S: serde::ser::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
39                $crate::ser::string_or_integer::serialize(self, s)
40            }
41        }
42
43        impl<'de> serde::Deserialize<'de> for $t {
44            fn deserialize<D: serde::de::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
45                $crate::ser::string_or_integer::deserialize(d)
46            }
47        }
48    };
49}
50pub use crate::impl_serde_from_string_or_integer;
51
52/// Deserialize a type from either a string or integer in human-readable encodings.
53///
54/// This serialization module can be used with any [`FromStringOrInteger`] type. It is usually used
55/// only indirectly by the expansion of the [`impl_string_or_integer`] macro.
56pub mod string_or_integer {
57    use super::*;
58
59    #[derive(Debug, Deserialize)]
60    #[serde(untagged)]
61    enum StringOrInteger<I> {
62        String(String),
63        Integer(I),
64    }
65
66    pub fn serialize<T: FromStringOrInteger, S: Serializer>(
67        t: &T,
68        s: S,
69    ) -> Result<S::Ok, S::Error> {
70        if s.is_human_readable() {
71            t.to_string().map_err(S::Error::custom)?.serialize(s)
72        } else {
73            t.to_binary().map_err(S::Error::custom)?.serialize(s)
74        }
75    }
76
77    pub fn deserialize<'a, T: FromStringOrInteger, D: Deserializer<'a>>(
78        d: D,
79    ) -> Result<T, D::Error> {
80        if d.is_human_readable() {
81            match StringOrInteger::deserialize(d)? {
82                StringOrInteger::String(s) => T::from_string(s).map_err(D::Error::custom),
83                StringOrInteger::Integer(i) => T::from_integer(i).map_err(D::Error::custom),
84            }
85        } else {
86            T::from_binary(T::Binary::deserialize(d)?).map_err(D::Error::custom)
87        }
88    }
89}