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(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 as_hex_string(&self.0).fmt(f)
126 }
127}
128
129#[derive(Debug, Clone)]
131pub(crate) struct Hash128([u8; Hash128::BYTE_LEN]);
132
133impl Hash128 {
134 const BYTE_LEN: usize = 16;
136 const HASH_LEN: usize = Self::BYTE_LEN * 2;
138
139 fn schema() -> MetaSchema {
141 let mut schema = MetaSchema::new("string");
142 schema.title = Some(format!("Cardano {}-bit Hash", Self::BYTE_LEN * 8));
143 schema.description = Some("Cardano Blake2b128 hash encoded in hex.");
144 schema.example = Some(Self::example().to_string().into());
145 schema.min_length = Some(Self::HASH_LEN + 2);
146 schema.max_length = Some(Self::HASH_LEN + 2);
147 schema.pattern = Some("^0x[0-9a-f]{32}$".to_string());
148 schema
149 }
150}
151
152impl Type for Hash128 {
153 type RawElementValueType = Self;
154 type RawValueType = Self;
155
156 const IS_REQUIRED: bool = true;
157
158 fn name() -> std::borrow::Cow<'static, str> {
159 "CardanoHash128".into()
160 }
161
162 fn schema_ref() -> MetaSchemaRef {
163 MetaSchemaRef::Inline(Box::new(Self::schema()))
164 }
165
166 fn as_raw_value(&self) -> Option<&Self::RawValueType> {
167 Some(self)
168 }
169
170 fn raw_element_iter<'a>(
171 &'a self,
172 ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
173 Box::new(self.as_raw_value().into_iter())
174 }
175}
176
177impl Example for Hash128 {
178 fn example() -> Self {
179 Self([
181 219, 80, 184, 161, 212, 192, 59, 24, 212, 26, 142, 152, 166, 161, 138, 237,
182 ])
183 }
184}
185
186impl TryFrom<Vec<u8>> for Hash128 {
187 type Error = anyhow::Error;
188
189 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
190 value
191 .try_into()
192 .map_err(|_| anyhow::anyhow!("Invalid {}-bit Cardano hash length.", Self::BYTE_LEN * 8))
193 .map(Self)
194 }
195}
196
197impl FromStr for Hash128 {
198 type Err = anyhow::Error;
199
200 fn from_str(s: &str) -> Result<Self, Self::Err> {
201 let hash = s.strip_prefix("0x").ok_or(anyhow::anyhow!(
202 "Invalid Cardano hash. Hex string must start with `0x`.",
203 ))?;
204
205 hex::decode(hash)
206 .map_err(|_| anyhow::anyhow!("Invalid Cardano hash. Must be hex string."))?
207 .try_into()
208 }
209}
210
211impl AsRef<[u8]> for Hash128 {
212 fn as_ref(&self) -> &[u8] {
213 &self.0
214 }
215}
216
217impl ParseFromParameter for Hash128 {
218 fn parse_from_parameter(param: &str) -> ParseResult<Self> {
219 Self::from_str(param).map_err(ParseError::custom)
220 }
221}
222
223impl ParseFromJSON for Hash128 {
224 fn parse_from_json(value: Option<serde_json::Value>) -> ParseResult<Self> {
225 String::parse_from_json(value)
226 .map_err(ParseError::propagate)
227 .map(|v| Self::from_str(&v))?
228 .map_err(ParseError::custom)
229 }
230}
231
232impl ToJSON for Hash128 {
233 fn to_json(&self) -> Option<serde_json::Value> {
234 Some(self.to_string().into())
235 }
236}
237
238impl fmt::Display for Hash128 {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 as_hex_string(&self.0).fmt(f)
241 }
242}