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