ironrdp_core/
macros.rs

1/// Asserts that the traits support dynamic dispatch.
2///
3/// From <https://docs.rs/static_assertions/1.1.0/src/static_assertions/assert_obj_safe.rs.html#72-76>
4#[macro_export]
5macro_rules! assert_obj_safe {
6    ($($xs:path),+ $(,)?) => {
7        $(const _: Option<&dyn $xs> = None;)+
8    };
9}
10
11/// Asserts that the type implements _all_ of the given traits.
12///
13/// From <https://docs.rs/static_assertions/1.1.0/src/static_assertions/assert_impl.rs.html#113-121>
14#[macro_export]
15macro_rules! assert_impl {
16    ($type:ty: $($trait:path),+ $(,)?) => {
17        const _: fn() = || {
18            // Only callable when `$type` implements all traits in `$($trait)+`.
19            fn assert_impl_all<T: ?Sized $(+ $trait)+>() {}
20            assert_impl_all::<$type>();
21        };
22    };
23}
24
25/// Finds the name of the function in which this macro is expanded
26#[macro_export]
27macro_rules! function {
28    // Taken from https://stackoverflow.com/a/40234666
29    () => {{
30        fn f() {}
31        fn type_name_of<T>(_: T) -> &'static str {
32            core::any::type_name::<T>()
33        }
34        let name = type_name_of(f);
35        name.strip_suffix("::f").unwrap()
36    }};
37}
38
39/// Creates a "not enough bytes" error with context information.
40///
41/// This macro generates an error indicating that there weren't enough bytes
42/// in a buffer for a particular operation.
43///
44/// # Arguments
45///
46/// * `context` - The context in which the error occurred (optional)
47/// * `received` - The number of bytes actually received
48/// * `expected` - The number of bytes expected
49///
50/// # Examples
51///
52/// ```
53/// use ironrdp_core::not_enough_bytes_err;
54///
55/// let err = not_enough_bytes_err!("parsing header", 5, 10);
56/// ```
57///
58/// # Note
59///
60/// If the context is not provided, it will use the current function name.
61#[macro_export]
62macro_rules! not_enough_bytes_err {
63    ( $context:expr, $received:expr , $expected:expr $(,)? ) => {{
64        $crate::not_enough_bytes_err($context, $received, $expected)
65    }};
66    ( $received:expr , $expected:expr $(,)? ) => {{
67        $crate::not_enough_bytes_err!($crate::function!(), $received, $expected)
68    }};
69}
70
71/// Creates an "invalid field" error with context information.
72///
73/// This macro generates an error indicating that a field in a data structure
74/// or input is invalid for some reason.
75///
76/// # Arguments
77///
78/// * `context` - The context in which the error occurred (optional)
79/// * `field` - The name of the invalid field
80/// * `reason` - The reason why the field is invalid
81///
82/// # Examples
83///
84/// ```
85/// use ironrdp_core::invalid_field_err;
86///
87/// let err = invalid_field_err!("user input", "Age", "must be positive");
88/// ```
89///
90/// # Note
91///
92/// If the context is not provided, it will use the current function name.
93#[macro_export]
94macro_rules! invalid_field_err {
95    ( $context:expr, $field:expr , $reason:expr $(,)? ) => {{
96        $crate::invalid_field_err($context, $field, $reason)
97    }};
98    ( $field:expr , $reason:expr $(,)? ) => {{
99        $crate::invalid_field_err!($crate::function!(), $field, $reason)
100    }};
101}
102
103/// Creates an "unexpected message type" error with context information.
104///
105/// This macro generates an error indicating that an unexpected message type
106/// was received in a particular context.
107///
108/// # Arguments
109///
110/// * `context` - The context in which the error occurred (optional)
111/// * `got` - The unexpected message type that was received
112///
113/// # Examples
114///
115/// ```
116/// use ironrdp_core::unexpected_message_type_err;
117///
118/// let err = unexpected_message_type_err!("Erase");
119/// ```
120///
121/// # Note
122///
123/// If the context is not provided, it will use the current function name.
124#[macro_export]
125macro_rules! unexpected_message_type_err {
126    ( $context:expr, $got:expr $(,)? ) => {{
127        $crate::unexpected_message_type_err($context, $got)
128    }};
129    ( $got:expr $(,)? ) => {{
130        $crate::unexpected_message_type_err!($crate::function!(), $got)
131    }};
132}
133
134/// Creates an "unsupported version" error with context information.
135///
136/// This macro generates an error indicating that an unsupported version
137/// was encountered in a particular context.
138///
139/// # Arguments
140///
141/// * `context` - The context in which the error occurred (optional)
142/// * `got` - The unsupported version that was encountered
143///
144/// # Examples
145///
146/// ```
147/// use ironrdp_core::unsupported_version_err;
148///
149/// let err = unsupported_version_err!("protocol version", 12);
150/// ```
151///
152/// # Note
153///
154/// If the context is not provided, it will use the current function name.
155#[macro_export]
156macro_rules! unsupported_version_err {
157    ( $context:expr, $got:expr $(,)? ) => {{
158        $crate::unsupported_version_err($context, $got)
159    }};
160    ( $got:expr $(,)? ) => {{
161        $crate::unsupported_version_err!($crate::function!(), $got)
162    }};
163}
164
165/// Creates an "unsupported value" error with context information.
166///
167/// This macro generates an error indicating that an unsupported value
168/// was encountered for a specific named parameter or field.
169///
170/// # Arguments
171///
172/// * `context` - The context in which the error occurred (optional)
173/// * `name` - The name of the parameter or field with the unsupported value
174/// * `value` - The unsupported value that was encountered
175///
176/// # Examples
177///
178/// ```
179/// use ironrdp_core::unsupported_value_err;
180///
181/// let err = unsupported_value_err!("configuration", "log_level", "EXTREME");
182/// ```
183///
184/// # Note
185///
186/// If the context is not provided, it will use the current function name.
187#[macro_export]
188macro_rules! unsupported_value_err {
189    ( $context:expr, $name:expr, $value:expr $(,)? ) => {{
190        $crate::unsupported_value_err($context, $name, $value)
191    }};
192    ( $name:expr, $value:expr $(,)? ) => {{
193        $crate::unsupported_value_err!($crate::function!(), $name, $value)
194    }};
195}
196
197/// Creates a generic "other" error with optional context and source information.
198///
199/// This macro generates a generic error that can include a description, context,
200/// and an optional source error. It's useful for creating custom errors or
201/// wrapping other errors with additional context.
202///
203/// # Arguments
204///
205/// * `description` - A description of the error (optional)
206/// * `context` - The context in which the error occurred (optional)
207/// * `source` - The source error, if this error is wrapping another (optional)
208///
209/// # Examples
210///
211/// ```
212/// use ironrdp_core::other_err;
213///
214/// // With description and source
215/// let source_err = std::io::Error::new(std::io::ErrorKind::Other, "Source error");
216/// let err = other_err!("Something went wrong", source: source_err);
217///
218/// // With context and description
219/// let err = other_err!("parsing input", "Unexpected end of file");
220///
221/// // With only description
222/// let err = other_err!("Operation failed");
223///
224/// // With only source
225/// let err = other_err!(source: std::io::Error::new(std::io::ErrorKind::Other, "IO error"));
226/// ```
227///
228/// # Note
229///
230/// If the context is not provided, it will use the current function name.
231#[macro_export]
232macro_rules! other_err {
233    ( $context:expr, source: $source:expr $(,)? ) => {{
234        $crate::other_err_with_source($context, "", $source)
235    }};
236    ( $context:expr, $description:expr $(,)? ) => {{
237        $crate::other_err($context, $description)
238    }};
239    ( source: $source:expr $(,)? ) => {{
240        $crate::other_err!($crate::function!(), source: $source)
241    }};
242    ( $description:expr $(,)? ) => {{
243        $crate::other_err!($crate::function!(), $description)
244    }};
245}
246
247/// Ensures that a buffer has at least the expected size.
248///
249/// This macro checks if the buffer length is greater than or equal to the expected size.
250/// If not, it returns a "not enough bytes" error.
251///
252/// # Arguments
253///
254/// * `ctx` - The context for the error message (optional)
255/// * `buf` - The buffer to check
256/// * `expected` - The expected minimum size of the buffer
257///
258/// # Examples
259///
260/// ```
261/// use ironrdp_core::ensure_size;
262///
263/// fn parse_data(buf: &[u8]) -> Result<(), Error> {
264///     ensure_size!(in: buf, size: 10);
265///     // ... rest of the parsing logic
266///     Ok(())
267/// }
268/// ```
269///
270/// # Note
271///
272/// If the context is not provided, it will use the current function name.
273#[macro_export]
274macro_rules! ensure_size {
275    (ctx: $ctx:expr, in: $buf:ident, size: $expected:expr) => {{
276        let received = $buf.len();
277        let expected = $expected;
278        if !(received >= expected) {
279            return Err($crate::not_enough_bytes_err($ctx, received, expected));
280        }
281    }};
282    (in: $buf:ident, size: $expected:expr) => {{
283        $crate::ensure_size!(ctx: $crate::function!(), in: $buf, size: $expected)
284    }};
285}
286
287/// Ensures that a buffer has at least the fixed part size of a struct.
288///
289/// This macro is a specialized version of `ensure_size` that uses the
290/// `FIXED_PART_SIZE` constant of the current struct.
291///
292/// # Examples
293///
294/// ```
295/// use ironrdp_core::ensure_fixed_part_size;
296///
297/// struct MyStruct {
298///     // ... fields
299/// }
300///
301/// impl MyStruct {
302///     const FIXED_PART_SIZE: usize = 20;
303///
304///     fn parse(buf: &[u8]) -> Result<Self, Error> {
305///         ensure_fixed_part_size!(in: buf);
306///         // ... parsing logic
307///     }
308/// }
309/// ```
310///
311/// # Note
312///
313/// This macro assumes that the current struct has a `FIXED_PART_SIZE` constant defined.
314#[macro_export]
315macro_rules! ensure_fixed_part_size {
316    (in: $buf:ident) => {{
317        $crate::ensure_size!(ctx: $crate::function!(), in: $buf, size: Self::FIXED_PART_SIZE)
318    }};
319}
320
321/// Safely casts a length to a different integer type.
322///
323/// This macro attempts to convert a length value to a different integer type,
324/// returning an error if the conversion fails due to overflow.
325///
326/// # Arguments
327///
328/// * `ctx` - The context for the error message (optional)
329/// * `field` - The name of the field being cast
330/// * `len` - The length value to cast
331///
332/// # Examples
333///
334/// ```
335/// use ironrdp_core::cast_length;
336///
337/// fn process_data(data: &[u8]) -> Result<(), Error> {
338///     let len: u16 = cast_length!("data length", data.len())?;
339///     // ... rest of the processing logic
340///     Ok(())
341/// }
342/// ```
343///
344/// # Note
345///
346/// If the context is not provided, it will use the current function name.
347#[macro_export]
348macro_rules! cast_length {
349    ($ctx:expr, $field:expr, $len:expr) => {{
350        $len.try_into()
351            .map_err(|e| $crate::invalid_field_err_with_source($ctx, $field, "too many elements", e))
352    }};
353    ($field:expr, $len:expr) => {{
354        $crate::cast_length!($crate::function!(), $field, $len)
355    }};
356}
357
358/// Safely casts an integer to a different integer type.
359///
360/// This macro attempts to convert an integer value to a different integer type,
361/// returning an error if the conversion fails due to out-of-range issues.
362///
363/// # Arguments
364///
365/// * `ctx` - The context for the error message (optional)
366/// * `field` - The name of the field being cast
367/// * `len` - The integer value to cast
368///
369/// # Examples
370///
371/// ```
372/// use ironrdp_core::cast_int;
373///
374/// fn process_value(value: u64) -> Result<i32, Error> {
375///     let casted_value: i32 = cast_int!("input value", value)?;
376///     Ok(casted_value)
377/// }
378/// ```
379///
380/// # Note
381///
382/// If the context is not provided, it will use the current function name.
383#[macro_export]
384macro_rules! cast_int {
385    ($ctx:expr, $field:expr, $len:expr) => {{
386        $len.try_into().map_err(|e| {
387            $crate::invalid_field_err_with_source($ctx, $field, "out of range integral type conversion", e)
388        })
389    }};
390    ($field:expr, $len:expr) => {{
391        $crate::cast_int!($crate::function!(), $field, $len)
392    }};
393}
394
395/// Writes zeroes using as few `write_u*` calls as possible.
396///
397/// This is similar to `ironrdp_core::padding::write`, but the loop is optimized out when a single
398/// operation is enough.
399#[macro_export]
400macro_rules! write_padding {
401    ($dst:expr, 1) => {
402        $dst.write_u8(0)
403    };
404    ($dst:expr, 2) => {
405        $dst.write_u16(0)
406    };
407    ($dst:expr, 4) => {
408        $dst.write_u32(0)
409    };
410    ($dst:expr, 8) => {
411        $dst.write_u64(0)
412    };
413    ($dst:expr, $n:expr) => {
414        $crate::write_padding($dst, $n)
415    };
416}
417
418/// Moves read cursor, ignoring padding bytes.
419///
420/// This is similar to `ironrdp_pdu::padding::read`, only exists for consistency with `write_padding!`.
421#[macro_export]
422macro_rules! read_padding {
423    ($src:expr, $n:expr) => {
424        $crate::read_padding($src, $n)
425    };
426}