dpp/address_funds/fee_strategy/
mod.rs1pub mod deduct_fee_from_inputs_and_outputs;
2
3pub use deduct_fee_from_inputs_and_outputs::FeeDeductionResult;
4
5use bincode::{Decode, Encode};
6#[cfg(feature = "serde-conversion")]
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq, Hash)]
10pub enum AddressFundsFeeStrategyStep {
11 DeductFromInput(u16),
14 ReduceOutput(u16),
17}
18
19impl Default for AddressFundsFeeStrategyStep {
20 fn default() -> Self {
21 AddressFundsFeeStrategyStep::DeductFromInput(0)
22 }
23}
24
25pub type AddressFundsFeeStrategy = Vec<AddressFundsFeeStrategyStep>;
26
27#[cfg(feature = "serde-conversion")]
32impl Serialize for AddressFundsFeeStrategyStep {
33 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
34 where
35 S: serde::Serializer,
36 {
37 use serde::ser::SerializeStruct;
38
39 let mut state = serializer.serialize_struct("AddressFundsFeeStrategyStep", 2)?;
40 match self {
41 AddressFundsFeeStrategyStep::DeductFromInput(index) => {
42 state.serialize_field("$type", "deductFromInput")?;
43 state.serialize_field("index", index)?;
44 }
45 AddressFundsFeeStrategyStep::ReduceOutput(index) => {
46 state.serialize_field("$type", "reduceOutput")?;
47 state.serialize_field("index", index)?;
48 }
49 }
50 state.end()
51 }
52}
53
54#[cfg(feature = "serde-conversion")]
55impl<'de> Deserialize<'de> for AddressFundsFeeStrategyStep {
56 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
57 where
58 D: serde::Deserializer<'de>,
59 {
60 use serde::de::{self, MapAccess, Visitor};
61 use std::fmt;
62
63 struct StepVisitor;
64
65 impl<'de> Visitor<'de> for StepVisitor {
66 type Value = AddressFundsFeeStrategyStep;
67
68 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
69 formatter.write_str("an AddressFundsFeeStrategyStep struct with type and index")
70 }
71
72 fn visit_map<V>(self, mut map: V) -> Result<AddressFundsFeeStrategyStep, V::Error>
73 where
74 V: MapAccess<'de>,
75 {
76 let mut step_type: Option<String> = None;
77 let mut index: Option<u16> = None;
78
79 while let Some(key) = map.next_key::<String>()? {
80 match key.as_str() {
81 "$type" => {
82 if step_type.is_some() {
83 return Err(de::Error::duplicate_field("$type"));
84 }
85 step_type = Some(map.next_value()?);
86 }
87 "index" => {
88 if index.is_some() {
89 return Err(de::Error::duplicate_field("index"));
90 }
91 index = Some(map.next_value()?);
92 }
93 _ => {
94 let _: serde::de::IgnoredAny = map.next_value()?;
95 }
96 }
97 }
98
99 let step_type = step_type.ok_or_else(|| de::Error::missing_field("$type"))?;
100 let index = index.ok_or_else(|| de::Error::missing_field("index"))?;
101
102 match step_type.as_str() {
103 "deductFromInput" => Ok(AddressFundsFeeStrategyStep::DeductFromInput(index)),
104 "reduceOutput" => Ok(AddressFundsFeeStrategyStep::ReduceOutput(index)),
105 other => Err(de::Error::unknown_variant(
106 other,
107 &["deductFromInput", "reduceOutput"],
108 )),
109 }
110 }
111 }
112
113 deserializer.deserialize_struct(
114 "AddressFundsFeeStrategyStep",
115 &["$type", "index"],
116 StepVisitor,
117 )
118 }
119}
120
121#[cfg(all(test, feature = "serde-conversion"))]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn deduct_from_input_serializes_with_type_and_index() {
127 let step = AddressFundsFeeStrategyStep::DeductFromInput(7);
128 let json = serde_json::to_value(&step).unwrap();
129 assert_eq!(
130 json,
131 serde_json::json!({ "$type": "deductFromInput", "index": 7 })
132 );
133 }
134
135 #[test]
136 fn reduce_output_serializes_with_type_and_index() {
137 let step = AddressFundsFeeStrategyStep::ReduceOutput(3);
138 let json = serde_json::to_value(&step).unwrap();
139 assert_eq!(
140 json,
141 serde_json::json!({ "$type": "reduceOutput", "index": 3 })
142 );
143 }
144
145 #[test]
146 fn deserializes_from_type_and_index() {
147 let step: AddressFundsFeeStrategyStep =
148 serde_json::from_value(serde_json::json!({ "$type": "deductFromInput", "index": 9 }))
149 .unwrap();
150 assert_eq!(step, AddressFundsFeeStrategyStep::DeductFromInput(9));
151
152 let step: AddressFundsFeeStrategyStep =
153 serde_json::from_value(serde_json::json!({ "$type": "reduceOutput", "index": 2 }))
154 .unwrap();
155 assert_eq!(step, AddressFundsFeeStrategyStep::ReduceOutput(2));
156 }
157
158 #[test]
159 fn rejects_unknown_variant() {
160 let result: Result<AddressFundsFeeStrategyStep, _> =
161 serde_json::from_value(serde_json::json!({ "$type": "burn", "index": 0 }));
162 assert!(result.is_err());
163 }
164
165 #[test]
166 fn round_trips_through_json() {
167 for original in [
168 AddressFundsFeeStrategyStep::DeductFromInput(0),
169 AddressFundsFeeStrategyStep::DeductFromInput(42),
170 AddressFundsFeeStrategyStep::ReduceOutput(0),
171 AddressFundsFeeStrategyStep::ReduceOutput(42),
172 ] {
173 let json = serde_json::to_string(&original).unwrap();
174 let restored: AddressFundsFeeStrategyStep = serde_json::from_str(&json).unwrap();
175 assert_eq!(original, restored);
176 }
177 }
178}
179
180#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
181impl crate::serialization::JsonConvertible for AddressFundsFeeStrategyStep {}
182
183#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
184impl crate::serialization::ValueConvertible for AddressFundsFeeStrategyStep {}
185
186#[cfg(all(
187 test,
188 feature = "json-conversion",
189 feature = "value-conversion",
190 feature = "serde-conversion"
191))]
192mod json_convertible_tests_address_funds_fee_strategy_step {
193 use super::*;
194
195 use platform_value::platform_value;
196 use serde_json::json;
197
198 #[test]
199 fn json_round_trip_deduct_from_input() {
200 use crate::serialization::JsonConvertible;
201 let original = AddressFundsFeeStrategyStep::DeductFromInput(7);
202 let json = original.to_json().expect("to_json");
203 assert_eq!(json, json!({"$type": "deductFromInput", "index": 7}));
207 let recovered = AddressFundsFeeStrategyStep::from_json(json).expect("from_json");
208 assert_eq!(recovered, AddressFundsFeeStrategyStep::DeductFromInput(7));
209 }
210
211 #[test]
212 fn json_round_trip_reduce_output() {
213 use crate::serialization::JsonConvertible;
214 let original = AddressFundsFeeStrategyStep::ReduceOutput(u16::MAX);
215 let json = original.to_json().expect("to_json");
216 assert_eq!(json, json!({"$type": "reduceOutput", "index": u16::MAX}));
218 let recovered = AddressFundsFeeStrategyStep::from_json(json).expect("from_json");
219 assert_eq!(
220 recovered,
221 AddressFundsFeeStrategyStep::ReduceOutput(u16::MAX)
222 );
223 }
224
225 #[test]
226 fn value_round_trip_deduct_from_input() {
227 use crate::serialization::ValueConvertible;
228 let original = AddressFundsFeeStrategyStep::DeductFromInput(7);
229 let value = original.to_object().expect("to_object");
230 assert_eq!(
236 value,
237 platform_value!({"$type": "deductFromInput", "index": 7u16})
238 );
239 let recovered = AddressFundsFeeStrategyStep::from_object(value).expect("from_object");
240 assert_eq!(recovered, AddressFundsFeeStrategyStep::DeductFromInput(7));
241 }
242
243 #[test]
244 fn value_round_trip_reduce_output() {
245 use crate::serialization::ValueConvertible;
246 let original = AddressFundsFeeStrategyStep::ReduceOutput(u16::MAX);
247 let value = original.to_object().expect("to_object");
248 assert_eq!(
249 value,
250 platform_value!({"$type": "reduceOutput", "index": u16::MAX})
251 );
252 let recovered = AddressFundsFeeStrategyStep::from_object(value).expect("from_object");
253 assert_eq!(
254 recovered,
255 AddressFundsFeeStrategyStep::ReduceOutput(u16::MAX)
256 );
257 }
258}