bee_block/output/feature/
mod.rs

1// Copyright 2021-2022 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4mod issuer;
5mod metadata;
6mod sender;
7mod tag;
8
9use alloc::{boxed::Box, vec::Vec};
10
11use bitflags::bitflags;
12use derive_more::{Deref, From};
13use iterator_sorted::is_unique_sorted;
14use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable};
15
16pub use self::{issuer::IssuerFeature, metadata::MetadataFeature, sender::SenderFeature, tag::TagFeature};
17pub(crate) use self::{metadata::MetadataFeatureLength, tag::TagFeatureLength};
18use crate::{create_bitflags, Error};
19
20///
21#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, 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::InvalidFeatureKind)]
29pub enum Feature {
30    /// A sender feature.
31    #[packable(tag = SenderFeature::KIND)]
32    Sender(SenderFeature),
33    /// An issuer feature.
34    #[packable(tag = IssuerFeature::KIND)]
35    Issuer(IssuerFeature),
36    /// A metadata feature.
37    #[packable(tag = MetadataFeature::KIND)]
38    Metadata(MetadataFeature),
39    /// A tag feature.
40    #[packable(tag = TagFeature::KIND)]
41    Tag(TagFeature),
42}
43
44impl Feature {
45    /// Return the output kind of an `Output`.
46    pub fn kind(&self) -> u8 {
47        match self {
48            Self::Sender(_) => SenderFeature::KIND,
49            Self::Issuer(_) => IssuerFeature::KIND,
50            Self::Metadata(_) => MetadataFeature::KIND,
51            Self::Tag(_) => TagFeature::KIND,
52        }
53    }
54
55    /// Returns the [`FeatureFlags`] for the given [`Feature`].
56    pub fn flag(&self) -> FeatureFlags {
57        match self {
58            Self::Sender(_) => FeatureFlags::SENDER,
59            Self::Issuer(_) => FeatureFlags::ISSUER,
60            Self::Metadata(_) => FeatureFlags::METADATA,
61            Self::Tag(_) => FeatureFlags::TAG,
62        }
63    }
64}
65
66create_bitflags!(
67    /// A bitflags-based representation of the set of active [`Feature`]s.
68    pub FeatureFlags,
69    u16,
70    [
71        (SENDER, SenderFeature),
72        (ISSUER, IssuerFeature),
73        (METADATA, MetadataFeature),
74        (TAG, TagFeature),
75    ]
76);
77
78pub(crate) type FeatureCount = BoundedU8<0, { Features::COUNT_MAX }>;
79
80///
81#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Deref, Packable)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidFeatureCount(p.into())))]
84pub struct Features(#[packable(verify_with = verify_unique_sorted)] BoxedSlicePrefix<Feature, FeatureCount>);
85
86impl TryFrom<Vec<Feature>> for Features {
87    type Error = Error;
88
89    #[inline(always)]
90    fn try_from(features: Vec<Feature>) -> Result<Self, Self::Error> {
91        Self::new(features)
92    }
93}
94
95impl IntoIterator for Features {
96    type Item = Feature;
97    type IntoIter = alloc::vec::IntoIter<Self::Item>;
98
99    fn into_iter(self) -> Self::IntoIter {
100        Vec::from(Into::<Box<[Feature]>>::into(self.0)).into_iter()
101    }
102}
103
104impl Features {
105    ///
106    pub const COUNT_MAX: u8 = 4;
107
108    /// Creates a new [`Features`].
109    pub fn new(features: Vec<Feature>) -> Result<Self, Error> {
110        let mut features = BoxedSlicePrefix::<Feature, FeatureCount>::try_from(features.into_boxed_slice())
111            .map_err(Error::InvalidFeatureCount)?;
112
113        features.sort_by_key(Feature::kind);
114        // Sort is obviously fine now but uniqueness still needs to be checked.
115        verify_unique_sorted::<true>(&features, &())?;
116
117        Ok(Self(features))
118    }
119
120    /// Gets a reference to a [`Feature`] from a feature kind, if any.
121    #[inline(always)]
122    pub fn get(&self, key: u8) -> Option<&Feature> {
123        self.0
124            .binary_search_by_key(&key, Feature::kind)
125            // PANIC: indexation is fine since the index has been found.
126            .map(|index| &self.0[index])
127            .ok()
128    }
129
130    /// Gets a reference to a [`SenderFeature`], if any.
131    pub fn sender(&self) -> Option<&SenderFeature> {
132        if let Some(Feature::Sender(sender)) = self.get(SenderFeature::KIND) {
133            Some(sender)
134        } else {
135            None
136        }
137    }
138
139    /// Gets a reference to a [`IssuerFeature`], if any.
140    pub fn issuer(&self) -> Option<&IssuerFeature> {
141        if let Some(Feature::Issuer(issuer)) = self.get(IssuerFeature::KIND) {
142            Some(issuer)
143        } else {
144            None
145        }
146    }
147
148    /// Gets a reference to a [`MetadataFeature`], if any.
149    pub fn metadata(&self) -> Option<&MetadataFeature> {
150        if let Some(Feature::Metadata(metadata)) = self.get(MetadataFeature::KIND) {
151            Some(metadata)
152        } else {
153            None
154        }
155    }
156
157    /// Gets a reference to a [`TagFeature`], if any.
158    pub fn tag(&self) -> Option<&TagFeature> {
159        if let Some(Feature::Tag(tag)) = self.get(TagFeature::KIND) {
160            Some(tag)
161        } else {
162            None
163        }
164    }
165}
166
167#[inline]
168fn verify_unique_sorted<const VERIFY: bool>(features: &[Feature], _: &()) -> Result<(), Error> {
169    if VERIFY && !is_unique_sorted(features.iter().map(Feature::kind)) {
170        Err(Error::FeaturesNotUniqueSorted)
171    } else {
172        Ok(())
173    }
174}
175
176pub(crate) fn verify_allowed_features(features: &Features, allowed_features: FeatureFlags) -> Result<(), Error> {
177    for (index, feature) in features.iter().enumerate() {
178        if !allowed_features.contains(feature.flag()) {
179            return Err(Error::UnallowedFeature {
180                index,
181                kind: feature.kind(),
182            });
183        }
184    }
185
186    Ok(())
187}
188
189#[cfg(test)]
190mod test {
191    use super::*;
192
193    #[test]
194    fn all_flags_present() {
195        assert_eq!(
196            FeatureFlags::ALL_FLAGS,
197            &[
198                FeatureFlags::SENDER,
199                FeatureFlags::ISSUER,
200                FeatureFlags::METADATA,
201                FeatureFlags::TAG
202            ]
203        );
204    }
205}
206
207#[cfg(feature = "dto")]
208#[allow(missing_docs)]
209pub mod dto {
210    use serde::{Deserialize, Serialize, Serializer};
211    use serde_json::Value;
212
213    pub use self::{
214        issuer::dto::IssuerFeatureDto, metadata::dto::MetadataFeatureDto, sender::dto::SenderFeatureDto,
215        tag::dto::TagFeatureDto,
216    };
217    use super::*;
218    use crate::error::dto::DtoError;
219
220    #[derive(Clone, Debug, Eq, PartialEq, From)]
221    pub enum FeatureDto {
222        /// A sender feature.
223        Sender(SenderFeatureDto),
224        /// An issuer feature.
225        Issuer(IssuerFeatureDto),
226        /// A metadata feature.
227        Metadata(MetadataFeatureDto),
228        /// A tag feature.
229        Tag(TagFeatureDto),
230    }
231
232    impl<'de> Deserialize<'de> for FeatureDto {
233        fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
234            let value = Value::deserialize(d)?;
235            Ok(
236                match value
237                    .get("type")
238                    .and_then(Value::as_u64)
239                    .ok_or_else(|| serde::de::Error::custom("invalid feature type"))? as u8
240                {
241                    SenderFeature::KIND => {
242                        FeatureDto::Sender(SenderFeatureDto::deserialize(value).map_err(|e| {
243                            serde::de::Error::custom(format!("cannot deserialize sender feature: {}", e))
244                        })?)
245                    }
246                    IssuerFeature::KIND => {
247                        FeatureDto::Issuer(IssuerFeatureDto::deserialize(value).map_err(|e| {
248                            serde::de::Error::custom(format!("cannot deserialize issuer feature: {}", e))
249                        })?)
250                    }
251                    MetadataFeature::KIND => {
252                        FeatureDto::Metadata(MetadataFeatureDto::deserialize(value).map_err(|e| {
253                            serde::de::Error::custom(format!("cannot deserialize metadata feature: {}", e))
254                        })?)
255                    }
256                    TagFeature::KIND => FeatureDto::Tag(
257                        TagFeatureDto::deserialize(value)
258                            .map_err(|e| serde::de::Error::custom(format!("cannot deserialize tag feature: {}", e)))?,
259                    ),
260                    _ => return Err(serde::de::Error::custom("invalid feature type")),
261                },
262            )
263        }
264    }
265
266    impl Serialize for FeatureDto {
267        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
268        where
269            S: Serializer,
270        {
271            #[derive(Serialize)]
272            #[serde(untagged)]
273            enum FeatureDto_<'a> {
274                T1(&'a SenderFeatureDto),
275                T2(&'a IssuerFeatureDto),
276                T3(&'a MetadataFeatureDto),
277                T4(&'a TagFeatureDto),
278            }
279            #[derive(Serialize)]
280            struct TypedFeature<'a> {
281                #[serde(flatten)]
282                feature: FeatureDto_<'a>,
283            }
284            let feature = match self {
285                FeatureDto::Sender(o) => TypedFeature {
286                    feature: FeatureDto_::T1(o),
287                },
288                FeatureDto::Issuer(o) => TypedFeature {
289                    feature: FeatureDto_::T2(o),
290                },
291                FeatureDto::Metadata(o) => TypedFeature {
292                    feature: FeatureDto_::T3(o),
293                },
294                FeatureDto::Tag(o) => TypedFeature {
295                    feature: FeatureDto_::T4(o),
296                },
297            };
298            feature.serialize(serializer)
299        }
300    }
301
302    impl From<&Feature> for FeatureDto {
303        fn from(value: &Feature) -> Self {
304            match value {
305                Feature::Sender(v) => Self::Sender(SenderFeatureDto {
306                    kind: SenderFeature::KIND,
307                    address: v.address().into(),
308                }),
309                Feature::Issuer(v) => Self::Issuer(IssuerFeatureDto {
310                    kind: IssuerFeature::KIND,
311                    address: v.address().into(),
312                }),
313                Feature::Metadata(v) => Self::Metadata(MetadataFeatureDto {
314                    kind: MetadataFeature::KIND,
315                    data: v.to_string(),
316                }),
317                Feature::Tag(v) => Self::Tag(TagFeatureDto {
318                    kind: TagFeature::KIND,
319                    tag: v.to_string(),
320                }),
321            }
322        }
323    }
324
325    impl TryFrom<&FeatureDto> for Feature {
326        type Error = DtoError;
327
328        fn try_from(value: &FeatureDto) -> Result<Self, Self::Error> {
329            Ok(match value {
330                FeatureDto::Sender(v) => Self::Sender(SenderFeature::new((&v.address).try_into()?)),
331                FeatureDto::Issuer(v) => Self::Issuer(IssuerFeature::new((&v.address).try_into()?)),
332                FeatureDto::Metadata(v) => Self::Metadata(MetadataFeature::new(
333                    prefix_hex::decode(&v.data).map_err(|_e| DtoError::InvalidField("MetadataFeature"))?,
334                )?),
335                FeatureDto::Tag(v) => Self::Tag(TagFeature::new(
336                    prefix_hex::decode(&v.tag).map_err(|_e| DtoError::InvalidField("TagFeature"))?,
337                )?),
338            })
339        }
340    }
341
342    impl FeatureDto {
343        /// Return the feature kind of a `FeatureDto`.
344        pub fn kind(&self) -> u8 {
345            match self {
346                Self::Sender(_) => SenderFeature::KIND,
347                Self::Issuer(_) => IssuerFeature::KIND,
348                Self::Metadata(_) => MetadataFeature::KIND,
349                Self::Tag(_) => TagFeature::KIND,
350            }
351        }
352    }
353}