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