mecha10-core 0.9.0

Mecha10 core runtime and foundational types
Documentation
//! Mecha10 Framework Macros
//!
//! This module provides convenience macros to reduce boilerplate in node and driver implementations.

#[macro_export]
macro_rules! builder_setter {
    ($(#[$meta:meta])* assign, $name:ident, $field:ident, $ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, $field: $ty) -> Self {
            self.$field = $field;
            self
        }
    };
    ($(#[$meta:meta])* assign, $name:ident, $holder:ident.$field:ident, $ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, $field: $ty) -> Self {
            self.$holder.$field = $field;
            self
        }
    };
    ($(#[$meta:meta])* into, $name:ident, $field:ident, $ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, $field: $ty) -> Self {
            self.$field = $field.into();
            self
        }
    };
    ($(#[$meta:meta])* into, $name:ident, $holder:ident.$field:ident, $ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, $field: $ty) -> Self {
            self.$holder.$field = $field.into();
            self
        }
    };
    ($(#[$meta:meta])* into_some, $name:ident, $field:ident, $ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, $field: $ty) -> Self {
            self.$field = Some($field.into());
            self
        }
    };
    ($(#[$meta:meta])* into_some, $name:ident, $holder:ident.$field:ident, $ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, $field: $ty) -> Self {
            self.$holder.$field = Some($field.into());
            self
        }
    };
    ($(#[$meta:meta])* push, $name:ident, $field:ident, $ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, value: $ty) -> Self {
            self.$field.push(value.into());
            self
        }
    };
    ($(#[$meta:meta])* insert, $name:ident, $field:ident, $key_ty:ty, $val_ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, key: $key_ty, value: $val_ty) -> Self {
            self.$field.insert(key.into(), value.into());
            self
        }
    };
    ($(#[$meta:meta])* insert, $name:ident, $holder:ident.$field:ident, $key_ty:ty, $val_ty:ty) => {
        $(#[$meta])*
        pub fn $name(mut self, key: $key_ty, value: $val_ty) -> Self {
            self.$holder.$field.insert(key.into(), value.into());
            self
        }
    };
    ($(#[$meta:meta])* predicate, $name:ident, $field:ident, $($bound:tt)+) => {
        $(#[$meta])*
        pub fn $name<F>(mut self, value: F) -> Self
        where
            F: $($bound)+,
        {
            self.$field = Some(Box::new(value));
            self
        }
    };
    ($(#[$meta:meta])* duration_millis, $name:ident, $field:ident) => {
        $(#[$meta])*
        pub fn $name(mut self, duration: std::time::Duration) -> Self {
            self.$field = Some(duration.as_millis() as u64);
            self
        }
    };
    ($(#[$meta:meta])* clamp, $name:ident, $holder:ident.$field:ident, $min:expr, $max:expr) => {
        $(#[$meta])*
        pub fn $name(mut self, $field: f32) -> Self {
            self.$holder.$field = $field.clamp($min, $max);
            self
        }
    };
}

#[macro_export]
macro_rules! builder_build {
    ($(#[$meta:meta])* $node:ty) => {
        $(#[$meta])*
        pub fn build(self) -> anyhow::Result<$node> {
            Ok(<$node>::new(self.config))
        }
    };
}

#[macro_export]
macro_rules! enum_str_method {
    (
        $(#[$meta:meta])*
        $vis:vis fn $method:ident($enum:ident) -> $ret:ty {
            $($variant:ident => $lit:literal),+ $(,)?
        }
    ) => {
        impl $enum {
            $(#[$meta])*
            $vis fn $method(&self) -> $ret {
                match self {
                    $(Self::$variant => $lit,)+
                }
            }
        }
    };
}

#[macro_export]
macro_rules! enum_display_via_str {
    ($enum:ident { $($variant:ident => $lit:literal),+ $(,)? }) => {
        impl std::fmt::Display for $enum {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                let s = match self {
                    $(Self::$variant => $lit,)+
                };
                write!(f, "{s}")
            }
        }
    };
}

#[macro_export]
macro_rules! debug_struct_fields {
    ($struct:ident { $($label:literal => $field:ident $(. $method:ident ( $($arg:expr),* $(,)? ))?),+ $(,)? }) => {
        impl std::fmt::Debug for $struct {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                f.debug_struct(stringify!($struct))
                    $(.field($label, &self.$field $(. $method ( $($arg),* ))?))+
                    .finish()
            }
        }
    };
}

#[macro_export]
macro_rules! default_struct_fields {
    ($struct:ident { $($field:ident: $value:expr),+ $(,)? }) => {
        impl Default for $struct {
            fn default() -> Self {
                Self {
                    $($field: $value,)+
                }
            }
        }
    };
}

#[macro_export]
macro_rules! zeroed_count_init_body {
    ($count_field:ident, $config:ident, $($log_arg:tt)*) => {{
        $crate::prelude::info!($($log_arg)*);
        Ok(Self {
            $count_field: 0,
            $config,
        })
    }};
}

#[macro_export]
macro_rules! log_shutdown_fn_body {
    ($shutdown_msg:literal, $($final_log_arg:tt)*) => {{
        $crate::prelude::info!($shutdown_msg);
        $crate::prelude::info!($($final_log_arg)*);
        Ok(())
    }};
}

#[macro_export]
macro_rules! model_preflight {
    ($model:expr, $label:literal, [$($hint:expr),+ $(,)?]) => {
        $crate::error::preflight_model_exists(
            std::path::Path::new(&$model.path),
            $label,
            &[$($hint),+],
        )
    };
}

#[macro_export]
macro_rules! camera_image_decode_fn {
    ($vis:vis fn $name:ident($msg_ty:ty, $decoder_ty:ty) -> $ret:ty) => {
        $vis fn $name(msg: $msg_ty, h264_decoder: $decoder_ty) -> $ret {
            mecha10_camera_codec::decode_camera_image(&msg.format, &msg.image_bytes, msg.width, msg.height, h264_decoder)
        }
    };
}

#[macro_export]
macro_rules! odometry_publish_fn {
    ($odom_ty:ident, $topic:expr) => {
        async fn publish_odometry(&self, ctx: &$crate::context::Context) -> $crate::error::Result<()> {
            let odom = $odom_ty {
                timestamp: $crate::prelude::now_micros(),
                x: self.x,
                y: self.y,
                theta: self.theta,
                linear_vel: self.cmd_linear,
                angular_vel: self.cmd_angular,
            };

            ctx.publish_to($topic, &odom).await?;
            Ok(())
        }
    };
}

#[macro_export]
macro_rules! new_with_defaults {
    (
        $(#[$meta:meta])*
        $vis:vis fn new($($field:ident : $ty:ty),+ $(,)?) -> Self
    ) => {
        $(#[$meta])*
        $vis fn new($($field: $ty),+) -> Self {
            Self {
                $($field,)+
            }
        }
    };
    (
        $(#[$meta:meta])*
        $vis:vis fn new($($field:ident : $ty:ty),+ $(,)?) -> Self {
            $($default_field:ident : $default_value:expr),+ $(,)?
        }
    ) => {
        $(#[$meta])*
        $vis fn new($($field: $ty),+) -> Self {
            Self {
                $($field,)+
                $($default_field: $default_value,)+
            }
        }
    };
}

#[macro_export]
macro_rules! node_main {
    ($node_type:ty, $node_name:expr) => {
        $crate::node_main!($node_type, $node_name, normal);
    };
    ($node_type:ty, $node_name:expr, $health:ident) => {
        // Registers this node's `NodeDescriptor` (see `node_registry.rs`) so it's discoverable
        // without a hand-written per-type entry elsewhere -- a no-op until the node also sets
        // `NodeImpl::CRATE_NAME`.
        $crate::register_node!($node_type);

        pub async fn run() -> $crate::error::Result<()> {
            let ctx = $crate::context::Context::new($node_name).await?;
            // LAB-4066: validated against `<$node_type as NodeImpl>::Config`'s own
            // `schemars`-derived schema -- the load-at-startup counterpart to preflight's
            // declared-schema validation, see `Context::load_and_validate_node_config`.
            let config: <$node_type as $crate::node::NodeImpl>::Config =
                ctx.load_and_validate_node_config($node_name).await?;
            $crate::node::run_node::<$node_type>(config, ctx, $crate::node::HealthReportingConfig::$health()).await
        }
    };
}

#[macro_export]
macro_rules! new_then_run_fn {
    ($node_type:ty) => {
        /// Run the node.
        ///
        /// This is the entry point used by the generated node registry.
        pub async fn run() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
            let node = <$node_type>::new().await?;
            node.run().await?;
            Ok(())
        }
    };
}