1use serde::{Deserialize, Deserializer, Serialize};
2use sha2::{Digest as Sha2Digest, Sha256};
3use std::str::FromStr;
4use utoipa::ToSchema;
5
6#[cfg(feature = "ts-bindings")]
7use ts_rs::TS;
8
9macro_rules! new_hash_id_type {
10 ($type_name:ident, true) => {
11 new_hash_id_type!(@base $type_name);
12 new_hash_id_type!(@intoany $type_name);
13 };
14
15 ($type_name:ident, false) => {
16 new_hash_id_type!(@base $type_name);
17 };
18
19 (@base $type_name:ident) => {
21 #[cfg_attr(feature = "ts-bindings", derive(TS))]
23 #[cfg_attr(feature = "ts-bindings", ts(export, type = "string"))]
24 #[derive(
25 Clone,
26 PartialEq,
27 Eq,
28 PartialOrd,
29 Ord,
30 Hash,
31 ToSchema,
32 bincode::Decode,
33 bincode::Encode,
34 )]
35 pub struct $type_name([u8; 32]);
36
37 impl $type_name {
38 pub fn hash(bytes: impl AsRef<[u8]>) -> Self {
39 let mut digest = [0u8; 32];
40 let mut hasher = Sha256::new();
41 hasher.update(bytes);
42 hasher.finalize_into((&mut digest).into());
43 $type_name(digest)
44 }
45
46 pub fn inner(&self) -> [u8;32] {
47 self.0
48 }
49
50 }
51
52 impl From<[u8; 32]> for $type_name {
53 fn from(value: [u8; 32]) -> Self {
54 $type_name(value)
55 }
56 }
57
58 impl AsRef<[u8]> for $type_name {
59 fn as_ref(&self) -> &[u8] {
60 &self.0
61 }
62 }
63
64 impl std::fmt::Display for $type_name {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "{}",const_hex::encode(self.0.as_slice()))
67 }
68 }
69
70 impl std::fmt::Debug for $type_name {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "{}", self)
73 }
74 }
75
76 impl FromStr for $type_name {
77 type Err = const_hex::FromHexError;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 let mut bytes = [0u8; 32];
81 const_hex::decode_to_slice(s, &mut bytes)?;
82 Ok($type_name(bytes))
83 }
84 }
85
86 impl Serialize for $type_name {
87 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
88 where
89 S: serde::Serializer,
90 {
91 serializer.serialize_str(&self.to_string())
92 }
93 }
94
95 impl<'de> Deserialize<'de> for $type_name {
96 fn deserialize<D>(deserializer: D) -> Result<$type_name, D::Error>
97 where
98 D: Deserializer<'de>,
99 {
100 struct StrVisitor;
101
102 impl<'de> serde::de::Visitor<'de> for StrVisitor {
103 type Value = $type_name;
104
105 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
106 formatter.write_str("expected hex-encoded string")
107 }
108
109 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
110 where
111 E: serde::de::Error,
112 {
113 $type_name::from_str(value).map_err(serde::de::Error::custom)
114 }
115 }
116
117 deserializer.deserialize_str(StrVisitor)
118 }
119 }
120 };
121
122 (@intoany $type_name:ident) => {
127 impl From<$type_name> for AnyDigest {
128 fn from(digest: $type_name) -> Self {
129 AnyDigest(digest.inner())
130 }
131 }
132 };
133}
134
135new_hash_id_type!(AnyDigest, false);
137new_hash_id_type!(ServiceDigest, true);
139new_hash_id_type!(ComponentDigest, true);
141
142new_hash_id_type!(ServiceId, true);