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}