1use core::fmt::{Debug, Formatter};
2use std::marker::PhantomData;
3
4use warg_crypto::hash::{Hash, SupportedDigest};
5use warg_crypto::VisitBytes;
6
7use super::link::Link;
8use super::node::Node;
9use super::path::Path;
10use super::proof::Proof;
11
12pub struct Map<D, K, V>
64where
65 D: SupportedDigest,
66 K: VisitBytes + Clone,
67 V: VisitBytes + Clone,
68{
69 link: Link<D>,
70 len: usize,
71 _key: PhantomData<K>,
72 _value: PhantomData<V>,
73}
74
75impl<D, K, V> Map<D, K, V>
76where
77 D: SupportedDigest,
78 K: VisitBytes + Clone,
79 V: VisitBytes + Clone,
80{
81 pub(crate) fn new(link: Link<D>, len: usize) -> Self {
82 Self {
83 link,
84 len,
85 _key: PhantomData,
86 _value: PhantomData,
87 }
88 }
89}
90
91impl<D, K, V> Clone for Map<D, K, V>
92where
93 D: SupportedDigest,
94 K: VisitBytes + Clone,
95 V: VisitBytes + Clone,
96{
97 fn clone(&self) -> Self {
98 Self {
99 link: self.link.clone(),
100 len: self.len,
101 _key: PhantomData,
102 _value: PhantomData,
103 }
104 }
105}
106
107impl<D, K, V> Default for Map<D, K, V>
108where
109 D: SupportedDigest,
110 K: VisitBytes + Clone,
111 V: VisitBytes + Clone,
112{
113 fn default() -> Self {
114 Self {
115 link: Link::new(Node::Empty(256)),
116 len: 0,
117 _key: PhantomData,
118 _value: PhantomData,
119 }
120 }
121}
122
123impl<D, K, V> Eq for Map<D, K, V>
124where
125 D: SupportedDigest,
126 K: VisitBytes + Clone,
127 V: VisitBytes + Clone,
128{
129}
130
131impl<D, K, V> PartialEq for Map<D, K, V>
132where
133 D: SupportedDigest,
134 K: VisitBytes + Clone,
135 V: VisitBytes + Clone,
136{
137 fn eq(&self, other: &Self) -> bool {
138 self.link.hash() == other.link.hash()
139 }
140}
141
142impl<D, K, V> core::hash::Hash for Map<D, K, V>
143where
144 D: SupportedDigest,
145 K: VisitBytes + Clone,
146 V: VisitBytes + Clone,
147{
148 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
149 self.link.hash().hash(state);
150 }
151}
152
153impl<D, K, V> Debug for Map<D, K, V>
154where
155 D: SupportedDigest,
156 K: VisitBytes + Clone,
157 V: VisitBytes + Clone,
158{
159 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
160 f.write_fmt(format_args!("Map({:?})", self.link.hash()))
161 }
162}
163
164impl<D, K, V> Map<D, K, V>
165where
166 D: SupportedDigest,
167 K: VisitBytes + Clone + ?Sized,
168 V: VisitBytes + Clone,
169{
170 pub fn root(&self) -> &Hash<D> {
174 self.link.hash()
175 }
176
177 pub fn len(&self) -> usize {
179 self.len
180 }
181
182 pub fn is_empty(&self) -> bool {
184 self.len == 0
185 }
186
187 pub fn prove(&self, key: K) -> Option<Proof<D, K, V>>
189where {
190 self.link.node().prove(Path::new(&Hash::of(key)))
191 }
192
193 pub fn insert(&self, key: K, val: V) -> Self {
197 let key_hash = Hash::<D>::of(&key);
198 let mut path: Path<'_, D> = Path::new(&key_hash);
199 let (node, new) = self.link.node().insert(&mut path, hash_leaf(val));
200 Self::new(Link::new(node), self.len + usize::from(new))
201 }
202
203 pub fn extend(&self, iter: impl IntoIterator<Item = (K, V)>) -> Self {
205 let mut here = self.clone();
206
207 for (key, val) in iter {
208 let key_hash = Hash::<D>::of(&key);
209 let mut path: Path<'_, D> = Path::new(&key_hash);
210 let (node, new) = here.link.node().insert(&mut path, hash_leaf(val));
211 here = Self::new(Link::new(node), here.len + usize::from(new));
212 }
213
214 here
215 }
216}
217
218#[allow(dead_code)]
221pub(crate) fn hash_empty<D: SupportedDigest>() -> Hash<D> {
222 hash_leaf(())
223}
224
225pub(crate) fn hash_leaf<D, V>(value: V) -> Hash<D>
229where
230 D: SupportedDigest,
231 V: VisitBytes,
232{
233 Hash::of(&(0b0, value))
234}
235
236pub(crate) fn hash_branch<D>(lhs: &Hash<D>, rhs: &Hash<D>) -> Hash<D>
239where
240 D: SupportedDigest,
241{
242 Hash::of((0b1, lhs, rhs))
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248 use warg_crypto::hash::Sha256;
249 #[test]
250 fn empty_map() {
251 let map: Map<Sha256, &str, &str> = Map::default();
252 assert_eq!(Sha256::empty_tree_hash(256), map.link.hash());
253 }
254}