From 9eddfc1f96928b3e8d99bad44be2bae1bec36826 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Mon, 9 Feb 2026 15:11:53 +0530 Subject: [PATCH] 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 33a06e56..2c53fe85 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 586a8976..6b86ef25 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 62af6cad..38fc387e 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 00000000..fc1343c6 --- /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 867378f4..f47008e4 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 39ef55c6..aae1ee03 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