From 1117a9f7c6a1dce88be01976bfda12c56660da9a Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Thu, 2 Jul 2026 14:03:15 +0530 Subject: [PATCH 01/13] Rust::com Field Initial Design Plan * Created seperate crate for field APIs * SampleMut updated based making common for Event and Field --- .../basic-consumer-producer.rs | 6 +- .../impl/rust/com-api/com-api-concept/BUILD | 1 + .../com-api-concept/com_api_concept.rs | 31 ++++--- .../com-api/com-api-concept/com_api_field.rs | 93 +++++++++++++++++++ .../impl/rust/com-api/com-api-concept/lib.rs | 2 + .../com-api/com-api-runtime-lola/producer.rs | 11 ++- .../com-api/com-api-runtime-mock/runtime.rs | 12 ++- .../com/impl/rust/com-api/com-api/com_api.rs | 14 +-- 8 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.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 90079e61f..763a40c55 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 @@ -22,9 +22,9 @@ use clap::Parser; use std::path::PathBuf; use com_api::{ - Builder, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl, - OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, SampleContainer, - SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, + Builder, EventSampleMut, FindServiceSpecifier, InstanceSpecifier, Interface, + LolaRuntimeBuilderImpl, OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, + SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, }; use com_api_gen::{Exhaust, Tire, VehicleInterface}; 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 aefc913db..7e9254010 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 @@ -22,6 +22,7 @@ rust_library( "interface_macros.rs", "lib.rs", "reloc.rs", + "com_api_field.rs", ], edition = "2024", proc_macro_deps = [ 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 79697a168..cac11e14f 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 @@ -321,6 +321,12 @@ where { } +pub trait SampleMut: DerefMut + Debug +where + T: CommData + Debug, +{ +} + /// A `SampleMut` provides a reference to a memory buffer of an event with mutable value. /// /// By implementing the `DerefMut` trait implementations of the trait support the `.` operator for @@ -329,18 +335,10 @@ where /// /// # Type Parameters /// * `T` - The relocatable event data type -pub trait SampleMut: DerefMut + Debug +pub trait EventSampleMut: SampleMut where T: CommData + Debug, { - /// Send the sample and consume it. - /// - /// # Returns - /// - /// A `Result` indicating success or failure of the send operation. - /// - /// # Errors - /// Returns 'Error' if the send operation fails. fn send(self) -> Result<()>; } @@ -477,8 +475,17 @@ pub trait Publisher where T: CommData + Debug, { - /// Associated sample type for uninitialized event data - type SampleMaybeUninit<'a>: SampleMaybeUninit + 'a + /// The fully-initialized sample type produced by `SampleMaybeUninit::write` / `assume_init`. + /// Must implement `EventSampleMut` so the default `send()` impl can call `.send()` on it. + type CommittedSample<'a>: EventSampleMut + where + Self: 'a; + + /// Associated sample type for uninitialized event data. + /// Its `SampleMut` is pinned to `Self::CommittedSample` via associated-type equality, + /// which lets the compiler verify the `EventSampleMut` bound without a self-referential + /// where clause on the GAT itself. + type SampleMaybeUninit<'a>: SampleMaybeUninit> + 'a where Self: 'a; /// Allocate a buffer slot for the event publication. @@ -947,7 +954,7 @@ pub trait Subscription { /// If the stream encounters an error, it will yield `Err(Error)` for that sample, but will continue to yield subsequent samples as they become available. /// The stream only terminates when the subscription is unsubscribed or dropped or if the stream is explicitly dropped by the user. /// With this design, users can handle errors on it side and take appropriate actions. - fn to_stream<'a>(&'a mut self) -> impl Stream>> + Unpin + 'a; + fn to_stream<'a>(&'a mut self) -> impl Stream>> + Unpin + 'a; } /// A trait for types that can be default-constructed in place, skipping intermediate moves. diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs new file mode 100644 index 000000000..507a6316e --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs @@ -0,0 +1,93 @@ +//field.rs +// Need to discuss: +// 1.Get and Set methods for field it is enabled based on tag, do we want to keep same kind of mechanism +// or by default we will enable for user, My suggestion is we can keep it default enable as of now, +// and later we can add tag based mechanism if required because Interface side we need to check how we can do this +use crate::*; +use std::fmt::Debug; + +#[allow(dead_code)] + +//Temp for build test +struct MethodReturnTypePtr { + pub value: T, + pub status: Result<()>, +} + +// we are using the event trait as a base trait for field, because we do offer same subscription APIs +// Only addation to that is get and set methods for field, so we can keep it as a base trait for field as well. +// FieldMethods trait is added to provide get and set methods for field. +pub trait FieldSubscriber: + FieldMethods + com_api_concept::Subscriber +{ +} + +// Again we are using the event trait as a base trait for field, because we do offer same subscription APIs +// Like try_receive(GetNewSamples) and receive with async, +// Also we do need to provide get and set method because when subscription is created it takes the subscriber as a value. +// It has additional methods to get the number of new samples available and get the number of free sample count available for subscription. +pub trait FieldSubscription: + com_api_concept::Subscription + FieldMethods +{ + /// Get the number of new samples available in the field subscription. + /// This does not take max_samples parameter like try_receive, but returns the number of samples available to be received. + fn get_num_new_samples_available(&self) -> Result; + + /// Get the number of sample count that can be received from the field subscription. + fn get_free_sample_count(&self) -> Result; +} + +/// FieldMethods trait provides get and set methods for field. +// MethodReturnTypePtr is like samplePtr for event. +pub trait FieldMethods { + ///Get the current value of the field. + /// + /// #returns + /// Return the result of `MethodReturnTypePtr` which contains the current value of the field. + fn get(&self) -> Result>; + + ///Set the value of the field. + /// + /// # Parameters + /// * `value` - The value to set for the field. + /// + /// # Returns + /// Return the result of `MethodReturnTypePtr` which contains the status of the set operation. + /// with the current value of the field. + fn set(&self, value: T) -> Result>; +} + +// We can not use publisher trait from event because that contains the Send Method which is not correct semantic for field. +// TODO: SampleMaybeUninit is return by allocated and that trait has write method but which return SampleMut +// which has send method, we can add Update method there but in that case that will be exposed to Event and still Send is exposed to Event. +// Tried to fix this issue making SampleMut trait as marker trait in concept and then implementing Event/Field specific SampleMut trait which has send/update method. +pub trait FieldPublisher { + type CommittedSample<'a>: FieldSampleMut + where + Self: 'a; + + type SampleMaybeUninit<'a>: SampleMaybeUninit> + 'a + where + Self: 'a; + + /// Create a new publisher for the specified event source. + fn new(identifier: &str, instance_info: R::ProviderInfo) -> Result + where + Self: Sized; + + /// Allocate a buffer slot for the event publication. + fn allocate(&self) -> Result>; + + /// Publish the event data to the field source. + fn update(&self, value: T) -> Result<()>; + + /// Set a handler for the field publication from proxy set request. + fn set_handler<'a>(&self) -> impl Future> + 'a; +} + +pub trait FieldSampleMut: com_api_concept::SampleMut +where + T: CommData + Debug, +{ + fn update(self) -> Result<()>; +} 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 a944aa17f..04927dd38 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 @@ -28,3 +28,5 @@ pub use error::*; #[doc(hidden)] pub use paste; pub use reloc::Reloc; +mod com_api_field; +pub use com_api_field::*; diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/producer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/producer.rs index f8e11756e..906076432 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/producer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/producer.rs @@ -187,7 +187,12 @@ where } } -impl<'a, T, B: FFIBridge> com_api_concept::SampleMut for SampleMut<'a, T, B> +impl<'a, T, B: FFIBridge> com_api_concept::SampleMut for SampleMut<'a, T, B> where + T: CommData + Debug +{ +} + +impl<'a, T, B: FFIBridge> com_api_concept::EventSampleMut for SampleMut<'a, T, B> where T: CommData + Debug, { @@ -405,6 +410,10 @@ impl com_api_concept::Publisher> for Publ where T: CommData + Debug, { + type CommittedSample<'a> + = SampleMut<'a, T, B> + where + Self: 'a; type SampleMaybeUninit<'a> = SampleMaybeUninit<'a, T, B> where diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs index d026b68a0..8d7837f2c 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs @@ -190,7 +190,11 @@ where lifetime: PhantomData<&'a T>, } -impl<'a, T> com_api_concept::SampleMut for SampleMut<'a, T> +/// Base trait — no commit operation, just DerefMut access. +impl<'a, T> com_api_concept::SampleMut for SampleMut<'a, T> where T: CommData + Debug {} + +/// Event-specific extension: `send()` consumes and publishes the sample. +impl<'a, T> com_api_concept::EventSampleMut for SampleMut<'a, T> where T: CommData + Debug, { @@ -373,6 +377,12 @@ impl com_api_concept::Publisher for Publisher where T: CommData + Debug, { + // SampleMut<'a, T> implements EventSampleMut, satisfying the CommittedSample bound. + type CommittedSample<'a> + = SampleMut<'a, T> + where + Self: 'a; + type SampleMaybeUninit<'a> = SampleMaybeUninit<'a, T> where 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 66ec17bed..545e1bfcf 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 @@ -115,17 +115,17 @@ //! //! // Or wait asynchronously for events //! let (container, result) = subscription.receive(container, 1, 3).await; -//! +//! //! // Or use the cancelable future to wait for events with timeout //! let timeout_future = tokio::time::sleep(std::time::Duration::from_secs(1)); //! let (container, result) = subscription.cancellable_receive(container, 1, 3, timeout_future).await; -//! +//! //! // Or use the stream interface to process events as they arrive //! let mut stream = subscription.to_stream(); //! while let Some(sample) = stream.next().await { //! println!("Tire pressure: {}", sample.pressure); //! } -//! +//! //! ``` //! # Further reading //! - `com_api_concept` crate — trait definitions and full API documentation @@ -138,10 +138,10 @@ pub use com_api_runtime_mock::RuntimeBuilderImpl as MockRuntimeBuilderImpl; pub use com_api_concept::{ interface, interface_common, interface_consumer, interface_producer, Builder, CommData, - Consumer, ConsumerBuilder, ConsumerDescriptor, Error, FindServiceSpecifier, InstanceSpecifier, - Interface, OfferedProducer, PlacementDefault, Producer, ProducerBuilder, ProviderInfo, - Publisher, Reloc, Result, Runtime, RuntimeBuilder, SampleContainer, SampleMaybeUninit, - SampleMut, ServiceDiscovery, Subscriber, Subscription, + Consumer, ConsumerBuilder, ConsumerDescriptor, Error, EventSampleMut, FindServiceSpecifier, + InstanceSpecifier, Interface, OfferedProducer, PlacementDefault, Producer, ProducerBuilder, + ProviderInfo, Publisher, Reloc, Result, Runtime, RuntimeBuilder, SampleContainer, + SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, }; #[doc(hidden)] From c9b83888dd9f2cc0b98f7216043d2369a661d6bc Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Thu, 2 Jul 2026 14:40:57 +0530 Subject: [PATCH 02/13] Rust::com Field APIs design in rust com library * Updated SampleMut trait * Created impl for field trait * Updated API documentation --- .../basic-consumer-producer.rs | 6 +- .../com-api-gen/com_api_gen.rs | 10 +- .../impl/rust/com-api/com-api-concept/BUILD | 9 +- .../com-api-concept/com_api_concept.rs | 4 + .../com-api/com-api-concept/com_api_field.rs | 67 ++++++---- .../com-api-concept/interface_macros.rs | 103 +++++++++++++++- .../rust/com-api/com-api-runtime-lola/BUILD | 7 +- .../com-api-runtime-lola/field_consumer.rs | 116 ++++++++++++++++++ .../com-api-runtime-lola/field_producer.rs | 104 ++++++++++++++++ .../rust/com-api/com-api-runtime-lola/lib.rs | 11 +- .../com-api/com-api-runtime-lola/runtime.rs | 6 +- score/mw/com/impl/rust/com-api/com-api/BUILD | 2 +- .../com/impl/rust/com-api/com-api/com_api.rs | 13 +- 13 files changed, 402 insertions(+), 56 deletions(-) create mode 100644 score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs create mode 100644 score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.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 763a40c55..90079e61f 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 @@ -22,9 +22,9 @@ use clap::Parser; use std::path::PathBuf; use com_api::{ - Builder, EventSampleMut, FindServiceSpecifier, InstanceSpecifier, Interface, - LolaRuntimeBuilderImpl, OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, - SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, + Builder, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl, + OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, SampleContainer, + SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, }; use com_api_gen::{Exhaust, Tire, VehicleInterface}; 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 506df07aa..3b3b5a426 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,7 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -use com_api::{interface, CommData, ProviderInfo, Publisher, Reloc, Subscriber}; +use com_api::{interface, CommData, FieldPublisher, ProviderInfo, Publisher, Reloc, Subscriber}; #[derive(Debug, Reloc, CommData)] #[repr(C)] @@ -46,3 +46,11 @@ interface!( exhaust: Event, } ); + +interface!( + interface VehicleField, { + Id = "VehicleFieldInterface", + left_tire: Field, + exhaust: Field, + } +); 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 7e9254010..87faddebf 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 @@ -16,14 +16,7 @@ load("//quality/unit_testing:unit_testing.bzl", "rust_unit_test") rust_library( name = "com-api-concept", - srcs = [ - "com_api_concept.rs", - "error.rs", - "interface_macros.rs", - "lib.rs", - "reloc.rs", - "com_api_field.rs", - ], + srcs = glob(["**/*.rs"]), edition = "2024", proc_macro_deps = [ "//score/mw/com/impl/rust/com-api/com-api-concept-macros", 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 cac11e14f..dcd48c18b 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 @@ -48,6 +48,7 @@ //! - Structures //! - Tuples +use crate::com_api_field::*; use crate::error::*; use crate::Reloc; pub use com_api_concept_macros::CommData; @@ -106,6 +107,9 @@ pub trait Runtime { /// `ConsumerInfo` types for Configuration data for service consumers instances type ConsumerInfo: Send + Clone; + type FieldSubscriber: FieldSubscriber; + type FieldPublisher: FieldPublisher; + /// Find a service instance for the given interface and instance specifier. /// Locate available instances of a service interface. /// diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs index 507a6316e..e90794e24 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs @@ -1,31 +1,52 @@ -//field.rs -// Need to discuss: +/******************************************************************************** + * Copyright (c) 2026 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 + ********************************************************************************/ + +// TODO: Need to discuss: // 1.Get and Set methods for field it is enabled based on tag, do we want to keep same kind of mechanism -// or by default we will enable for user, My suggestion is we can keep it default enable as of now, +// or by default we will enable for user, +// -> My suggestion is we can keep it default enable as of now, // and later we can add tag based mechanism if required because Interface side we need to check how we can do this + +// Note: We are using the event related trait as a base trait for whereever we have same common +// APIs or functionality, as of now there are derived from concept crate but +// we will create a module which will have common trait for event and field which will be used by both event and field as a super trait and +// for this we need to create marker trait for event. + use crate::*; use std::fmt::Debug; #[allow(dead_code)] -//Temp for build test -struct MethodReturnTypePtr { +// Temp for build test +// We will remove this once memory layout of same created in rust side like SamplePtr. +pub struct MethodReturnTypePtr { pub value: T, pub status: Result<()>, } -// we are using the event trait as a base trait for field, because we do offer same subscription APIs -// Only addation to that is get and set methods for field, so we can keep it as a base trait for field as well. -// FieldMethods trait is added to provide get and set methods for field. +/// FieldSubscriber trait is used to subscribe to a field and get the value of the field. +/// It provides the `get` and `set` methods to get and set the value of the field. +/// It derived from `com_api_concept::Subscriber` trait which provides the `subscribe` method to create a field subscription. +/// `FieldMethods` trait is used to provide the `get` and `set` methods for the field instance which can be used before subscription. pub trait FieldSubscriber: FieldMethods + com_api_concept::Subscriber { } -// Again we are using the event trait as a base trait for field, because we do offer same subscription APIs -// Like try_receive(GetNewSamples) and receive with async, -// Also we do need to provide get and set method because when subscription is created it takes the subscriber as a value. -// It has additional methods to get the number of new samples available and get the number of free sample count available for subscription. +/// FieldSubscriber trait is provides the receiving APIs for the field subscription and +/// it is derived from `com_api_concept::Subscription` trait which provides the receiving APIs for the field subscription. +/// Additional methods which the field subscription provides are added in this trait. +/// `FieldMethods` trait is used to provide the `get` and `set` methods for the field subscription. pub trait FieldSubscription: com_api_concept::Subscription + FieldMethods { @@ -37,8 +58,8 @@ pub trait FieldSubscription: fn get_free_sample_count(&self) -> Result; } -/// FieldMethods trait provides get and set methods for field. -// MethodReturnTypePtr is like samplePtr for event. +/// FieldMethods trait provides the `get` and `set` methods for the field instance which can be used before subscription. +/// The `get` method is used to get the current value of the field, and the `set` method is used to set the value of the field. pub trait FieldMethods { ///Get the current value of the field. /// @@ -54,13 +75,12 @@ pub trait FieldMethods { /// # Returns /// Return the result of `MethodReturnTypePtr` which contains the status of the set operation. /// with the current value of the field. + // TODO: should we pass value by reference as underlying API is taking value as reference. fn set(&self, value: T) -> Result>; } -// We can not use publisher trait from event because that contains the Send Method which is not correct semantic for field. -// TODO: SampleMaybeUninit is return by allocated and that trait has write method but which return SampleMut -// which has send method, we can add Update method there but in that case that will be exposed to Event and still Send is exposed to Event. -// Tried to fix this issue making SampleMut trait as marker trait in concept and then implementing Event/Field specific SampleMut trait which has send/update method. +/// FieldPublisher trait is used to publish a field and update the value of the field. +// Note: We can not use publisher trait from event because that contains the Send Method which is not correct semantic for field. pub trait FieldPublisher { type CommittedSample<'a>: FieldSampleMut where @@ -75,16 +95,19 @@ pub trait FieldPublisher { where Self: Sized; - /// Allocate a buffer slot for the event publication. + /// Get the allocate sample ptr for the field publisher. fn allocate(&self) -> Result>; - /// Publish the event data to the field source. + /// Update the value of the field with the provided value. + /// This is not zero-copy API. fn update(&self, value: T) -> Result<()>; - /// Set a handler for the field publication from proxy set request. - fn set_handler<'a>(&self) -> impl Future> + 'a; + /// This API will return a future whenever the field value is set by using `set' method of the field subscriber. + /// Future will be resolved with value. + fn register_set_handler<'a>(&self) -> impl Future> + 'a; } +/// FieldSampleMut trait is used to update the value of the field sample for zero-copy API. pub trait FieldSampleMut: com_api_concept::SampleMut where T: CommData + Debug, 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 868859576..14a2d2b19 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 @@ -102,11 +102,18 @@ macro_rules! interface { ); }; - (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." - ); + (interface $id:ident { $($field_name:ident : Field<$field_type:ty>),+$(,)? }) => { + $crate::interface_common!($id); + $crate::interface_consumer!($id, $($field_name, Field<$field_type>),+); + $crate::interface_producer!($id, $($field_name, Field<$field_type>),+); + }; + (interface $id:ident, { + Id = $uid:expr, + $($field_name:ident : Field<$field_type:ty>),+ $(,)? + }) => { + $crate::interface_common!($id, $uid); + $crate::interface_consumer!($id, $($field_name, Field<$field_type>),+); + $crate::interface_producer!($id, $($field_name, Field<$field_type>),+); }; } @@ -172,6 +179,32 @@ macro_rules! interface_consumer { } } }; + + ($id:ident, $($field_name:ident, Field<$field_type:ty>),+$(,)?) => { + com_api::paste::paste! { + pub struct [<$id Consumer>] { + $( + pub $field_name: R::FieldSubscriber<$field_type>, + )+ + } + + impl com_api::Consumer for [<$id Consumer>] { + fn new(instance_info: R::ConsumerInfo) -> Self { + [<$id Consumer>] { + $( + $field_name: R::FieldSubscriber::new( + stringify!($field_name), + instance_info.clone() + ).expect(&format!( + "Failed to create subscriber for {}", + stringify!($field_name) + )), + )+ + } + } + } + } + }; } /// Macro to implement the Producer and OfferedProducer traits for @@ -238,6 +271,66 @@ macro_rules! interface_producer { } } }; + + ($id:ident, $($field_name:ident, Field<$field_type:ty>),+$(,)?) => { + com_api::paste::paste! { + pub struct [<$id Producer>] { + _runtime: core::marker::PhantomData, + instance_info: R::ProviderInfo, + } + + pub struct [<$id OfferedProducer>] { + $( + pub $field_name: R::FieldPublisher<$field_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>] { + $( + $field_name: R::FieldPublisher::new( + stringify!($field_name), + self.instance_info.clone() + ).expect(&format!( + "Failed to create field publisher for {}", + stringify!($field_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) + } + } + } + }; } mod tests { diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/BUILD b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/BUILD index 1fbf183a8..c1b290169 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/BUILD @@ -15,12 +15,7 @@ load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_library", "rust_test") rust_library( name = "com-api-runtime-lola", - srcs = [ - "consumer.rs", - "lib.rs", - "producer.rs", - "runtime.rs", - ], + srcs = glob(["**/*.rs"]), edition = "2024", visibility = [ "//visibility:public", # platform_only diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs new file mode 100644 index 000000000..b1648a635 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs @@ -0,0 +1,116 @@ +/******************************************************************************** + * Copyright (c) 2026 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 + ********************************************************************************/ + +//! Field consumer implementation for Lola runtime. +//! It implements the field consumer related traits. +//! We are using the event related trait as a base trait for whereever we have same common +//! APIs or functionality, as of now there are derived from concept crate but +//! we will create a module which will have common trait for event and field which will be used by both event and field consumer/publisher. + +use core::fmt::Debug; +use core::marker::PhantomData; + +use bridge_ffi_rs::FFIBridge; +use com_api_concept::{ + CommData, FieldMethods, FieldSubscriber, MethodReturnTypePtr, Result, SampleContainer, + Subscriber, Subscription, +}; + +use crate::consumer::Sample; +use crate::{LolaConsumerInfo, LolaRuntimeImpl}; + +/// Field subscriber type which implements the FieldSubscriber trait for Lola runtime. +/// It will implement `subscribe` method to create a field subscription and `get` and `set` methods to get and set the value of the field. +pub struct LolaFieldSubscriber { + _data: PhantomData, + _bridge: PhantomData, +} + +/// Marker implementation of FieldSubscriber trait. +impl FieldSubscriber> + for LolaFieldSubscriber +{ +} + +/// Implementation of FieldMethods trait which provides `get` and `set` methods for LolaFieldSubscriber. +impl FieldMethods> + for LolaFieldSubscriber +{ + fn get(&self) -> Result> { + todo!() + } + fn set(&self, _value: T) -> Result> { + todo!() + } +} + +/// Implementation of Subscriber trait which provides `new` and `subscribe` methods for LolaFieldSubscriber. +impl Subscriber> + for LolaFieldSubscriber +{ + type Subscription = LolaFieldSubscription; + + fn new(_identifier: &'static str, _instance_info: LolaConsumerInfo) -> Result { + todo!() + } + + fn subscribe(self, _max_num_samples: usize) -> Result { + todo!() + } +} + +/// FieldSubscription type which provides data receiving APIs and unsubscribe method. +pub struct LolaFieldSubscription { + _data: PhantomData, + _bridge: PhantomData, +} + +/// Implementation of Subscription trait which provides receiving APIs. +impl Subscription> + for LolaFieldSubscription +{ + type Subscriber = LolaFieldSubscriber; + type Sample<'a> + = Sample + where + Self: 'a; + + fn unsubscribe(self) -> Self::Subscriber { + todo!() + } + + fn try_receive<'a>( + &'a self, + _scratch: &'_ mut SampleContainer>, + _max_samples: usize, + ) -> Result { + todo!() + } + + fn cancellable_receive<'a>( + &'a self, + _scratch: SampleContainer>, + _new_samples: usize, + _max_samples: usize, + _cancellation: impl core::future::Future + Send + 'static, + ) -> impl core::future::Future>, Result)> + 'a + { + async { todo!() } + } + + fn to_stream<'a>( + &'a mut self, + ) -> impl futures::stream::Stream>> + Unpin + 'a { + futures::stream::empty() + } +} diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs new file mode 100644 index 000000000..9c1ea7412 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs @@ -0,0 +1,104 @@ +/******************************************************************************** + * Copyright (c) 2026 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 + ********************************************************************************/ + +//! Field producer implementation for Lola runtime. + +use core::fmt::Debug; +use core::marker::PhantomData; + +use bridge_ffi_rs::FFIBridge; +use com_api_concept::{ + CommData, FieldPublisher, FieldSampleMut, Result, SampleMaybeUninit as SampleMaybeUninitTrait, +}; + +use crate::LolaProviderInfo; +use crate::LolaRuntimeImpl; + +pub struct LolaFieldPublisher { + _data: PhantomData, + _bridge: PhantomData, +} + +#[derive(Debug)] +pub struct LolaFieldSampleMut { + _data: PhantomData, +} + +impl core::ops::Deref for LolaFieldSampleMut { + type Target = T; + fn deref(&self) -> &T { + todo!() + } +} +impl core::ops::DerefMut for LolaFieldSampleMut { + fn deref_mut(&mut self) -> &mut T { + todo!() + } +} + +impl com_api_concept::SampleMut for LolaFieldSampleMut {} + +impl FieldSampleMut for LolaFieldSampleMut { + fn update(self) -> Result<()> { + todo!() + } +} + +#[derive(Debug)] +pub struct LolaFieldSampleMaybeUninit<'a, T> { + _data: core::mem::MaybeUninit, + _lt: PhantomData<&'a T>, +} + +impl<'a, T: CommData + Debug> AsMut> + for LolaFieldSampleMaybeUninit<'a, T> +{ + fn as_mut(&mut self) -> &mut core::mem::MaybeUninit { + &mut self._data + } +} +impl<'a, T: CommData + Debug> SampleMaybeUninitTrait for LolaFieldSampleMaybeUninit<'a, T> { + type SampleMut = LolaFieldSampleMut; + unsafe fn assume_init(self) -> LolaFieldSampleMut { + todo!() + } + fn write(self, _value: T) -> LolaFieldSampleMut { + todo!() + } +} + +impl FieldPublisher> + for LolaFieldPublisher +{ + type CommittedSample<'a> + = LolaFieldSampleMut + where + Self: 'a; + type SampleMaybeUninit<'a> + = LolaFieldSampleMaybeUninit<'a, T> + where + Self: 'a; + + fn new(_identifier: &str, _instance_info: LolaProviderInfo) -> Result { + todo!() + } + fn allocate(&self) -> Result> { + todo!() + } + fn update(&self, _value: T) -> Result<()> { + todo!() + } + fn register_set_handler<'a>(&self) -> impl core::future::Future> + 'a { + async { todo!() } + } +} diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/lib.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/lib.rs index a1c958ff7..63eb4d38e 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/lib.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/lib.rs @@ -26,12 +26,19 @@ //! that utilize the COM API abstractions. mod consumer; +mod field_consumer; +mod field_producer; mod producer; mod runtime; -pub use consumer::{LolaConsumerInfo, Sample, LolaConsumerDiscovery, SubscribableImpl}; +pub use consumer::{ + LolaConsumerDiscovery, LolaConsumerInfo, Sample, SubscribableImpl, +}; +pub use field_consumer::LolaFieldSubscriber; +pub use field_producer::LolaFieldPublisher; pub use producer::{ - LolaProviderInfo, Publisher, SampleMaybeUninit, SampleMut, LolaProducerBuilder, + LolaProducerBuilder, LolaProviderInfo, Publisher, SampleMaybeUninit, + SampleMut, }; pub use runtime::{LolaRuntimeImpl, RuntimeBuilderImpl}; diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/runtime.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/runtime.rs index f079c8646..2eabf8c69 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/runtime.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/runtime.rs @@ -16,8 +16,8 @@ use core::marker::PhantomData; use std::path::{Path, PathBuf}; use crate::{ - LolaConsumerDiscovery, LolaConsumerInfo, LolaProducerBuilder, LolaProviderInfo, Publisher, - SubscribableImpl, + LolaConsumerDiscovery, LolaConsumerInfo, LolaFieldPublisher, LolaFieldSubscriber, + LolaProducerBuilder, LolaProviderInfo, Publisher, SubscribableImpl, }; use com_api_concept::{ Builder, CommData, FindServiceSpecifier, InstanceSpecifier, Interface, Result, Runtime, @@ -37,6 +37,8 @@ impl Runtime for LolaRuntimeImpl { type Publisher = Publisher; type ProviderInfo = LolaProviderInfo; type ConsumerInfo = LolaConsumerInfo; + type FieldPublisher = LolaFieldPublisher; + type FieldSubscriber = LolaFieldSubscriber; fn find_service( &self, 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 e7c57f55f..4587c7688 100644 --- a/score/mw/com/impl/rust/com-api/com-api/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api/BUILD @@ -25,6 +25,6 @@ rust_library( deps = [ "//score/mw/com/impl/rust/com-api/com-api-concept", "//score/mw/com/impl/rust/com-api/com-api-runtime-lola", - "//score/mw/com/impl/rust/com-api/com-api-runtime-mock", + #"//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 545e1bfcf..0b722f5f2 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 @@ -133,15 +133,16 @@ pub use com_api_runtime_lola::LolaRuntimeImpl; pub use com_api_runtime_lola::RuntimeBuilderImpl as LolaRuntimeBuilderImpl; -pub use com_api_runtime_mock::MockRuntimeImpl; -pub use com_api_runtime_mock::RuntimeBuilderImpl as MockRuntimeBuilderImpl; +// pub use com_api_runtime_mock::MockRuntimeImpl; +// pub use com_api_runtime_mock::RuntimeBuilderImpl as MockRuntimeBuilderImpl; pub use com_api_concept::{ interface, interface_common, interface_consumer, interface_producer, Builder, CommData, - Consumer, ConsumerBuilder, ConsumerDescriptor, Error, EventSampleMut, FindServiceSpecifier, - InstanceSpecifier, Interface, OfferedProducer, PlacementDefault, Producer, ProducerBuilder, - ProviderInfo, Publisher, Reloc, Result, Runtime, RuntimeBuilder, SampleContainer, - SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, + Consumer, ConsumerBuilder, ConsumerDescriptor, Error, EventSampleMut as SampleMut, + FieldMethods, FieldPublisher, FieldSampleMut, FieldSubscriber, FieldSubscription, + FindServiceSpecifier, InstanceSpecifier, Interface, MethodReturnTypePtr, OfferedProducer, + PlacementDefault, Producer, ProducerBuilder, ProviderInfo, Publisher, Reloc, Result, Runtime, + RuntimeBuilder, SampleContainer, SampleMaybeUninit, ServiceDiscovery, Subscriber, Subscription, }; #[doc(hidden)] From 210862bd46626eaf00a3767dbe99d56ae2f17d8b Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Mon, 6 Jul 2026 14:33:19 +0530 Subject: [PATCH 03/13] Rust::com Update Producer Example --- score/mw/com/example/com-api-example/BUILD | 1 + .../basic-consumer-producer.rs | 52 +++++++++++++++++-- .../com-api/com-api-concept/com_api_field.rs | 15 +++--- .../com-api-concept/interface_macros.rs | 48 +++++++++++++---- .../com-api-runtime-lola/field_producer.rs | 16 +++++- 5 files changed, 111 insertions(+), 21 deletions(-) diff --git a/score/mw/com/example/com-api-example/BUILD b/score/mw/com/example/com-api-example/BUILD index 91bfb1091..7fc95e16f 100644 --- a/score/mw/com/example/com-api-example/BUILD +++ b/score/mw/com/example/com-api-example/BUILD @@ -23,6 +23,7 @@ rust_binary( "//score/mw/com/example/com-api-example/com-api-gen", "//score/mw/com/impl/rust/com-api/com-api", "@score_communication_crate_index//:clap", + "@score_communication_crate_index//:tokio", ], ) 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 90079e61f..70267c898 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 @@ -22,12 +22,12 @@ use clap::Parser; use std::path::PathBuf; use com_api::{ - Builder, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl, - OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, SampleContainer, - SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, + Builder, FieldPublisher, FindServiceSpecifier, InstanceSpecifier, Interface, + LolaRuntimeBuilderImpl, OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, + SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, }; -use com_api_gen::{Exhaust, Tire, VehicleInterface}; +use com_api_gen::{Exhaust, Tire, VehicleFieldInterface, VehicleInterface}; #[derive(Parser)] struct Arguments { @@ -45,6 +45,11 @@ 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; +// VehicleFieldProducer is the producer type for the VehicleField interface (before offering) +type VehicleFieldProducer = ::Producer; +// VehicleFieldOfferedProducer is the offered producer type for the VehicleField interface (fields support update/set-handler) +type VehicleFieldOfferedProducer = + <::Producer as Producer>::OfferedProducer; // Example struct demonstrating composition with VehicleConsumer pub struct VehicleMonitor { @@ -179,6 +184,45 @@ fn create_producer( producer.offer().expect("Failed to offer producer instance") } +async fn process_received_set_handler(producer: VehicleFieldProducer) { + loop { + let value: Tire = producer + .left_tire + .register_set_handler() + .await + .expect("Failed to register set handler"); + producer + .left_tire + .update(value) + .expect("Failed to update producer instance"); + } +} + +fn create_producer_field( + runtime: &R, + service_id: InstanceSpecifier, + initial_value: Tire, +) -> VehicleFieldOfferedProducer { + let producer_builder = runtime.producer_builder::(service_id); + let producer = producer_builder + .build() + .expect("Failed to build producer instance"); + producer + .left_tire + .update(initial_value) + .expect("Failed to update producer instance"); + + // Register set handler BEFORE offering + // Clone producer for the async handler task (TODO: need to think about clone) + // As we need to clone publisher as well in macro. + let producer_clone = producer.clone(); + tokio::task::spawn_local(async move { + process_received_set_handler(producer_clone).await; + }); + + producer.offer().expect("Failed to offer producer instance") +} + // Run the example with the specified runtime fn run_with_runtime(name: &str, runtime: &R) { println!("\n=== Running with {name} runtime ==="); diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs index e90794e24..017fbb474 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs @@ -38,6 +38,7 @@ pub struct MethodReturnTypePtr { /// It provides the `get` and `set` methods to get and set the value of the field. /// It derived from `com_api_concept::Subscriber` trait which provides the `subscribe` method to create a field subscription. /// `FieldMethods` trait is used to provide the `get` and `set` methods for the field instance which can be used before subscription. +/// Event related APIs follow the same restriction for concurrent access. pub trait FieldSubscriber: FieldMethods + com_api_concept::Subscriber { @@ -50,11 +51,13 @@ pub trait FieldSubscriber: pub trait FieldSubscription: com_api_concept::Subscription + FieldMethods { - /// Get the number of new samples available in the field subscription. - /// This does not take max_samples parameter like try_receive, but returns the number of samples available to be received. + /// Returns the number of new samples a call to try_receive (given parameter max_num_samples + /// doesn't restrict it) would currently provide. + /// How many new sample available for the user of this field subscription to receive. fn get_num_new_samples_available(&self) -> Result; - /// Get the number of sample count that can be received from the field subscription. + /// Get the number of samples that can still be received by the user of this field. + /// This is for checking the capacity of the field subscription and to avoid overflow of the field subscription limit. fn get_free_sample_count(&self) -> Result; } @@ -81,12 +84,12 @@ pub trait FieldMethods { /// FieldPublisher trait is used to publish a field and update the value of the field. // Note: We can not use publisher trait from event because that contains the Send Method which is not correct semantic for field. -pub trait FieldPublisher { - type CommittedSample<'a>: FieldSampleMut +pub trait FieldPublisher: Clone { + type FieldSample<'a>: FieldSampleMut where Self: 'a; - type SampleMaybeUninit<'a>: SampleMaybeUninit> + 'a + type SampleMaybeUninit<'a>: SampleMaybeUninit> + 'a where Self: 'a; 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 14a2d2b19..ac74a381f 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 @@ -275,10 +275,23 @@ macro_rules! interface_producer { ($id:ident, $($field_name:ident, Field<$field_type:ty>),+$(,)?) => { com_api::paste::paste! { pub struct [<$id Producer>] { - _runtime: core::marker::PhantomData, + $( + pub $field_name: R::FieldPublisher<$field_type>, + )+ instance_info: R::ProviderInfo, } + impl Clone for [<$id Producer>] { + fn clone(&self) -> Self { + [<$id Producer>] { + $( + $field_name: self.$field_name.clone(), + )+ + instance_info: self.instance_info.clone(), + } + } + } + pub struct [<$id OfferedProducer>] { $( pub $field_name: R::FieldPublisher<$field_type>, @@ -286,19 +299,24 @@ macro_rules! interface_producer { instance_info: R::ProviderInfo, } + impl Clone for [<$id OfferedProducer>] { + fn clone(&self) -> Self { + [<$id OfferedProducer>] { + $( + $field_name: self.$field_name.clone(), + )+ + instance_info: self.instance_info.clone(), + } + } + } + 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>] { $( - $field_name: R::FieldPublisher::new( - stringify!($field_name), - self.instance_info.clone() - ).expect(&format!( - "Failed to create field publisher for {}", - stringify!($field_name) - )), + $field_name: self.$field_name.clone(), )+ instance_info: self.instance_info.clone(), }; @@ -309,7 +327,15 @@ macro_rules! interface_producer { fn new(instance_info: R::ProviderInfo) -> com_api::Result { Ok([<$id Producer>] { - _runtime: core::marker::PhantomData, + $( + $field_name: R::FieldPublisher::new( + stringify!($field_name), + instance_info.clone() + ).expect(&format!( + "Failed to create field publisher for {}", + stringify!($field_name) + )), + )+ instance_info, }) } @@ -321,7 +347,9 @@ macro_rules! interface_producer { type Producer = [<$id Producer>]; fn unoffer(self) -> com_api::Result { let producer = [<$id Producer>] { - _runtime: core::marker::PhantomData, + $( + $field_name: self.$field_name.clone(), + )+ instance_info: self.instance_info.clone(), }; // Stop offering the service instance to withdraw it from system availability diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs index 9c1ea7412..e8cab232c 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs @@ -29,6 +29,20 @@ pub struct LolaFieldPublisher { _bridge: PhantomData, } +//TODO: Need to think about this clone, we added as Producer and OfferProducer both +//Type is using same field publisher to call the Update and set handler. +//As Set handler is async call so we need to keep producer active for that. +impl Clone for LolaFieldPublisher { + fn clone(&self) -> Self { + LolaFieldPublisher { + _data: PhantomData, + _bridge: PhantomData, + } + } +} + +impl Copy for LolaFieldPublisher {} + #[derive(Debug)] pub struct LolaFieldSampleMut { _data: PhantomData, @@ -80,7 +94,7 @@ impl<'a, T: CommData + Debug> SampleMaybeUninitTrait for LolaFieldSampleMaybe impl FieldPublisher> for LolaFieldPublisher { - type CommittedSample<'a> + type FieldSample<'a> = LolaFieldSampleMut where Self: 'a; From 3ea510130a5ea741d7712fafa1703db6310e1211 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Mon, 6 Jul 2026 16:05:00 +0530 Subject: [PATCH 04/13] Rust::com Consumer API example update --- .../basic-consumer-producer.rs | 69 ++++++++++++++++++- .../com-api/com-api-concept/com_api_field.rs | 3 +- .../com-api-runtime-lola/field_consumer.rs | 28 +++++++- 3 files changed, 94 insertions(+), 6 deletions(-) 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 70267c898..4d7f9015f 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 @@ -22,9 +22,10 @@ use clap::Parser; use std::path::PathBuf; use com_api::{ - Builder, FieldPublisher, FindServiceSpecifier, InstanceSpecifier, Interface, - LolaRuntimeBuilderImpl, OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, - SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, + Builder, FieldMethods, FieldPublisher, FieldSubscription, FindServiceSpecifier, + InstanceSpecifier, Interface, LolaRuntimeBuilderImpl, OfferedProducer, Producer, Publisher, + Result, Runtime, RuntimeBuilder, SampleContainer, SampleMaybeUninit, SampleMut, + ServiceDiscovery, Subscriber, Subscription, }; use com_api_gen::{Exhaust, Tire, VehicleFieldInterface, VehicleInterface}; @@ -51,6 +52,8 @@ type VehicleFieldProducer = ::Producer type VehicleFieldOfferedProducer = <::Producer as Producer>::OfferedProducer; +type VehicleFieldConsumer = ::Consumer; + // Example struct demonstrating composition with VehicleConsumer pub struct VehicleMonitor { producer: VehicleOfferedProducer, @@ -223,6 +226,66 @@ fn create_producer_field( producer.offer().expect("Failed to offer producer instance") } +fn create_consumer_field( + runtime: &R, + service_id: InstanceSpecifier, +) -> VehicleFieldConsumer { + let consumer_discovery = + runtime.find_service::(FindServiceSpecifier::Specific(service_id)); + let available_service_instances = consumer_discovery + .get_available_instances() + .expect("Failed to get available service instances"); + + // Select service instance at specific handle_index + let handle_index = 0; // or any index you need from vector of instances + let consumer_builder = available_service_instances + .into_iter() + .nth(handle_index) + .expect("Failed to get consumer builder at specified handle index"); + + consumer_builder + .build() + .expect("Failed to build consumer instance") +} + +fn consumer_processing_field(consumer: VehicleFieldConsumer) { + //currently we do not have MethodReturnTypePtr implementation for Lola runtime. + let _ = consumer.left_tire.get().expect("Failed to get field value"); + + let _ = consumer + .left_tire + .set(Tire { pressure: 30.0 }) + .expect("Failed to set field value"); + + // Subscribe to the field to receive updates + let subscription = consumer + .left_tire + .subscribe(3) + .expect("Failed to subscribe to field"); + let mut sample_buf = SampleContainer::new(3); + + // Poll for updates (non-blocking) + match subscription.try_receive(&mut sample_buf, 1) { + Ok(n) if n > 0 => { + while let Some(sample) = sample_buf.pop_front() { + println!("Updated tire pressure: {:?}", *sample); + } + } + _ => { + println!("No new tire pressure updates available"); + } + } + + let _ = subscription + .set(Tire { pressure: 35.0 }) + .expect("Failed to set field value"); + let _ = subscription.get().expect("Failed to get field value"); + + drop(sample_buf); + + let _ = subscription.unsubscribe(); +} + // Run the example with the specified runtime fn run_with_runtime(name: &str, runtime: &R) { println!("\n=== Running with {name} runtime ==="); diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs index 017fbb474..fd76de19a 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs @@ -40,8 +40,9 @@ pub struct MethodReturnTypePtr { /// `FieldMethods` trait is used to provide the `get` and `set` methods for the field instance which can be used before subscription. /// Event related APIs follow the same restriction for concurrent access. pub trait FieldSubscriber: - FieldMethods + com_api_concept::Subscriber + FieldMethods + com_api_concept::Subscriber> { + } /// FieldSubscriber trait is provides the receiving APIs for the field subscription and diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs index b1648a635..75bb58ffd 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs @@ -22,8 +22,8 @@ use core::marker::PhantomData; use bridge_ffi_rs::FFIBridge; use com_api_concept::{ - CommData, FieldMethods, FieldSubscriber, MethodReturnTypePtr, Result, SampleContainer, - Subscriber, Subscription, + CommData, FieldMethods, FieldSubscriber, FieldSubscription, MethodReturnTypePtr, Result, + SampleContainer, Subscriber, Subscription, }; use crate::consumer::Sample; @@ -75,6 +75,18 @@ pub struct LolaFieldSubscription { _bridge: PhantomData, } +impl FieldSubscription> + for LolaFieldSubscription +{ + fn get_free_sample_count(&self) -> Result { + todo!() + } + + fn get_num_new_samples_available(&self) -> Result { + todo!() + } +} + /// Implementation of Subscription trait which provides receiving APIs. impl Subscription> for LolaFieldSubscription @@ -114,3 +126,15 @@ impl Subscription> futures::stream::empty() } } + +/// Implementation of FieldMethods trait which provides `get` and `set` methods for LolaFieldSubscriber. +impl FieldMethods> + for LolaFieldSubscription +{ + fn get(&self) -> Result> { + todo!() + } + fn set(&self, _value: T) -> Result> { + todo!() + } +} From 5d851e1b2db20baab851035994e0a33488840785 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Mon, 6 Jul 2026 16:26:23 +0530 Subject: [PATCH 05/13] Rust::com Mock Runtime update with same APIs --- .../com-api/com-api-runtime-mock/runtime.rs | 219 +++++++++++++++++- 1 file changed, 216 insertions(+), 3 deletions(-) diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs index 8d7837f2c..d8f38b6f2 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs @@ -36,9 +36,11 @@ use std::collections::VecDeque; use std::path::Path; use com_api_concept::{ - Builder, CommData, Consumer, ConsumerBuilder, ConsumerDescriptor, FindServiceSpecifier, - InstanceSpecifier, Interface, Producer, ProducerBuilder, ProviderInfo, Result, Runtime, - SampleContainer, ServiceDiscovery, Subscriber, Subscription, + Builder, CommData, Consumer, ConsumerBuilder, ConsumerDescriptor, FieldMethods, FieldPublisher, + FieldSampleMut, FieldSubscriber, FieldSubscription, FindServiceSpecifier, InstanceSpecifier, + Interface, MethodReturnTypePtr, Producer, ProducerBuilder, ProviderInfo, Result, Runtime, + SampleContainer, SampleMaybeUninit as SampleMaybeUninitTrait, ServiceDiscovery, Subscriber, + Subscription, }; pub struct MockRuntimeImpl {} @@ -70,6 +72,8 @@ impl Runtime for MockRuntimeImpl { type Publisher = Publisher; type ProviderInfo = MockProviderInfo; type ConsumerInfo = MockConsumerInfo; + type FieldSubscriber = MockFieldSubscriber; + type FieldPublisher = MockFieldPublisher; fn find_service( &self, @@ -519,6 +523,215 @@ impl RuntimeBuilderImpl { } } +/// Field subscriber type which implements the FieldSubscriber trait for Mock runtime. +pub struct MockFieldSubscriber { + identifier: &'static str, + instance_info: MockConsumerInfo, + _data: PhantomData, +} + +/// Marker implementation of FieldSubscriber trait. +impl FieldSubscriber for MockFieldSubscriber {} + +/// Implementation of FieldMethods trait for MockFieldSubscriber. +impl FieldMethods for MockFieldSubscriber { + fn get(&self) -> Result> { + todo!() + } + fn set(&self, _value: T) -> Result> { + todo!() + } +} + +/// Implementation of Subscriber trait for MockFieldSubscriber. +impl Subscriber for MockFieldSubscriber { + type Subscription = MockFieldSubscription; + + fn new(identifier: &'static str, instance_info: MockConsumerInfo) -> Result { + Ok(Self { + identifier, + instance_info, + _data: PhantomData, + }) + } + + fn subscribe(self, _max_num_samples: usize) -> Result { + Ok(MockFieldSubscription { + identifier: self.identifier, + instance_info: self.instance_info, + _data: PhantomData, + }) + } +} + +/// FieldSubscription type which provides data receiving APIs and unsubscribe method. +pub struct MockFieldSubscription { + identifier: &'static str, + instance_info: MockConsumerInfo, + _data: PhantomData, +} + +impl FieldSubscription for MockFieldSubscription { + fn get_free_sample_count(&self) -> Result { + todo!() + } + + fn get_num_new_samples_available(&self) -> Result { + todo!() + } +} + +/// Implementation of Subscription trait which provides receiving APIs. +impl Subscription for MockFieldSubscription { + type Subscriber = MockFieldSubscriber; + type Sample<'a> + = Sample<'a, T> + where + Self: 'a; + + fn unsubscribe(self) -> Self::Subscriber { + MockFieldSubscriber { + identifier: self.identifier, + instance_info: self.instance_info, + _data: PhantomData, + } + } + + fn try_receive<'a>( + &'a self, + _scratch: &'_ mut SampleContainer>, + _max_samples: usize, + ) -> Result { + todo!() + } + + fn cancellable_receive<'a>( + &'a self, + _scratch: SampleContainer>, + _new_samples: usize, + _max_samples: usize, + _cancellation: impl Future + Send + 'static, + ) -> impl Future>, Result)> + 'a { + async { todo!() } + } + + fn to_stream<'a>(&'a mut self) -> impl Stream>> + Unpin + 'a { + stream::empty() + } +} + +/// Implementation of FieldMethods trait for MockFieldSubscription. +impl FieldMethods for MockFieldSubscription { + fn get(&self) -> Result> { + todo!() + } + fn set(&self, _value: T) -> Result> { + todo!() + } +} + +/// Field publisher type for Mock runtime. +pub struct MockFieldPublisher { + _data: PhantomData, +} + +impl Clone for MockFieldPublisher { + fn clone(&self) -> Self { + MockFieldPublisher { _data: PhantomData } + } +} + +impl Copy for MockFieldPublisher {} + +/// Field sample mutable type. +#[derive(Debug)] +pub struct MockFieldSampleMut<'a, T: CommData + Debug> { + data: T, + _lifetime: PhantomData<&'a T>, +} + +impl<'a, T: CommData + Debug> Deref for MockFieldSampleMut<'a, T> { + type Target = T; + fn deref(&self) -> &T { + &self.data + } +} + +impl<'a, T: CommData + Debug> DerefMut for MockFieldSampleMut<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.data + } +} + +impl<'a, T: CommData + Debug> com_api_concept::SampleMut for MockFieldSampleMut<'a, T> {} + +impl<'a, T: CommData + Debug> FieldSampleMut for MockFieldSampleMut<'a, T> { + fn update(self) -> Result<()> { + todo!() + } +} + +/// Field sample maybe uninit type. +#[derive(Debug)] +pub struct MockFieldSampleMaybeUninit<'a, T: CommData + Debug> { + data: MaybeUninit, + _lifetime: PhantomData<&'a T>, +} + +impl<'a, T: CommData + Debug> AsMut> for MockFieldSampleMaybeUninit<'a, T> { + fn as_mut(&mut self) -> &mut MaybeUninit { + &mut self.data + } +} + +impl<'a, T: CommData + Debug> SampleMaybeUninitTrait for MockFieldSampleMaybeUninit<'a, T> { + type SampleMut = MockFieldSampleMut<'a, T>; + + unsafe fn assume_init(self) -> MockFieldSampleMut<'a, T> { + MockFieldSampleMut { + data: unsafe { self.data.assume_init() }, + _lifetime: PhantomData, + } + } + + fn write(self, value: T) -> MockFieldSampleMut<'a, T> { + MockFieldSampleMut { + data: value, + _lifetime: PhantomData, + } + } +} + +impl FieldPublisher for MockFieldPublisher { + type FieldSample<'a> + = MockFieldSampleMut<'a, T> + where + Self: 'a; + type SampleMaybeUninit<'a> + = MockFieldSampleMaybeUninit<'a, T> + where + Self: 'a; + + fn new(_identifier: &str, _instance_info: MockProviderInfo) -> Result { + Ok(Self { _data: PhantomData }) + } + + fn allocate(&self) -> Result> { + Ok(MockFieldSampleMaybeUninit { + data: MaybeUninit::uninit(), + _lifetime: PhantomData, + }) + } + + fn update(&self, _value: T) -> Result<()> { + todo!() + } + + fn register_set_handler<'a>(&self) -> impl Future> + 'a { + async { todo!() } + } +} + #[cfg(test)] mod test { use com_api_concept::{Publisher, SampleContainer, SampleMaybeUninit, SampleMut, Subscription}; From f27f3cb00c9eed26076f5e9eb0eab55009c897c5 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Mon, 6 Jul 2026 17:01:05 +0530 Subject: [PATCH 06/13] Rust::com Resolve the build issue --- score/mw/com/example/com-api-example/BUILD | 2 +- .../example/com-api-example/basic-consumer-producer.rs | 8 ++++---- score/mw/com/impl/rust/com-api/com-api/com_api.rs | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/score/mw/com/example/com-api-example/BUILD b/score/mw/com/example/com-api-example/BUILD index 7fc95e16f..d16336b83 100644 --- a/score/mw/com/example/com-api-example/BUILD +++ b/score/mw/com/example/com-api-example/BUILD @@ -23,7 +23,7 @@ rust_binary( "//score/mw/com/example/com-api-example/com-api-gen", "//score/mw/com/impl/rust/com-api/com-api", "@score_communication_crate_index//:clap", - "@score_communication_crate_index//:tokio", + "@score_communication_crate_index//:futures", ], ) 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 4d7f9015f..133afc51f 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 @@ -11,13 +11,14 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +#![allow(unused)] + // This demo app writing and reading tire pressure data using producer and consumer respectively. // It is demonstrating the composition of consumer and producer in one struct, // but they can be used separately as well. // The example is using Lola runtime, but it can be used with any runtime by changing the runtime initialization part. // Note: The example is using unwrap and panic in some places for simplicity, // but it is recommended to handle errors properly in production code. - use clap::Parser; use std::path::PathBuf; @@ -219,9 +220,8 @@ fn create_producer_field( // Clone producer for the async handler task (TODO: need to think about clone) // As we need to clone publisher as well in macro. let producer_clone = producer.clone(); - tokio::task::spawn_local(async move { - process_received_set_handler(producer_clone).await; - }); + //TODO: Test this with tokio::spawn. + futures::executor::block_on(process_received_set_handler(producer_clone)); producer.offer().expect("Failed to offer producer instance") } 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 0b722f5f2..db3a36cad 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 @@ -133,6 +133,7 @@ pub use com_api_runtime_lola::LolaRuntimeImpl; pub use com_api_runtime_lola::RuntimeBuilderImpl as LolaRuntimeBuilderImpl; +//TODO: This is just commented for now, we will enable it before PR open for review. We need to implement the MockRuntimeImpl and MockRuntimeBuilderImpl for the mock runtime. // pub use com_api_runtime_mock::MockRuntimeImpl; // pub use com_api_runtime_mock::RuntimeBuilderImpl as MockRuntimeBuilderImpl; From 777947349a793b5574c8a2ba954c3f0ff587b041 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Tue, 7 Jul 2026 09:45:32 +0530 Subject: [PATCH 07/13] Rust::com Updated async call in example --- score/mw/com/example/com-api-example/BUILD | 1 + .../com-api-example/basic-consumer-producer.rs | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/score/mw/com/example/com-api-example/BUILD b/score/mw/com/example/com-api-example/BUILD index d16336b83..7a3e5da5b 100644 --- a/score/mw/com/example/com-api-example/BUILD +++ b/score/mw/com/example/com-api-example/BUILD @@ -24,6 +24,7 @@ rust_binary( "//score/mw/com/impl/rust/com-api/com-api", "@score_communication_crate_index//:clap", "@score_communication_crate_index//:futures", + "@score_communication_crate_index//:tokio", ], ) 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 133afc51f..843f374b9 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 @@ -206,7 +206,11 @@ fn create_producer_field( runtime: &R, service_id: InstanceSpecifier, initial_value: Tire, -) -> VehicleFieldOfferedProducer { +) -> VehicleFieldOfferedProducer +where + ::FieldPublisher: Send, + ::FieldPublisher: Send, +{ let producer_builder = runtime.producer_builder::(service_id); let producer = producer_builder .build() @@ -220,8 +224,11 @@ fn create_producer_field( // Clone producer for the async handler task (TODO: need to think about clone) // As we need to clone publisher as well in macro. let producer_clone = producer.clone(); - //TODO: Test this with tokio::spawn. - futures::executor::block_on(process_received_set_handler(producer_clone)); + //TODO: Test with tokio spawn as well + // Spawn handler in a separate thread to avoid blocking + std::thread::spawn(move || { + futures::executor::block_on(process_received_set_handler(producer_clone)); + }); producer.offer().expect("Failed to offer producer instance") } From bb63b7174c267c82303b173add76bb162cdb9217 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Tue, 7 Jul 2026 15:56:01 +0530 Subject: [PATCH 08/13] Rust::com Update APIs parameter --- .../example/com-api-example/basic-consumer-producer.rs | 8 ++++++-- .../impl/rust/com-api/com-api-concept/com_api_field.rs | 4 ++-- .../rust/com-api/com-api-runtime-lola/field_producer.rs | 4 ++-- .../com/impl/rust/com-api/com-api-runtime-mock/runtime.rs | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) 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 843f374b9..9ef32eb4e 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 @@ -190,11 +190,15 @@ fn create_producer( async fn process_received_set_handler(producer: VehicleFieldProducer) { loop { - let value: Tire = producer + // When register callback called C++ has internal update call to notify the other consumer + // Here producer can process the value or do some operation. + // Value return from C++ side is reference of FieldType. + let value: &Tire = producer .left_tire .register_set_handler() .await .expect("Failed to register set handler"); + //just for demonstration, updating same value. producer .left_tire .update(value) @@ -217,7 +221,7 @@ where .expect("Failed to build producer instance"); producer .left_tire - .update(initial_value) + .update(&initial_value) .expect("Failed to update producer instance"); // Register set handler BEFORE offering diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs index fd76de19a..0da4894ae 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs @@ -104,11 +104,11 @@ pub trait FieldPublisher: Clone { /// Update the value of the field with the provided value. /// This is not zero-copy API. - fn update(&self, value: T) -> Result<()>; + fn update(&self, value: &T) -> Result<()>; /// This API will return a future whenever the field value is set by using `set' method of the field subscriber. /// Future will be resolved with value. - fn register_set_handler<'a>(&self) -> impl Future> + 'a; + fn register_set_handler<'a>(&self) -> impl Future> + 'a; } /// FieldSampleMut trait is used to update the value of the field sample for zero-copy API. diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs index e8cab232c..7c685e6d4 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs @@ -109,10 +109,10 @@ impl FieldPublisher> fn allocate(&self) -> Result> { todo!() } - fn update(&self, _value: T) -> Result<()> { + fn update(&self, _value: &T) -> Result<()> { todo!() } - fn register_set_handler<'a>(&self) -> impl core::future::Future> + 'a { + fn register_set_handler<'a>(&self) -> impl core::future::Future> + 'a { async { todo!() } } } diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs index d8f38b6f2..0bd6f5118 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs @@ -723,11 +723,11 @@ impl FieldPublisher for MockFieldPublis }) } - fn update(&self, _value: T) -> Result<()> { + fn update(&self, _value: &T) -> Result<()> { todo!() } - fn register_set_handler<'a>(&self) -> impl Future> + 'a { + fn register_set_handler<'a>(&self) -> impl Future> + 'a { async { todo!() } } } From 526eb4168eae2463e52a7f8bcd5c7542359d9e08 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Wed, 8 Jul 2026 10:00:47 +0530 Subject: [PATCH 09/13] Rust::com Intial value update for field * Update the intial value for field in macro --- .../com-api-example/com-api-gen/com_api_gen.rs | 14 ++++++++++++++ .../com-api/com-api-concept/com_api_concept.rs | 9 ++++++++- .../com-api/com-api-concept/interface_macros.rs | 11 +++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) 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 3b3b5a426..f3dd20b3d 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 @@ -20,11 +20,23 @@ pub struct Tire { pub pressure: f32, } +impl Default for Tire { + fn default() -> Self { + Tire { pressure: 0.0 } + } +} + #[derive(Debug, Reloc, CommData)] #[repr(C)] // No explicit ID provided, so it will be auto-generated as "com_api_gen::Exhaust" pub struct Exhaust {} +impl Default for Exhaust { + fn default() -> Self { + Exhaust {} + } +} + // 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 = "VehicleInterface" @@ -47,6 +59,8 @@ interface!( } ); +// Field intialize the intial value based on the default value of the field type at the time of creating field publisher. +// If user want to update the value after that as well then they can use update method of the field publisher to update the value of the field. interface!( interface VehicleField, { Id = "VehicleFieldInterface", 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 dcd48c18b..4f2fab963 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 @@ -53,6 +53,7 @@ use crate::error::*; use crate::Reloc; pub use com_api_concept_macros::CommData; use containers::fixed_capacity::FixedCapacityQueue; +use core::default::Default; use core::fmt::Debug; use core::future::Future; use core::ops::{Deref, DerefMut}; @@ -210,7 +211,13 @@ where /// avoid conflicts in the system. /// If no custom ID is provided, the type name will be used as the default ID with module path as /// prefix to ensure uniqueness. -pub trait CommData: Reloc { +/// +/// # Future Breaking Change +/// **Note**: `Default` will become a required supertrait in a future version to support +/// in-place initialization patterns. Existing `CommData` types should implement `Default` +/// to prepare for this change and avoid future breakage. +// TODO: Remove Default trait in final PR, with above warning +pub trait CommData: Reloc + Default { const ID: &'static str; } 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 ac74a381f..3a52db48a 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 @@ -326,7 +326,7 @@ macro_rules! interface_producer { } fn new(instance_info: R::ProviderInfo) -> com_api::Result { - Ok([<$id Producer>] { + let instance = Self { $( $field_name: R::FieldPublisher::new( stringify!($field_name), @@ -337,7 +337,14 @@ macro_rules! interface_producer { )), )+ instance_info, - }) + }; + $( + instance.$field_name.update(&Default::default()).expect(&format!( + "Failed to initialize field {} with default value", + stringify!($field_name) + )); + )+ + Ok(instance) } } From 6dc14b7b1404fb16addf70d77cc440d6079a0be7 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Thu, 9 Jul 2026 10:02:25 +0530 Subject: [PATCH 10/13] Rust::com Taking callback form user for Set Handler --- .../basic-consumer-producer.rs | 60 +++++++++---------- .../com-api/com-api-concept/com_api_field.rs | 8 ++- .../com-api-runtime-lola/field_consumer.rs | 15 ++++- .../com-api-runtime-lola/field_producer.rs | 12 +++- 4 files changed, 58 insertions(+), 37 deletions(-) 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 9ef32eb4e..109cfc477 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 @@ -188,22 +188,10 @@ fn create_producer( producer.offer().expect("Failed to offer producer instance") } -async fn process_received_set_handler(producer: VehicleFieldProducer) { - loop { - // When register callback called C++ has internal update call to notify the other consumer - // Here producer can process the value or do some operation. - // Value return from C++ side is reference of FieldType. - let value: &Tire = producer - .left_tire - .register_set_handler() - .await - .expect("Failed to register set handler"); - //just for demonstration, updating same value. - producer - .left_tire - .update(value) - .expect("Failed to update producer instance"); - } +fn process_received(val: &Tire) { + //Update some internal value and process it + //maybe need to apply synchronation if we are using thread pool + //just for demonstration, updating same value with incremented pressure. } fn create_producer_field( @@ -212,7 +200,7 @@ fn create_producer_field( initial_value: Tire, ) -> VehicleFieldOfferedProducer where - ::FieldPublisher: Send, + ::FieldPublisher: Send + Sync, ::FieldPublisher: Send, { let producer_builder = runtime.producer_builder::(service_id); @@ -224,15 +212,20 @@ where .update(&initial_value) .expect("Failed to update producer instance"); - // Register set handler BEFORE offering - // Clone producer for the async handler task (TODO: need to think about clone) - // As we need to clone publisher as well in macro. - let producer_clone = producer.clone(); - //TODO: Test with tokio spawn as well - // Spawn handler in a separate thread to avoid blocking - std::thread::spawn(move || { - futures::executor::block_on(process_received_set_handler(producer_clone)); - }); + producer + .left_tire + .register_set_handler(|value: &Tire| { + //just for demonstration, updating same value with incremented pressure. + //Update some internal value and process it + //maybe user need to apply synchronation mechanism if Lib is using thread pool + producer + .left_tire + .update(&Tire { + pressure: value.pressure + 1.0, + }) + .expect("Failed to update field value"); + }) + .expect("Failed to register set handler"); producer.offer().expect("Failed to offer producer instance") } @@ -261,9 +254,16 @@ fn create_consumer_field( fn consumer_processing_field(consumer: VehicleFieldConsumer) { //currently we do not have MethodReturnTypePtr implementation for Lola runtime. - let _ = consumer.left_tire.get().expect("Failed to get field value"); - let _ = consumer + //If user did not get the insatnce before the subscribe call then consumer will be consumed. + let field_method = consumer.get_field_method_instance(); + + let _ = field_method + .left_tire + .get() + .expect("Failed to get field value"); + + let _ = field_method .left_tire .set(Tire { pressure: 30.0 }) .expect("Failed to set field value"); @@ -287,10 +287,10 @@ fn consumer_processing_field(consumer: VehicleFieldConsumer) { } } - let _ = subscription + let _ = field_method .set(Tire { pressure: 35.0 }) .expect("Failed to set field value"); - let _ = subscription.get().expect("Failed to get field value"); + let _ = field_method.get().expect("Failed to get field value"); drop(sample_buf); diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs index 0da4894ae..a8cd97c29 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs @@ -40,9 +40,11 @@ pub struct MethodReturnTypePtr { /// `FieldMethods` trait is used to provide the `get` and `set` methods for the field instance which can be used before subscription. /// Event related APIs follow the same restriction for concurrent access. pub trait FieldSubscriber: - FieldMethods + com_api_concept::Subscriber> + com_api_concept::Subscriber> { + type FieldMethodsInstance: FieldMethods; + fn get_field_method_instance(&self) -> Self::FieldMethodsInstance; } /// FieldSubscriber trait is provides the receiving APIs for the field subscription and @@ -50,7 +52,7 @@ pub trait FieldSubscriber: /// Additional methods which the field subscription provides are added in this trait. /// `FieldMethods` trait is used to provide the `get` and `set` methods for the field subscription. pub trait FieldSubscription: - com_api_concept::Subscription + FieldMethods + com_api_concept::Subscription { /// Returns the number of new samples a call to try_receive (given parameter max_num_samples /// doesn't restrict it) would currently provide. @@ -108,7 +110,7 @@ pub trait FieldPublisher: Clone { /// This API will return a future whenever the field value is set by using `set' method of the field subscriber. /// Future will be resolved with value. - fn register_set_handler<'a>(&self) -> impl Future> + 'a; + fn register_set_handler<'a>(&self, callback: impl Fn(&T) + Send + 'a) -> Result<()>; } /// FieldSampleMut trait is used to update the value of the field sample for zero-copy API. diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs index 75bb58ffd..09e88a836 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs @@ -19,11 +19,12 @@ use core::fmt::Debug; use core::marker::PhantomData; +use core::todo; use bridge_ffi_rs::FFIBridge; use com_api_concept::{ CommData, FieldMethods, FieldSubscriber, FieldSubscription, MethodReturnTypePtr, Result, - SampleContainer, Subscriber, Subscription, + Runtime, SampleContainer, Subscriber, Subscription, }; use crate::consumer::Sample; @@ -36,15 +37,25 @@ pub struct LolaFieldSubscriber { _bridge: PhantomData, } +pub struct LolaFieldMethods { + _data: PhantomData, + _bridge: PhantomData, +} + /// Marker implementation of FieldSubscriber trait. impl FieldSubscriber> for LolaFieldSubscriber { + type FieldMethodsInstance = LolaFieldMethods; + + fn get_field_method_instance(&self) -> Self::FieldMethodsInstance { + todo!() + } } /// Implementation of FieldMethods trait which provides `get` and `set` methods for LolaFieldSubscriber. impl FieldMethods> - for LolaFieldSubscriber + for LolaFieldMethods { fn get(&self) -> Result> { todo!() diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs index 7c685e6d4..72f17dabc 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_producer.rs @@ -15,6 +15,7 @@ use core::fmt::Debug; use core::marker::PhantomData; +use core::todo; use bridge_ffi_rs::FFIBridge; use com_api_concept::{ @@ -112,7 +113,14 @@ impl FieldPublisher> fn update(&self, _value: &T) -> Result<()> { todo!() } - fn register_set_handler<'a>(&self) -> impl core::future::Future> + 'a { - async { todo!() } + fn register_set_handler<'a>(&self, callback: impl Fn(&T) + Send + 'a) -> Result<()> { + + //If waker get the notification form FFI call then + //Create a task to call the callback with value. + //Thread pool is a option here to run the callback in a separate thread. + //But i feel we still need to think about exection order of that callback, + //Because separate thread can raise concurrency issue / race condition. + + todo!() } } From 8d3921a6c41ef36b85fbea9d1676f31008b94556 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Fri, 10 Jul 2026 15:57:45 +0530 Subject: [PATCH 11/13] Rust::com Macro based Complie time check for field --- .../basic-consumer-producer.rs | 78 +++-- .../com-api-gen/com_api_gen.rs | 22 +- .../rust/com-api/com-api-concept-macros/BUILD | 5 +- .../com-api/com-api-concept-macros/lib.rs | 34 ++ .../type_state_validator.rs | 293 ++++++++++++++++++ .../com-api-concept/com_api_concept.rs | 6 + .../com-api-concept/interface_macros.rs | 60 ++-- .../impl/rust/com-api/com-api-concept/lib.rs | 4 + .../com/impl/rust/com-api/com-api/com_api.rs | 7 +- 9 files changed, 422 insertions(+), 87 deletions(-) create mode 100644 score/mw/com/impl/rust/com-api/com-api-concept-macros/type_state_validator.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 109cfc477..354966c6c 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 @@ -188,7 +188,7 @@ fn create_producer( producer.offer().expect("Failed to offer producer instance") } -fn process_received(val: &Tire) { +fn process_received(val: &Tire) { //Update some internal value and process it //maybe need to apply synchronation if we are using thread pool //just for demonstration, updating same value with incremented pressure. @@ -197,7 +197,8 @@ fn process_received(val: &Tire) { fn create_producer_field( runtime: &R, service_id: InstanceSpecifier, - initial_value: Tire, + initial_tire_value: Tire, + initial_exhaust_value: Exhaust, ) -> VehicleFieldOfferedProducer where ::FieldPublisher: Send + Sync, @@ -207,27 +208,40 @@ where let producer = producer_builder .build() .expect("Failed to build producer instance"); - producer - .left_tire - .update(&initial_value) - .expect("Failed to update producer instance"); - producer - .left_tire - .register_set_handler(|value: &Tire| { - //just for demonstration, updating same value with incremented pressure. - //Update some internal value and process it - //maybe user need to apply synchronation mechanism if Lib is using thread pool - producer - .left_tire - .update(&Tire { - pressure: value.pressure + 1.0, - }) - .expect("Failed to update field value"); + // Use validator pattern with compile-time type-state validation + // Must register handlers AND initialize all fields before offer() is available + let offered = producer + .validator() + .register_set_handler_left_tire(move |val: &Tire| { + println!("Received tire pressure update: {:?}", val); + process_received(val); }) - .expect("Failed to register set handler"); + .register_set_handler_exhaust(|_val: &Exhaust| { + println!("Received exhaust update"); + }) + .update_left_tire(&initial_tire_value) + .expect("Failed to update left_tire field") + .update_exhaust(&initial_exhaust_value) + .expect("Failed to update exhaust field") + .offer() + .expect("Failed to offer producer instance"); + + offered +} - producer.offer().expect("Failed to offer producer instance") +fn offered_producer_process(offered_producer: VehicleFieldOfferedProducer) { + // Use the offered producer to update fields + let new_tire_value = Tire { pressure: 32.0 }; + let new_exhaust_value = Exhaust {}; + offered_producer + .left_tire + .update(&new_tire_value) + .expect("Failed to update left_tire field"); + offered_producer + .exhaust + .update(&new_exhaust_value) + .expect("Failed to update exhaust field"); } fn create_consumer_field( @@ -256,17 +270,14 @@ fn consumer_processing_field(consumer: VehicleFieldConsumer) { //currently we do not have MethodReturnTypePtr implementation for Lola runtime. //If user did not get the insatnce before the subscribe call then consumer will be consumed. - let field_method = consumer.get_field_method_instance(); - let _ = field_method - .left_tire - .get() - .expect("Failed to get field value"); + // TODO: Field consumer API methods (get/set) not yet fully implemented + // let _ = consumer.left_tire.get().expect("Failed to get field value"); - let _ = field_method - .left_tire - .set(Tire { pressure: 30.0 }) - .expect("Failed to set field value"); + // let _ = consumer + // .left_tire + // .set(Tire { pressure: 30.0 }) + // .expect("Failed to set field value"); // Subscribe to the field to receive updates let subscription = consumer @@ -287,10 +298,11 @@ fn consumer_processing_field(consumer: VehicleFieldConsumer) { } } - let _ = field_method - .set(Tire { pressure: 35.0 }) - .expect("Failed to set field value"); - let _ = field_method.get().expect("Failed to get field value"); + // TODO: Field subscription API methods (set/get_async) not yet fully implemented + // let _ = subscription + // .set(Tire { pressure: 35.0 }) + // .expect("Failed to set field value"); + // let _ = subscription.get_async().expect("Failed to get field value"); drop(sample_buf); 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 f3dd20b3d..0e2b5c761 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 @@ -13,30 +13,18 @@ use com_api::{interface, CommData, FieldPublisher, ProviderInfo, Publisher, Reloc, Subscriber}; -#[derive(Debug, Reloc, CommData)] +#[derive(Debug, Clone, Default, Reloc, CommData)] #[repr(C)] #[comm_data(id = "Tire")] pub struct Tire { pub pressure: f32, } -impl Default for Tire { - fn default() -> Self { - Tire { pressure: 0.0 } - } -} - -#[derive(Debug, Reloc, CommData)] +#[derive(Debug, Clone, Default, Reloc, CommData)] #[repr(C)] // No explicit ID provided, so it will be auto-generated as "com_api_gen::Exhaust" pub struct Exhaust {} -impl Default for Exhaust { - fn default() -> Self { - Exhaust {} - } -} - // 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 = "VehicleInterface" @@ -51,6 +39,7 @@ impl Default for Exhaust { // "exhaust" events. // - VehicleOfferedProducer struct that implements OfferedProducer trait for offering // "left_tire" and "exhaust" events. +//Let's remove interface!( interface Vehicle, { Id = "VehicleInterface", @@ -59,8 +48,9 @@ interface!( } ); -// Field intialize the intial value based on the default value of the field type at the time of creating field publisher. -// If user want to update the value after that as well then they can use update method of the field publisher to update the value of the field. +// Field-based interface with compile-time initialization safety. +// All fields must be explicitly initialized via the validator pattern before offering. +// The validator pattern ensures that you cannot call offer() until all fields have been updated. interface!( interface VehicleField, { Id = "VehicleFieldInterface", 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 7b0903ba6..f2839e148 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 @@ -15,7 +15,10 @@ load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_proc_macro") rust_proc_macro( name = "com-api-concept-macros", - srcs = ["lib.rs"], + srcs = [ + "lib.rs", + "type_state_validator.rs", + ], crate_name = "com_api_concept_macros", visibility = [ "//visibility:public", diff --git a/score/mw/com/impl/rust/com-api/com-api-concept-macros/lib.rs b/score/mw/com/impl/rust/com-api/com-api-concept-macros/lib.rs index f19b5c94c..0721add2e 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept-macros/lib.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept-macros/lib.rs @@ -13,8 +13,11 @@ use proc_macro::TokenStream; use quote::quote; +use syn::spanned::Spanned; use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, Generics, Meta, Type}; +mod type_state_validator; + /// Derive macro for the `CommData` trait. /// /// Implements `CommData` for a struct or C-like enum, providing a stable string identity @@ -335,6 +338,37 @@ fn collect_field_types(data: &Data) -> Result, ()> { Ok(out) } +/// Procedural macro to generate compile-time type-state validator for Field-based producers. +/// +/// This macro generates a validator struct with phantom type parameters that track +/// the initialization state of each field at compile time. The `offer()` method is only +/// available when all fields have been initialized, preventing runtime errors. +/// +/// # Usage +/// +/// Apply this macro alongside the `interface!` macro for Field-based interfaces: +/// +/// ```ignore +/// #[derive(TypeStateFieldValidator)] +/// struct VehicleFieldProducerInternal { +/// #[field_name = "left_tire"] +/// left_tire_field: R::FieldPublisher, +/// #[field_name = "exhaust"] +/// exhaust_field: R::FieldPublisher, +/// } +/// ``` +/// +/// The macro generates: +/// - `Uninit` and `Init` marker types +/// - `ValidatorN<'a, R, Field1State, Field2State, ...>` struct +/// - `update_field_name()` methods that transition states +/// - `update_field_name()` methods for each field +/// - `offer()` method that validates all fields initialized before proceeding +#[proc_macro_derive(TypeStateFieldValidator, attributes(field_name))] +pub fn derive_typestate_field_validator(input: TokenStream) -> TokenStream { + type_state_validator::derive_typestate_field_validator_impl(input) +} + // Use doctest to test failed compilations and successful ones /// ``` diff --git a/score/mw/com/impl/rust/com-api/com-api-concept-macros/type_state_validator.rs b/score/mw/com/impl/rust/com-api/com-api-concept-macros/type_state_validator.rs new file mode 100644 index 000000000..06ca27954 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-concept-macros/type_state_validator.rs @@ -0,0 +1,293 @@ +/******************************************************************************** + * Copyright (c) 2026 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 + ********************************************************************************/ + +use proc_macro::TokenStream; +use quote::quote; +use syn::spanned::Spanned; +use syn::{parse_macro_input, Data, DeriveInput, Fields, Type}; + +/// Procedural macro to generate compile-time type-state validator for Field-based producers. +/// +/// This macro generates a validator struct with phantom type parameters that track +/// both the initialization state AND handler registration state of each field at compile time. +/// The `offer()` method is only available when all fields have been initialized AND +/// all handlers have been registered, preventing runtime errors. +/// +/// # Usage +/// +/// Apply this macro alongside the `interface!` macro for Field-based interfaces: +/// +/// ```ignore +/// #[derive(TypeStateFieldValidator)] +/// struct VehicleFieldProducerInternal { +/// #[field_name = "left_tire"] +/// left_tire_field: R::FieldPublisher, +/// #[field_name = "exhaust"] +/// exhaust_field: R::FieldPublisher, +/// } +/// ``` +/// +/// The macro generates: +/// - `Uninit`/`Init` marker types for update state +/// - `HandlerNotSet`/`HandlerSet` marker types for handler registration state +/// - `ValidatorN<'a, R, S0, S1, ..., H0, H1, ...>` struct with dual type-state tracking +/// - `update_field_name()` methods that transition update state from Uninit -> Init +/// - `register_set_handler_field_name()` methods that transition handler state from HandlerNotSet -> HandlerSet +/// - `offer()` method only available when all fields are Init AND all handlers are HandlerSet +pub fn derive_typestate_field_validator_impl(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = &input.ident; + + // Extract runtime generic parameter + let (runtime_param_name, runtime_param_with_bounds) = + if let Some(param) = input.generics.params.first() { + match param { + syn::GenericParam::Type(type_param) => { + let name = &type_param.ident; + (quote! { #name }, quote! { #param }) + } + _ => (quote! { R }, quote! { R: com_api::Runtime + ?Sized }), + } + } else { + (quote! { R }, quote! { R: com_api::Runtime + ?Sized }) + }; + + let fields = match &input.data { + Data::Struct(data) => match &data.fields { + Fields::Named(fields) => &fields.named, + _ => { + return syn::Error::new_spanned( + name, + "TypeStateFieldValidator only supports structs with named fields", + ) + .to_compile_error() + .into(); + } + }, + _ => { + return syn::Error::new_spanned(name, "TypeStateFieldValidator only supports structs") + .to_compile_error() + .into(); + } + }; + + // Extract field information - use all fields except instance_info + let field_info: Vec<_> = fields + .iter() + .filter_map(|f| { + let ident = f.ident.as_ref()?; + + // Skip instance_info field + if ident == "instance_info" { + return None; + } + + Some(( + ident, // struct field name + ident, // public field name (same as struct field) + &f.ty, // field type + )) + }) + .collect(); + + if field_info.is_empty() { + return syn::Error::new_spanned( + name, + "No fields found for validation (excluding instance_info)", + ) + .to_compile_error() + .into(); + } + + let struct_field_names: Vec<_> = field_info.iter().map(|(sf, _, _)| sf).collect(); + let public_field_names: Vec<_> = field_info.iter().map(|(_, pf, _)| pf).collect(); + let field_types: Vec<_> = field_info.iter().map(|(_, _, ty)| ty).collect(); + + // Extract inner types from R::FieldPublisher -> T + let inner_types: Vec<_> = field_types + .iter() + .map(|ty| { + // Try to extract T from R::FieldPublisher + if let Type::Path(type_path) = ty { + // Look for the last segment which should be FieldPublisher + if let Some(segment) = type_path.path.segments.last() { + if segment.ident == "FieldPublisher" { + // Extract the type argument + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { + return inner_ty; + } + } + } + } + } + // Fallback: use the full type + *ty + }) + .collect(); + + let validator_name = syn::Ident::new(&format!("{}Validator", name), name.span()); + + // Generate type parameters for each field's UPDATE state (S0, S1, S2, ...) + let field_update_state_params: Vec<_> = public_field_names + .iter() + .enumerate() + .map(|(i, _)| syn::Ident::new(&format!("S{}", i), proc_macro::Span::call_site().into())) + .collect(); + + // Generate type parameters for each field's HANDLER state (H0, H1, H2, ...) + let field_handler_state_params: Vec<_> = public_field_names + .iter() + .enumerate() + .map(|(i, _)| syn::Ident::new(&format!("H{}", i), proc_macro::Span::call_site().into())) + .collect(); + + // Generate update methods - each one changes its field's UPDATE state from current to Init + // while preserving HANDLER state + let update_methods = public_field_names + .iter() + .zip(struct_field_names.iter()) + .zip(inner_types.iter()) + .enumerate() + .map(|(i, ((pub_name, struct_name), inner_ty))| { + let update_fn = syn::Ident::new(&format!("update_{}", pub_name), pub_name.span()); + + // Build the "after" UPDATE state parameter list where this field is Init + let after_update_states: Vec<_> = field_update_state_params + .iter() + .enumerate() + .map(|(j, param)| { + if i == j { + quote! { ::com_api::Init } + } else { + quote! { #param } + } + }) + .collect(); + + quote! { + impl<#runtime_param_with_bounds, #(#field_update_state_params),*, #(#field_handler_state_params),*> + #validator_name<#runtime_param_name, #(#field_update_state_params),*, #(#field_handler_state_params),*> + { + pub fn #update_fn( + mut self, + value: &#inner_ty + ) -> com_api::Result<#validator_name<#runtime_param_name, #(#after_update_states),*, #(#field_handler_state_params),*>> + { + self.producer.#struct_name.update(value)?; + Ok(#validator_name { + producer: self.producer, + _phantom: core::marker::PhantomData, + }) + } + } + } + }); + + // Generate register_set_handler methods - each one changes its field's HANDLER state + // from HandlerNotSet to HandlerSet while preserving UPDATE state + let register_handler_methods = public_field_names + .iter() + .zip(struct_field_names.iter()) + .zip(inner_types.iter()) + .enumerate() + .map(|(i, ((pub_name, struct_name), inner_ty))| { + let register_fn = syn::Ident::new( + &format!("register_set_handler_{}", pub_name), + pub_name.span(), + ); + + // Build the "after" HANDLER state parameter list where this field is HandlerSet + let after_handler_states: Vec<_> = field_handler_state_params + .iter() + .enumerate() + .map(|(j, param)| { + if i == j { + quote! { ::com_api::HandlerSet } + } else { + quote! { #param } + } + }) + .collect(); + + quote! { + impl<#runtime_param_with_bounds, #(#field_update_state_params),*, #(#field_handler_state_params),*> + #validator_name<#runtime_param_name, #(#field_update_state_params),*, #(#field_handler_state_params),*> + where + <#runtime_param_name as com_api::Runtime>::FieldPublisher<#inner_ty>: Send, + { + pub fn #register_fn(mut self, handler: F) -> #validator_name<#runtime_param_name, #(#field_update_state_params),*, #(#after_handler_states),*> + where + F: Fn(&#inner_ty) + Send + 'static, + { + self.producer.#struct_name.register_set_handler(handler); + #validator_name { + producer: self.producer, + _phantom: core::marker::PhantomData, + } + } + } + } + }); + + // Generate list of all Init states for the offer() impl + let all_init_states = vec![quote! { ::com_api::Init }; field_update_state_params.len()]; + + // Generate list of all HandlerSet states for the offer() impl + let all_handler_set_states = + vec![quote! { ::com_api::HandlerSet }; field_handler_state_params.len()]; + + // Generate list of all Uninit states for the validator() method + let all_uninit_states = vec![quote! { ::com_api::Uninit }; field_update_state_params.len()]; + + // Generate list of all HandlerNotSet states for the validator() method + let all_handler_not_set_states = + vec![quote! { ::com_api::HandlerNotSet }; field_handler_state_params.len()]; + + let expanded = quote! { + // Validator struct with dual type-state tracking: + // - First set of params (S0, S1, ...) track field UPDATE state (Uninit/Init) + // - Second set of params (H0, H1, ...) track HANDLER registration state (HandlerNotSet/HandlerSet) + pub struct #validator_name<#runtime_param_with_bounds, #(#field_update_state_params),*, #(#field_handler_state_params),*> { + producer: #name<#runtime_param_name>, + _phantom: core::marker::PhantomData<(#(#field_update_state_params,)* #(#field_handler_state_params,)*)>, + } + + // Update methods that change UPDATE state types (Uninit -> Init) + #(#update_methods)* + + // Register set handler methods that change HANDLER state types (HandlerNotSet -> HandlerSet) + #(#register_handler_methods)* + + // offer() is only available when ALL fields are Init AND all handlers are HandlerSet + impl<#runtime_param_with_bounds> #validator_name<#runtime_param_name, #(#all_init_states),*, #(#all_handler_set_states),*> { + pub fn offer(self) -> com_api::Result<<#name<#runtime_param_name> as com_api::Producer<#runtime_param_name>>::OfferedProducer> { + // Extract producer and call its offer() method + use com_api::Producer; + self.producer.offer() + } + } + + // validator() method consumes producer and returns validator with all fields Uninit and all handlers HandlerNotSet + impl<#runtime_param_with_bounds> #name<#runtime_param_name> { + pub fn validator(self) -> #validator_name<#runtime_param_name, #(#all_uninit_states),*, #(#all_handler_not_set_states),*> { + #validator_name { + producer: self, + _phantom: core::marker::PhantomData, + } + } + } + }; + + TokenStream::from(expanded) +} 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 4f2fab963..318a6a8e2 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 @@ -63,6 +63,12 @@ use std::path::Path; /// Result type alias with `std::result::Result` using `com_api::Error` as error type pub type Result = core::result::Result; +pub enum TagValue { + Field, + Method, + Event, +} + /// A factory-like trait for constructing complex objects through a builder pattern. /// /// This trait enables type-safe construction of objects by moving self during the build phase, 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 3a52db48a..278bb06c7 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,22 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +/// Type-state marker for uninitialized field state (compile-time tracking). +#[allow(dead_code)] +pub struct Uninit; + +/// Type-state marker for initialized field state (compile-time tracking). +#[allow(dead_code)] +pub struct Init; + +/// Type-state marker for handler not registered (compile-time tracking). +#[allow(dead_code)] +pub struct HandlerNotSet; + +/// Type-state marker for handler registered (compile-time tracking). +#[allow(dead_code)] +pub struct HandlerSet; + /// Main interface macro that generates Consumer, Producer, and OfferedProducer types /// along with all necessary trait implementations. /// @@ -259,6 +275,7 @@ macro_rules! interface_producer { 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, @@ -273,23 +290,14 @@ macro_rules! interface_producer { }; ($id:ident, $($field_name:ident, Field<$field_type:ty>),+$(,)?) => { - com_api::paste::paste! { + com_api::paste::paste! { + // Producer struct with proc macro validation + #[derive($crate::com_api_concept_macros::TypeStateFieldValidator)] pub struct [<$id Producer>] { $( pub $field_name: R::FieldPublisher<$field_type>, )+ - instance_info: R::ProviderInfo, - } - - impl Clone for [<$id Producer>] { - fn clone(&self) -> Self { - [<$id Producer>] { - $( - $field_name: self.$field_name.clone(), - )+ - instance_info: self.instance_info.clone(), - } - } + pub instance_info: R::ProviderInfo, } pub struct [<$id OfferedProducer>] { @@ -299,21 +307,12 @@ macro_rules! interface_producer { instance_info: R::ProviderInfo, } - impl Clone for [<$id OfferedProducer>] { - fn clone(&self) -> Self { - [<$id OfferedProducer>] { - $( - $field_name: self.$field_name.clone(), - )+ - instance_info: self.instance_info.clone(), - } - } - } - impl com_api::Producer for [<$id Producer>] { type Interface = [<$id Interface>]; type OfferedProducer = [<$id OfferedProducer>]; + fn offer(self) -> com_api::Result { + // Create OfferedProducer from consumed producer let offered = [<$id OfferedProducer>] { $( $field_name: self.$field_name.clone(), @@ -326,7 +325,7 @@ macro_rules! interface_producer { } fn new(instance_info: R::ProviderInfo) -> com_api::Result { - let instance = Self { + Ok(Self { $( $field_name: R::FieldPublisher::new( stringify!($field_name), @@ -337,14 +336,7 @@ macro_rules! interface_producer { )), )+ instance_info, - }; - $( - instance.$field_name.update(&Default::default()).expect(&format!( - "Failed to initialize field {} with default value", - stringify!($field_name) - )); - )+ - Ok(instance) + }) } } @@ -352,6 +344,7 @@ macro_rules! interface_producer { for [<$id OfferedProducer>] { type Interface = [<$id Interface>]; type Producer = [<$id Producer>]; + fn unoffer(self) -> com_api::Result { let producer = [<$id Producer>] { $( @@ -359,7 +352,6 @@ macro_rules! interface_producer { )+ 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-concept/lib.rs b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs index 04927dd38..df25158c4 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 @@ -25,6 +25,10 @@ mod interface_macros; mod reloc; pub use com_api_concept::*; pub use error::*; +// Export type-state markers for compile-time field validation +#[doc(hidden)] +pub use com_api_concept_macros; +pub use interface_macros::{HandlerNotSet, HandlerSet, Init, Uninit}; #[doc(hidden)] pub use paste; 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 db3a36cad..94f1f3eec 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 @@ -141,9 +141,10 @@ pub use com_api_concept::{ interface, interface_common, interface_consumer, interface_producer, Builder, CommData, Consumer, ConsumerBuilder, ConsumerDescriptor, Error, EventSampleMut as SampleMut, FieldMethods, FieldPublisher, FieldSampleMut, FieldSubscriber, FieldSubscription, - FindServiceSpecifier, InstanceSpecifier, Interface, MethodReturnTypePtr, OfferedProducer, - PlacementDefault, Producer, ProducerBuilder, ProviderInfo, Publisher, Reloc, Result, Runtime, - RuntimeBuilder, SampleContainer, SampleMaybeUninit, ServiceDiscovery, Subscriber, Subscription, + FindServiceSpecifier, HandlerNotSet, HandlerSet, Init, InstanceSpecifier, Interface, + MethodReturnTypePtr, OfferedProducer, PlacementDefault, Producer, ProducerBuilder, + ProviderInfo, Publisher, Reloc, Result, Runtime, RuntimeBuilder, SampleContainer, + SampleMaybeUninit, ServiceDiscovery, Subscriber, Subscription, Uninit, }; #[doc(hidden)] From 558674c14c18fe58e84eda0f96475e88c4ed7987 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Fri, 10 Jul 2026 18:24:13 +0530 Subject: [PATCH 12/13] Rust::com added field get set method on subscription trait --- .../basic-consumer-producer.rs | 21 ++++++++----------- .../type_state_validator.rs | 16 +++++++------- .../com-api/com-api-concept/com_api_field.rs | 7 ++----- .../com-api-runtime-lola/field_consumer.rs | 12 +---------- 4 files changed, 20 insertions(+), 36 deletions(-) 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 354966c6c..c67f11bc5 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 @@ -269,15 +269,12 @@ fn create_consumer_field( fn consumer_processing_field(consumer: VehicleFieldConsumer) { //currently we do not have MethodReturnTypePtr implementation for Lola runtime. - //If user did not get the insatnce before the subscribe call then consumer will be consumed. + let _ = consumer.left_tire.get().expect("Failed to get field value"); - // TODO: Field consumer API methods (get/set) not yet fully implemented - // let _ = consumer.left_tire.get().expect("Failed to get field value"); - - // let _ = consumer - // .left_tire - // .set(Tire { pressure: 30.0 }) - // .expect("Failed to set field value"); + let _ = consumer + .left_tire + .set(Tire { pressure: 30.0 }) + .expect("Failed to set field value"); // Subscribe to the field to receive updates let subscription = consumer @@ -299,10 +296,10 @@ fn consumer_processing_field(consumer: VehicleFieldConsumer) { } // TODO: Field subscription API methods (set/get_async) not yet fully implemented - // let _ = subscription - // .set(Tire { pressure: 35.0 }) - // .expect("Failed to set field value"); - // let _ = subscription.get_async().expect("Failed to get field value"); + let _ = subscription + .set(Tire { pressure: 35.0 }) + .expect("Failed to set field value"); + let _ = subscription.get().expect("Failed to get field value"); drop(sample_buf); diff --git a/score/mw/com/impl/rust/com-api/com-api-concept-macros/type_state_validator.rs b/score/mw/com/impl/rust/com-api/com-api-concept-macros/type_state_validator.rs index 06ca27954..a43d47cf1 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept-macros/type_state_validator.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept-macros/type_state_validator.rs @@ -16,10 +16,8 @@ use quote::quote; use syn::spanned::Spanned; use syn::{parse_macro_input, Data, DeriveInput, Fields, Type}; -/// Procedural macro to generate compile-time type-state validator for Field-based producers. -/// -/// This macro generates a validator struct with phantom type parameters that track -/// both the initialization state AND handler registration state of each field at compile time. +/// The macro generates a validator struct with phantom type parameters that track +/// both the initial value update AND handler registration of each field at compile time. /// The `offer()` method is only available when all fields have been initialized AND /// all handlers have been registered, preventing runtime errors. /// @@ -30,10 +28,8 @@ use syn::{parse_macro_input, Data, DeriveInput, Fields, Type}; /// ```ignore /// #[derive(TypeStateFieldValidator)] /// struct VehicleFieldProducerInternal { -/// #[field_name = "left_tire"] -/// left_tire_field: R::FieldPublisher, -/// #[field_name = "exhaust"] -/// exhaust_field: R::FieldPublisher, +/// left_tire: R::FieldPublisher, +/// exhaust: R::FieldPublisher, /// } /// ``` /// @@ -88,6 +84,8 @@ pub fn derive_typestate_field_validator_impl(input: TokenStream) -> TokenStream let ident = f.ident.as_ref()?; // Skip instance_info field + // Note: type name is using here as we have same name in interface_macros + // If that change then this also need to be updated. if ident == "instance_info" { return None; } @@ -121,6 +119,8 @@ pub fn derive_typestate_field_validator_impl(input: TokenStream) -> TokenStream if let Type::Path(type_path) = ty { // Look for the last segment which should be FieldPublisher if let Some(segment) = type_path.path.segments.last() { + //Note: Same here we are using trait name directly + // But if that change then this also need to be updated. if segment.ident == "FieldPublisher" { // Extract the type argument if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs index a8cd97c29..8016ab0aa 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs @@ -40,11 +40,8 @@ pub struct MethodReturnTypePtr { /// `FieldMethods` trait is used to provide the `get` and `set` methods for the field instance which can be used before subscription. /// Event related APIs follow the same restriction for concurrent access. pub trait FieldSubscriber: - com_api_concept::Subscriber> + com_api_concept::Subscriber> + FieldMethods { - type FieldMethodsInstance: FieldMethods; - - fn get_field_method_instance(&self) -> Self::FieldMethodsInstance; } /// FieldSubscriber trait is provides the receiving APIs for the field subscription and @@ -52,7 +49,7 @@ pub trait FieldSubscriber: /// Additional methods which the field subscription provides are added in this trait. /// `FieldMethods` trait is used to provide the `get` and `set` methods for the field subscription. pub trait FieldSubscription: - com_api_concept::Subscription + com_api_concept::Subscription + FieldMethods { /// Returns the number of new samples a call to try_receive (given parameter max_num_samples /// doesn't restrict it) would currently provide. diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs index 09e88a836..d0d029323 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs @@ -37,25 +37,15 @@ pub struct LolaFieldSubscriber { _bridge: PhantomData, } -pub struct LolaFieldMethods { - _data: PhantomData, - _bridge: PhantomData, -} - /// Marker implementation of FieldSubscriber trait. impl FieldSubscriber> for LolaFieldSubscriber { - type FieldMethodsInstance = LolaFieldMethods; - - fn get_field_method_instance(&self) -> Self::FieldMethodsInstance { - todo!() - } } /// Implementation of FieldMethods trait which provides `get` and `set` methods for LolaFieldSubscriber. impl FieldMethods> - for LolaFieldMethods + for LolaFieldSubscriber { fn get(&self) -> Result> { todo!() From 0dd4b02e44df5cf4cbdcde6540772b629eb9a609 Mon Sep 17 00:00:00 2001 From: bharatgoswami Date: Sun, 12 Jul 2026 16:43:05 +0530 Subject: [PATCH 13/13] Rust::com Updated async set method for Field --- .../basic-consumer-producer.rs | 73 +++++++++++++------ .../com-api/com-api-concept/com_api_field.rs | 37 +++++++--- .../com-api-runtime-lola/field_consumer.rs | 29 +++----- .../com-api/com-api-runtime-mock/runtime.rs | 22 +----- .../com/impl/rust/com-api/com-api/com_api.rs | 10 +-- 5 files changed, 93 insertions(+), 78 deletions(-) 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 c67f11bc5..fad5b980a 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 @@ -23,7 +23,7 @@ use clap::Parser; use std::path::PathBuf; use com_api::{ - Builder, FieldMethods, FieldPublisher, FieldSubscription, FindServiceSpecifier, + Builder, FieldPublisher, FieldSubscriber, FieldSubscription, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl, OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, @@ -266,44 +266,75 @@ fn create_consumer_field( .expect("Failed to build consumer instance") } -fn consumer_processing_field(consumer: VehicleFieldConsumer) { - //currently we do not have MethodReturnTypePtr implementation for Lola runtime. +async fn process_subscription_async(subscription: S) +where + S: FieldSubscription, + R: Runtime, +{ + // Get field value asynchronously + match subscription.get().await { + Ok(_method_return) => { + println!("Current tire pressure from spawned task"); + } + Err(e) => eprintln!("Failed to get tire pressure: {:?}", e), + } - let _ = consumer.left_tire.get().expect("Failed to get field value"); + println!("Async subscription processing in spawned task completed"); +} + +fn consumer_processing_field(consumer: VehicleFieldConsumer) +where + <::FieldSubscriber as Subscriber>::Subscription: Send + 'static, +{ + // Field consumer API methods + // But they demonstrate the correct API usage pattern + // TODO: Currently we are not offering the get method async in FieldSubscriber + // because async call will may run in different thread and that will cause the issue in subscription. + let _ = consumer + .left_tire + .get() + .map(|result| println!("Got field value via consumer: {:?}", result)); let _ = consumer .left_tire .set(Tire { pressure: 30.0 }) - .expect("Failed to set field value"); + .map(|result| println!("Set field value via consumer: {:?}", result)); // Subscribe to the field to receive updates let subscription = consumer .left_tire .subscribe(3) .expect("Failed to subscribe to field"); - let mut sample_buf = SampleContainer::new(3); - // Poll for updates (non-blocking) - match subscription.try_receive(&mut sample_buf, 1) { - Ok(n) if n > 0 => { - while let Some(sample) = sample_buf.pop_front() { - println!("Updated tire pressure: {:?}", *sample); + // Create scope for sample_buf to ensure it's dropped before tokio::spawn + { + let mut sample_buf = SampleContainer::new(3); + + // Poll for updates (non-blocking) + match subscription.try_receive(&mut sample_buf, 1) { + Ok(n) if n > 0 => { + while let Some(sample) = sample_buf.pop_front() { + println!("Updated tire pressure: {:?}", *sample); + } + } + _ => { + println!("No new tire pressure updates available"); } } - _ => { - println!("No new tire pressure updates available"); - } + // sample_buf is dropped here at end of scope } - // TODO: Field subscription API methods (set/get_async) not yet fully implemented + // Set via subscription let _ = subscription .set(Tire { pressure: 35.0 }) - .expect("Failed to set field value"); - let _ = subscription.get().expect("Failed to get field value"); - - drop(sample_buf); - - let _ = subscription.unsubscribe(); + .map(|result| println!("Set field value via subscription: {:?}", result)); + + // Spawn async task with subscription + // The subscription is moved into the task + tokio::spawn(async move { + process_subscription_async(subscription).await; + // subscription is automatically unsubscribed when dropped at end of task + }); } // Run the example with the specified runtime diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs index 8016ab0aa..59650524a 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_field.rs @@ -16,6 +16,8 @@ // or by default we will enable for user, // -> My suggestion is we can keep it default enable as of now, // and later we can add tag based mechanism if required because Interface side we need to check how we can do this +// 2. We are offering get method after subscrption async but before subscription it is sync, +// It is because subscribe API take the consumer instance by value and if we offer async get method then it will create issue with subscription. // Note: We are using the event related trait as a base trait for whereever we have same common // APIs or functionality, as of now there are derived from concept crate but @@ -24,11 +26,13 @@ use crate::*; use std::fmt::Debug; +use std::future::Future; #[allow(dead_code)] - // Temp for build test // We will remove this once memory layout of same created in rust side like SamplePtr. +#[repr(C)] +#[derive(Debug)] pub struct MethodReturnTypePtr { pub value: T, pub status: Result<()>, @@ -37,19 +41,34 @@ pub struct MethodReturnTypePtr { /// FieldSubscriber trait is used to subscribe to a field and get the value of the field. /// It provides the `get` and `set` methods to get and set the value of the field. /// It derived from `com_api_concept::Subscriber` trait which provides the `subscribe` method to create a field subscription. -/// `FieldMethods` trait is used to provide the `get` and `set` methods for the field instance which can be used before subscription. +/// The `get` and `set` methods for the field instance can be used before subscription. /// Event related APIs follow the same restriction for concurrent access. pub trait FieldSubscriber: - com_api_concept::Subscriber> + FieldMethods + com_api_concept::Subscriber> { + ///Get the current value of the field. + /// + /// #returns + /// Return the result of `MethodReturnTypePtr` which contains the current value of the field. + fn get(&self) -> Result>; + + ///Set the value of the field. + /// + /// # Parameters + /// * `value` - The value to set for the field. + /// + /// # Returns + /// Return the result of `MethodReturnTypePtr` which contains the status of the set operation. + /// with the current value of the field. + // TODO: should we pass value by reference as underlying API is taking value as reference. + fn set(&self, value: T) -> Result>; } /// FieldSubscriber trait is provides the receiving APIs for the field subscription and /// it is derived from `com_api_concept::Subscription` trait which provides the receiving APIs for the field subscription. /// Additional methods which the field subscription provides are added in this trait. -/// `FieldMethods` trait is used to provide the `get` and `set` methods for the field subscription. pub trait FieldSubscription: - com_api_concept::Subscription + FieldMethods + com_api_concept::Subscription { /// Returns the number of new samples a call to try_receive (given parameter max_num_samples /// doesn't restrict it) would currently provide. @@ -59,16 +78,12 @@ pub trait FieldSubscription: /// Get the number of samples that can still be received by the user of this field. /// This is for checking the capacity of the field subscription and to avoid overflow of the field subscription limit. fn get_free_sample_count(&self) -> Result; -} -/// FieldMethods trait provides the `get` and `set` methods for the field instance which can be used before subscription. -/// The `get` method is used to get the current value of the field, and the `set` method is used to set the value of the field. -pub trait FieldMethods { ///Get the current value of the field. /// /// #returns - /// Return the result of `MethodReturnTypePtr` which contains the current value of the field. - fn get(&self) -> Result>; + /// Return the result of `MethodReturnTypePtr` which contains the current value of the field. /// The returned future is `Send` to allow usage with `tokio::spawn`. + fn get(&self) -> impl Future>> + Send; ///Set the value of the field. /// diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs index d0d029323..024b3eb27 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/field_consumer.rs @@ -23,8 +23,8 @@ use core::todo; use bridge_ffi_rs::FFIBridge; use com_api_concept::{ - CommData, FieldMethods, FieldSubscriber, FieldSubscription, MethodReturnTypePtr, Result, - Runtime, SampleContainer, Subscriber, Subscription, + CommData, FieldSubscriber, FieldSubscription, MethodReturnTypePtr, Result, Runtime, + SampleContainer, Subscriber, Subscription, }; use crate::consumer::Sample; @@ -40,12 +40,6 @@ pub struct LolaFieldSubscriber { /// Marker implementation of FieldSubscriber trait. impl FieldSubscriber> for LolaFieldSubscriber -{ -} - -/// Implementation of FieldMethods trait which provides `get` and `set` methods for LolaFieldSubscriber. -impl FieldMethods> - for LolaFieldSubscriber { fn get(&self) -> Result> { todo!() @@ -86,6 +80,13 @@ impl FieldSubscription> fn get_num_new_samples_available(&self) -> Result { todo!() } + + fn get(&self) -> impl Future>> + Send { + async { todo!() } + } + fn set(&self, _value: T) -> Result> { + todo!() + } } /// Implementation of Subscription trait which provides receiving APIs. @@ -127,15 +128,3 @@ impl Subscription> futures::stream::empty() } } - -/// Implementation of FieldMethods trait which provides `get` and `set` methods for LolaFieldSubscriber. -impl FieldMethods> - for LolaFieldSubscription -{ - fn get(&self) -> Result> { - todo!() - } - fn set(&self, _value: T) -> Result> { - todo!() - } -} diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs index 0bd6f5118..0e6cb0f8c 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-mock/runtime.rs @@ -36,7 +36,7 @@ use std::collections::VecDeque; use std::path::Path; use com_api_concept::{ - Builder, CommData, Consumer, ConsumerBuilder, ConsumerDescriptor, FieldMethods, FieldPublisher, + Builder, CommData, Consumer, ConsumerBuilder, ConsumerDescriptor, FieldPublisher, FieldSampleMut, FieldSubscriber, FieldSubscription, FindServiceSpecifier, InstanceSpecifier, Interface, MethodReturnTypePtr, Producer, ProducerBuilder, ProviderInfo, Result, Runtime, SampleContainer, SampleMaybeUninit as SampleMaybeUninitTrait, ServiceDiscovery, Subscriber, @@ -533,16 +533,6 @@ pub struct MockFieldSubscriber { /// Marker implementation of FieldSubscriber trait. impl FieldSubscriber for MockFieldSubscriber {} -/// Implementation of FieldMethods trait for MockFieldSubscriber. -impl FieldMethods for MockFieldSubscriber { - fn get(&self) -> Result> { - todo!() - } - fn set(&self, _value: T) -> Result> { - todo!() - } -} - /// Implementation of Subscriber trait for MockFieldSubscriber. impl Subscriber for MockFieldSubscriber { type Subscription = MockFieldSubscription; @@ -620,16 +610,6 @@ impl Subscription for MockFieldSubscrip } } -/// Implementation of FieldMethods trait for MockFieldSubscription. -impl FieldMethods for MockFieldSubscription { - fn get(&self) -> Result> { - todo!() - } - fn set(&self, _value: T) -> Result> { - todo!() - } -} - /// Field publisher type for Mock runtime. pub struct MockFieldPublisher { _data: PhantomData, 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 94f1f3eec..c88a392e8 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 @@ -140,11 +140,11 @@ pub use com_api_runtime_lola::RuntimeBuilderImpl as LolaRuntimeBuilderImpl; pub use com_api_concept::{ interface, interface_common, interface_consumer, interface_producer, Builder, CommData, Consumer, ConsumerBuilder, ConsumerDescriptor, Error, EventSampleMut as SampleMut, - FieldMethods, FieldPublisher, FieldSampleMut, FieldSubscriber, FieldSubscription, - FindServiceSpecifier, HandlerNotSet, HandlerSet, Init, InstanceSpecifier, Interface, - MethodReturnTypePtr, OfferedProducer, PlacementDefault, Producer, ProducerBuilder, - ProviderInfo, Publisher, Reloc, Result, Runtime, RuntimeBuilder, SampleContainer, - SampleMaybeUninit, ServiceDiscovery, Subscriber, Subscription, Uninit, + FieldPublisher, FieldSampleMut, FieldSubscriber, FieldSubscription, FindServiceSpecifier, + HandlerNotSet, HandlerSet, Init, InstanceSpecifier, Interface, MethodReturnTypePtr, + OfferedProducer, PlacementDefault, Producer, ProducerBuilder, ProviderInfo, Publisher, Reloc, + Result, Runtime, RuntimeBuilder, SampleContainer, SampleMaybeUninit, ServiceDiscovery, + Subscriber, Subscription, Uninit, }; #[doc(hidden)]