1use std::borrow::Cow;
8
9use allocative::Allocative;
10use custom_debug_derive::Debug;
11use serde::{Deserialize, Serialize};
12
13use crate::crypto::{BcsHashable, CryptoHash};
14
15#[derive(Debug, Allocative)]
17pub struct Hashed<T> {
18 value: T,
19 hash: CryptoHash,
21}
22
23impl<T> Hashed<T> {
24 pub fn new<'de>(value: T) -> Self
26 where
27 T: BcsHashable<'de>,
28 {
29 let hash = CryptoHash::new(&value);
30 Self { value, hash }
31 }
32
33 pub fn with_hash(value: T, hash: CryptoHash) -> Self {
37 Self { value, hash }
38 }
39
40 pub fn hash(&self) -> CryptoHash {
42 self.hash
43 }
44
45 pub fn inner(&self) -> &T {
47 &self.value
48 }
49
50 pub fn into_inner(self) -> T {
52 self.value
53 }
54}
55
56impl<T: Serialize> Serialize for Hashed<T> {
57 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58 where
59 S: serde::Serializer,
60 {
61 self.value.serialize(serializer)
62 }
63}
64
65impl<'de, T: BcsHashable<'de>> Deserialize<'de> for Hashed<T> {
66 fn deserialize<D>(deserializer: D) -> Result<Hashed<T>, D::Error>
67 where
68 D: serde::Deserializer<'de>,
69 {
70 Ok(Hashed::new(T::deserialize(deserializer)?))
71 }
72}
73
74impl<T: Clone> Clone for Hashed<T> {
75 fn clone(&self) -> Self {
76 Self {
77 value: self.value.clone(),
78 hash: self.hash,
79 }
80 }
81}
82
83impl<T: async_graphql::OutputType> async_graphql::TypeName for Hashed<T> {
84 fn type_name() -> Cow<'static, str> {
85 format!("Hashed{}", T::type_name()).into()
86 }
87}
88
89#[async_graphql::Object(cache_control(no_cache), name_type)]
90impl<T: async_graphql::OutputType + Clone> Hashed<T> {
91 #[graphql(derived(name = "hash"))]
92 async fn _hash(&self) -> CryptoHash {
93 self.hash()
94 }
95
96 #[graphql(derived(name = "value"))]
97 async fn _value(&self) -> T {
98 self.inner().clone()
99 }
100}
101
102impl<T> PartialEq for Hashed<T> {
103 fn eq(&self, other: &Self) -> bool {
104 self.hash() == other.hash()
105 }
106}
107
108impl<T> Eq for Hashed<T> {}
109
110#[cfg(test)]
111mod tests {
112 use crate::{
113 crypto::{BcsHashable, CryptoHash},
114 hashed::Hashed,
115 };
116
117 #[derive(serde::Serialize, serde::Deserialize)]
118 struct Dummy(u8);
119 impl BcsHashable<'_> for Dummy {}
120
121 #[test]
122 fn with_hash_stores_provided_hash() {
123 let forced = CryptoHash::from([9u8; 32]);
124 let hashed = Hashed::with_hash(Dummy(7), forced);
125 assert_eq!(hashed.hash(), forced);
126 assert_ne!(hashed.hash(), CryptoHash::new(&Dummy(7)));
127 }
128}