Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<R> = <VehicleInterface as Interface>::Consumer<R>;
// VehicleOfferedProducer is the offered producer type generated for the Vehicle interface, parameterized by the runtime R
type VehicleOfferedProducer<R> =
<<VehicleInterface as Interface>::Producer<R> as Producer<R>>::OfferedProducer;

// Example struct demonstrating composition with VehicleConsumer
pub struct VehicleMonitor<R: Runtime> {
Expand Down
103 changes: 21 additions & 82 deletions score/mw/com/example/com-api-example/com-api-gen/com_api_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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<R: Runtime + ?Sized> = VehicleConsumer<R>;
type Producer<R: Runtime + ?Sized> = VehicleProducer<R>;
}

pub struct VehicleConsumer<R: Runtime + ?Sized> {
pub left_tire: R::Subscriber<Tire>,
pub exhaust: R::Subscriber<Exhaust>,
}

impl<R: Runtime + ?Sized> Consumer<R> for VehicleConsumer<R> {
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<R: Runtime + ?Sized> {
_runtime: core::marker::PhantomData<R>,
instance_info: R::ProviderInfo,
}

impl<R: Runtime + ?Sized> Producer<R> for VehicleProducer<R> {
type Interface = VehicleInterface;
type OfferedProducer = VehicleOfferedProducer<R>;

fn offer(self) -> com_api::Result<Self::OfferedProducer> {
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<Self> {
Ok(VehicleProducer {
_runtime: core::marker::PhantomData,
instance_info,
})
}
}

pub struct VehicleOfferedProducer<R: Runtime + ?Sized> {
pub left_tire: R::Publisher<Tire>,
pub exhaust: R::Publisher<Exhaust>,
instance_info: R::ProviderInfo,
}

impl<R: Runtime + ?Sized> OfferedProducer<R> for VehicleOfferedProducer<R> {
type Interface = VehicleInterface;
type Producer = VehicleProducer<R>;

fn unoffer(self) -> com_api::Result<Self::Producer> {
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<R>, VehicleProducer<R>, VehicleOfferedProducer<R> 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<R> struct that implements Consumer trait for subscribing to "left_tire"
// and "exhaust" events.
// - VehicleProducer<R> struct that implements Producer trait for producing "left_tire" and
// "exhaust" events.
// - VehicleOfferedProducer<R> struct that implements OfferedProducer trait for offering
// "left_tire" and "exhaust" events.
interface!(
interface Vehicle {
left_tire: Event<Tire>,
exhaust: Event<Exhaust>,
}
);
14 changes: 13 additions & 1 deletion score/mw/com/impl/rust/com-api/com-api-concept-macros/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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<R>` - Consumer implementation with event subscribers
/// - `{id}Producer<R>` - Producer implementation
/// - `{id}OfferedProducer<R>` - 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<Tire>,
/// exhaust: Event<Exhaust>,
/// }
/// );
/// ```
/// The generated code will include:
/// - `VehicleInterface` struct with `INTERFACE_ID = "Vehicle"`
/// - `VehicleConsumer<R>` struct that implements `Consumer` trait for subscribing to "left_tire" and "exhaust" events.
/// - `VehicleProducer<R>` struct that implements `Producer` trait for producing "left_tire" and "exhaust" events.
/// - `VehicleOfferedProducer<R>` 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<T> 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<T> 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<R: com_api::Runtime + ?Sized> = [<$id Consumer>]<R>;
type Producer<R: com_api::Runtime + ?Sized> = [<$id Producer>]<R>;
}
}
};
}

/// 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>]<R: com_api::Runtime + ?Sized> {
$(
pub $event_name: R::Subscriber<$event_type>,
)+
}

impl<R: com_api::Runtime + ?Sized> com_api::Consumer<R> for [<$id Consumer>]<R> {
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>]<R: com_api::Runtime + ?Sized> {
_runtime: core::marker::PhantomData<R>,
instance_info: R::ProviderInfo,
}

pub struct [<$id OfferedProducer>]<R: com_api::Runtime + ?Sized> {
$(
pub $event_name: R::Publisher<$event_type>,
)+
instance_info: R::ProviderInfo,
}

impl<R: com_api::Runtime + ?Sized> com_api::Producer<R> for [<$id Producer>]<R> {
type Interface = [<$id Interface>];
type OfferedProducer = [<$id OfferedProducer>]<R>;
fn offer(self) -> com_api::Result<Self::OfferedProducer> {
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<Self> {
Ok([<$id Producer>] {
_runtime: core::marker::PhantomData,
instance_info,
})
}
}

impl<R: com_api::Runtime + ?Sized> com_api::OfferedProducer<R> for [<$id OfferedProducer>]<R> {
type Interface = [<$id Interface>];
type Producer = [<$id Producer>]<R>;
fn unoffer(self) -> com_api::Result<Self::Producer> {
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)
}
}
}
};
}
1 change: 1 addition & 0 deletions score/mw/com/impl/rust/com-api/com-api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
Expand Down
2 changes: 2 additions & 0 deletions score/mw/com/impl/rust/com-api/com-api/com_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Loading