cat_gateway/service/common/objects/cardano/
hash.rs1use core::fmt;
4use std::str::FromStr;
5
6use poem_openapi::{
7 registry::{MetaSchema, MetaSchemaRef},
8 types::{Example, ParseError, ParseFromJSON, ParseFromParameter, ParseResult, ToJSON, Type},
9};
10
11use crate::service::utilities::as_hex_string;
12
13#[derive(Debug, Clone)]
15pub(crate) struct Hash256([u8; Hash256::BYTE_LEN]);
16
17impl Hash256 {
18 const BYTE_LEN: usize = 32;
20 const HASH_LEN: usize = Self::BYTE_LEN * 2;
22
23 fn schema() -> MetaSchema {
25 let mut schema = MetaSchema::new("string");
26 schema.title = Some(format!("Cardano {}-bit Hash", Self::BYTE_LEN * 8));
27 schema.description = Some("Cardano Blake2b256 hash encoded in hex.");
28 schema.example = Some(Self::example().to_string().into());
29 schema.min_length = Some(Self::HASH_LEN + 2);
30 schema.max_length = Some(Self::HASH_LEN + 2);
31 schema.pattern = Some("^0x[0-9a-f]{64}$".to_string());
32 schema
33 }
34}
35
36impl Type for Hash256 {
37 type RawElementValueType = Self;
38 type RawValueType = Self;
39
40 const IS_REQUIRED: bool = true;
41
42 fn name() -> std::borrow::Cow<'static, str> {
43 "CardanoHash256".into()
44 }
45
46 fn schema_ref() -> MetaSchemaRef {
47 MetaSchemaRef::Inline(Box::new(Self::schema()))
48 }
49
50 fn as_raw_value(&self) -> Option<&Self::RawValueType> {
51 Some(self)
52 }
53
54 fn raw_element_iter<'a>(
55 &'a self
56 ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
57 Box::new(self.as_raw_value().into_iter())
58 }
59}
60
61impl Example for Hash256 {
62 fn example() -> Self {
63 Self([
65 255, 86, 28, 28, 230, 190, 207, 19, 106, 93, 48, 99, 245, 13, 120, 184, 219, 80, 184,
66 161, 212, 192, 59, 24, 212, 26, 142, 152, 166, 161, 138, 237,
67 ])
68 }
69}
70
71impl TryFrom<Vec<u8>> for Hash256 {
72 type Error = anyhow::Error;
73
74 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
75 value
76 .try_into()
77 .map_err(|_| anyhow::anyhow!("Invalid {}-bit Cardano hash length.", Self::BYTE_LEN * 8))
78 .map(Self)
79 }
80}
81
82impl FromStr for Hash256 {
83 type Err = anyhow::Error;
84
85 fn from_str(s: &str) -> Result<Self, Self::Err> {
86 let hash = s.strip_prefix("0x").ok_or(anyhow::anyhow!(
87 "Invalid Cardano hash. Hex string must start with `0x`.",
88 ))?;
89
90 hex::decode(hash)
91 .map_err(|_| anyhow::anyhow!("Invalid Cardano hash. Must be hex string."))?
92 .try_into()
93 }
94}
95
96impl AsRef<[u8]> for Hash256 {
97 fn as_ref(&self) -> &[u8] {
98 &self.0
99 }
100}
101
102impl ParseFromParameter for Hash256 {
103 fn parse_from_parameter(param: &str) -> ParseResult<Self> {
104 Self::from_str(param).map_err(ParseError::custom)
105 }
106}
107
108impl ParseFromJSON for Hash256 {
109 fn parse_from_json(value: Option<serde_json::Value>) -> ParseResult<Self> {
110 String::parse_from_json(value)
111 .map_err(ParseError::propagate)
112 .map(|v| Self::from_str(&v))?
113 .map_err(ParseError::custom)
114 }
115}
116
117impl ToJSON for Hash256 {
118 fn to_json(&self) -> Option<serde_json::Value> {
119 Some(self.to_string().into())
120 }
121}
122
123impl fmt::Display for Hash256 {
124 fn fmt(
125 &self,
126 f: &mut fmt::Formatter<'_>,
127 ) -> fmt::Result {
128 as_hex_string(&self.0).fmt(f)
129 }
130}
131
132#[derive(Debug, Clone)]
134pub(crate) struct Hash128([u8; Hash128::BYTE_LEN]);
135
136impl Hash128 {
137 const BYTE_LEN: usize = 16;
139 const HASH_LEN: usize = Self::BYTE_LEN * 2;
141
142 fn schema() -> MetaSchema {
144 let mut schema = MetaSchema::new("string");
145 schema.title = Some(format!("Cardano {}-bit Hash", Self::BYTE_LEN * 8));
146 schema.description = Some("Cardano Blake2b128 hash encoded in hex.");
147 schema.example = Some(Self::example().to_string().into());
148 schema.min_length = Some(Self::HASH_LEN + 2);
149 schema.max_length = Some(Self::HASH_LEN + 2);
150 schema.pattern = Some("^0x[0-9a-f]{32}$".to_string());
151 schema
152 }
153}
154
155impl Type for Hash128 {
156 type RawElementValueType = Self;
157 type RawValueType = Self;
158
159 const IS_REQUIRED: bool = true;
160
161 fn name() -> std::borrow::Cow<'static, str> {
162 "CardanoHash128".into()
163 }
164
165 fn schema_ref() -> MetaSchemaRef {
166 MetaSchemaRef::Inline(Box::new(Self::schema()))
167 }
168
169 fn as_raw_value(&self) -> Option<&Self::RawValueType> {
170 Some(self)
171 }
172
173 fn raw_element_iter<'a>(
174 &'a self
175 ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
176 Box::new(self.as_raw_value().into_iter())
177 }
178}
179
180impl Example for Hash128 {
181 fn example() -> Self {
182 Self([
184 219, 80, 184, 161, 212, 192, 59, 24, 212, 26, 142, 152, 166, 161, 138, 237,
185 ])
186 }
187}
188
189impl TryFrom<Vec<u8>> for Hash128 {
190 type Error = anyhow::Error;
191
192 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
193 value
194 .try_into()
195 .map_err(|_| anyhow::anyhow!("Invalid {}-bit Cardano hash length.", Self::BYTE_LEN * 8))
196 .map(Self)
197 }
198}
199
200impl FromStr for Hash128 {
201 type Err = anyhow::Error;
202
203 fn from_str(s: &str) -> Result<Self, Self::Err> {
204 let hash = s.strip_prefix("0x").ok_or(anyhow::anyhow!(
205 "Invalid Cardano hash. Hex string must start with `0x`.",
206 ))?;
207
208 hex::decode(hash)
209 .map_err(|_| anyhow::anyhow!("Invalid Cardano hash. Must be hex string."))?
210 .try_into()
211 }
212}
213
214impl AsRef<[u8]> for Hash128 {
215 fn as_ref(&self) -> &[u8] {
216 &self.0
217 }
218}
219
220impl ParseFromParameter for Hash128 {
221 fn parse_from_parameter(param: &str) -> ParseResult<Self> {
222 Self::from_str(param).map_err(ParseError::custom)
223 }
224}
225
226impl ParseFromJSON for Hash128 {
227 fn parse_from_json(value: Option<serde_json::Value>) -> ParseResult<Self> {
228 String::parse_from_json(value)
229 .map_err(ParseError::propagate)
230 .map(|v| Self::from_str(&v))?
231 .map_err(ParseError::custom)
232 }
233}
234
235impl ToJSON for Hash128 {
236 fn to_json(&self) -> Option<serde_json::Value> {
237 Some(self.to_string().into())
238 }
239}
240
241impl fmt::Display for Hash128 {
242 fn fmt(
243 &self,
244 f: &mut fmt::Formatter<'_>,
245 ) -> fmt::Result {
246 as_hex_string(&self.0).fmt(f)
247 }
248}