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}