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