Skip to main content

mecha10_core/
macros.rs

1//! Mecha10 Framework Macros
2//!
3//! This module provides convenience macros to reduce boilerplate in node and driver implementations.
4
5#[macro_export]
6macro_rules! builder_setter {
7    ($(#[$meta:meta])* assign, $name:ident, $field:ident, $ty:ty) => {
8        $(#[$meta])*
9        pub fn $name(mut self, $field: $ty) -> Self {
10            self.$field = $field;
11            self
12        }
13    };
14    ($(#[$meta:meta])* assign, $name:ident, $holder:ident.$field:ident, $ty:ty) => {
15        $(#[$meta])*
16        pub fn $name(mut self, $field: $ty) -> Self {
17            self.$holder.$field = $field;
18            self
19        }
20    };
21    ($(#[$meta:meta])* into, $name:ident, $field:ident, $ty:ty) => {
22        $(#[$meta])*
23        pub fn $name(mut self, $field: $ty) -> Self {
24            self.$field = $field.into();
25            self
26        }
27    };
28    ($(#[$meta:meta])* into, $name:ident, $holder:ident.$field:ident, $ty:ty) => {
29        $(#[$meta])*
30        pub fn $name(mut self, $field: $ty) -> Self {
31            self.$holder.$field = $field.into();
32            self
33        }
34    };
35    ($(#[$meta:meta])* into_some, $name:ident, $field:ident, $ty:ty) => {
36        $(#[$meta])*
37        pub fn $name(mut self, $field: $ty) -> Self {
38            self.$field = Some($field.into());
39            self
40        }
41    };
42    ($(#[$meta:meta])* into_some, $name:ident, $holder:ident.$field:ident, $ty:ty) => {
43        $(#[$meta])*
44        pub fn $name(mut self, $field: $ty) -> Self {
45            self.$holder.$field = Some($field.into());
46            self
47        }
48    };
49    ($(#[$meta:meta])* push, $name:ident, $field:ident, $ty:ty) => {
50        $(#[$meta])*
51        pub fn $name(mut self, value: $ty) -> Self {
52            self.$field.push(value.into());
53            self
54        }
55    };
56    ($(#[$meta:meta])* insert, $name:ident, $field:ident, $key_ty:ty, $val_ty:ty) => {
57        $(#[$meta])*
58        pub fn $name(mut self, key: $key_ty, value: $val_ty) -> Self {
59            self.$field.insert(key.into(), value.into());
60            self
61        }
62    };
63    ($(#[$meta:meta])* insert, $name:ident, $holder:ident.$field:ident, $key_ty:ty, $val_ty:ty) => {
64        $(#[$meta])*
65        pub fn $name(mut self, key: $key_ty, value: $val_ty) -> Self {
66            self.$holder.$field.insert(key.into(), value.into());
67            self
68        }
69    };
70    ($(#[$meta:meta])* predicate, $name:ident, $field:ident, $($bound:tt)+) => {
71        $(#[$meta])*
72        pub fn $name<F>(mut self, value: F) -> Self
73        where
74            F: $($bound)+,
75        {
76            self.$field = Some(Box::new(value));
77            self
78        }
79    };
80    ($(#[$meta:meta])* duration_millis, $name:ident, $field:ident) => {
81        $(#[$meta])*
82        pub fn $name(mut self, duration: std::time::Duration) -> Self {
83            self.$field = Some(duration.as_millis() as u64);
84            self
85        }
86    };
87    ($(#[$meta:meta])* clamp, $name:ident, $holder:ident.$field:ident, $min:expr, $max:expr) => {
88        $(#[$meta])*
89        pub fn $name(mut self, $field: f32) -> Self {
90            self.$holder.$field = $field.clamp($min, $max);
91            self
92        }
93    };
94}
95
96#[macro_export]
97macro_rules! builder_build {
98    ($(#[$meta:meta])* $node:ty) => {
99        $(#[$meta])*
100        pub fn build(self) -> anyhow::Result<$node> {
101            Ok(<$node>::new(self.config))
102        }
103    };
104}
105
106#[macro_export]
107macro_rules! enum_str_method {
108    (
109        $(#[$meta:meta])*
110        $vis:vis fn $method:ident($enum:ident) -> $ret:ty {
111            $($variant:ident => $lit:literal),+ $(,)?
112        }
113    ) => {
114        impl $enum {
115            $(#[$meta])*
116            $vis fn $method(&self) -> $ret {
117                match self {
118                    $(Self::$variant => $lit,)+
119                }
120            }
121        }
122    };
123}
124
125#[macro_export]
126macro_rules! enum_display_via_str {
127    ($enum:ident { $($variant:ident => $lit:literal),+ $(,)? }) => {
128        impl std::fmt::Display for $enum {
129            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130                let s = match self {
131                    $(Self::$variant => $lit,)+
132                };
133                write!(f, "{s}")
134            }
135        }
136    };
137}
138
139#[macro_export]
140macro_rules! debug_struct_fields {
141    ($struct:ident { $($label:literal => $field:ident $(. $method:ident ( $($arg:expr),* $(,)? ))?),+ $(,)? }) => {
142        impl std::fmt::Debug for $struct {
143            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144                f.debug_struct(stringify!($struct))
145                    $(.field($label, &self.$field $(. $method ( $($arg),* ))?))+
146                    .finish()
147            }
148        }
149    };
150}
151
152#[macro_export]
153macro_rules! default_struct_fields {
154    ($struct:ident { $($field:ident: $value:expr),+ $(,)? }) => {
155        impl Default for $struct {
156            fn default() -> Self {
157                Self {
158                    $($field: $value,)+
159                }
160            }
161        }
162    };
163}
164
165#[macro_export]
166macro_rules! zeroed_count_init_body {
167    ($count_field:ident, $config:ident, $($log_arg:tt)*) => {{
168        $crate::prelude::info!($($log_arg)*);
169        Ok(Self {
170            $count_field: 0,
171            $config,
172        })
173    }};
174}
175
176#[macro_export]
177macro_rules! log_shutdown_fn_body {
178    ($shutdown_msg:literal, $($final_log_arg:tt)*) => {{
179        $crate::prelude::info!($shutdown_msg);
180        $crate::prelude::info!($($final_log_arg)*);
181        Ok(())
182    }};
183}
184
185#[macro_export]
186macro_rules! model_preflight {
187    ($model:expr, $label:literal, [$($hint:expr),+ $(,)?]) => {
188        $crate::error::preflight_model_exists(
189            std::path::Path::new(&$model.path),
190            $label,
191            &[$($hint),+],
192        )
193    };
194}
195
196#[macro_export]
197macro_rules! camera_image_decode_fn {
198    ($vis:vis fn $name:ident($msg_ty:ty, $decoder_ty:ty) -> $ret:ty) => {
199        $vis fn $name(msg: $msg_ty, h264_decoder: $decoder_ty) -> $ret {
200            mecha10_camera_codec::decode_camera_image(&msg.format, &msg.image_bytes, msg.width, msg.height, h264_decoder)
201        }
202    };
203}
204
205#[macro_export]
206macro_rules! odometry_publish_fn {
207    ($odom_ty:ident, $topic:expr) => {
208        async fn publish_odometry(&self, ctx: &$crate::context::Context) -> $crate::error::Result<()> {
209            let odom = $odom_ty {
210                timestamp: $crate::prelude::now_micros(),
211                x: self.x,
212                y: self.y,
213                theta: self.theta,
214                linear_vel: self.cmd_linear,
215                angular_vel: self.cmd_angular,
216            };
217
218            ctx.publish_to($topic, &odom).await?;
219            Ok(())
220        }
221    };
222}
223
224#[macro_export]
225macro_rules! new_with_defaults {
226    (
227        $(#[$meta:meta])*
228        $vis:vis fn new($($field:ident : $ty:ty),+ $(,)?) -> Self
229    ) => {
230        $(#[$meta])*
231        $vis fn new($($field: $ty),+) -> Self {
232            Self {
233                $($field,)+
234            }
235        }
236    };
237    (
238        $(#[$meta:meta])*
239        $vis:vis fn new($($field:ident : $ty:ty),+ $(,)?) -> Self {
240            $($default_field:ident : $default_value:expr),+ $(,)?
241        }
242    ) => {
243        $(#[$meta])*
244        $vis fn new($($field: $ty),+) -> Self {
245            Self {
246                $($field,)+
247                $($default_field: $default_value,)+
248            }
249        }
250    };
251}
252
253#[macro_export]
254macro_rules! node_main {
255    ($node_type:ty, $node_name:expr) => {
256        $crate::node_main!($node_type, $node_name, normal);
257    };
258    ($node_type:ty, $node_name:expr, $health:ident) => {
259        // Registers this node's `NodeDescriptor` (see `node_registry.rs`) so it's discoverable
260        // without a hand-written per-type entry elsewhere -- a no-op until the node also sets
261        // `NodeImpl::CRATE_NAME`.
262        $crate::register_node!($node_type);
263
264        pub async fn run() -> $crate::error::Result<()> {
265            let ctx = $crate::context::Context::new($node_name).await?;
266            // LAB-4066: validated against `<$node_type as NodeImpl>::Config`'s own
267            // `schemars`-derived schema -- the load-at-startup counterpart to preflight's
268            // declared-schema validation, see `Context::load_and_validate_node_config`.
269            let config: <$node_type as $crate::node::NodeImpl>::Config =
270                ctx.load_and_validate_node_config($node_name).await?;
271            $crate::node::run_node::<$node_type>(config, ctx, $crate::node::HealthReportingConfig::$health()).await
272        }
273    };
274}
275
276#[macro_export]
277macro_rules! new_then_run_fn {
278    ($node_type:ty) => {
279        /// Run the node.
280        ///
281        /// This is the entry point used by the generated node registry.
282        pub async fn run() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
283            let node = <$node_type>::new().await?;
284            node.run().await?;
285            Ok(())
286        }
287    };
288}