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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//! Defines API schema of Cardano hash type.

use poem_openapi::{
    registry::{MetaSchema, MetaSchemaRef, Registry},
    types::{ParseError, ParseFromJSON, ParseFromParameter, ParseResult, ToJSON, Type},
};

use crate::service::utilities::as_hex_string;

/// Cardano Blake2b256 hash encoded in hex.
#[derive(Debug)]
pub(crate) struct Hash(Vec<u8>);

impl From<Vec<u8>> for Hash {
    fn from(hash: Vec<u8>) -> Self {
        Self(hash)
    }
}

impl Hash {
    /// Creates a `CardanoStakeAddress` schema definition.
    fn schema() -> MetaSchema {
        let mut schema = MetaSchema::new("string");
        schema.title = Some(Self::name().to_string());
        schema.description = Some("Cardano Blake2b256 hash encoded in hex.");
        schema.example = Some(serde_json::Value::String(
            // cspell: disable
            "0x0000000000000000000000000000000000000000000000000000000000000000".to_string(),
            // cspell: enable
        ));
        schema.max_length = Some(66);
        schema.pattern = Some("0x[0-9a-f]{64}".to_string());
        schema
    }
}

impl Type for Hash {
    type RawElementValueType = Self;
    type RawValueType = Self;

    const IS_REQUIRED: bool = true;

    fn name() -> std::borrow::Cow<'static, str> {
        "CardanoHash".into()
    }

    fn schema_ref() -> MetaSchemaRef {
        MetaSchemaRef::Reference(Self::name().to_string())
    }

    fn register(registry: &mut Registry) {
        registry.create_schema::<Self, _>(Self::name().to_string(), |_| Self::schema());
    }

    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
        Some(self)
    }

    fn raw_element_iter<'a>(
        &'a self,
    ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
        Box::new(self.as_raw_value().into_iter())
    }
}

impl ParseFromParameter for Hash {
    fn parse_from_parameter(param: &str) -> ParseResult<Self> {
        hex::decode(param.strip_prefix("0x").ok_or(ParseError::custom(
            "Invalid Cardano hash. hex string should start with `0x`.",
        ))?)
        .map_err(|_| ParseError::custom("Invalid Cardano hash. Should be hex string."))
        .map(Self)
    }
}

impl ParseFromJSON for Hash {
    fn parse_from_json(value: Option<serde_json::Value>) -> ParseResult<Self> {
        let value = value.ok_or(ParseError::custom(
            "Invalid Cardano hash. Null or missing value.",
        ))?;
        let str = value.as_str().ok_or(ParseError::custom(
            "Invalid Cardano hash. Should be string.",
        ))?;
        hex::decode(str.strip_prefix("0x").ok_or(ParseError::custom(
            "Invalid Cardano hash. hex string should start with `0x`.",
        ))?)
        .map_err(|_| ParseError::custom("Invalid Cardano hash. Should be hex string."))
        .map(Self)
    }
}

impl ToJSON for Hash {
    fn to_json(&self) -> Option<serde_json::Value> {
        Some(serde_json::Value::String(as_hex_string(&self.0)))
    }
}