iota_stardust_types/block/
macro.rs

1// Copyright (c) 2026 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4/// Convenience macro to implement and derive base features of identifiers.
5#[macro_export]
6macro_rules! impl_id {
7    ($vis:vis $name:ident, $length:literal, $doc:literal) => {
8        #[doc = $doc]
9        #[derive(
10            Clone,
11            Copy,
12            Eq,
13            Hash,
14            PartialEq,
15            Ord,
16            PartialOrd,
17            derive_more::From,
18            derive_more::AsRef,
19            packable::Packable,
20        )]
21        #[as_ref(forward)]
22        $vis struct $name([u8; $name::LENGTH]);
23
24        impl $name {
25            #[doc = concat!("The length of a [`", stringify!($name),"`].")]
26            $vis const LENGTH: usize = $length;
27
28            #[doc = concat!("Creates a new [`", stringify!($name),"`].")]
29            $vis fn new(bytes: [u8; $name::LENGTH]) -> Self {
30                Self::from(bytes)
31            }
32
33            #[doc = concat!("Creates a null [`", stringify!($name),"`].")]
34            pub fn null() -> Self {
35                Self::from([0u8; $name::LENGTH])
36            }
37
38            #[doc = concat!("Checks if the [`", stringify!($name),"`] is null.")]
39            pub fn is_null(&self) -> bool {
40                self.0.iter().all(|&b| b == 0)
41            }
42        }
43
44        impl core::str::FromStr for $name {
45            type Err = $crate::block::Error;
46
47            fn from_str(s: &str) -> Result<Self, Self::Err> {
48                Ok($name::new(prefix_hex::decode(s).map_err($crate::block::Error::Hex)?))
49            }
50        }
51
52        impl TryFrom<&alloc::string::String> for $name {
53            type Error = $crate::block::Error;
54
55            fn try_from(s: &alloc::string::String) -> Result<Self, Self::Error> {
56                core::str::FromStr::from_str(s.as_str())
57            }
58        }
59
60        impl TryFrom<&str> for $name {
61            type Error = $crate::block::Error;
62
63            fn try_from(s: &str) -> Result<Self, Self::Error> {
64                core::str::FromStr::from_str(s)
65            }
66        }
67
68        impl $crate::block::ConvertTo<$name> for &alloc::string::String {
69            fn convert(self) -> Result<$name, $crate::block::Error> {
70                self.try_into()
71            }
72        }
73
74        impl $crate::block::ConvertTo<$name> for &str {
75            fn convert(self) -> Result<$name, $crate::block::Error> {
76                self.try_into()
77            }
78        }
79
80        impl core::fmt::Display for $name {
81            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82                write!(f, "{}", prefix_hex::encode(self.0))
83            }
84        }
85
86        impl core::fmt::Debug for $name {
87            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88                write!(f, "{}({})", stringify!($name), self)
89            }
90        }
91
92        impl core::ops::Deref for $name {
93            type Target = [u8; $name::LENGTH];
94
95            fn deref(&self) -> &Self::Target {
96                &self.0
97            }
98        }
99    };
100}
101
102/// Convenience macro to serialize types to string via serde.
103#[macro_export]
104#[cfg(feature = "serde")]
105macro_rules! string_serde_impl {
106    ($type:ty) => {
107        impl serde::Serialize for $type {
108            fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
109                use alloc::string::ToString;
110
111                s.serialize_str(&self.to_string())
112            }
113        }
114
115        impl<'de> serde::Deserialize<'de> for $type {
116            fn deserialize<D>(deserializer: D) -> Result<$type, D::Error>
117            where
118                D: serde::Deserializer<'de>,
119            {
120                struct StringVisitor;
121
122                impl<'de> serde::de::Visitor<'de> for StringVisitor {
123                    type Value = $type;
124
125                    fn expecting(
126                        &self,
127                        formatter: &mut core::fmt::Formatter<'_>,
128                    ) -> core::fmt::Result {
129                        formatter.write_str("a string representing the value")
130                    }
131
132                    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
133                    where
134                        E: serde::de::Error,
135                    {
136                        let value =
137                            core::str::FromStr::from_str(v).map_err(serde::de::Error::custom)?;
138                        Ok(value)
139                    }
140                }
141
142                deserializer.deserialize_str(StringVisitor)
143            }
144        }
145    };
146}
147
148/// Macro to create bitflags.
149///
150/// Convenience macro to work around the fact the `[bitflags]` crate does not
151/// yet support iterating over the individual flags. This macro essentially
152/// creates the `[bitflags]` and puts the individual flags into an associated
153/// constant `pub const ALL_FLAGS: &'static []`.
154#[macro_export]
155macro_rules! create_bitflags {
156    ($(#[$meta:meta])* $vis:vis $Name:ident, $TagType:ty, [$(($FlagName:ident, $TypeName:ident),)+]) => {
157        bitflags! {
158            $(#[$meta])*
159            #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
160            $vis struct $Name: $TagType {
161                $(
162                    #[doc = concat!("Signals the presence of a [`", stringify!($TypeName), "`].")]
163                    const $FlagName = 1 << $TypeName::KIND;
164                )*
165            }
166        }
167
168        impl $Name {
169            #[allow(dead_code)]
170            /// Returns a slice of all possible base flags.
171            $vis const ALL_FLAGS: &'static [$Name] = &[$($Name::$FlagName),*];
172        }
173    };
174}