Skip to main content

simple_someip/
traits.rs

1use crate::protocol::sd;
2use crate::protocol::{self, MessageId, sd::Flags};
3
4/// Information about a service endpoint extracted from an SD message.
5pub struct OfferedEndpoint {
6    /// The SOME/IP service ID.
7    pub service_id: u16,
8    /// The SOME/IP instance ID.
9    pub instance_id: u16,
10    /// The major version of the offered service interface.
11    pub major_version: u8,
12    /// The minor version of the offered service interface.
13    pub minor_version: u32,
14    /// The full endpoint (IPv4 socket + transport protocol) extracted
15    /// from the SD options, if present.
16    pub endpoint: Option<crate::NetEndpoint>,
17    /// `true` for `OfferService`, `false` for `StopOfferService`.
18    pub is_offer: bool,
19}
20
21/// Crate-local conveniences over any codec [`Encode`](automotive_wire_codec::Encode) type.
22///
23/// The codec's `Encode` trait provides `encode`, `encoded_size`, and
24/// `encode_to_slice`; this extension adds the heap-allocating
25/// `encode_to_vec` helper for `std` builds. It is blanket-implemented for
26/// every `Encode` type, so bringing it into scope makes `encode_to_vec`
27/// available anywhere.
28pub trait EncodeExt: automotive_wire_codec::Encode {
29    /// Encode into a newly allocated `Vec<u8>`.
30    ///
31    /// # Errors
32    /// Returns an error if encoding fails.
33    #[cfg(feature = "std")]
34    fn encode_to_vec(&self) -> Result<std::vec::Vec<u8>, Self::Error> {
35        let mut buf = std::vec![0u8; self.encoded_size()?];
36        let mut cursor: &mut [u8] = &mut buf;
37        self.encode(&mut cursor)?;
38        Ok(buf)
39    }
40}
41
42impl<T: automotive_wire_codec::Encode> EncodeExt for T {}
43
44/// A trait for SOME/IP Payload types that can be serialized to a
45/// [`Writer`](embedded_io::Write) and constructed from raw payload bytes.
46///
47/// The encode side is provided by the [`Encode`](automotive_wire_codec::Encode)
48/// supertrait (`encoded_size` + `encode`); implementors get `encode_to_slice`
49/// and — under `std` — the crate's [`EncodeExt`]`::encode_to_vec` for free.
50///
51/// Note that SOME/IP payloads are not self identifying, so the [Message ID](protocol::MessageId)
52/// must be provided by the caller: `Encode` alone cannot reconstruct a payload
53/// from bytes, which is why [`from_payload_bytes`](Self::from_payload_bytes)
54/// remains an inherent requirement.
55pub trait PayloadWireFormat:
56    automotive_wire_codec::Encode<Error = protocol::Error> + core::fmt::Debug + Send + Sized + Sync
57{
58    /// The SD header type used by this payload implementation.
59    // `Send + Sync` used to come for free from this trait's predecessor's own
60    // `Send + Sync` supertrait (removed in the codec migration). The codec's
61    // `Encode` has no such supertrait, but the client's channel-carried types
62    // (`DiscoveryMessage`,
63    // `ClientUpdate`, `ControlMessage`) embed `SdHeader` and flow through
64    // `Send`-bounded channels, so the bound is pervasive rather than
65    // localized. Restate it here on the associated type (all concrete
66    // `SdHeader` types — `VecSdHeader`, `HeaplessSdHeader`, `sd::Header<'a>`,
67    // and the test header — are plain owned/borrowed structs that are auto
68    // `Send + Sync`), instead of threading a `where` clause through every
69    // client type definition and impl.
70    // `Error = protocol::Error` matches the bound this trait already puts on
71    // `Self`. Without it the associated error type is opaque, which is what
72    // pushed `Message::new_sd` into swallowing a failed `encoded_size` with
73    // `unwrap_or(0)` -- it could not name the error to propagate it. Every
74    // concrete `SdHeader` already uses `protocol::Error`, so this costs
75    // nothing in tree and closes the hole for downstream impls.
76    type SdHeader: automotive_wire_codec::Encode<Error = protocol::Error>
77        + Clone
78        + core::fmt::Debug
79        + Eq
80        + Send
81        + Sync;
82
83    /// Get the Message ID for the payload
84    fn message_id(&self) -> MessageId;
85    /// Get the payload as a service discovery header
86    fn as_sd_header(&self) -> Option<&Self::SdHeader>;
87    /// Construct a payload from raw bytes and a message ID.
88    /// # Errors
89    /// - If the message ID is not supported
90    /// - If the payload bytes cannot be parsed
91    fn from_payload_bytes(message_id: MessageId, payload: &[u8]) -> Result<Self, protocol::Error>;
92    /// Create a `PayloadWireFormat` from a service discovery [Header](protocol::sd::Header)
93    fn new_sd_payload(header: &Self::SdHeader) -> Self;
94    /// Return the SD flags if this payload is a service discovery message.
95    fn sd_flags(&self) -> Option<Flags>;
96
97    /// Construct an SD header for subscribing to an event group.
98    #[allow(clippy::too_many_arguments)]
99    fn new_subscription_sd_header(
100        service_id: u16,
101        instance_id: u16,
102        major_version: u8,
103        ttl: u32,
104        event_group_id: u16,
105        client_ip: core::net::Ipv4Addr,
106        protocol: sd::TransportProtocol,
107        client_port: u16,
108        reboot_flag: sd::RebootFlag,
109    ) -> Self::SdHeader;
110
111    /// Override the reboot flag on an SD header in-place.
112    ///
113    /// Used by `Client::sd_announcements_loop` to refresh the reboot
114    /// flag per-tick from the client's tracked state. Defaults to a
115    /// no-op so payload types that never participate in SD reboot
116    /// tracking (e.g. `RawPayload` for static-only SD use) don't have
117    /// to provide an impl that will never be called.
118    fn set_reboot_flag(_header: &mut Self::SdHeader, _reboot: sd::RebootFlag) {}
119
120    /// Visit each offered / stopped service endpoint in this SD
121    /// payload with `f`.
122    ///
123    /// Visitor pattern (rather than returning a `Vec`) so the trait
124    /// is `no_std`-compatible: the implementation walks its internal
125    /// SD entries and invokes `f` for each `OfferedEndpoint`. The
126    /// `Client` run loop uses this to auto-populate its service
127    /// registry from inbound discovery messages.
128    ///
129    /// The default implementation visits nothing — payload types
130    /// that don't carry SD entries (e.g. application payloads) leave
131    /// it unimplemented; SD-bearing types (e.g. `RawPayload`'s
132    /// `VecSdHeader` payload) override.
133    fn for_each_offered_endpoint<F>(&self, _f: F)
134    where
135        F: FnMut(OfferedEndpoint),
136    {
137    }
138
139    /// Visit `(service_id, instance_id)` for every SD entry in this
140    /// payload, regardless of entry type, with `f`.
141    ///
142    /// Used by the `Client` run loop for per-service-instance
143    /// session/reboot tracking so that all SD traffic (not just
144    /// offers) contributes to reboot detection.
145    ///
146    /// Visitor pattern for the same `no_std` reason as
147    /// [`Self::for_each_offered_endpoint`]; default visits nothing.
148    fn for_each_service_instance<F>(&self, _f: F)
149    where
150        F: FnMut(u16, u16),
151    {
152    }
153
154    /// Convenience accessor returning all offered endpoints as a heap
155    /// `Vec`. Wraps [`Self::for_each_offered_endpoint`] so std users
156    /// get the original ergonomic shape; bare-metal users use the
157    /// visitor directly. Gated on `feature = "std"`.
158    #[cfg(feature = "std")]
159    fn offered_endpoints(&self) -> std::vec::Vec<OfferedEndpoint> {
160        let mut out = std::vec::Vec::new();
161        self.for_each_offered_endpoint(|ep| out.push(ep));
162        out
163    }
164
165    /// Convenience accessor returning all `(service_id, instance_id)`
166    /// pairs as a heap `Vec`. Wraps
167    /// [`Self::for_each_service_instance`] for std users. Gated on
168    /// `feature = "std"`.
169    #[cfg(feature = "std")]
170    fn service_instances(&self) -> std::vec::Vec<(u16, u16)> {
171        let mut out = std::vec::Vec::new();
172        self.for_each_service_instance(|svc, inst| out.push((svc, inst)));
173        out
174    }
175}