dig_events_protocol/
value.rs1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
14pub struct WalletId(pub u32);
15
16impl std::fmt::Display for WalletId {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 write!(f, "{}", self.0)
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
32pub struct Amount(pub u64);
33
34impl Amount {
35 pub fn mojos(self) -> u64 {
37 self.0
38 }
39}
40
41impl Serialize for Amount {
42 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
44 serializer.serialize_str(&self.0.to_string())
45 }
46}
47
48impl<'de> Deserialize<'de> for Amount {
49 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
51 #[derive(Deserialize)]
53 #[serde(untagged)]
54 enum StringOrNumber {
55 Text(String),
56 Number(u64),
57 }
58 match StringOrNumber::deserialize(deserializer)? {
59 StringOrNumber::Text(s) => s.parse().map(Amount).map_err(serde::de::Error::custom),
60 StringOrNumber::Number(n) => Ok(Amount(n)),
61 }
62 }
63}
64
65impl std::fmt::Display for Amount {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 write!(f, "{}", self.0)
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
76pub struct AssetId(pub String);
77
78impl std::fmt::Display for AssetId {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 write!(f, "{}", self.0)
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 const MAX_JS_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
91
92 #[test]
93 fn small_amount_serializes_as_string() {
94 assert_eq!(serde_json::to_string(&Amount(5)).unwrap(), "\"5\"");
95 assert_eq!(serde_json::to_string(&Amount(0)).unwrap(), "\"0\"");
96 }
97
98 #[test]
99 fn large_amount_serializes_as_string() {
100 let big = Amount(MAX_JS_SAFE_INTEGER + 1);
101 assert_eq!(serde_json::to_string(&big).unwrap(), "\"9007199254740992\"");
102 }
103
104 #[test]
108 fn large_amount_matches_extraction_source_byte_for_byte() {
109 let value = MAX_JS_SAFE_INTEGER + 1;
111 assert_eq!(
112 serde_json::to_string(&Amount(value)).unwrap(),
113 "\"9007199254740992\"",
114 );
115 assert_eq!(
117 serde_json::to_string(&Amount(u64::MAX)).unwrap(),
118 "\"18446744073709551615\"",
119 );
120 }
121
122 #[test]
123 fn amount_deserializes_from_string() {
124 let from_str: Amount = serde_json::from_str("\"9007199254740992\"").unwrap();
125 assert_eq!(from_str, Amount(9_007_199_254_740_992));
126 }
127
128 #[test]
129 fn amount_leniently_deserializes_from_bare_number() {
130 let from_num: Amount = serde_json::from_str("42").unwrap();
131 assert_eq!(from_num, Amount(42));
132 }
133
134 #[test]
135 fn amount_round_trips_across_the_threshold() {
136 for value in [
137 0u64,
138 1,
139 MAX_JS_SAFE_INTEGER,
140 MAX_JS_SAFE_INTEGER + 1,
141 u64::MAX,
142 ] {
143 let json = serde_json::to_string(&Amount(value)).unwrap();
144 let back: Amount = serde_json::from_str(&json).unwrap();
145 assert_eq!(back, Amount(value), "round-trip failed for {value}");
146 }
147 }
148
149 #[test]
150 fn amount_bad_string_is_an_error() {
151 assert!(serde_json::from_str::<Amount>("\"not-a-number\"").is_err());
152 }
153
154 #[test]
155 fn mojos_accessor_returns_raw() {
156 assert_eq!(Amount(555).mojos(), 555);
157 }
158}