Skip to main content

harmos_domain/message/
identity.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10// Portable Schema And Content Identity Values
11
12/* === Modules === */
13
14/* === Re-Exports === */
15
16/* === Imports === */
17
18// Standard Library Imports
19use std::fmt;
20// Third Party Imports
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22// Local Imports
23
24/* === Constants === */
25
26/* === Macros === */
27
28macro_rules! identity {
29    ($ty:ident) => {
30        impl fmt::Display for $ty {
31            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
32                for byte in self.0 {
33                    write!(formatter, "{byte:02x}")?;
34                }
35                Ok(())
36            }
37        }
38
39        impl Serialize for $ty {
40            fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
41                if serializer.is_human_readable() { serializer.collect_str(self) } else { self.0.serialize(serializer) }
42            }
43        }
44
45        impl<'de> Deserialize<'de> for $ty {
46            fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
47                if !deserializer.is_human_readable() {
48                    return <[u8; 32]>::deserialize(deserializer).map(Self);
49                }
50                let text = String::deserialize(deserializer)?;
51                if text.len() != 64 || !text.bytes().all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) {
52                    return Err(serde::de::Error::custom("identity must be 64 lowercase hexadecimal digits"));
53                }
54                let mut bytes = [0; 32];
55                for (index, byte) in bytes.iter_mut().enumerate() {
56                    *byte = u8::from_str_radix(&text[index * 2..index * 2 + 2], 16).map_err(serde::de::Error::custom)?;
57                }
58                Ok(Self(bytes))
59            }
60        }
61    };
62}
63
64identity!(Fingerprint);
65identity!(Digest);
66
67/* === Type Aliases === */
68
69/* === Structs === */
70
71#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
72pub struct Fingerprint(pub [u8; 32]);
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
75pub struct Digest(pub [u8; 32]);
76
77/* === Enums === */
78
79/* === Traits === */
80
81/* === Implementations === */
82
83/* === Public Functions === */
84
85/* === Private Functions === */
86
87/* === Tests === */
88
89#[cfg(test)]
90mod tests {
91    use crate::{Message, Portable};
92
93    #[test]
94    fn float_identity_vectors() {
95        assert_eq!(f64::fingerprint().to_string(), "c2be9d59dd899a0610a0733ea228e01124f88493c4769be406a3298311a6c907");
96        for (bits, expected) in [
97            (0, "35f3767c67d333522f9e1bbc6af672f58ab5b5ce79a682eb73b07509495ef0bf"),
98            (0x8000000000000000, "119a7f9f7f4abd037483acd7fc28dff27e8790b537c553e6d95c274dfc4c6cc3"),
99            (0x7ff8000000000001, "f79e77241c991154c361b16a433784f1bacbe34570cbf8afda3f0543a3112802"),
100            (0x7ff8000000000002, "73786941275e993a7bc30aa530a9b6a7c765139f8e47a576c8e71b5d99989a83"),
101        ] {
102            let value = f64::from_bits(bits);
103            assert_eq!(value.digest().to_string(), expected);
104            assert_eq!(f64::decode(&value.encode()).unwrap().to_bits(), bits);
105        }
106        let fingerprint = f64::fingerprint();
107        assert_eq!(postcard::to_allocvec(&fingerprint).unwrap(), fingerprint.0);
108        assert_eq!(serde_json::from_str::<super::Fingerprint>(&serde_json::to_string(&fingerprint).unwrap()).unwrap(), fingerprint);
109    }
110}