hopr_primitive_types/
traits.rs

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