diff --git a/src/server/event_publisher.rs b/src/server/event_publisher.rs index 8d05dcf..8a85f20 100644 --- a/src/server/event_publisher.rs +++ b/src/server/event_publisher.rs @@ -7,7 +7,7 @@ use crate::e2e::E2EKey; use crate::protocol::{Header, Message}; use crate::traits::{PayloadWireFormat, WireFormat}; use crate::transport::{E2ERegistryHandle, SocketHandle, TransportSocket}; -#[cfg(test)] +#[cfg(any(feature = "embassy_channels", feature = "server"))] use alloc::sync::Arc; use core::net::SocketAddrV4; use heapless::Vec as HeaplessVec; @@ -451,6 +451,100 @@ where } } +/// Shared handle to the [`EventPublisher`] backing a +/// [`Server`](super::Server). +/// +/// Abstracts how the event publisher is shared between the Server's +/// run loop and any external task that wants to publish events +/// (`server.publisher().publish_event(...)`). Two impls ship out +/// of the box, mirroring the pattern established by +/// [`crate::transport::SocketHandle`] and +/// [`super::SdStateHandle`]: +/// +/// - `Arc>` on alloc-using builds — the +/// default for `Server::new_with_deps` / `new_passive_with_deps`. +/// - `&'static EventPublisher` on bare-metal-no-alloc +/// — caller declares a `static` somewhere and supplies the +/// reference into a future `Server::new_with_handles` +/// constructor. +/// +/// `Clone + 'static` only — neither `Send` nor `Sync` at the trait +/// level. Method-level `where` clauses on Server add Send bounds +/// at use sites that need them (`announcement_loop`'s +/// `+ Send`-bounded return type, etc.). +pub trait EventPublisherHandle: Clone + 'static +where + R: E2ERegistryHandle, + S: SubscriptionHandle, + H: SocketHandle, +{ + /// Borrow the underlying [`EventPublisher`] for read-only + /// access. Used by Server's run loop and accessor. + fn publisher(&self) -> &EventPublisher; +} + +// `&'static EventPublisher<...>`: trivially `Copy + Clone + 'static` +// for any 'static publisher. Caller arranges the static storage. +impl EventPublisherHandle for &'static EventPublisher +where + R: E2ERegistryHandle, + S: SubscriptionHandle, + H: SocketHandle, +{ + fn publisher(&self) -> &EventPublisher { + self + } +} + +#[cfg(any(feature = "embassy_channels", feature = "server"))] +impl EventPublisherHandle for Arc> +where + R: E2ERegistryHandle, + S: SubscriptionHandle, + H: SocketHandle, +{ + fn publisher(&self) -> &EventPublisher { + self + } +} + +/// Extension of [`EventPublisherHandle`] for handles that can be +/// constructed inline from an owned [`EventPublisher`]. +/// +/// Required by `Server` constructors that build the publisher +/// internally (the alloc-using path). The future +/// `Server::new_with_handles` will accept a pre-built `Hep: +/// EventPublisherHandle` directly and won't need this trait — +/// callers using `&'static EventPublisher<...>` declare their +/// `static` storage themselves and pass the reference in. +/// +/// Mirrors the [`crate::transport::WrappableSocketHandle`] / +/// [`super::WrappableSdStateHandle`] split: the basic `Handle` +/// trait gives read access; the `Wrappable*` extension adds the +/// inline-construction path that requires an allocator. +pub trait WrappableEventPublisherHandle: EventPublisherHandle +where + R: E2ERegistryHandle, + S: SubscriptionHandle, + H: SocketHandle, +{ + /// Place an owned [`EventPublisher`] behind this handle's + /// shared storage. + fn wrap(publisher: EventPublisher) -> Self; +} + +#[cfg(any(feature = "embassy_channels", feature = "server"))] +impl WrappableEventPublisherHandle for Arc> +where + R: E2ERegistryHandle, + S: SubscriptionHandle, + H: SocketHandle, +{ + fn wrap(publisher: EventPublisher) -> Self { + Arc::new(publisher) + } +} + #[cfg(all(test, feature = "server-tokio"))] mod tests { use super::*; diff --git a/src/server/mod.rs b/src/server/mod.rs index f77d40e..ae9bb2d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -13,7 +13,7 @@ mod service_info; mod subscription_manager; pub use error::Error; -pub use event_publisher::EventPublisher; +pub use event_publisher::{EventPublisher, EventPublisherHandle, WrappableEventPublisherHandle}; pub use service_info::Subscriber; #[cfg(feature = "std")] pub use service_info::{EventGroupInfo, ServiceInfo}; @@ -143,8 +143,15 @@ where /// these as `Arc>` / `Arc>` /// / `TokioTransport` / `TokioTimer`. Bare-metal callers use /// [`Self::new_with_deps`] (under `server`) and supply their own. -pub struct Server::Socket>, Hsd = Arc> -where +pub struct Server< + R, + S, + F, + Tm, + H = Arc<::Socket>, + Hsd = Arc, + Hep = Arc>, +> where R: E2ERegistryHandle, S: SubscriptionHandle, F: TransportFactory + 'static, @@ -152,6 +159,7 @@ where Tm: Timer + Clone + 'static, H: SocketHandle, Hsd: SdStateHandle, + Hep: EventPublisherHandle, { config: ServerConfig, /// Socket for receiving subscription requests, behind whatever @@ -163,8 +171,10 @@ where sd_socket: H, /// Subscription manager subscriptions: S, - /// Event publisher - publisher: Arc>, + /// Event publisher, behind whatever shared-storage `Hep` chose + /// (`Arc>` on std, + /// `&'static EventPublisher` on bare-metal-no-alloc). + publisher: Hep, /// SD session-ID counter and announcement emitter, behind whatever /// shared-storage `Hsd` chose (`Arc` on std, /// `&'static SdStateManager` on bare-metal-no-alloc). @@ -282,7 +292,7 @@ impl } } -impl Server +impl Server where R: E2ERegistryHandle, S: SubscriptionHandle, @@ -291,6 +301,7 @@ where Tm: Timer + Clone + 'static, H: WrappableSocketHandle, Hsd: WrappableSdStateHandle, + Hep: WrappableEventPublisherHandle, { /// Bare-metal-friendly constructor that takes every dependency /// explicitly via a [`ServerDeps`] bundle. The `server-tokio` @@ -358,7 +369,7 @@ where sd::MULTICAST_IP ); - let publisher = Arc::new(EventPublisher::new( + let publisher = Hep::wrap(EventPublisher::new( subscriptions.clone(), unicast_socket.clone(), e2e_registry.clone(), @@ -428,7 +439,7 @@ where sd_placeholder_addr ); - let publisher = Arc::new(EventPublisher::new( + let publisher = Hep::wrap(EventPublisher::new( subscriptions.clone(), unicast_socket.clone(), e2e_registry.clone(), @@ -450,7 +461,7 @@ where } } -impl Server +impl Server where R: E2ERegistryHandle, S: SubscriptionHandle, @@ -459,6 +470,7 @@ where Tm: Timer + Clone + 'static, H: SocketHandle, Hsd: SdStateHandle, + Hep: EventPublisherHandle, { /// Build the periodic-SD-announcement future. /// @@ -694,10 +706,14 @@ where Ok(()) } - /// Get the event publisher for sending events + /// Get a clone of the event-publisher handle for sending events. + /// + /// Returns the [`EventPublisherHandle`] type — `Arc>` for std users (the default `Hep`), + /// `&'static EventPublisher` for bare-metal-no-alloc. #[must_use] - pub fn publisher(&self) -> Arc> { - Arc::clone(&self.publisher) + pub fn publisher(&self) -> Hep { + self.publisher.clone() } /// Get the local address of the unicast socket. @@ -1219,7 +1235,7 @@ fn extract_subscriber_endpoint( } } -impl Server +impl Server where R: E2ERegistryHandle, S: SubscriptionHandle, @@ -1228,6 +1244,7 @@ where Tm: Timer + Clone + 'static, H: SocketHandle, Hsd: SdStateHandle, + Hep: EventPublisherHandle, { /// Send `SubscribeAck` from an entry view async fn send_subscribe_ack_from_view(