hopr_primitive_types/
traits.rs

1use crate::errors::{GeneralError, GeneralError::ParseError, Result};
2
3/// A generic type that can be converted to a hexadecimal string.
4pub trait ToHex {
5    /// Hexadecimal representation of this type.
6    fn to_hex(&self) -> String;
7
8    /// Tries to parse the type from the hexadecimal representation.
9    fn from_hex(str: &str) -> Result<Self>
10    where
11        Self: Sized;
12}
13
14/// Represents a type that can be encoded to/decoded from a fixed sized byte array of size `N`.
15/// This requires processing and memory allocation to represent the type in binary encoding.
16///
17/// Differences between [BytesEncodable] and [BytesRepresentable]:
18/// - [BytesRepresentable] is already internally carrying the encoded representation of the type, so no additional
19///   encoding or allocation is required to represent the type as a byte array.
20/// - [BytesEncodable] requires additional transformation and allocation to represent the type as a fixed size byte
21///   array.
22/// - [BytesEncodable] is the strict superset of [BytesRepresentable]: meaning the former can be possibly implemented
23///   for a type that already implements the latter, but it is not possible vice versa.
24pub trait BytesEncodable<const N: usize, E = GeneralError>:
25    Into<[u8; N]> + for<'a> TryFrom<&'a [u8], Error = E>
26{
27    /// Size of the encoded byte array. Defaults to `N` and should not be overridden.
28    const SIZE: usize = N;
29
30    /// Convenience function to represent the
31    /// A shorthand for `let v: [u8; N] = self.into()`.
32    #[inline]
33    fn into_encoded(self) -> [u8; N] {
34        self.into()
35    }
36
37    /// Convenience function to encode the type into a Box.
38    #[inline]
39    fn into_boxed(self) -> Box<[u8]> {
40        Box::new(self.into_encoded())
41    }
42}
43
44/// Represents a type that is already internally represented by a fixed size byte array,
45/// and therefore requires no memory allocation to represent the type in binary encoding.
46///
47/// This is a strict subset of [BytesEncodable], see its documentation for details.
48pub trait BytesRepresentable<E = GeneralError>: AsRef<[u8]> + for<'a> TryFrom<&'a [u8], Error = E> {
49    /// Size of the encoded byte array.
50    const SIZE: usize;
51
52    /// Convenience function to copy this type's binary representation into a Box.
53    #[inline]
54    fn into_boxed(self) -> Box<[u8]> {
55        self.as_ref().to_vec().into_boxed_slice()
56    }
57}
58
59impl<T: BytesRepresentable> ToHex for T {
60    fn to_hex(&self) -> String {
61        format!("0x{}", hex::encode(self.as_ref()))
62    }
63
64    fn from_hex(str: &str) -> Result<Self> {
65        if !str.is_empty() && str.len() % 2 == 0 {
66            let data = if &str[..2] == "0x" || &str[..2] == "0X" {
67                &str[2..]
68            } else {
69                str
70            };
71
72            hex::decode(data)
73                .map_err(|e| ParseError(e.to_string()))
74                .and_then(|bytes| T::try_from(&bytes))
75        } else {
76            Err(ParseError("invalid hex length".into()))
77        }
78    }
79}
80
81impl<const N: usize> BytesRepresentable<std::array::TryFromSliceError> for [u8; N] {
82    const SIZE: usize = N;
83}
84
85/// Allows type to be multiplied and divided by a float in range [0.0, 1.0].
86pub trait UnitaryFloatOps: Sized {
87    /// Multiply with float in the interval [0.0, 1.0]
88    fn mul_f64(&self, rhs: f64) -> Result<Self>;
89    /// Divide by float in the interval (0.0, 1.0]
90    fn div_f64(&self, rhs: f64) -> Result<Self>;
91}
92
93/// Extension trait for fixed size numbers to allow conversion to/from endian representations.
94pub trait IntoEndian<const N: usize> {
95    /// Create instance from Big Endian bytes. Should panic if size is more than `N`.
96    fn from_be_bytes<T: AsRef<[u8]>>(bytes: T) -> Self;
97    /// Create instance from Little Endian bytes. Should panic if size is more than `N`.
98    fn from_le_bytes<T: AsRef<[u8]>>(bytes: T) -> Self;
99    /// Convert instance to Little Endian bytes.
100    fn to_le_bytes(self) -> [u8; N];
101    /// Convert instance to Big Endian bytes.
102    fn to_be_bytes(self) -> [u8; N];
103}
104
105/// A trait that adds extension method to represent a time object as `Duration` since Unix epoch.
106pub trait AsUnixTimestamp {
107    /// Represents self as `Duration` since Unix epoch.
108    fn as_unix_timestamp(&self) -> std::time::Duration;
109}
110
111impl AsUnixTimestamp for std::time::SystemTime {
112    fn as_unix_timestamp(&self) -> std::time::Duration {
113        self.saturating_sub(std::time::SystemTime::UNIX_EPOCH)
114    }
115}
116
117/// A trait that adds extension method to perform saturated substractions on `SystemTime` instances.
118pub trait SaturatingSub {
119    /// Performs saturated substraction on `SystemTime` instances.
120    fn saturating_sub(&self, earlier: std::time::SystemTime) -> std::time::Duration;
121}
122
123impl SaturatingSub for std::time::SystemTime {
124    fn saturating_sub(&self, earlier: std::time::SystemTime) -> std::time::Duration {
125        self.duration_since(earlier).unwrap_or(std::time::Duration::ZERO)
126    }
127}