whisky_common/data/
value.rs

1use serde::{Deserialize, Serialize};
2
3use crate::models::Asset;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7pub struct Value(pub HashMap<String, u64>);
8
9impl Value {
10    pub fn new() -> Self {
11        Value(HashMap::new())
12    }
13
14    pub fn from_asset(asset: &Asset) -> Self {
15        let mut asset_map = HashMap::new();
16        asset_map.insert(
17            Value::sanitize_unit(&asset.unit()),
18            asset.quantity().parse::<u64>().unwrap(),
19        );
20        Value(asset_map)
21    }
22
23    pub fn from_asset_vec(assets: &[Asset]) -> Self {
24        let mut asset_map = HashMap::new();
25        for asset in assets {
26            let current_value = asset_map
27                .entry(Value::sanitize_unit(&asset.unit()))
28                .or_insert(0);
29            *current_value += asset.quantity().parse::<u64>().unwrap();
30        }
31        Value(asset_map)
32    }
33
34    pub fn add_asset(&mut self, unit: &str, quantity: u64) -> &mut Self {
35        let current_value = self.0.entry(Value::sanitize_unit(unit)).or_insert(0);
36        *current_value += quantity;
37        self
38    }
39
40    pub fn add_assets(&mut self, assets: &[Asset]) -> &mut Self {
41        for asset in assets {
42            self.add_asset(&asset.unit(), asset.quantity().parse::<u64>().unwrap());
43        }
44        self
45    }
46
47    pub fn merge(&mut self, other: &Value) -> &mut Self {
48        for (key, value) in other.0.clone() {
49            let current_value = self.0.entry(Value::sanitize_unit(&key)).or_insert(0);
50            *current_value += value;
51        }
52        self
53    }
54
55    pub fn negate_asset(&mut self, unit: &str, quantity: u64) -> &mut Self {
56        let current_value = self.0.entry(Value::sanitize_unit(unit)).or_insert(0);
57        if *current_value <= quantity {
58            self.0.remove(unit);
59        } else {
60            *current_value -= quantity;
61        };
62        self
63    }
64
65    pub fn negate_assets(&mut self, other: &[Asset]) -> &mut Self {
66        for asset in other {
67            self.negate_asset(&asset.unit(), asset.quantity().parse::<u64>().unwrap());
68        }
69        self
70    }
71
72    pub fn negate_value(&mut self, other: &Value) -> &mut Self {
73        for (key, value) in other.0.clone() {
74            let unit = Value::sanitize_unit(&key);
75            let current_value = self.0.entry(unit.clone()).or_insert(0);
76            if *current_value <= value {
77                self.0.remove(&unit);
78            } else {
79                *current_value -= value;
80            }
81        }
82        self
83    }
84
85    pub fn to_asset_vec(&self) -> Vec<Asset> {
86        let mut assets = vec![];
87        for (unit, quantity) in &self.0 {
88            assets.push(Asset::new(Value::sanitize_unit(unit), quantity.to_string()));
89        }
90        assets
91    }
92
93    // Accessor
94    pub fn get(&self, key: &str) -> u64 {
95        let key = if key.is_empty() { "lovelace" } else { key };
96        match self.0.get(key) {
97            Some(value) => *value,
98            None => 0,
99        }
100    }
101
102    pub fn keys(&self) -> Vec<String> {
103        self.0.keys().cloned().collect()
104    }
105
106    pub fn len(&self) -> usize {
107        self.0.len()
108    }
109
110    // Comparison function
111    pub fn geq(&self, other: &Value) -> bool {
112        for (key, value) in &other.0 {
113            if self
114                .0
115                .get(&Value::sanitize_unit(key))
116                .is_some_and(|v| v < value)
117            {
118                return false;
119            }
120        }
121        true
122    }
123
124    pub fn leq(&self, other: &Value) -> bool {
125        for (key, value) in &other.0 {
126            if self
127                .0
128                .get(&Value::sanitize_unit(key))
129                .is_some_and(|v| v > value)
130            {
131                return false;
132            }
133        }
134        true
135    }
136
137    pub fn is_empty(&self) -> bool {
138        self.0.is_empty()
139    }
140
141    pub fn sanitize_unit(unit: &str) -> String {
142        if unit.is_empty() {
143            "lovelace".to_string()
144        } else {
145            unit.to_string()
146        }
147    }
148}
149
150impl Default for Value {
151    fn default() -> Self {
152        Value::new()
153    }
154}