rasn/
macros.rs

1//! Macros to automate common operations in `rasn` such as constructing
2//! constraints.
3
4pub use crate::{
5    constraints, permitted_alphabet_constraint, size_constraint, value_constraint, Decode, Encode,
6};
7
8#[macro_use]
9#[cfg(test)]
10pub mod test;
11
12/// Helper macro to create constant value constraints.
13///
14/// Usage:
15/// ```rust
16/// use rasn::{types::Constraint, macros::*};
17/// // Full range
18/// const FULL_RANGE: Constraint = value_constraint!(0, 100);
19/// const FULL_RANGE_EXTEND: Constraint = value_constraint!(0, 100, extensible);
20/// const START_ONLY: Constraint = value_constraint!(start: 42);
21/// const START_ONLY_EXTENDED: Constraint = value_constraint!(start: 42, extensible);
22/// const END_ONLY: Constraint = value_constraint!(end: 42);
23/// const SINGLE: Constraint = value_constraint!(42);
24/// const EXT_SINGLE: Constraint = value_constraint!(42, extensible);
25/// ```
26#[macro_export]
27macro_rules! value_constraint {
28    ($($args:tt)*) => {
29        $crate::bounded_constraint!(Value, $($args)*)
30    };
31}
32
33/// Helper macro to create constant size constraints.
34///
35/// Usage:
36/// ```rust
37/// use rasn::{types::Constraint, macros::*};
38/// // Full range
39/// const RANGE: Constraint = size_constraint!(0, 100);
40/// const RANGE_EXTEND: Constraint = size_constraint!(0, 100, extensible);
41/// const START_ONLY: Constraint = size_constraint!(start: 42);
42/// const START_ONLY_EXTENDED: Constraint = size_constraint!(start: 42, extensible);
43/// const END_ONLY: Constraint = size_constraint!(end: 42);
44/// const FIXED: Constraint = size_constraint!(42);
45/// const EXT_FIXED: Constraint = size_constraint!(42, extensible);
46/// ```
47#[macro_export]
48macro_rules! size_constraint {
49    ($($args:tt)*) => {
50        $crate::bounded_constraint!(Size, $($args)*)
51    };
52}
53
54/// Helper macro to create an array of constant constraints.
55///
56/// Usage:
57/// ```rust
58/// use rasn::{types::Constraints, macros::*};
59/// const CONSTRAINTS: Constraints = constraints!(value_constraint!(0, 100), size_constraint!(0, 100));
60/// ```
61/// See other macros about the parameter usage.
62#[macro_export]
63macro_rules! constraints {
64    ($($constraint:expr),+ $(,)?) => {
65        $crate::types::constraints::Constraints::new(&[$($constraint),+])
66    };
67}
68/// Helper macro to create a permitted alphabet constraint.
69///
70/// Usage:
71/// ```rust
72/// use rasn::prelude::*;
73/// use rasn::macros::*;
74/// const CONSTRAINT: Constraints = constraints!(permitted_alphabet_constraint!(&[
75///     b'0' as u32,
76///     b'1' as u32,
77///     b'2' as u32,
78///     b'3' as u32,
79///     b'4' as u32,
80///     b'5' as u32
81/// ]));
82/// ```
83#[macro_export]
84macro_rules! permitted_alphabet_constraint {
85    ( $alphabet:expr) => {
86        $crate::types::constraints::Constraint::PermittedAlphabet(
87            $crate::types::constraints::Extensible::new(
88                $crate::types::constraints::PermittedAlphabet::new($alphabet),
89            ),
90        )
91    };
92}
93
94#[doc(hidden)]
95#[macro_export]
96macro_rules! bounded_constraint {
97    // Single value with extensibility
98    ($constraint_type:ident, $single:expr, extensible) => {
99        $crate::types::constraints::Constraint::$constraint_type(
100            $crate::types::constraints::Extensible::new(
101                $crate::types::constraints::$constraint_type::new(
102                    $crate::types::constraints::Bounded::Single($single),
103                ),
104            )
105            .set_extensible(true),
106        )
107    };
108
109    // Range with extensibility
110    ($constraint_type:ident, $start:expr, $end:expr, extensible) => {
111        $crate::types::constraints::Constraint::$constraint_type(
112            $crate::types::constraints::Extensible::new(
113                $crate::types::constraints::$constraint_type::new(
114                    $crate::types::constraints::Bounded::const_new($start, $end),
115                ),
116            )
117            .set_extensible(true),
118        )
119    };
120
121    // Only start with extensibility
122    ($constraint_type:ident, start: $start:expr, extensible) => {
123        $crate::types::constraints::Constraint::$constraint_type(
124            $crate::types::constraints::Extensible::new(
125                $crate::types::constraints::$constraint_type::new(
126                    $crate::types::constraints::Bounded::start_from($start),
127                ),
128            )
129            .set_extensible(true),
130        )
131    };
132
133    // Only end with extensibility
134    ($constraint_type:ident, end: $end:expr, extensible) => {
135        $crate::types::constraints::Constraint::$constraint_type(
136            $crate::types::constraints::Extensible::new(
137                $crate::types::constraints::$constraint_type::new(
138                    $crate::types::constraints::Bounded::up_to($end),
139                ),
140            )
141            .set_extensible(true),
142        )
143    };
144
145    // Start and end
146    ($constraint_type:ident, $start:expr, $end:expr) => {
147        $crate::types::constraints::Constraint::$constraint_type(
148            $crate::types::constraints::Extensible::new(
149                $crate::types::constraints::$constraint_type::new(
150                    $crate::types::constraints::Bounded::const_new($start, $end),
151                ),
152            ),
153        )
154    };
155
156    // Only start provided
157    ($constraint_type:ident, start: $start:expr) => {
158        $crate::types::constraints::Constraint::$constraint_type(
159            $crate::types::constraints::Extensible::new(
160                $crate::types::constraints::$constraint_type::new(
161                    $crate::types::constraints::Bounded::start_from($start),
162                ),
163            ),
164        )
165    };
166
167    // Only end provided
168    ($constraint_type:ident, end: $end:expr) => {
169        $crate::types::constraints::Constraint::$constraint_type(
170            $crate::types::constraints::Extensible::new(
171                $crate::types::constraints::$constraint_type::new(
172                    $crate::types::constraints::Bounded::up_to($end),
173                ),
174            ),
175        )
176    };
177
178    // Single value
179    ($constraint_type:ident, $single:expr) => {
180        $crate::types::constraints::Constraint::$constraint_type(
181            $crate::types::constraints::Extensible::new(
182                $crate::types::constraints::$constraint_type::new(
183                    $crate::types::constraints::Bounded::Single($single),
184                ),
185            ),
186        )
187    };
188}
189
190/// Helper macro to create a `&'static Oid` from a string literal. The string
191/// literal must conform to the standard OID format and must be a valid OID
192/// (first arc must be <= 2), but also accepts OIDs with and without a leading
193/// `.`.
194///
195/// Usage:
196/// ```rust
197/// const SYS_DESCR: &'static rasn::types::Oid = rasn::oid!(".1.3.6.1.2.1.1.1.0");
198/// assert_eq!(SYS_DESCR, rasn::types::Oid::new(&[1, 3, 6, 1, 2, 1, 1, 1, 0]).unwrap());
199/// ```
200#[macro_export]
201macro_rules! oid {
202    ($s:literal) => {{
203        const OID: &'static $crate::types::Oid = const {
204            const BYTE_STRING: &'static [u8] = const {
205                let s: &'static str = $s;
206                let bytes = s.as_bytes();
207
208                core::assert!(!bytes.is_empty(), "OID string literals cannot be empty");
209                core::assert!(bytes.is_ascii(), "OID string literals must be ASCII");
210                core::assert!(bytes[bytes.len() - 1] != b'.', "OID string literals cannot end with a period");
211
212                match bytes {
213                    [b'.', rest @ ..] => rest,
214                    bytes => bytes,
215                }
216            };
217
218            const COMPONENT_LEN: usize = const {
219                let mut component_count = 1;
220                let mut index = 0;
221                while index < BYTE_STRING.len() {
222                    let byte = BYTE_STRING[index];
223
224                    core::assert!(core::matches!(byte, b'0'..=b'9' | b'.'), "OID string literals can only contain ASCII digits and periods");
225
226                    if byte == b'.' {
227                        if index + 1 < BYTE_STRING.len() {
228                            core::assert!(BYTE_STRING[index + 1] != b'.', "OID string literals cannot contain two or more consecutive periods");
229                        }
230                        component_count += 1;
231                    }
232
233                    index += 1;
234                }
235
236                component_count
237            };
238
239            const COMPONENTS: [u32; COMPONENT_LEN] = const {
240                let mut bytes_index = 0;
241
242                let mut components = [0u32; COMPONENT_LEN];
243                let mut index = 0;
244                while bytes_index < BYTE_STRING.len() {
245                    let byte = BYTE_STRING[bytes_index];
246                    match byte {
247                        b'0'..=b'9' => components[index] = components[index] * 10 + ((byte - b'0') as u32),
248                        b'.' => index += 1,
249                        _ => core::unreachable!(),
250                    }
251
252                    bytes_index += 1;
253                }
254
255                core::assert!(components[0] <= 2, "the first OID arc must be <= 2");
256
257                components
258            };
259
260            $crate::types::Oid::new(&COMPONENTS).unwrap()
261        };
262
263        OID
264    }}
265}