cat_gateway/service/common/objects/cardano/
hash.rs

1//! Defines API schema of Cardano hash type.
2
3use 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/// Cardano Blake2b256 hash encoded in hex.
14#[derive(Debug, Clone)]
15pub(crate) struct Hash256([u8; Hash256::BYTE_LEN]);
16
17impl Hash256 {
18    /// The byte size for this hash.
19    const BYTE_LEN: usize = 32;
20    /// The hex-encoded hash length of this hash type.
21    const HASH_LEN: usize = Self::BYTE_LEN * 2;
22
23    /// Creates a `Hash256` schema definition.
24    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        // 0xff561c1ce6becf136a5d3063f50d78b8db50b8a1d4c03b18d41a8e98a6a18aed
64        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/// Cardano Blake2b128 hash encoded in hex.
130#[derive(Debug, Clone)]
131pub(crate) struct Hash128([u8; Hash128::BYTE_LEN]);
132
133impl Hash128 {
134    /// The byte size for this hash.
135    const BYTE_LEN: usize = 16;
136    /// The hex-encoded hash length of this hash type.
137    const HASH_LEN: usize = Self::BYTE_LEN * 2;
138
139    /// Creates a `Hash128` schema definition.
140    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        // 0xdb50b8a1d4c03b18d41a8e98a6a18aed
180        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}