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 hash(&self) -> CryptoHash {
35 self.hash
36 }
37
38 pub fn inner(&self) -> &T {
40 &self.value
41 }
42
43 pub fn into_inner(self) -> T {
45 self.value
46 }
47}
48
49impl<T: Serialize> Serialize for Hashed<T> {
50 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51 where
52 S: serde::Serializer,
53 {
54 self.value.serialize(serializer)
55 }
56}
57
58impl<'de, T: BcsHashable<'de>> Deserialize<'de> for Hashed<T> {
59 fn deserialize<D>(deserializer: D) -> Result<Hashed<T>, D::Error>
60 where
61 D: serde::Deserializer<'de>,
62 {
63 Ok(Hashed::new(T::deserialize(deserializer)?))
64 }
65}
66
67impl<T: Clone> Clone for Hashed<T> {
68 fn clone(&self) -> Self {
69 Self {
70 value: self.value.clone(),
71 hash: self.hash,
72 }
73 }
74}
75
76impl<T: async_graphql::OutputType> async_graphql::TypeName for Hashed<T> {
77 fn type_name() -> Cow<'static, str> {
78 format!("Hashed{}", T::type_name()).into()
79 }
80}
81
82#[async_graphql::Object(cache_control(no_cache), name_type)]
83impl<T: async_graphql::OutputType + Clone> Hashed<T> {
84 #[graphql(derived(name = "hash"))]
85 async fn _hash(&self) -> CryptoHash {
86 self.hash()
87 }
88
89 #[graphql(derived(name = "value"))]
90 async fn _value(&self) -> T {
91 self.inner().clone()
92 }
93}
94
95impl<T> PartialEq for Hashed<T> {
96 fn eq(&self, other: &Self) -> bool {
97 self.hash() == other.hash()
98 }
99}
100
101impl<T> Eq for Hashed<T> {}