From 9eddfc1f96928b3e8d99bad44be2bae1bec36826 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Mon, 9 Feb 2026 15:11:53 +0530 Subject: [PATCH 1/5] Rust::com Interface declarative macro for com-api * Enabled the interface and dependent type generation with macro for user --- .../basic-consumer-producer.rs | 11 +- .../com-api-gen/com_api_gen.rs | 103 +++-------- .../rust/com-api/com-api-concept-macros/BUILD | 14 +- .../interface_macros.rs | 170 ++++++++++++++++++ score/mw/com/impl/rust/com-api/com-api/BUILD | 1 + .../com/impl/rust/com-api/com-api/com_api.rs | 2 + 6 files changed, 216 insertions(+), 85 deletions(-) create mode 100644 score/mw/com/impl/rust/com-api/com-api-concept-macros/interface_macros.rs diff --git a/score/mw/com/example/com-api-example/basic-consumer-producer.rs b/score/mw/com/example/com-api-example/basic-consumer-producer.rs index 33a06e562..2c53fe85e 100644 --- a/score/mw/com/example/com-api-example/basic-consumer-producer.rs +++ b/score/mw/com/example/com-api-example/basic-consumer-producer.rs @@ -12,12 +12,19 @@ ********************************************************************************/ use com_api::{ - Builder, Error, FindServiceSpecifier, InstanceSpecifier, LolaRuntimeBuilderImpl, + Builder, Error, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl, OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, }; -use com_api_gen::{Tire, VehicleConsumer, VehicleInterface, VehicleOfferedProducer}; +use com_api_gen::{Tire, VehicleInterface }; + +// Type aliases for generated consumer and offered producer types for the Vehicle interface +// VehicleConsumer is the consumer type generated for the Vehicle interface, parameterized by the runtime R +type VehicleConsumer = ::Consumer; +// VehicleOfferedProducer is the offered producer type generated for the Vehicle interface, parameterized by the runtime R +type VehicleOfferedProducer = + <::Producer as Producer>::OfferedProducer; // Example struct demonstrating composition with VehicleConsumer pub struct VehicleMonitor { diff --git a/score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs b/score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs index 586a8976c..6b86ef255 100644 --- a/score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs +++ b/score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs @@ -11,10 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -use com_api::{ - CommData, Consumer, Interface, OfferedProducer, Producer, ProviderInfo, Publisher, Reloc, - Runtime, Subscriber, -}; +use com_api::{interface, CommData, ProviderInfo, Publisher, Reloc, Subscriber}; #[derive(Debug, Reloc)] #[repr(C)] @@ -34,81 +31,23 @@ impl CommData for Exhaust { const ID: &'static str = "Exhaust"; } -pub struct VehicleInterface {} - -/// Generic -impl Interface for VehicleInterface { - const INTERFACE_ID: &'static str = "VehicleInterface"; - type Consumer = VehicleConsumer; - type Producer = VehicleProducer; -} - -pub struct VehicleConsumer { - pub left_tire: R::Subscriber, - pub exhaust: R::Subscriber, -} - -impl Consumer for VehicleConsumer { - fn new(instance_info: R::ConsumerInfo) -> Self { - VehicleConsumer { - left_tire: R::Subscriber::new("left_tire", instance_info.clone()) - .expect("Failed to create subscriber"), - exhaust: R::Subscriber::new("exhaust", instance_info.clone()) - .expect("Failed to create subscriber"), - } - } -} - -pub struct AnotherInterface {} - -pub struct VehicleProducer { - _runtime: core::marker::PhantomData, - instance_info: R::ProviderInfo, -} - -impl Producer for VehicleProducer { - type Interface = VehicleInterface; - type OfferedProducer = VehicleOfferedProducer; - - fn offer(self) -> com_api::Result { - let vehicle_offered_producer = VehicleOfferedProducer { - left_tire: R::Publisher::new("left_tire", self.instance_info.clone()) - .expect("Failed to create publisher"), - exhaust: R::Publisher::new("exhaust", self.instance_info.clone()) - .expect("Failed to create publisher"), - instance_info: self.instance_info.clone(), - }; - // Offer the service instance to make it discoverable - // this is called after skeleton created using producer_builder API - self.instance_info.offer_service()?; - Ok(vehicle_offered_producer) - } - - fn new(instance_info: R::ProviderInfo) -> com_api::Result { - Ok(VehicleProducer { - _runtime: core::marker::PhantomData, - instance_info, - }) - } -} - -pub struct VehicleOfferedProducer { - pub left_tire: R::Publisher, - pub exhaust: R::Publisher, - instance_info: R::ProviderInfo, -} - -impl OfferedProducer for VehicleOfferedProducer { - type Interface = VehicleInterface; - type Producer = VehicleProducer; - - fn unoffer(self) -> com_api::Result { - let vehicle_producer = VehicleProducer { - _runtime: std::marker::PhantomData, - instance_info: self.instance_info.clone(), - }; - // Stop offering the service instance to withdraw it from system availability - self.instance_info.stop_offer_service()?; - Ok(vehicle_producer) - } -} +// Example interface definition using the interface macro +// This will generate the following types and trait implementations: +// - VehicleInterface struct with INTERFACE_ID = "Vehicle" +// - VehicleConsumer, VehicleProducer, VehicleOfferedProducer with appropriate trait +// implementations for the Vehicle interface. +// The macro invocation defines an interface named "Vehicle" with two events: "left_tire" and "exhaust". +// The generated code will include: +// - VehicleInterface struct with INTERFACE_ID = "Vehicle" +// - VehicleConsumer struct that implements Consumer trait for subscribing to "left_tire" +// and "exhaust" events. +// - VehicleProducer struct that implements Producer trait for producing "left_tire" and +// "exhaust" events. +// - VehicleOfferedProducer struct that implements OfferedProducer trait for offering +// "left_tire" and "exhaust" events. +interface!( + interface Vehicle { + left_tire: Event, + exhaust: Event, + } +); diff --git a/score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD b/score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD index 62af6cad3..38fc387e0 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD @@ -10,7 +10,7 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_proc_macro") +load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_library", "rust_proc_macro") rust_proc_macro( name = "com-api-concept-macros", @@ -25,6 +25,18 @@ rust_proc_macro( ], ) +rust_library( + name = "com-api-interface-macros", + srcs = ["interface_macros.rs"], + crate_name = "com_api_concept_interface_macros", + proc_macro_deps = [ + "@crate_index//:paste", + ], + visibility = [ + "//visibility:public", + ], +) + rust_doc_test( name = "com-api-concept-macros-tests", crate = ":com-api-concept-macros", diff --git a/score/mw/com/impl/rust/com-api/com-api-concept-macros/interface_macros.rs b/score/mw/com/impl/rust/com-api/com-api-concept-macros/interface_macros.rs new file mode 100644 index 000000000..fc1343c61 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-concept-macros/interface_macros.rs @@ -0,0 +1,170 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// Main interface macro that generates Consumer, Producer, and OfferedProducer types +/// along with all necessary trait implementations. +/// +/// Automatically generates unique type names from the identifier of macro invocation. +/// For an interface with identifier `{id}`, it generates: +/// - `{id}Interface` - Struct representing the interface with INTERFACE_ID constant +/// - `{id}Consumer` - Consumer implementation with event subscribers +/// - `{id}Producer` - Producer implementation +/// - `{id}OfferedProducer` - Offered producer implementation with event publishers +/// - Implements the `Interface`, `Consumer`, `Producer`, and `OfferedProducer` traits +/// for the respective types. +/// - `id` is used to be UID for the interface. +/// +/// Parameters: +/// - Keywords: `interface` followed by the interface identifier and a block of event definitions. +/// - `$id`: Simple identifier used for type name generation (e.g., Vehicle, Engine) +/// - `$event_name`: Event field name +/// - `$event_type`: Event data type +/// Example usage: +/// ``` +/// interface!( +/// interface Vehicle { +/// left_tire: Event, +/// exhaust: Event, +/// } +/// ); +/// ``` +/// The generated code will include: +/// - `VehicleInterface` struct with `INTERFACE_ID = "Vehicle"` +/// - `VehicleConsumer` struct that implements `Consumer` trait for subscribing to "left_tire" and "exhaust" events. +/// - `VehicleProducer` struct that implements `Producer` trait for producing "left_tire" and "exhaust" events. +/// - `VehicleOfferedProducer` struct that implements `OfferedProducer` trait for offering "left_tire" and "exhaust" events. +pub use paste::paste; + +#[macro_export] +macro_rules! interface { + (interface $id:ident { $($event_name:ident : Event<$event_type:ty>),+$(,)? }) => { + $crate::paste! { + $crate::interface_common!($id); + $crate::interface_consumer!($id, $($event_name, Event<$event_type>),+); + $crate::interface_producer!($id, $($event_name, Event<$event_type>),+); + } + }; + + (interface $id:ident { $($event_name:ident : Method<$event_type:ty>),+$(,)? }) => { + compile_error!("Method definitions are not supported in this macro version. Please use Event syntax for defining events."); + }; + + (interface $id:ident { $($event_name:ident : Field<$event_type:ty>),+$(,)? }) => { + compile_error!("Field definitions are not supported in this macro version. Please use Event syntax for defining events."); + }; +} + +/// Macro create a unique interface struct and implement the Interface trait for it. +/// Generates the INTERFACE_ID constant and associated Consumer/Producer types. +#[macro_export] +macro_rules! interface_common { + ($id:ident) => { + $crate::paste! { + pub struct [<$id Interface>] {} + impl com_api::Interface for [<$id Interface>] { + const INTERFACE_ID: &'static str = stringify!($id); + type Consumer = [<$id Consumer>]; + type Producer = [<$id Producer>]; + } + } + }; +} + +/// Macro to implement the Consumer trait for a given interface ID and its events. +/// Generates the Consumer struct with subscribers for each event. +#[macro_export] +macro_rules! interface_consumer { + ($id:ident, $($event_name:ident, Event<$event_type:ty>),+$(,)?) => { + $crate::paste! { + pub struct [<$id Consumer>] { + $( + pub $event_name: R::Subscriber<$event_type>, + )+ + } + + impl com_api::Consumer for [<$id Consumer>] { + fn new(instance_info: R::ConsumerInfo) -> Self { + [<$id Consumer>] { + $( + $event_name: R::Subscriber::new( + stringify!($event_name), + instance_info.clone() + ).expect(&format!("Failed to create subscriber for {}", stringify!($event_name))), + )+ + } + } + } + } + }; +} + +/// Macro to implement the Producer and OfferedProducer traits for a given interface ID and its events. +/// Generates Producer and OfferedProducer structs with publishers for each event. +#[macro_export] +macro_rules! interface_producer { + ($id:ident, $($event_name:ident, Event<$event_type:ty>),+$(,)?) => { + $crate::paste! { + pub struct [<$id Producer>] { + _runtime: core::marker::PhantomData, + instance_info: R::ProviderInfo, + } + + pub struct [<$id OfferedProducer>] { + $( + pub $event_name: R::Publisher<$event_type>, + )+ + instance_info: R::ProviderInfo, + } + + impl com_api::Producer for [<$id Producer>] { + type Interface = [<$id Interface>]; + type OfferedProducer = [<$id OfferedProducer>]; + fn offer(self) -> com_api::Result { + let offered = [<$id OfferedProducer>] { + $( + $event_name: R::Publisher::new( + stringify!($event_name), + self.instance_info.clone() + ).expect(&format!("Failed to create publisher for {}", stringify!($event_name))), + )+ + instance_info: self.instance_info.clone(), + }; + // Offer the service instance to make it discoverable + self.instance_info.offer_service()?; + Ok(offered) + } + + fn new(instance_info: R::ProviderInfo) -> com_api::Result { + Ok([<$id Producer>] { + _runtime: core::marker::PhantomData, + instance_info, + }) + } + } + + impl com_api::OfferedProducer for [<$id OfferedProducer>] { + type Interface = [<$id Interface>]; + type Producer = [<$id Producer>]; + fn unoffer(self) -> com_api::Result { + let producer = [<$id Producer>] { + _runtime: core::marker::PhantomData, + instance_info: self.instance_info.clone(), + }; + // Stop offering the service instance to withdraw it from system availability + self.instance_info.stop_offer_service()?; + Ok(producer) + } + } + } + }; +} diff --git a/score/mw/com/impl/rust/com-api/com-api/BUILD b/score/mw/com/impl/rust/com-api/com-api/BUILD index 867378f48..f47008e44 100644 --- a/score/mw/com/impl/rust/com-api/com-api/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api/BUILD @@ -22,6 +22,7 @@ rust_library( ], deps = [ "//score/mw/com/impl/rust/com-api/com-api-concept", + "//score/mw/com/impl/rust/com-api/com-api-concept-macros:com-api-interface-macros", "//score/mw/com/impl/rust/com-api/com-api-runtime-lola", "//score/mw/com/impl/rust/com-api/com-api-runtime-mock", ], diff --git a/score/mw/com/impl/rust/com-api/com-api/com_api.rs b/score/mw/com/impl/rust/com-api/com-api/com_api.rs index 39ef55c68..aae1ee03d 100644 --- a/score/mw/com/impl/rust/com-api/com-api/com_api.rs +++ b/score/mw/com/impl/rust/com-api/com-api/com_api.rs @@ -22,3 +22,5 @@ pub use com_api_concept::{ ProviderInfo, Publisher, Reloc, Result, Runtime, RuntimeBuilder, SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, }; + +pub use com_api_concept_interface_macros::interface; \ No newline at end of file From b861424a855e42a5f7cea867665612e683d28ac1 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Mon, 23 Feb 2026 09:53:40 +0530 Subject: [PATCH 2/5] Rust::com Interface macro review comment * Moved interface macro file from macro to concept crate * Auto ID added with Module Path * Created lib crate root for concept module --- .../com-api-gen/com_api_gen.rs | 8 +-- .../rust/com-api/com-api-concept-macros/BUILD | 14 +---- .../impl/rust/com-api/com-api-concept/BUILD | 4 +- .../com-api-concept/com_api_concept.rs | 3 +- .../interface_macros.rs | 61 +++++++++++++++---- .../impl/rust/com-api/com-api-concept/lib.rs | 24 ++++++++ score/mw/com/impl/rust/com-api/com-api/BUILD | 1 - .../com/impl/rust/com-api/com-api/com_api.rs | 3 +- 8 files changed, 84 insertions(+), 34 deletions(-) rename score/mw/com/impl/rust/com-api/{com-api-concept-macros => com-api-concept}/interface_macros.rs (74%) create mode 100644 score/mw/com/impl/rust/com-api/com-api-concept/lib.rs diff --git a/score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs b/score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs index 6b86ef255..c888eb75d 100644 --- a/score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs +++ b/score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs @@ -31,14 +31,14 @@ impl CommData for Exhaust { const ID: &'static str = "Exhaust"; } -// Example interface definition using the interface macro +// Example interface definition using the interface macro with a custom UID for the interface. // This will generate the following types and trait implementations: -// - VehicleInterface struct with INTERFACE_ID = "Vehicle" +// - VehicleInterface struct with INTERFACE_ID = "VehicleInterface" // - VehicleConsumer, VehicleProducer, VehicleOfferedProducer with appropriate trait // implementations for the Vehicle interface. // The macro invocation defines an interface named "Vehicle" with two events: "left_tire" and "exhaust". // The generated code will include: -// - VehicleInterface struct with INTERFACE_ID = "Vehicle" +// - VehicleInterface struct with INTERFACE_ID = "VehicleInterface" // - VehicleConsumer struct that implements Consumer trait for subscribing to "left_tire" // and "exhaust" events. // - VehicleProducer struct that implements Producer trait for producing "left_tire" and @@ -46,7 +46,7 @@ impl CommData for Exhaust { // - VehicleOfferedProducer struct that implements OfferedProducer trait for offering // "left_tire" and "exhaust" events. interface!( - interface Vehicle { + interface Vehicle, "VehicleInterface", { left_tire: Event, exhaust: Event, } diff --git a/score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD b/score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD index 38fc387e0..62af6cad3 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD @@ -10,7 +10,7 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_library", "rust_proc_macro") +load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_proc_macro") rust_proc_macro( name = "com-api-concept-macros", @@ -25,18 +25,6 @@ rust_proc_macro( ], ) -rust_library( - name = "com-api-interface-macros", - srcs = ["interface_macros.rs"], - crate_name = "com_api_concept_interface_macros", - proc_macro_deps = [ - "@crate_index//:paste", - ], - visibility = [ - "//visibility:public", - ], -) - rust_doc_test( name = "com-api-concept-macros-tests", crate = ":com-api-concept-macros", diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD index 501975788..a9825fb90 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD @@ -15,12 +15,14 @@ load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") rust_library( name = "com-api-concept", srcs = [ + "lib.rs", "com_api_concept.rs", "reloc.rs", + "interface_macros.rs", ], - crate_root = "com_api_concept.rs", proc_macro_deps = [ "//score/mw/com/impl/rust/com-api/com-api-concept-macros", + "@crate_index//:paste", ], visibility = [ "//visibility:public", # platform_only diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_concept.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_concept.rs index d391c9071..bc8fe407b 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_concept.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_concept.rs @@ -49,10 +49,9 @@ use core::fmt::Debug; use core::future::Future; use core::ops::{Deref, DerefMut}; -pub mod reloc; use containers::fixed_capacity::FixedCapacityQueue; -pub use reloc::Reloc; use std::path::Path; +use crate::Reloc; /// Error enumeration for different failure cases in the Consumer/Producer/Runtime APIs. #[derive(Debug)] diff --git a/score/mw/com/impl/rust/com-api/com-api-concept-macros/interface_macros.rs b/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs similarity index 74% rename from score/mw/com/impl/rust/com-api/com-api-concept-macros/interface_macros.rs rename to score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs index fc1343c61..21be0a7a6 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept-macros/interface_macros.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs @@ -22,7 +22,8 @@ /// - `{id}OfferedProducer` - Offered producer implementation with event publishers /// - Implements the `Interface`, `Consumer`, `Producer`, and `OfferedProducer` traits /// for the respective types. -/// - `id` is used to be UID for the interface. +/// - `Interface_ID` is generated by default as the module path + interface name, +/// but can be overridden by providing a custom UID as a second parameter to the macro. /// /// Parameters: /// - Keywords: `interface` followed by the interface identifier and a block of event definitions. @@ -30,29 +31,53 @@ /// - `$event_name`: Event field name /// - `$event_type`: Event data type /// Example usage: +/// With default UID generation (module path + interface name): /// ``` +/// mod abc { /// interface!( /// interface Vehicle { /// left_tire: Event, /// exhaust: Event, /// } /// ); +/// } /// ``` /// The generated code will include: -/// - `VehicleInterface` struct with `INTERFACE_ID = "Vehicle"` +/// - `VehicleInterface` struct with `INTERFACE_ID = "abc::Vehicle"` +/// - `VehicleConsumer` struct that implements `Consumer` trait for subscribing to "left_tire" and "exhaust" events. +/// - `VehicleProducer` struct that implements `Producer` trait for producing "left_tire" and "exhaust" events. +/// - `VehicleOfferedProducer` struct that implements `OfferedProducer` trait for offering "left_tire" and "exhaust" events. +/// +/// With custom UID: +/// ``` +/// mod abc { +/// interface!( +/// interface Vehicle, "AbcInterface", { +/// left_tire: Event, +/// exhaust: Event, +/// }); +/// } +/// ``` +/// The generated code will include: +/// - `VehicleInterface` struct with `INTERFACE_ID = "AbcInterface"` /// - `VehicleConsumer` struct that implements `Consumer` trait for subscribing to "left_tire" and "exhaust" events. /// - `VehicleProducer` struct that implements `Producer` trait for producing "left_tire" and "exhaust" events. /// - `VehicleOfferedProducer` struct that implements `OfferedProducer` trait for offering "left_tire" and "exhaust" events. -pub use paste::paste; #[macro_export] macro_rules! interface { - (interface $id:ident { $($event_name:ident : Event<$event_type:ty>),+$(,)? }) => { - $crate::paste! { - $crate::interface_common!($id); - $crate::interface_consumer!($id, $($event_name, Event<$event_type>),+); - $crate::interface_producer!($id, $($event_name, Event<$event_type>),+); - } + // Default unique ID based on the module path and interface name + (interface $id:ident { $($event_name:ident : Event<$event_type:ty>),+ $(,)? }) => { + $crate::interface_common!($id); + $crate::interface_consumer!($id, $($event_name, Event<$event_type>),+); + $crate::interface_producer!($id, $($event_name, Event<$event_type>),+); + }; + + // Custom unique ID provided by the user + (interface $id:ident, $uid:expr, { $($event_name:ident : Event<$event_type:ty>),+ $(,)? }) => { + $crate::interface_common!($id, $uid); + $crate::interface_consumer!($id, $($event_name, Event<$event_type>),+); + $crate::interface_producer!($id, $($event_name, Event<$event_type>),+); }; (interface $id:ident { $($event_name:ident : Method<$event_type:ty>),+$(,)? }) => { @@ -66,20 +91,34 @@ macro_rules! interface { /// Macro create a unique interface struct and implement the Interface trait for it. /// Generates the INTERFACE_ID constant and associated Consumer/Producer types. +/// INTERFACE_ID is generated by default as the module path + interface name, +/// but can be overridden by providing a custom UID as a second parameter to the macro. #[macro_export] macro_rules! interface_common { + // default: auto ID = module path + type name ($id:ident) => { $crate::paste! { pub struct [<$id Interface>] {} impl com_api::Interface for [<$id Interface>] { - const INTERFACE_ID: &'static str = stringify!($id); + const INTERFACE_ID: &'static str = + concat!(module_path!(), "::", stringify!($id)); + type Consumer = [<$id Consumer>]; + type Producer = [<$id Producer>]; + } + } + }; + // explicit ID override + ($id:ident, $uid:expr) => { + $crate::paste! { + pub struct [<$id Interface>] {} + impl com_api::Interface for [<$id Interface>] { + const INTERFACE_ID: &'static str = $uid; type Consumer = [<$id Consumer>]; type Producer = [<$id Producer>]; } } }; } - /// Macro to implement the Consumer trait for a given interface ID and its events. /// Generates the Consumer struct with subscribers for each event. #[macro_export] diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs new file mode 100644 index 000000000..cd5a2bd74 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +///This is main library file for the com-api-concept crate, which defines the core traits, types, and macros for the communication API concept. +/// It re-exports the main components of the crate, including the interface macro and the Reloc type, which is used for safe data relocation in the communication API. +/// The interface macro generates the necessary types and trait implementations for defining communication interfaces, +/// while the Reloc type provides a safe abstraction for moving data across thread or process boundaries without violating Rust's ownership rules. + +pub use paste::paste; +mod com_api_concept; +mod interface_macros; +mod reloc; +pub use com_api_concept::*; +pub use reloc::Reloc; diff --git a/score/mw/com/impl/rust/com-api/com-api/BUILD b/score/mw/com/impl/rust/com-api/com-api/BUILD index f47008e44..867378f48 100644 --- a/score/mw/com/impl/rust/com-api/com-api/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api/BUILD @@ -22,7 +22,6 @@ rust_library( ], deps = [ "//score/mw/com/impl/rust/com-api/com-api-concept", - "//score/mw/com/impl/rust/com-api/com-api-concept-macros:com-api-interface-macros", "//score/mw/com/impl/rust/com-api/com-api-runtime-lola", "//score/mw/com/impl/rust/com-api/com-api-runtime-mock", ], diff --git a/score/mw/com/impl/rust/com-api/com-api/com_api.rs b/score/mw/com/impl/rust/com-api/com-api/com_api.rs index aae1ee03d..53c7f120a 100644 --- a/score/mw/com/impl/rust/com-api/com-api/com_api.rs +++ b/score/mw/com/impl/rust/com-api/com-api/com_api.rs @@ -21,6 +21,5 @@ pub use com_api_concept::{ InstanceSpecifier, Interface, OfferedProducer, PlacementDefault, Producer, ProducerBuilder, ProviderInfo, Publisher, Reloc, Result, Runtime, RuntimeBuilder, SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, + interface, interface_common, interface_consumer, interface_producer, }; - -pub use com_api_concept_interface_macros::interface; \ No newline at end of file From 4273929a39041a1316fc4486aa96bf89107cc929 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Tue, 24 Feb 2026 12:40:01 +0530 Subject: [PATCH 3/5] Rust::com Document test for interface macro * Added document test for interface macro --- .../impl/rust/com-api/com-api-concept/BUILD | 8 +- .../com-api-concept/interface_macros.rs | 456 +++++++++++++++++- .../impl/rust/com-api/com-api-concept/lib.rs | 2 +- 3 files changed, 445 insertions(+), 21 deletions(-) diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD index a9825fb90..77f76cb0e 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD @@ -10,7 +10,7 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test","rust_doc_test") rust_library( name = "com-api-concept", @@ -38,3 +38,9 @@ rust_test( tags = ["manual"], deps = [":com-api-concept"], ) + +rust_doc_test( + name = "com-api-concept-macros-tests", + crate = ":com-api-concept", + deps = ["//score/mw/com/impl/rust/com-api/com-api",], +) diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs b/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs index 21be0a7a6..d719509a6 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs @@ -22,7 +22,7 @@ /// - `{id}OfferedProducer` - Offered producer implementation with event publishers /// - Implements the `Interface`, `Consumer`, `Producer`, and `OfferedProducer` traits /// for the respective types. -/// - `Interface_ID` is generated by default as the module path + interface name, +/// - `Interface_ID` is generated by default as the module path + interface name, /// but can be overridden by providing a custom UID as a second parameter to the macro. /// /// Parameters: @@ -32,14 +32,15 @@ /// - `$event_type`: Event data type /// Example usage: /// With default UID generation (module path + interface name): -/// ``` +/// ```ignore /// mod abc { -/// interface!( -/// interface Vehicle { -/// left_tire: Event, -/// exhaust: Event, -/// } -/// ); +/// use com_api_concept::interface; +/// interface!( +/// interface Vehicle { +/// left_tire: Event, +/// exhaust: Event, +/// } +/// ); /// } /// ``` /// The generated code will include: @@ -47,15 +48,17 @@ /// - `VehicleConsumer` struct that implements `Consumer` trait for subscribing to "left_tire" and "exhaust" events. /// - `VehicleProducer` struct that implements `Producer` trait for producing "left_tire" and "exhaust" events. /// - `VehicleOfferedProducer` struct that implements `OfferedProducer` trait for offering "left_tire" and "exhaust" events. -/// +/// /// With custom UID: -/// ``` +/// ```ignore /// mod abc { -/// interface!( -/// interface Vehicle, "AbcInterface", { -/// left_tire: Event, -/// exhaust: Event, -/// }); +/// use com_api_concept::interface; +/// interface!( +/// interface Vehicle, "AbcInterface", { +/// left_tire: Event, +/// exhaust: Event, +/// } +/// ); /// } /// ``` /// The generated code will include: @@ -89,13 +92,14 @@ macro_rules! interface { }; } -/// Macro create a unique interface struct and implement the Interface trait for it. +/// Macro to create a unique interface struct and implement the Interface trait for it. +/// /// Generates the INTERFACE_ID constant and associated Consumer/Producer types. -/// INTERFACE_ID is generated by default as the module path + interface name, +/// INTERFACE_ID is generated by default as the module path + interface name, /// but can be overridden by providing a custom UID as a second parameter to the macro. #[macro_export] macro_rules! interface_common { - // default: auto ID = module path + type name + // Default: auto ID = module path + type name ($id:ident) => { $crate::paste! { pub struct [<$id Interface>] {} @@ -107,7 +111,7 @@ macro_rules! interface_common { } } }; - // explicit ID override + // Explicit ID override ($id:ident, $uid:expr) => { $crate::paste! { pub struct [<$id Interface>] {} @@ -119,7 +123,9 @@ macro_rules! interface_common { } }; } + /// Macro to implement the Consumer trait for a given interface ID and its events. +/// /// Generates the Consumer struct with subscribers for each event. #[macro_export] macro_rules! interface_consumer { @@ -148,6 +154,7 @@ macro_rules! interface_consumer { } /// Macro to implement the Producer and OfferedProducer traits for a given interface ID and its events. +/// /// Generates Producer and OfferedProducer structs with publishers for each event. #[macro_export] macro_rules! interface_producer { @@ -207,3 +214,414 @@ macro_rules! interface_producer { } }; } + +mod tests { + /// ``` + /// mod my_module { + /// use com_api::{interface,CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// + /// interface!( + /// interface Vehicle { + /// left_tire: Event, + /// exhaust: Event, + /// } + /// ); + /// } + /// ``` + /// This will generate the following types and trait implementations: + /// - `VehicleInterface` struct with `INTERFACE_ID = "my_module::Vehicle"` + /// - `VehicleConsumer`, `VehicleProducer`, `VehicleOfferedProducer` with appropriate trait implementations for the Vehicle interface. + #[cfg(doctest)] + fn interface_macro_with_auto_id() {} + + /// ``` + /// mod my_module { + /// use com_api::{interface, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// + /// interface!( + /// interface Vehicle, "VehicleInterface", { + /// left_tire: Event, + /// exhaust: Event, + /// } + /// ); + /// } + /// ``` + /// This will generate the following types and trait implementations: + /// - `VehicleInterface` struct with `INTERFACE_ID = "VehicleInterface"` + /// - `VehicleConsumer`, `VehicleProducer`, `VehicleOfferedProducer` with appropriate trait implementations for the Vehicle interface. + #[cfg(doctest)] + fn interface_macro_with_custom_id() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// + /// interface!( + /// interface Vehicle, "VehicleInterface", { + /// left_tire: Method, + /// exhaust: Method, + /// } + /// ); + /// } + /// ``` + /// This will fail to compile because the macro does not support Method definitions and will produce a compile-time error indicating that Method definitions are not supported. + #[cfg(doctest)] + fn interface_macro_with_Method() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// + /// interface!( + /// interface Vehicle { + /// left_tire: Field, + /// exhaust: Field, + /// } + /// ); + /// } + /// ``` + /// This will fail to compile because the macro does not support Field definitions and will produce a compile-time error indicating that Field definitions are not supported. + #[cfg(doctest)] + fn interface_macro_with_Field() {} + + /// ``` + /// mod my_module { + /// use com_api::{interface_common, interface_consumer, interface_producer, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_common!(Vehicle); + /// interface_consumer!(Vehicle, left_tire, Event, exhaust, Event); + /// interface_producer!(Vehicle, left_tire, Event, exhaust, Event); + /// } + /// ``` + /// This will generate a `VehicleInterface` struct with an `INTERFACE_ID` constant that is automatically generated as the module path plus the interface name (e.g., "my_module::Vehicle"). + /// It will also define associated `Consumer` and `Producer` types for the `VehicleInterface`. + /// The `VehicleConsumer` struct will implement the `Consumer` trait for the `VehicleInterface`, with subscribers for the `left_tire` and `exhaust` events. + /// The `VehicleProducer` struct will implement the `Producer` trait for the `VehicleInterface`, with publishers for the `left_tire` and `exhaust` events. + #[cfg(doctest)] + fn individual_common_macro_with_auto_id() {} + + /// ``` + /// mod my_module { + /// use com_api::{interface_common, interface_consumer, interface_producer, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_common!(Vehicle, "CustomVehicleInterface"); + /// interface_consumer!(Vehicle, left_tire, Event, exhaust, Event); + /// interface_producer!(Vehicle, left_tire, Event, exhaust, Event); + /// } + /// ``` + /// This will generate a `VehicleInterface` struct with an `INTERFACE_ID` constant set to "CustomVehicleInterface". + /// It will also define associated `Consumer` and `Producer` types for the `VehicleInterface`. + /// The `VehicleConsumer` struct will implement the `Consumer` trait for the `VehicleInterface`, with subscribers for the `left_tire` and `exhaust` events. + /// The `VehicleProducer` struct will implement the `Producer` trait for the `VehicleInterface`, with publishers for the `left_tire` and `exhaust` events. + #[cfg(doctest)] + fn individual_macro_with_custom_id() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface_common, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_common!(Vehicle, "CustomVehicleInterface", { + /// left_tire: Event, + /// exhaust: Event, + /// }); + /// } + /// ``` + /// This will fail to compile because the `interface_common!` macro does not accept event definitions and will produce a compile-time error indicating that the macro does not support event definitions. + #[cfg(doctest)] + fn interface_common_macro_with_events() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface_common, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_common!(Vehicle, "CustomVehicleInterface", { + /// left_tire: Method, + /// exhaust: Method, + /// }); + /// } + /// ``` + /// This will fail to compile because the `interface_common!` macro does not accept method definitions and will produce a compile-time error indicating that the macro does not support method definitions. + #[cfg(doctest)] + fn interface_common_macro_with_methods() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface_common, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_common!(Vehicle, "CustomVehicleInterface", { + /// left_tire: Field, + /// exhaust: Field, + /// }); + /// } + /// ``` + /// This will fail to compile because the `interface_common!` macro does not accept field definitions and will produce a compile-time error indicating that the macro does not support field definitions. + #[cfg(doctest)] + fn interface_common_macro_with_fields() {} + + /// ``` + /// mod my_module { + /// use com_api::{interface_consumer, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_consumer!(Vehicle, left_tire, Event, exhaust, Event); + /// } + /// ``` + /// This will generate a `VehicleConsumer` struct that implements the `Consumer` trait for the `VehicleInterface`, with subscribers for the `left_tire` and `exhaust` events. + /// The generated `VehicleConsumer` struct will have fields for each event subscriber, and the `new` method will initialize these subscribers using the runtime's `Subscriber::new` method. + /// The macro will also include error handling to ensure that subscriber creation failures are properly reported. + #[cfg(doctest)] + fn interface_consumer_macro() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface_consumer, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_consumer!(Vehicle, left_tire, Method, exhaust, Method); + /// } + /// ``` + /// This will fail to compile because the `interface_consumer!` macro does not support Method definitions and will produce a compile-time error indicating that Method definitions are not supported. + #[cfg(doctest)] + fn interface_consumer_macro_with_Method() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface_consumer, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_consumer!(Vehicle, left_tire, Field, exhaust, Field); + /// } + /// ``` + /// This will fail to compile because the `interface_consumer!` macro does not support Field definitions and will produce a compile-time error indicating that Field definitions are not supported. + #[cfg(doctest)] + fn interface_consumer_macro_with_Field() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface_producer, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_producer!(Vehicle, left_tire, Event, exhaust, Event); + /// } + /// ``` + /// This will generate a `VehicleProducer` struct that implements the `Producer` trait for the + /// `VehicleInterface`, with publishers for the `left_tire` and `exhaust` events. + /// So it requires interface_common macro to be called before to generate the VehicleInterface struct + /// and implement the Interface trait for it. + #[cfg(doctest)] + fn interface_producer_macro() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface_producer, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_producer!(Vehicle, left_tire, Method, exhaust, Method); + /// } + /// ``` + /// This will fail to compile because the `interface_producer!` macro does not support Method definitions and will produce a compile-time error indicating that Method definitions are not supported. + #[cfg(doctest)] + fn interface_producer_macro_with_Method() {} + + /// ```compile_fail + /// mod my_module { + /// use com_api::{interface_producer, CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Tire { pub pressure: f32 } + /// impl CommData for Tire { + /// const ID: &'static str = "Tire"; + /// } + /// + /// #[derive(Debug, Reloc)] + /// #[repr(C)] + /// pub struct Exhaust {} + /// impl CommData for Exhaust { + /// const ID: &'static str = "Exhaust"; + /// } + /// interface_producer!(Vehicle, left_tire, Field, exhaust, Field); + /// } + /// ``` + /// This will fail to compile because the `interface_producer!` macro does not support Field definitions and will produce a compile-time error indicating that Field definitions are not supported. + #[cfg(doctest)] + fn interface_producer_macro_with_Field() {} +} diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs index cd5a2bd74..2d3e7825c 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs @@ -16,9 +16,9 @@ /// The interface macro generates the necessary types and trait implementations for defining communication interfaces, /// while the Reloc type provides a safe abstraction for moving data across thread or process boundaries without violating Rust's ownership rules. -pub use paste::paste; mod com_api_concept; mod interface_macros; mod reloc; +pub use paste::paste; pub use com_api_concept::*; pub use reloc::Reloc; From a1083bab150f590c3412ec226a4ded6bb7f8f39f Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Wed, 25 Feb 2026 09:28:31 +0530 Subject: [PATCH 4/5] Rust::com Test case for validating id and type generated from macro * validate the ID and type generated from macro --- .../impl/rust/com-api/com-api-concept/BUILD | 9 + .../com-api-concept/interface_macros.rs | 411 +++++++++++++++++- .../impl/rust/com-api/com-api-concept/lib.rs | 3 +- .../com/impl/rust/com-api/com-api/com_api.rs | 3 + 4 files changed, 421 insertions(+), 5 deletions(-) diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD index 77f76cb0e..5b75e1bbd 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD @@ -44,3 +44,12 @@ rust_doc_test( crate = ":com-api-concept", deps = ["//score/mw/com/impl/rust/com-api/com-api",], ) + +rust_test( + name = "com-api-concept-macros-unit-tests", + srcs = ["interface_macros.rs"], + proc_macro_deps = [ + "@crate_index//:paste", + ], + deps = ["//score/mw/com/impl/rust/com-api/com-api",], +) diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs b/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs index d719509a6..15e7c6729 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs @@ -11,6 +11,10 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +#[doc(hidden)] +#[allow(unused_imports)] +pub use paste; + /// Main interface macro that generates Consumer, Producer, and OfferedProducer types /// along with all necessary trait implementations. /// @@ -101,7 +105,7 @@ macro_rules! interface { macro_rules! interface_common { // Default: auto ID = module path + type name ($id:ident) => { - $crate::paste! { + $crate::paste::paste! { pub struct [<$id Interface>] {} impl com_api::Interface for [<$id Interface>] { const INTERFACE_ID: &'static str = @@ -113,7 +117,7 @@ macro_rules! interface_common { }; // Explicit ID override ($id:ident, $uid:expr) => { - $crate::paste! { + $crate::paste::paste! { pub struct [<$id Interface>] {} impl com_api::Interface for [<$id Interface>] { const INTERFACE_ID: &'static str = $uid; @@ -130,7 +134,7 @@ macro_rules! interface_common { #[macro_export] macro_rules! interface_consumer { ($id:ident, $($event_name:ident, Event<$event_type:ty>),+$(,)?) => { - $crate::paste! { + $crate::paste::paste! { pub struct [<$id Consumer>] { $( pub $event_name: R::Subscriber<$event_type>, @@ -159,7 +163,7 @@ macro_rules! interface_consumer { #[macro_export] macro_rules! interface_producer { ($id:ident, $($event_name:ident, Event<$event_type:ty>),+$(,)?) => { - $crate::paste! { + $crate::paste::paste! { pub struct [<$id Producer>] { _runtime: core::marker::PhantomData, instance_info: R::ProviderInfo, @@ -624,4 +628,403 @@ mod tests { /// This will fail to compile because the `interface_producer!` macro does not support Field definitions and will produce a compile-time error indicating that Field definitions are not supported. #[cfg(doctest)] fn interface_producer_macro_with_Field() {} + +} + +#[cfg(test)] +#[allow(dead_code)] +#[allow(unused_imports)] +mod validation_tests { + use crate::paste; + + #[test] + fn test_interface_type_name_vehicle() { + mod test_module { + use com_api::{CommData, Reloc, ProviderInfo, Subscriber, Publisher}; + + #[derive(Debug, Reloc)] + #[repr(C)] + pub struct Tire { pub pressure: f32 } + impl CommData for Tire { + const ID: &'static str = "Tire"; + } + + crate::interface!( + interface Vehicle { + left_tire: Event, + } + ); + + pub fn validate() { + let actual = std::any::type_name::(); + // Extract only the last part of the type name (the original type) + let type_name_only = actual.split("::").last().unwrap_or(""); + let expected = "VehicleInterface"; + assert_eq!(type_name_only, expected, "Type name mismatch for VehicleInterface"); + + //id validation + let interface_id = ::INTERFACE_ID; + let expected_id = concat!(module_path!(), "::", "Vehicle"); + assert_eq!(interface_id, expected_id, "Interface ID mismatch for VehicleInterface"); + + //wrong id validation + let wrong_id = "SomeInterfaceID"; + assert_ne!(interface_id, wrong_id, "Interface ID should not match the wrong ID"); + + //wrong type name validation + let wrong_type_name = "SomeInterface"; + assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); + } + } + test_module::validate(); + } + + #[test] + fn test_interface_consumer_type_name() { + mod test_module { + use com_api::{CommData, Reloc, Consumer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime}; + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Tire { pub pressure: f32 } + impl CommData for Tire { + const ID: &'static str = "Tire"; + } + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Exhaust { pub temp: f32 } + impl CommData for Exhaust { + const ID: &'static str = "Exhaust"; + } + + crate::interface!( + interface Vehicle { + left_tire: Event, + exhaust: Event, + } + ); + + pub fn validate() { + let actual = std::any::type_name::>(); + // Extract type name before generic parameters + let type_name_only = actual.split('<').next() + .and_then(|s| s.split("::").last()) + .unwrap_or(""); + let expected = "VehicleConsumer"; + assert_eq!(type_name_only, expected, "Type name mismatch for VehicleConsumer"); + + // Verify struct fields exist + assert!(std::mem::size_of::>() > 0, + "VehicleConsumer should have struct fields"); + + //wrong type name validation + let wrong_type_name = "SomeConsumer"; + assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); + } + } + test_module::validate(); + } + + #[test] + fn test_interface_producer_type_name() { + mod test_module { + use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime}; + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Tire { pub pressure: f32 } + impl CommData for Tire { + const ID: &'static str = "Tire"; + } + + crate::interface!( + interface Engine { + rpm: Event, + } + ); + + pub fn validate() { + let actual = std::any::type_name::>(); + // Extract type name before generic parameters + let type_name_only = actual.split('<').next() + .and_then(|s| s.split("::").last()) + .unwrap_or(""); + let expected = "EngineProducer"; + assert_eq!(type_name_only, expected, "Type name mismatch for EngineProducer"); + + //wrong type name validation + let wrong_type_name = "SomeProducer"; + assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); + } + } + test_module::validate(); + } + + #[test] + fn test_interface_offered_producer_type_name() { + mod test_module { + use com_api::{CommData, Reloc, Producer, OfferedProducer ,ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime}; + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Tire { pub pressure: f32 } + impl CommData for Tire { + const ID: &'static str = "Tire"; + } + + crate::interface!( + interface Transmission { + gear: Event, + } + ); + + pub fn validate() { + let actual = std::any::type_name::>(); + // Extract type name before generic parameters + let type_name_only = actual.split('<').next() + .and_then(|s| s.split("::").last()) + .unwrap_or(""); + let expected = "TransmissionOfferedProducer"; + assert_eq!(type_name_only, expected, "Type name mismatch for TransmissionOfferedProducer"); + + //wrong type name validation + let wrong_type_name = "SomeOfferedProducer"; + assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); + + // Verify struct fields exist for publishers + assert!(std::mem::size_of::>() > 0, + "TransmissionOfferedProducer should have publisher fields"); + } + } + test_module::validate(); + } + + #[test] + fn test_interface_with_custom_id_validation() { + mod test_module { + use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Tire { pub pressure: f32 } + impl CommData for Tire { + const ID: &'static str = "Tire"; + } + + crate::interface!( + interface Battery, "com.example.Battery", { + voltage: Event, + } + ); + + pub fn validate() { + let interface_id = ::INTERFACE_ID; + let expected_id = "com.example.Battery"; + assert_eq!(interface_id, expected_id, + "Custom interface ID should match provided UID"); + + let actual_type = std::any::type_name::(); + let type_name_only = actual_type.split("::").last().unwrap_or(""); + assert_eq!(type_name_only, "BatteryInterface", + "Type name should still be BatteryInterface regardless of custom ID"); + + //wrong type name validation + let wrong_type_name = "SomeInterface"; + assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); + } + } + test_module::validate(); + } + + #[test] + fn test_interface_with_multiple_events_validation() { + mod test_module { + use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Event1Data { pub value: i32 } + impl CommData for Event1Data { + const ID: &'static str = "Event1Data"; + } + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Event2Data { pub value: f64 } + impl CommData for Event2Data { + const ID: &'static str = "Event2Data"; + } + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Event3Data { pub value: bool } + impl CommData for Event3Data { + const ID: &'static str = "Event3Data"; + } + + crate::interface!( + interface MultiEvent { + event_one: Event, + event_two: Event, + event_three: Event, + } + ); + + pub fn validate() { + // Validate interface + let interface_id = ::INTERFACE_ID; + assert!(interface_id.contains("MultiEvent"), + "Interface ID should contain MultiEvent"); + + // Validate Consumer with multiple event subscribers + let consumer_type = std::any::type_name::>(); + assert!(consumer_type.contains("MultiEventConsumer"), + "MultiEventConsumer type should be generated"); + + // Validate Producer with multiple event publishers + let producer_type = std::any::type_name::>(); + assert!(producer_type.contains("MultiEventProducer"), + "MultiEventProducer type should be generated"); + + // Validate OfferedProducer + let offered_type = std::any::type_name::>(); + assert!(offered_type.contains("MultiEventOfferedProducer"), + "MultiEventOfferedProducer type should be generated"); + } + } + test_module::validate(); + } + + #[test] + fn test_interface_type_consistency_across_traits() { + mod test_module { + use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Tire { pub pressure: f32 } + impl CommData for Tire { + const ID: &'static str = "Tire"; + } + + crate::interface!( + interface Suspension { + travel: Event, + } + ); + + pub fn validate() { + // Verify that Consumer and Producer reference the same Interface type + let interface_id = ::INTERFACE_ID; + + // Type names should be consistent + let suspension_interface_type = std::any::type_name::(); + let consumer_type = std::any::type_name::>(); + let producer_type = std::any::type_name::>(); + let offered_type = std::any::type_name::>(); + + assert!(suspension_interface_type.contains("SuspensionInterface"), + "Interface type name should contain SuspensionInterface"); + assert!(consumer_type.contains("SuspensionConsumer"), + "Consumer type name should contain SuspensionConsumer"); + assert!(producer_type.contains("SuspensionProducer"), + "Producer type name should contain SuspensionProducer"); + assert!(offered_type.contains("SuspensionOfferedProducer"), + "OfferedProducer type name should contain SuspensionOfferedProducer"); + + // All should be in the same module scope + assert_eq!(interface_id, concat!(module_path!(), "::", "Suspension"), + "Interface ID should be auto-generated from module path and interface name"); + } + } + test_module::validate(); + } + + #[test] + fn test_interface_invalid_id_mismatch() { + mod test_module { + use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Tire { pub pressure: f32 } + impl CommData for Tire { + const ID: &'static str = "Tire"; + } + + crate::interface!( + interface Cooling { + temperature: Event, + } + ); + + pub fn validate() { + let interface_id = ::INTERFACE_ID; + let wrong_id = "WrongCoolingID"; + + assert_ne!(interface_id, wrong_id, + "Interface ID should not match incorrect ID"); + + let wrong_type = "CoolingProducer"; + let actual_type = std::any::type_name::(); + let type_name_only = actual_type.split("::").last().unwrap_or(""); + + assert_ne!(type_name_only, wrong_type, + "CoolingInterface type name should not match CoolingProducer"); + } + } + test_module::validate(); + } + + #[test] + fn test_interface_naming_convention_validation() { + mod test_module { + use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; + + #[derive(Debug, Reloc, Clone)] + #[repr(C)] + pub struct Data { pub value: u32 } + impl CommData for Data { + const ID: &'static str = "Data"; + } + + crate::interface!( + interface ABS { + status: Event, + } + ); + + pub fn validate() { + // Verify naming conventions for all generated types + let interface = std::any::type_name::(); + let consumer = std::any::type_name::>(); + let producer = std::any::type_name::>(); + let offered = std::any::type_name::>(); + + // Extract type names before generic parameters and module path + let interface_name = interface.split("::").last().unwrap_or(""); + let consumer_name = consumer.split('<').next() + .and_then(|s| s.split("::").last()) + .unwrap_or(""); + let producer_name = producer.split('<').next() + .and_then(|s| s.split("::").last()) + .unwrap_or(""); + let offered_name = offered.split('<').next() + .and_then(|s| s.split("::").last()) + .unwrap_or(""); + + // Validate naming pattern: {InterfaceName}{Suffix} + assert_eq!(interface_name, "ABSInterface", + "Interface should follow pattern {{Name}}Interface"); + assert_eq!(consumer_name, "ABSConsumer", + "Consumer should follow pattern {{Name}}Consumer"); + assert_eq!(producer_name, "ABSProducer", + "Producer should follow pattern {{Name}}Producer"); + assert_eq!(offered_name, "ABSOfferedProducer", + "OfferedProducer should follow pattern {{Name}}OfferedProducer"); + } + } + test_module::validate(); + } } diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs index 2d3e7825c..d29d84dae 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs @@ -19,6 +19,7 @@ mod com_api_concept; mod interface_macros; mod reloc; -pub use paste::paste; +#[doc(hidden)] +pub use paste; pub use com_api_concept::*; pub use reloc::Reloc; diff --git a/score/mw/com/impl/rust/com-api/com-api/com_api.rs b/score/mw/com/impl/rust/com-api/com-api/com_api.rs index 53c7f120a..78fa43e9f 100644 --- a/score/mw/com/impl/rust/com-api/com-api/com_api.rs +++ b/score/mw/com/impl/rust/com-api/com-api/com_api.rs @@ -23,3 +23,6 @@ pub use com_api_concept::{ SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, interface, interface_common, interface_consumer, interface_producer, }; + +#[doc(hidden)] +pub use com_api_concept::paste; From 3d6b281ccd237447fa99aed68eb23cb7206871a8 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Fri, 27 Feb 2026 12:51:21 +0530 Subject: [PATCH 5/5] Rust::com Updated validation test cases --- .../impl/rust/com-api/com-api-concept/BUILD | 3 - .../com-api-concept/interface_macros.rs | 284 +++++------------- 2 files changed, 75 insertions(+), 212 deletions(-) diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD index 5b75e1bbd..382b489b9 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD @@ -48,8 +48,5 @@ rust_doc_test( rust_test( name = "com-api-concept-macros-unit-tests", srcs = ["interface_macros.rs"], - proc_macro_deps = [ - "@crate_index//:paste", - ], deps = ["//score/mw/com/impl/rust/com-api/com-api",], ) diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs b/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs index 15e7c6729..0e5a973ca 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/interface_macros.rs @@ -11,10 +11,6 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#[doc(hidden)] -#[allow(unused_imports)] -pub use paste; - /// Main interface macro that generates Consumer, Producer, and OfferedProducer types /// along with all necessary trait implementations. /// @@ -105,7 +101,7 @@ macro_rules! interface { macro_rules! interface_common { // Default: auto ID = module path + type name ($id:ident) => { - $crate::paste::paste! { + com_api::paste::paste! { pub struct [<$id Interface>] {} impl com_api::Interface for [<$id Interface>] { const INTERFACE_ID: &'static str = @@ -117,7 +113,7 @@ macro_rules! interface_common { }; // Explicit ID override ($id:ident, $uid:expr) => { - $crate::paste::paste! { + com_api::paste::paste! { pub struct [<$id Interface>] {} impl com_api::Interface for [<$id Interface>] { const INTERFACE_ID: &'static str = $uid; @@ -134,7 +130,7 @@ macro_rules! interface_common { #[macro_export] macro_rules! interface_consumer { ($id:ident, $($event_name:ident, Event<$event_type:ty>),+$(,)?) => { - $crate::paste::paste! { + com_api::paste::paste! { pub struct [<$id Consumer>] { $( pub $event_name: R::Subscriber<$event_type>, @@ -163,7 +159,7 @@ macro_rules! interface_consumer { #[macro_export] macro_rules! interface_producer { ($id:ident, $($event_name:ident, Event<$event_type:ty>),+$(,)?) => { - $crate::paste::paste! { + com_api::paste::paste! { pub struct [<$id Producer>] { _runtime: core::marker::PhantomData, instance_info: R::ProviderInfo, @@ -635,164 +631,118 @@ mod tests { #[allow(dead_code)] #[allow(unused_imports)] mod validation_tests { - use crate::paste; - #[test] - fn test_interface_type_name_vehicle() { + fn test_interface_id_auto_generated() { mod test_module { use com_api::{CommData, Reloc, ProviderInfo, Subscriber, Publisher}; - + #[derive(Debug, Reloc)] #[repr(C)] pub struct Tire { pub pressure: f32 } impl CommData for Tire { const ID: &'static str = "Tire"; } - + crate::interface!( interface Vehicle { left_tire: Event, } ); - - pub fn validate() { - let actual = std::any::type_name::(); - // Extract only the last part of the type name (the original type) - let type_name_only = actual.split("::").last().unwrap_or(""); - let expected = "VehicleInterface"; - assert_eq!(type_name_only, expected, "Type name mismatch for VehicleInterface"); - //id validation + pub fn validate() { + // Referencing VehicleInterface by name is itself proof the type was generated; + // the compiler enforces the name at compile time. let interface_id = ::INTERFACE_ID; let expected_id = concat!(module_path!(), "::", "Vehicle"); assert_eq!(interface_id, expected_id, "Interface ID mismatch for VehicleInterface"); - - //wrong id validation - let wrong_id = "SomeInterfaceID"; - assert_ne!(interface_id, wrong_id, "Interface ID should not match the wrong ID"); - - //wrong type name validation - let wrong_type_name = "SomeInterface"; - assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); } } test_module::validate(); } #[test] - fn test_interface_consumer_type_name() { + fn test_consumer_type_generated() { mod test_module { use com_api::{CommData, Reloc, Consumer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime}; - + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Tire { pub pressure: f32 } impl CommData for Tire { const ID: &'static str = "Tire"; } - + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Exhaust { pub temp: f32 } impl CommData for Exhaust { const ID: &'static str = "Exhaust"; } - + crate::interface!( interface Vehicle { left_tire: Event, exhaust: Event, } ); - + pub fn validate() { - let actual = std::any::type_name::>(); - // Extract type name before generic parameters - let type_name_only = actual.split('<').next() - .and_then(|s| s.split("::").last()) - .unwrap_or(""); - let expected = "VehicleConsumer"; - assert_eq!(type_name_only, expected, "Type name mismatch for VehicleConsumer"); - - // Verify struct fields exist + // Referencing VehicleConsumer by name is proof the type was generated. + // The size check verifies that subscriber fields were generated. assert!(std::mem::size_of::>() > 0, - "VehicleConsumer should have struct fields"); - - //wrong type name validation - let wrong_type_name = "SomeConsumer"; - assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); + "VehicleConsumer should have subscriber fields"); } } test_module::validate(); } #[test] - fn test_interface_producer_type_name() { + fn test_producer_type_generated() { mod test_module { use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime}; - + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Tire { pub pressure: f32 } impl CommData for Tire { const ID: &'static str = "Tire"; } - + crate::interface!( interface Engine { rpm: Event, } ); - + pub fn validate() { - let actual = std::any::type_name::>(); - // Extract type name before generic parameters - let type_name_only = actual.split('<').next() - .and_then(|s| s.split("::").last()) - .unwrap_or(""); - let expected = "EngineProducer"; - assert_eq!(type_name_only, expected, "Type name mismatch for EngineProducer"); - - //wrong type name validation - let wrong_type_name = "SomeProducer"; - assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); + // Referencing EngineProducer by name is proof the type was generated. + let _ = core::marker::PhantomData::>; } } test_module::validate(); } #[test] - fn test_interface_offered_producer_type_name() { + fn test_offered_producer_type_generated() { mod test_module { - use com_api::{CommData, Reloc, Producer, OfferedProducer ,ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime}; - + use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime}; + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Tire { pub pressure: f32 } impl CommData for Tire { const ID: &'static str = "Tire"; } - + crate::interface!( interface Transmission { gear: Event, } ); - + pub fn validate() { - let actual = std::any::type_name::>(); - // Extract type name before generic parameters - let type_name_only = actual.split('<').next() - .and_then(|s| s.split("::").last()) - .unwrap_or(""); - let expected = "TransmissionOfferedProducer"; - assert_eq!(type_name_only, expected, "Type name mismatch for TransmissionOfferedProducer"); - - //wrong type name validation - let wrong_type_name = "SomeOfferedProducer"; - assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); - - // Verify struct fields exist for publishers + // Referencing TransmissionOfferedProducer by name is proof the type was generated. + // The size check verifies that publisher fields were generated. assert!(std::mem::size_of::>() > 0, "TransmissionOfferedProducer should have publisher fields"); } @@ -803,35 +753,27 @@ mod validation_tests { #[test] fn test_interface_with_custom_id_validation() { mod test_module { - use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; - + use com_api::{CommData, Reloc, ProviderInfo, Subscriber, Publisher, Interface}; + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Tire { pub pressure: f32 } impl CommData for Tire { const ID: &'static str = "Tire"; } - + crate::interface!( interface Battery, "com.example.Battery", { voltage: Event, } ); - + pub fn validate() { + // Referencing BatteryInterface by name is proof the type was generated with the + // correct naming convention; the custom ID is a meaningful runtime assertion. let interface_id = ::INTERFACE_ID; - let expected_id = "com.example.Battery"; - assert_eq!(interface_id, expected_id, + assert_eq!(interface_id, "com.example.Battery", "Custom interface ID should match provided UID"); - - let actual_type = std::any::type_name::(); - let type_name_only = actual_type.split("::").last().unwrap_or(""); - assert_eq!(type_name_only, "BatteryInterface", - "Type name should still be BatteryInterface regardless of custom ID"); - - //wrong type name validation - let wrong_type_name = "SomeInterface"; - assert_ne!(type_name_only, wrong_type_name, "Type name should not match the wrong type name"); } } test_module::validate(); @@ -840,29 +782,29 @@ mod validation_tests { #[test] fn test_interface_with_multiple_events_validation() { mod test_module { - use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; - + use com_api::{CommData, Reloc, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Event1Data { pub value: i32 } impl CommData for Event1Data { const ID: &'static str = "Event1Data"; } - + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Event2Data { pub value: f64 } impl CommData for Event2Data { const ID: &'static str = "Event2Data"; } - + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Event3Data { pub value: bool } impl CommData for Event3Data { const ID: &'static str = "Event3Data"; } - + crate::interface!( interface MultiEvent { event_one: Event, @@ -870,27 +812,18 @@ mod validation_tests { event_three: Event, } ); - + pub fn validate() { - // Validate interface + // ID assertion is meaningful at runtime. let interface_id = ::INTERFACE_ID; - assert!(interface_id.contains("MultiEvent"), - "Interface ID should contain MultiEvent"); - - // Validate Consumer with multiple event subscribers - let consumer_type = std::any::type_name::>(); - assert!(consumer_type.contains("MultiEventConsumer"), - "MultiEventConsumer type should be generated"); - - // Validate Producer with multiple event publishers - let producer_type = std::any::type_name::>(); - assert!(producer_type.contains("MultiEventProducer"), - "MultiEventProducer type should be generated"); - - // Validate OfferedProducer - let offered_type = std::any::type_name::>(); - assert!(offered_type.contains("MultiEventOfferedProducer"), - "MultiEventOfferedProducer type should be generated"); + assert_eq!(interface_id, concat!(module_path!(), "::", "MultiEvent"), + "Interface ID should be auto-generated from module path and interface name"); + + // Referencing Consumer, Producer, and OfferedProducer by name proves all four + // types were generated; the compiler enforces the names at compile time. + let _ = core::marker::PhantomData::>; + let _ = core::marker::PhantomData::>; + let _ = core::marker::PhantomData::>; } } test_module::validate(); @@ -899,41 +832,30 @@ mod validation_tests { #[test] fn test_interface_type_consistency_across_traits() { mod test_module { - use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; - + use com_api::{CommData, Reloc, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Tire { pub pressure: f32 } impl CommData for Tire { const ID: &'static str = "Tire"; } - + crate::interface!( interface Suspension { travel: Event, } ); - + pub fn validate() { - // Verify that Consumer and Producer reference the same Interface type + // Referencing all four generated types by their expected names is proof of + // consistent naming; the compiler enforces the names at compile time. + let _ = core::marker::PhantomData::; + let _ = core::marker::PhantomData::>; + let _ = core::marker::PhantomData::>; + let _ = core::marker::PhantomData::>; + let interface_id = ::INTERFACE_ID; - - // Type names should be consistent - let suspension_interface_type = std::any::type_name::(); - let consumer_type = std::any::type_name::>(); - let producer_type = std::any::type_name::>(); - let offered_type = std::any::type_name::>(); - - assert!(suspension_interface_type.contains("SuspensionInterface"), - "Interface type name should contain SuspensionInterface"); - assert!(consumer_type.contains("SuspensionConsumer"), - "Consumer type name should contain SuspensionConsumer"); - assert!(producer_type.contains("SuspensionProducer"), - "Producer type name should contain SuspensionProducer"); - assert!(offered_type.contains("SuspensionOfferedProducer"), - "OfferedProducer type name should contain SuspensionOfferedProducer"); - - // All should be in the same module scope assert_eq!(interface_id, concat!(module_path!(), "::", "Suspension"), "Interface ID should be auto-generated from module path and interface name"); } @@ -941,88 +863,32 @@ mod validation_tests { test_module::validate(); } - #[test] - fn test_interface_invalid_id_mismatch() { - mod test_module { - use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; - - #[derive(Debug, Reloc, Clone)] - #[repr(C)] - pub struct Tire { pub pressure: f32 } - impl CommData for Tire { - const ID: &'static str = "Tire"; - } - - crate::interface!( - interface Cooling { - temperature: Event, - } - ); - - pub fn validate() { - let interface_id = ::INTERFACE_ID; - let wrong_id = "WrongCoolingID"; - - assert_ne!(interface_id, wrong_id, - "Interface ID should not match incorrect ID"); - - let wrong_type = "CoolingProducer"; - let actual_type = std::any::type_name::(); - let type_name_only = actual_type.split("::").last().unwrap_or(""); - - assert_ne!(type_name_only, wrong_type, - "CoolingInterface type name should not match CoolingProducer"); - } - } - test_module::validate(); - } - #[test] fn test_interface_naming_convention_validation() { mod test_module { - use com_api::{CommData, Reloc, Producer, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime, Interface}; - + use com_api::{CommData, Reloc, ProviderInfo, Subscriber, Publisher, LolaRuntimeImpl as LolaRuntime}; + #[derive(Debug, Reloc, Clone)] #[repr(C)] pub struct Data { pub value: u32 } impl CommData for Data { const ID: &'static str = "Data"; } - + crate::interface!( interface ABS { status: Event, } ); - + pub fn validate() { - // Verify naming conventions for all generated types - let interface = std::any::type_name::(); - let consumer = std::any::type_name::>(); - let producer = std::any::type_name::>(); - let offered = std::any::type_name::>(); - - // Extract type names before generic parameters and module path - let interface_name = interface.split("::").last().unwrap_or(""); - let consumer_name = consumer.split('<').next() - .and_then(|s| s.split("::").last()) - .unwrap_or(""); - let producer_name = producer.split('<').next() - .and_then(|s| s.split("::").last()) - .unwrap_or(""); - let offered_name = offered.split('<').next() - .and_then(|s| s.split("::").last()) - .unwrap_or(""); - - // Validate naming pattern: {InterfaceName}{Suffix} - assert_eq!(interface_name, "ABSInterface", - "Interface should follow pattern {{Name}}Interface"); - assert_eq!(consumer_name, "ABSConsumer", - "Consumer should follow pattern {{Name}}Consumer"); - assert_eq!(producer_name, "ABSProducer", - "Producer should follow pattern {{Name}}Producer"); - assert_eq!(offered_name, "ABSOfferedProducer", - "OfferedProducer should follow pattern {{Name}}OfferedProducer"); + // Referencing each generated type by its expected name is itself the proof of + // correct naming conventions; the compiler enforces the names at compile time. + // Pattern: {Name}Interface, {Name}Consumer, {Name}Producer, {Name}OfferedProducer + let _ = core::marker::PhantomData::; + let _ = core::marker::PhantomData::>; + let _ = core::marker::PhantomData::>; + let _ = core::marker::PhantomData::>; } } test_module::validate();