bee_block/payload/milestone/option/
mod.rs1mod parameters;
5mod receipt;
6
7use alloc::{boxed::Box, vec::Vec};
8
9use derive_more::{Deref, From};
10use iterator_sorted::is_unique_sorted;
11use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable};
12
13pub(crate) use self::{parameters::BinaryParametersLength, receipt::ReceiptFundsCount};
14pub use self::{
15 parameters::ParametersMilestoneOption,
16 receipt::{MigratedFundsEntry, ReceiptMilestoneOption, TailTransactionHash},
17};
18use crate::{protocol::ProtocolParameters, Error};
19
20#[derive(Clone, Debug, Eq, PartialEq, From, Packable)]
22#[cfg_attr(
23 feature = "serde",
24 derive(serde::Serialize, serde::Deserialize),
25 serde(tag = "type", content = "data")
26)]
27#[packable(unpack_error = Error)]
28#[packable(tag_type = u8, with_error = Error::InvalidMilestoneOptionKind)]
29#[packable(unpack_visitor = ProtocolParameters)]
30pub enum MilestoneOption {
31 #[packable(tag = ReceiptMilestoneOption::KIND)]
33 Receipt(ReceiptMilestoneOption),
34 #[packable(tag = ParametersMilestoneOption::KIND)]
36 Parameters(ParametersMilestoneOption),
37}
38
39impl MilestoneOption {
40 pub fn kind(&self) -> u8 {
42 match self {
43 Self::Receipt(_) => ReceiptMilestoneOption::KIND,
44 Self::Parameters(_) => ParametersMilestoneOption::KIND,
45 }
46 }
47}
48
49pub(crate) type MilestoneOptionCount = BoundedU8<0, { MilestoneOptions::COUNT_MAX }>;
50
51#[derive(Clone, Debug, Eq, PartialEq, Deref, Packable)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidMilestoneOptionCount(p.into())))]
55#[packable(unpack_visitor = ProtocolParameters)]
56pub struct MilestoneOptions(
57 #[packable(verify_with = verify_unique_sorted_packable)] BoxedSlicePrefix<MilestoneOption, MilestoneOptionCount>,
58);
59
60impl TryFrom<Vec<MilestoneOption>> for MilestoneOptions {
61 type Error = Error;
62
63 #[inline(always)]
64 fn try_from(milestone_options: Vec<MilestoneOption>) -> Result<Self, Self::Error> {
65 Self::new(milestone_options)
66 }
67}
68
69impl IntoIterator for MilestoneOptions {
70 type Item = MilestoneOption;
71 type IntoIter = alloc::vec::IntoIter<Self::Item>;
72
73 fn into_iter(self) -> Self::IntoIter {
74 Vec::from(Into::<Box<[MilestoneOption]>>::into(self.0)).into_iter()
75 }
76}
77
78impl MilestoneOptions {
79 pub const COUNT_MAX: u8 = 2;
81
82 pub fn new(milestone_options: Vec<MilestoneOption>) -> Result<Self, Error> {
84 let mut milestone_options =
85 BoxedSlicePrefix::<MilestoneOption, MilestoneOptionCount>::try_from(milestone_options.into_boxed_slice())
86 .map_err(Error::InvalidMilestoneOptionCount)?;
87
88 milestone_options.sort_by_key(MilestoneOption::kind);
89 verify_unique_sorted::<true>(&milestone_options)?;
91
92 Ok(Self(milestone_options))
93 }
94
95 #[inline(always)]
97 pub fn get(&self, key: u8) -> Option<&MilestoneOption> {
98 self.0
99 .binary_search_by_key(&key, MilestoneOption::kind)
100 .map(|index| &self.0[index])
102 .ok()
103 }
104
105 pub fn receipt(&self) -> Option<&ReceiptMilestoneOption> {
107 if let Some(MilestoneOption::Receipt(receipt)) = self.get(ReceiptMilestoneOption::KIND) {
108 Some(receipt)
109 } else {
110 None
111 }
112 }
113
114 pub fn parameters(&self) -> Option<&ParametersMilestoneOption> {
116 if let Some(MilestoneOption::Parameters(parameters)) = self.get(ParametersMilestoneOption::KIND) {
117 Some(parameters)
118 } else {
119 None
120 }
121 }
122}
123
124#[inline]
125fn verify_unique_sorted<const VERIFY: bool>(milestone_options: &[MilestoneOption]) -> Result<(), Error> {
126 if VERIFY && !is_unique_sorted(milestone_options.iter().map(MilestoneOption::kind)) {
127 Err(Error::MilestoneOptionsNotUniqueSorted)
128 } else {
129 Ok(())
130 }
131}
132
133#[inline]
134fn verify_unique_sorted_packable<const VERIFY: bool>(
135 milestone_options: &[MilestoneOption],
136 _visitor: &ProtocolParameters,
137) -> Result<(), Error> {
138 verify_unique_sorted::<VERIFY>(milestone_options)
139}
140
141#[cfg(feature = "dto")]
142#[allow(missing_docs)]
143pub mod dto {
144 use serde::{Deserialize, Serialize, Serializer};
145 use serde_json::Value;
146
147 pub use self::{
148 parameters::dto::ParametersMilestoneOptionDto,
149 receipt::dto::{MigratedFundsEntryDto, ReceiptMilestoneOptionDto},
150 };
151 use super::*;
152 use crate::error::dto::DtoError;
153
154 #[derive(Clone, Debug, Eq, PartialEq, From)]
155 pub enum MilestoneOptionDto {
156 Receipt(ReceiptMilestoneOptionDto),
158 Parameters(ParametersMilestoneOptionDto),
160 }
161
162 impl<'de> Deserialize<'de> for MilestoneOptionDto {
163 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
164 let value = Value::deserialize(d)?;
165 Ok(
166 match value
167 .get("type")
168 .and_then(Value::as_u64)
169 .ok_or_else(|| serde::de::Error::custom("invalid milestone option type"))?
170 as u8
171 {
172 ReceiptMilestoneOption::KIND => {
173 MilestoneOptionDto::Receipt(ReceiptMilestoneOptionDto::deserialize(value).map_err(|e| {
174 serde::de::Error::custom(format!("cannot deserialize receipt milestone option: {}", e))
175 })?)
176 }
177 ParametersMilestoneOption::KIND => MilestoneOptionDto::Parameters(
178 ParametersMilestoneOptionDto::deserialize(value).map_err(|e| {
179 serde::de::Error::custom(format!("cannot deserialize parameters milestone option: {}", e))
180 })?,
181 ),
182 _ => return Err(serde::de::Error::custom("invalid milestone option type")),
183 },
184 )
185 }
186 }
187
188 impl Serialize for MilestoneOptionDto {
189 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
190 where
191 S: Serializer,
192 {
193 #[derive(Serialize)]
194 #[serde(untagged)]
195 enum MilestoneOptionDto_<'a> {
196 T1(&'a ReceiptMilestoneOptionDto),
197 T2(&'a ParametersMilestoneOptionDto),
198 }
199 #[derive(Serialize)]
200 struct TypedMilestoneOption<'a> {
201 #[serde(flatten)]
202 milestone_option: MilestoneOptionDto_<'a>,
203 }
204 let milestone_option = match self {
205 MilestoneOptionDto::Receipt(o) => TypedMilestoneOption {
206 milestone_option: MilestoneOptionDto_::T1(o),
207 },
208 MilestoneOptionDto::Parameters(o) => TypedMilestoneOption {
209 milestone_option: MilestoneOptionDto_::T2(o),
210 },
211 };
212 milestone_option.serialize(serializer)
213 }
214 }
215
216 impl From<&MilestoneOption> for MilestoneOptionDto {
217 fn from(value: &MilestoneOption) -> Self {
218 match value {
219 MilestoneOption::Receipt(v) => Self::Receipt(v.into()),
220 MilestoneOption::Parameters(v) => Self::Parameters(v.into()),
221 }
222 }
223 }
224
225 impl MilestoneOption {
226 pub fn try_from_dto(value: &MilestoneOptionDto, token_supply: u64) -> Result<MilestoneOption, DtoError> {
227 Ok(match value {
228 MilestoneOptionDto::Receipt(v) => {
229 MilestoneOption::Receipt(ReceiptMilestoneOption::try_from_dto(v, token_supply)?)
230 }
231 MilestoneOptionDto::Parameters(v) => MilestoneOption::Parameters(v.try_into()?),
232 })
233 }
234 }
235
236 impl MilestoneOptionDto {
237 pub fn kind(&self) -> u8 {
239 match self {
240 Self::Receipt(_) => ReceiptMilestoneOption::KIND,
241 Self::Parameters(_) => ParametersMilestoneOption::KIND,
242 }
243 }
244 }
245}