plutus_ledger_api/v1/
assoc_map.rs

1use std::hash::Hash;
2
3#[cfg(feature = "lbf")]
4use lbr_prelude::json::{json_array, Json};
5use linked_hash_map::LinkedHashMap;
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError, PlutusType};
10
11//////////////
12// AssocMap //
13//////////////
14
15#[derive(Debug, PartialEq, Eq, Clone, Default, PartialOrd, Ord, Hash)]
16#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
17pub struct AssocMap<K, V>(pub Vec<(K, V)>);
18
19impl<K, V> AssocMap<K, V> {
20    pub fn new() -> Self {
21        AssocMap(Vec::new())
22    }
23
24    /// Inserts a key-value pair into the map.
25    ///
26    /// If the map did not have this key present, None is returned.
27    ///
28    /// If the map did have this key present, the value is updated, and the old value is returned. The key is not updated, though; this matters for types that can be == without being identical. See the module-level documentation for more.
29    pub fn insert(&mut self, key: K, mut value: V) -> Option<V>
30    where
31        K: PartialEq,
32    {
33        let old_value = self.0.iter_mut().find(|(k, _v)| k == &key);
34        match old_value {
35            None => {
36                self.0.push((key, value));
37                None
38            }
39            Some((_, v)) => {
40                std::mem::swap(v, &mut value);
41                Some(value)
42            }
43        }
44    }
45
46    /// Removes a key from the map, returning the value at the key if the key was previously in the map.
47    ///
48    /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed form must match the ordering on the key type.
49    pub fn remove(&mut self, key: &K) -> Option<V>
50    where
51        K: PartialEq,
52        V: Clone,
53    {
54        let vec = &mut self.0;
55
56        let old_value = vec
57            .iter()
58            .enumerate()
59            .find_map(|(i, (k, _))| if k == key { Some(i) } else { None });
60        match old_value {
61            None => None,
62            Some(i) => {
63                let (_, v) = vec.remove(i);
64                Some(v)
65            }
66        }
67    }
68}
69
70impl<K: IsPlutusData, V: IsPlutusData> IsPlutusData for AssocMap<K, V> {
71    fn to_plutus_data(&self) -> PlutusData {
72        PlutusData::Map(
73            self.0
74                .iter()
75                .map(|(k, v)| (k.to_plutus_data(), v.to_plutus_data()))
76                .collect(),
77        )
78    }
79
80    fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
81        match plutus_data {
82            PlutusData::Map(pairs) => pairs
83                .iter()
84                .map(|(k, v)| Ok((K::from_plutus_data(k)?, V::from_plutus_data(v)?)))
85                .collect::<Result<Vec<(K, V)>, PlutusDataError>>()
86                .map(Self),
87            _ => Err(PlutusDataError::UnexpectedPlutusType {
88                got: From::from(plutus_data),
89                wanted: PlutusType::Map,
90            }),
91        }
92    }
93}
94
95impl<K, V> From<Vec<(K, V)>> for AssocMap<K, V> {
96    fn from(vec: Vec<(K, V)>) -> Self {
97        AssocMap(vec)
98    }
99}
100
101impl<K: Clone, V: Clone, const N: usize> From<[(K, V); N]> for AssocMap<K, V> {
102    fn from(vec: [(K, V); N]) -> Self {
103        AssocMap(vec.to_vec())
104    }
105}
106
107impl<K, V> From<AssocMap<K, V>> for Vec<(K, V)> {
108    fn from(m: AssocMap<K, V>) -> Self {
109        m.0
110    }
111}
112
113impl<K: Hash + Eq, V> From<AssocMap<K, V>> for LinkedHashMap<K, V> {
114    fn from(m: AssocMap<K, V>) -> Self {
115        m.0.into_iter().collect()
116    }
117}
118
119impl<K: Hash + Eq, V> From<LinkedHashMap<K, V>> for AssocMap<K, V> {
120    fn from(value: LinkedHashMap<K, V>) -> Self {
121        AssocMap(value.into_iter().collect())
122    }
123}
124
125#[cfg(feature = "lbf")]
126impl<K: Json, V: Json> Json for AssocMap<K, V> {
127    fn to_json(&self) -> serde_json::Value {
128        json_array(
129            self.0
130                .iter()
131                .map(|(k, v)| json_array(vec![k.to_json(), v.to_json()]))
132                .collect(),
133        )
134    }
135
136    fn from_json(value: &serde_json::Value) -> Result<Self, lbr_prelude::json::Error> {
137        let vec_of_vectors: Vec<Vec<serde_json::Value>> = Json::from_json(value)?;
138        let vec_of_pairs = vec_of_vectors
139            .into_iter()
140            .map(|vec| {
141                let [k, v]: [serde_json::Value; 2] =
142                    TryFrom::try_from(vec).map_err(|vec: Vec<_>| {
143                        lbr_prelude::json::Error::UnexpectedArrayLength {
144                            got: vec.len(),
145                            wanted: 2,
146                            parser: "v1::assoc_map::AssocMap".into(),
147                        }
148                    })?;
149
150                let k = K::from_json(&k)?;
151                let v = V::from_json(&v)?;
152
153                Ok((k, v))
154            })
155            .collect::<Result<Vec<(K, V)>, _>>()?;
156
157        Ok(Self(vec_of_pairs))
158    }
159}
160
161#[cfg(test)]
162mod test {
163    use super::*;
164
165    #[test]
166    fn assoc_map_insert() {
167        let mut assoc_map = AssocMap::new();
168
169        assoc_map.insert(1, "one");
170        assoc_map.insert(2, "to");
171        assoc_map.insert(3, "three");
172        assoc_map.insert(2, "two");
173
174        let expected = AssocMap::from([(1, "one"), (2, "two"), (3, "three")]);
175
176        assert_eq!(assoc_map, expected);
177    }
178
179    #[test]
180    fn assoc_map_remove() {
181        let mut assoc_map = AssocMap::from([(1, "one"), (2, "two"), (3, "three")]);
182
183        let removed = assoc_map.remove(&1);
184
185        let expected = AssocMap::from([(2, "two"), (3, "three")]);
186
187        assert_eq!(assoc_map, expected);
188        assert_eq!(removed, Some("one"))
189    }
190}