Skip to main content

object_rainbow/
hash.rs

1use std::{fmt::Display, ops::Add};
2
3use typenum::{Add1, B0, B1, ToInt, U0, U1};
4
5use crate::*;
6
7#[cfg(feature = "hex")]
8mod hex;
9
10/// Valid [`Hash`]. Has restrictions on its byte layout (e.g. cannot be all zeroes);
11#[derive(
12    Debug,
13    ToOutput,
14    InlineOutput,
15    Tagged,
16    ListHashes,
17    Topological,
18    ParseAsInline,
19    Clone,
20    Copy,
21    PartialEq,
22    Eq,
23    PartialOrd,
24    Ord,
25    Hash,
26    Size,
27)]
28pub struct Hash([u8; HASH_SIZE]);
29
30impl Display for Hash {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        for x in self.0 {
33            write!(f, "{x:X}")?;
34        }
35        Ok(())
36    }
37}
38
39pub struct HashNiche<N>(N);
40
41impl<N: ToInt<u8> + Add<B1>> Niche for HashNiche<N> {
42    type NeedsTag = B0;
43    type N = <Hash as Size>::Size;
44    fn niche() -> GenericArray<u8, Self::N> {
45        let mut niche = GenericArray::default();
46        let last_byte = niche.len() - 1;
47        niche[last_byte] = N::to_int();
48        niche
49    }
50    type Next = SomeNiche<HashNiche<Add1<N>>>;
51}
52
53impl MaybeHasNiche for Hash {
54    type MnArray = SomeNiche<HashNiche<U0>>;
55}
56
57impl<I: ParseInput> ParseInline<I> for Hash {
58    fn parse_inline(input: &mut I) -> crate::Result<Self> {
59        input
60            .parse_inline::<OptionalHash>()?
61            .get()
62            .ok_or(Error::Zero)
63    }
64}
65
66impl Hash {
67    pub(crate) const fn from_sha256(hash: [u8; HASH_SIZE]) -> Self {
68        Self(hash)
69    }
70
71    /// Convert into raw bytes.
72    pub fn into_bytes(self) -> [u8; HASH_SIZE] {
73        self.0
74    }
75}
76
77impl Deref for Hash {
78    type Target = [u8; HASH_SIZE];
79
80    fn deref(&self) -> &Self::Target {
81        &self.0
82    }
83}
84
85impl AsRef<[u8]> for Hash {
86    fn as_ref(&self) -> &[u8] {
87        self.as_slice()
88    }
89}
90
91/// `Option<Hash>` but more explicitly represented as `[u8; HASH_SIZE]`.
92#[derive(
93    Debug,
94    Clone,
95    Copy,
96    PartialEq,
97    Eq,
98    PartialOrd,
99    Ord,
100    Hash,
101    ToOutput,
102    InlineOutput,
103    Parse,
104    ParseInline,
105    Tagged,
106    ListHashes,
107    Topological,
108    Size,
109    Default,
110)]
111pub struct OptionalHash([u8; HASH_SIZE]);
112
113impl Display for OptionalHash {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        if self.is_some() {
116            for x in self.0 {
117                write!(f, "{x:X}")?;
118            }
119        } else {
120            write!(f, "NONE")?;
121        }
122        Ok(())
123    }
124}
125
126impl MaybeHasNiche for OptionalHash {
127    type MnArray = SomeNiche<HashNiche<U1>>;
128}
129
130impl Equivalent<Option<Hash>> for OptionalHash {
131    fn into_equivalent(self) -> Option<Hash> {
132        self.get()
133    }
134
135    fn from_equivalent(object: Option<Hash>) -> Self {
136        object.map(Self::from).unwrap_or_default()
137    }
138}
139
140impl From<[u8; HASH_SIZE]> for OptionalHash {
141    fn from(hash: [u8; HASH_SIZE]) -> Self {
142        Self(hash)
143    }
144}
145
146impl From<Hash> for OptionalHash {
147    fn from(value: Hash) -> Self {
148        value.0.into()
149    }
150}
151
152impl OptionalHash {
153    /// No [`Hash`].
154    pub const NONE: Self = Self([0; HASH_SIZE]);
155
156    /// Get [`Hash`] if this isn't [`Self::NONE`].
157    pub fn get(&self) -> Option<Hash> {
158        self.is_some().then_some(Hash(self.0))
159    }
160
161    /// Check whether this is a [`Hash`].
162    pub fn is_some(&self) -> bool {
163        !self.is_none()
164    }
165
166    /// Check whether this is [`Self::NONE`].
167    pub fn is_none(&self) -> bool {
168        *self == Self::NONE
169    }
170
171    /// Get [`Hash`] or panic.
172    pub fn unwrap(&self) -> Hash {
173        self.get().unwrap()
174    }
175
176    /// Set to [`Self::NONE`].
177    pub fn clear(&mut self) {
178        *self = Self::NONE;
179    }
180}
181
182impl PartialEq<Hash> for OptionalHash {
183    fn eq(&self, hash: &Hash) -> bool {
184        self.0 == hash.0
185    }
186}
187
188impl PartialEq<OptionalHash> for Hash {
189    fn eq(&self, hash: &OptionalHash) -> bool {
190        self.0 == hash.0
191    }
192}
193
194#[test]
195fn none_is_zeros() {
196    assert_eq!(
197        None::<Hash>.to_array().into_array(),
198        [
199            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
200            0, 0, 0,
201        ]
202    );
203}
204
205#[test]
206fn none_none_is_one() {
207    assert_eq!(
208        None::<Option<Hash>>.to_array().into_array(),
209        [
210            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
211            0, 0, 1,
212        ]
213    );
214}
215
216#[test]
217fn none_none_none_is_two() {
218    assert_eq!(
219        None::<Option<Option<Hash>>>.to_array().into_array(),
220        [
221            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
222            0, 0, 2,
223        ]
224    );
225}