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/// A trait for types that can be serialized to a [`Writer`](embedded_io::Write).
22///
23/// `WireFormat` acts as the base trait for all types that can be serialized
24/// as part of the Simple SOME/IP ecosystem. Decoding is handled by zero-copy
25/// view types (`HeaderView`, `MessageView`, etc.) instead of this trait.
26pub trait WireFormat: Send + Sized + Sync {
27    /// Returns the number of bytes required to serialize this value.
28    fn required_size(&self) -> usize;
29
30    /// Serialize a value to a byte stream.
31    /// Returns the number of bytes written.
32    /// # Errors
33    /// - If the data cannot be written to the stream
34    fn encode<T: embedded_io::Write>(&self, writer: &mut T) -> Result<usize, protocol::Error>;
35
36    /// Encode into a byte slice, returning the number of bytes written.
37    ///
38    /// # Errors
39    /// Returns an error if `buf` is too small (requires at least
40    /// [`required_size()`](Self::required_size) bytes).
41    fn encode_to_slice(&self, buf: &mut [u8]) -> Result<usize, protocol::Error> {
42        // `embedded_io::Write` is implemented for `&mut [u8]` (the writer
43        // advances the slice), so the writer passed to `encode` is a
44        // reborrow of `buf` — named to avoid a `&mut &mut` expression.
45        let mut writer: &mut [u8] = buf;
46        self.encode(&mut writer)
47    }
48
49    /// Encode into a newly allocated `Vec<u8>`.
50    ///
51    /// # Errors
52    /// Returns an error if encoding fails.
53    #[cfg(feature = "std")]
54    fn encode_to_vec(&self) -> Result<std::vec::Vec<u8>, protocol::Error> {
55        let mut buf = std::vec![0u8; self.required_size()];
56        self.encode_to_slice(&mut buf)?;
57        Ok(buf)
58    }
59}
60
61/// A trait for SOME/IP Payload types that can be serialized to a
62/// [`Writer`](embedded_io::Write) and constructed from raw payload bytes.
63///
64/// Note that SOME/IP payloads are not self identifying, so the [Message ID](protocol::MessageId)
65/// must be provided by the caller.
66pub trait PayloadWireFormat: core::fmt::Debug + Send + Sized + Sync {
67    /// The SD header type used by this payload implementation.
68    type SdHeader: WireFormat + Clone + core::fmt::Debug + Eq;
69
70    /// Get the Message ID for the payload
71    fn message_id(&self) -> MessageId;
72    /// Get the payload as a service discovery header
73    fn as_sd_header(&self) -> Option<&Self::SdHeader>;
74    /// Construct a payload from raw bytes and a message ID.
75    /// # Errors
76    /// - If the message ID is not supported
77    /// - If the payload bytes cannot be parsed
78    fn from_payload_bytes(message_id: MessageId, payload: &[u8]) -> Result<Self, protocol::Error>;
79    /// Create a `PayloadWireFormat` from a service discovery [Header](protocol::sd::Header)
80    fn new_sd_payload(header: &Self::SdHeader) -> Self;
81    /// Return the SD flags if this payload is a service discovery message.
82    fn sd_flags(&self) -> Option<Flags>;
83    /// Number of bytes required to write the payload
84    fn required_size(&self) -> usize;
85    /// Serialize the payload to a [Writer](embedded_io::Write)
86    ///
87    /// # Errors
88    ///
89    /// Returns an error if the payload cannot be written to the writer.
90    fn encode<T: embedded_io::Write>(&self, writer: &mut T) -> Result<usize, protocol::Error>;
91
92    /// Construct an SD header for subscribing to an event group.
93    #[allow(clippy::too_many_arguments)]
94    fn new_subscription_sd_header(
95        service_id: u16,
96        instance_id: u16,
97        major_version: u8,
98        ttl: u32,
99        event_group_id: u16,
100        client_ip: core::net::Ipv4Addr,
101        protocol: sd::TransportProtocol,
102        client_port: u16,
103        reboot_flag: sd::RebootFlag,
104    ) -> Self::SdHeader;
105
106    /// Override the reboot flag on an SD header in-place.
107    ///
108    /// Used by `Client::sd_announcements_loop` to refresh the reboot
109    /// flag per-tick from the client's tracked state. Defaults to a
110    /// no-op so payload types that never participate in SD reboot
111    /// tracking (e.g. `RawPayload` for static-only SD use) don't have
112    /// to provide an impl that will never be called.
113    fn set_reboot_flag(_header: &mut Self::SdHeader, _reboot: sd::RebootFlag) {}
114
115    /// Visit each offered / stopped service endpoint in this SD
116    /// payload with `f`.
117    ///
118    /// Visitor pattern (rather than returning a `Vec`) so the trait
119    /// is `no_std`-compatible: the implementation walks its internal
120    /// SD entries and invokes `f` for each `OfferedEndpoint`. The
121    /// `Client` run loop uses this to auto-populate its service
122    /// registry from inbound discovery messages.
123    ///
124    /// The default implementation visits nothing — payload types
125    /// that don't carry SD entries (e.g. application payloads) leave
126    /// it unimplemented; SD-bearing types (e.g. `RawPayload`'s
127    /// `VecSdHeader` payload) override.
128    fn for_each_offered_endpoint<F>(&self, _f: F)
129    where
130        F: FnMut(OfferedEndpoint),
131    {
132    }
133
134    /// Visit `(service_id, instance_id)` for every SD entry in this
135    /// payload, regardless of entry type, with `f`.
136    ///
137    /// Used by the `Client` run loop for per-service-instance
138    /// session/reboot tracking so that all SD traffic (not just
139    /// offers) contributes to reboot detection.
140    ///
141    /// Visitor pattern for the same `no_std` reason as
142    /// [`Self::for_each_offered_endpoint`]; default visits nothing.
143    fn for_each_service_instance<F>(&self, _f: F)
144    where
145        F: FnMut(u16, u16),
146    {
147    }
148
149    /// Convenience accessor returning all offered endpoints as a heap
150    /// `Vec`. Wraps [`Self::for_each_offered_endpoint`] so std users
151    /// get the original ergonomic shape; bare-metal users use the
152    /// visitor directly. Gated on `feature = "std"`.
153    #[cfg(feature = "std")]
154    fn offered_endpoints(&self) -> std::vec::Vec<OfferedEndpoint> {
155        let mut out = std::vec::Vec::new();
156        self.for_each_offered_endpoint(|ep| out.push(ep));
157        out
158    }
159
160    /// Convenience accessor returning all `(service_id, instance_id)`
161    /// pairs as a heap `Vec`. Wraps
162    /// [`Self::for_each_service_instance`] for std users. Gated on
163    /// `feature = "std"`.
164    #[cfg(feature = "std")]
165    fn service_instances(&self) -> std::vec::Vec<(u16, u16)> {
166        let mut out = std::vec::Vec::new();
167        self.for_each_service_instance(|svc, inst| out.push((svc, inst)));
168        out
169    }
170}