async_coap/send_desc/mod.rs
1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16//! # Send Descriptors
17//!
18//! *Send Descriptors* are types that implement [`SendDesc`] that can be passed to the `send*`
19//! methods of [`LocalEndpoint`] and [`RemoteEndpoint`]. They define almost every aspect of how
20//! a message transaction is handled.
21//!
22//! Typical usage of this crate does not require writing implementing [`SendDesc`] by hand,
23//! although you could certainly do so if needed.
24//! Instead, `SyncDesc` instances are easily constructed using *combinators*.
25//!
26//! ## Example
27//!
28//! Here we create a `SendDesc` instance that just sends a GET request and waits for a response:
29//!
30//! ```
31//! # #![feature(async_await)]
32//! # use std::sync::Arc;
33//! # use futures::{prelude::*,executor::LocalPool,task::LocalSpawnExt};
34//! # use async_coap::prelude::*;
35//! # use async_coap::datagram::{DatagramLocalEndpoint, AllowStdUdpSocket, LoopbackSocket};
36//! # use async_coap::null::NullLocalEndpoint;
37//! # let socket = AllowStdUdpSocket::bind("[::]:0").expect("UDP bind failed");
38//! # let local_endpoint = Arc::new(DatagramLocalEndpoint::new(socket));
39//! # let mut pool = LocalPool::new();
40//! # pool.spawner().spawn_local(local_endpoint.clone().receive_loop_arc(null_receiver!()).map(|_|unreachable!()));
41//! # let future = async move {
42//! #
43//! let mut remote_endpoint = local_endpoint
44//! .remote_endpoint_from_uri(uri!("coap://coap.me:5683/test"))
45//! .expect("Remote endpoint lookup failed");
46//!
47//! let future = remote_endpoint.send(CoapRequest::get());
48//!
49//! assert_eq!(future.await, Ok(()));
50//! #
51//! #
52//! # };
53//! # pool.run_until(future);
54//! ```
55//!
56//! That `SendDesc` was perhaps a little *too* simple: it doesn't even interpret the results,
57//! returning `Ok(())` for any message responding with a `2.05 Content` message!
58//!
59//! By using the combinator `.emit_successful_response()`, we can have our `SendDesc` return
60//! an owned copy of the message it received ([`OwnedImmutableMessage`](crate::message::OwnedImmutableMessage)):
61//!
62//! ```
63//! # #![feature(async_await)]
64//! # use std::sync::Arc;
65//! # use futures::{prelude::*,executor::LocalPool,task::LocalSpawnExt};
66//! # use async_coap::prelude::*;
67//! # use async_coap::datagram::{DatagramLocalEndpoint, AllowStdUdpSocket, LoopbackSocket};
68//! # let socket = AllowStdUdpSocket::bind("[::]:0").expect("UDP bind failed");
69//! # let local_endpoint = Arc::new(DatagramLocalEndpoint::new(socket));
70//! # let mut pool = LocalPool::new();
71//! # pool.spawner().spawn_local(local_endpoint.clone().receive_loop_arc(null_receiver!()).map(|_|unreachable!()));
72//! # let future = async move {
73//! # use async_coap::message::OwnedImmutableMessage;
74//! # let mut remote_endpoint = local_endpoint
75//! # .remote_endpoint_from_uri(uri!("coap://coap.me:5683/test"))
76//! # .expect("Remote endpoint lookup failed");
77//! #
78//! #
79//! let send_desc = CoapRequest::get().emit_successful_response();
80//!
81//! let future = remote_endpoint.send(send_desc);
82//!
83//! let message = future.await.expect("Request failed");
84//!
85//! println!("Got reply: {:?}", message);
86//! #
87//! #
88//! # };
89//! # pool.run_until(future);
90//! ```
91//!
92//! What if we wanted the response in JSON? What if it was really large and we
93//! knew we would need to do a block2 transfer? We can do that easily:
94//!
95//! ```ignore
96//! let send_desc = CoapRequest::get()
97//! .accept(ContentFormat::APPLICATION_JSON)
98//! .block2(None)
99//! .emit_successful_collected_response();
100//!
101//! // Here we are specifying that we want to send the request to a specific
102//! // path on the remote endpoint, `/large` in this case.
103//! let future = remote_endpoint.send_to(rel_ref!("/large"), send_desc);
104//!
105//! let message = future.await.expect("Request failed");
106//!
107//! println!("Got reply: {:?}", message);
108//! ```
109//!
110//! But if this is a large amount of data, we won't get any indication about the transfer
111//! until it is done. What if we wanted to add some printouts about the status?
112//!
113//! ```ignore
114//! let send_desc = CoapRequest::get()
115//! .accept(ContentFormat::APPLICATION_JSON)
116//! .block2(None)
117//! .emit_successful_collected_response()
118//! .inspect(|context| {
119//! let addr = context.remote_address();
120//! let msg = context.message();
121//!
122//! // Print out each individual block message received.
123//! println!("Got {:?} from {}", msg, addr);
124//! });
125//!
126//! let future = remote_endpoint.send_to(rel_ref!("/large"), send_desc);
127//!
128//! let message = future.await.expect("Request failed");
129//!
130//! println!("Got reply: {:?}", message);
131//! ```
132//!
133//! There are [many more combinators][SendDescExt] for doing all sorts of things, such as
134//! adding additional options and [block2 message aggregation](SendDescUnicast::block2).
135
136use super::*;
137
138mod request;
139pub use request::*;
140
141mod observe;
142pub use observe::*;
143
144mod unicast_block2;
145pub use unicast_block2::*;
146
147mod handler;
148pub use handler::*;
149
150mod inspect;
151pub use inspect::*;
152
153mod payload;
154pub use payload::*;
155
156mod ping;
157pub use ping::Ping;
158
159mod add_option;
160pub use add_option::*;
161
162mod nonconfirmable;
163pub use nonconfirmable::*;
164
165mod multicast;
166pub use multicast::*;
167
168mod emit;
169pub use emit::*;
170
171mod include_socket_addr;
172pub use include_socket_addr::*;
173
174mod uri_host_path;
175pub use uri_host_path::UriHostPath;
176
177use std::iter::{once, Once};
178use std::marker::PhantomData;
179use std::ops::Bound;
180use std::time::Duration;
181
182/// # Send Descriptor Trait
183///
184/// Types implementing this trait can be passed to the `send*` methods of [`LocalEndpoint`]
185/// and [`RemoteEndpoint`], and can define almost every aspect of how a message transaction
186/// is handled.
187///
188/// See the [module level documentation](index.html) for more information on typical usage
189/// patterns.
190///
191/// ## Internals
192///
193/// There are several methods in this trait, but three of them are critical:
194///
195/// * [`write_options`](SendDesc::write_options)\: Defines which options are going to be
196/// included in the outbound message.
197/// * [`write_payload`](SendDesc::write_payload)\: Defines the contents of the payload for the
198/// outbound message.
199/// * [`handler`](SendDesc::handler)\: Handles inbound reply messages, as well as error conditions.
200///
201pub trait SendDesc<IC, R = (), TP = StandardCoapConstants>: Send
202where
203 IC: InboundContext,
204 R: Send,
205 TP: TransParams,
206{
207 /// **Experimental**: Gets custom transmission parameters.
208 fn trans_params(&self) -> Option<TP> {
209 None
210 }
211
212 /// **Experimental**: Used for determining if the given option seen in the reply message
213 /// is supported or not.
214 ///
215 /// Response messages with any options that cause this
216 /// method to return false will be rejected.
217 ///
218 fn supports_option(&self, option: OptionNumber) -> bool {
219 !option.is_critical()
220 }
221
222 /// Calculates the duration of the delay to wait before sending the next retransmission.
223 ///
224 /// If `None` is returned, then no further retransmissions will be attempted.
225 fn delay_to_retransmit(&self, retransmits_sent: u32) -> Option<Duration> {
226 if retransmits_sent > TP::COAP_MAX_RETRANSMIT {
227 return None;
228 }
229
230 let ret = (TP::COAP_ACK_TIMEOUT.as_millis() as u64) << retransmits_sent as u64;
231
232 const JDIV: u64 = 512u64;
233 let rmod: u64 = (JDIV as f32 * (TP::COAP_ACK_RANDOM_FACTOR - 1.0)) as u64;
234 let jmul = JDIV + rand::random::<u64>() % rmod;
235
236 Some(Duration::from_millis(ret * jmul / JDIV))
237 }
238
239 /// The delay to wait between when we have received a successful response and when
240 /// we should send out another request.
241 ///
242 /// The new request will have a new msg_id, but
243 /// the same token. The retransmission counter will be reset to zero.
244 ///
245 /// This mechanism is currently used exclusively for CoAP observing.
246 ///
247 /// The default return value is `None`, indicating that there are to be no message
248 /// restarts.
249 fn delay_to_restart(&self) -> Option<Duration> {
250 None
251 }
252
253 /// The maximum time to wait for an asynchronous response after having received an ACK.
254 fn max_rtt(&self) -> Duration {
255 TP::COAP_MAX_RTT
256 }
257
258 /// the maximum time from the first transmission of a Confirmable message to the time when
259 /// the sender gives up on receiving an acknowledgement or reset.
260 fn transmit_wait_duration(&self) -> Duration {
261 TP::COAP_MAX_TRANSMIT_WAIT
262 }
263
264 /// Defines which options are going to be included in the outbound message.
265 ///
266 /// Writes all options in the given range to `msg`.
267 fn write_options(
268 &self,
269 msg: &mut dyn OptionInsert,
270 socket_addr: &IC::SocketAddr,
271 start: Bound<OptionNumber>,
272 end: Bound<OptionNumber>,
273 ) -> Result<(), Error>;
274
275 /// Generates the outbound message by making calls into `msg`.
276 fn write_payload(
277 &self,
278 msg: &mut dyn MessageWrite,
279 socket_addr: &IC::SocketAddr,
280 ) -> Result<(), Error>;
281
282 /// Handles the response to the outbound message.
283 fn handler(&mut self, context: Result<&IC, Error>) -> Result<ResponseStatus<R>, Error>;
284}
285
286/// Marker trait for identifying that this `SendDesc` is for *unicast* requests.
287/// Also contains unicast-specific combinators, such as [`block2()`][SendDescUnicast::block2].
288pub trait SendDescUnicast {
289 /// Returns a send descriptor that will perform Block2 processing.
290 ///
291 /// Note that just adding this to your send descriptor chain alone is unlikely to do what
292 /// you want. You've got three options:
293 ///
294 /// * Add a call to [`emit_successful_collected_response`][UnicastBlock2::emit_successful_collected_response]
295 /// immediately after the call to this method. This will cause the message to be reconstructed from the blocks
296 /// and returned as a value from the future from `send`. You can optionally add an
297 /// [`inspect`][SendDescExt::inspect] combinator to get some feedback as the message is being
298 /// reconstructed from all of the individual block messages.
299 /// * Add a call to [`emit_successful_response`][SendDescExt::emit_successful_response] along
300 /// with using `send_to_stream` instead of `send`. This will give you a `Stream` that will
301 /// contain all of the individual block messages in the stream.
302 /// * [Add your own handler][SendDescExt::use_handler] to do whatever you need to do, returning
303 /// `ResponseStatus::SendNext` until all of the blocks have been received. This is
304 /// useful if you want to avoid memory allocation.
305 ///
306 /// There may be other valid combinations of combinators, depending on what you are trying
307 /// to do.
308 fn block2<IC, R, TP>(self, block2: Option<BlockInfo>) -> UnicastBlock2<Self, IC>
309 where
310 IC: InboundContext,
311 R: Send,
312 TP: TransParams,
313 Self: SendDesc<IC, R, TP> + Sized,
314 {
315 UnicastBlock2::new(self, block2)
316 }
317}
318
319/// Marker trait for identifying that this `SendDesc` is for *multicast* requests.
320/// Also contains multicast-specific extensions.
321pub trait SendDescMulticast {}
322
323/// Combinator extension trait for Send Descriptors.
324pub trait SendDescExt<IC, R, TP>: SendDesc<IC, R, TP> + Sized
325where
326 IC: InboundContext,
327 R: Send,
328 TP: TransParams,
329{
330 /// Adds zero or more instances of the option `key`, using values coming from `viter`.
331 ///
332 /// This method allows you to conditionally add options to a send descriptor. For example,
333 /// you could convert an `Option` to an iterator (using `into_iterator()`) and pass it to
334 /// this method: if the `Option` is `None` then no coap option will be added.
335 fn add_option_iter<K, I>(self, key: OptionKey<K>, viter: I) -> AddOption<Self, K, I, IC>
336 where
337 I: IntoIterator<Item = K> + Send + Clone,
338 K: Send + Clone,
339 {
340 AddOption {
341 inner: self,
342 key,
343 viter,
344 phantom: PhantomData,
345 }
346 }
347
348 /// Adds one instance of the option `key` with a value of `value`.
349 fn add_option<K>(self, key: OptionKey<K>, value: K) -> AddOption<Self, K, Once<K>, IC>
350 where
351 K: Send + Clone,
352 {
353 self.add_option_iter(key, once(value))
354 }
355
356 /// Adds an Accept option with the given `ContentFormat`.
357 fn accept(
358 self,
359 accept: ContentFormat,
360 ) -> AddOption<Self, ContentFormat, Once<ContentFormat>, IC> {
361 self.add_option(option::ACCEPT, accept)
362 }
363
364 /// Adds an Content-Format option with the given `ContentFormat`.
365 fn content_format(
366 self,
367 content_format: ContentFormat,
368 ) -> AddOption<Self, ContentFormat, Once<ContentFormat>, IC> {
369 self.add_option(option::CONTENT_FORMAT, content_format)
370 }
371
372 /// Adds a handler function to be called when a response message has been received (or when
373 /// an error has occurred).
374 fn use_handler<F, FR>(self, handler: F) -> Handler<Self, F>
375 where
376 F: FnMut(
377 Result<&dyn InboundContext<SocketAddr = IC::SocketAddr>, Error>,
378 ) -> Result<ResponseStatus<FR>, Error>
379 + Send,
380 FR: Send,
381 {
382 Handler {
383 inner: self,
384 handler,
385 }
386 }
387
388 /// Updates the send descriptor chain to emit any received message as a result, even
389 /// if that message has a message code that indicates an error.
390 fn emit_any_response(self) -> EmitAnyResponse<Self> {
391 EmitAnyResponse::new(self)
392 }
393
394 /// Updates the send descriptor chain to emit received message as a result, but only
395 /// if that message has a message code that indicates success.
396 fn emit_successful_response(self) -> EmitSuccessfulResponse<Self> {
397 EmitSuccessfulResponse::new(self)
398 }
399
400 /// Updates the send descriptor chain to emit only the message code of the received
401 /// response.
402 fn emit_msg_code(self) -> EmitMsgCode<Self> {
403 EmitMsgCode::new(self)
404 }
405
406 /// Updates the send descriptor chain to also emit the SocketAddr of the sender
407 /// of the response, resulting in tuple return type.
408 ///
409 /// This is useful for handling responses to a multicast request.
410 fn include_socket_addr(self) -> IncludeSocketAddr<Self> {
411 IncludeSocketAddr::new(self)
412 }
413
414 /// Adds an inspection closure that will be called for each received response message.
415 ///
416 /// The inspector closure will not be called if no responses are received, and it cannot
417 /// change the behavior of the send descriptor chain. If you need either of those
418 /// behaviors, see [`SendDescExt::use_handler`].
419 fn inspect<F>(self, inspect: F) -> Inspect<Self, F>
420 where
421 F: FnMut(&dyn InboundContext<SocketAddr = IC::SocketAddr>) + Send,
422 {
423 Inspect {
424 inner: self,
425 inspect,
426 }
427 }
428
429 /// Adds a closure that writes to the payload of the outbound message.
430 fn payload_writer<F>(self, writer: F) -> PayloadWriter<Self, F>
431 where
432 F: Fn(&mut dyn MessageWrite) -> Result<(), Error> + Send,
433 {
434 PayloadWriter {
435 inner: self,
436 writer,
437 }
438 }
439
440 /// Allows you to specify the URI_HOST, URI_PATH, and URI_QUERY option values
441 /// in a more convenient way than using `add_option_iter` manually.
442 fn uri_host_path<T: Into<RelRefBuf>>(
443 self,
444 host: Option<String>,
445 uri_path: T,
446 ) -> UriHostPath<Self, IC> {
447 UriHostPath {
448 inner: self,
449 host,
450 path_and_query: uri_path.into(),
451 phantom: PhantomData,
452 }
453 }
454}
455
456/// Blanket implementation of `SendDescExt` for all types implementing `SendDesc`.
457impl<T, IC, R, TP> SendDescExt<IC, R, TP> for T
458where
459 T: SendDesc<IC, R, TP>,
460 IC: InboundContext,
461 R: Send,
462 TP: TransParams,
463{
464}
465
466/// Helper macro that assists with writing correct implementations of [`SendDesc::write_options`].
467///
468/// ## Example
469///
470/// ```
471/// # use async_coap::uri::RelRefBuf;
472/// # use std::marker::PhantomData;
473/// # use async_coap::send_desc::SendDesc;
474/// # use async_coap::prelude::*;
475/// # use async_coap::write_options;
476/// # use async_coap::{InboundContext, Error, message::MessageWrite};
477/// # use std::ops::Bound;
478/// # pub struct WriteOptionsExample<IC>(PhantomData<IC>);
479/// # impl<IC: InboundContext> SendDesc<IC, ()> for WriteOptionsExample<IC> {
480/// #
481/// fn write_options(
482/// &self,
483/// msg: &mut dyn OptionInsert,
484/// socket_addr: &IC::SocketAddr,
485/// start: Bound<OptionNumber>,
486/// end: Bound<OptionNumber>,
487/// ) -> Result<(), Error> {
488/// write_options!((msg, socket_addr, start, end) {
489/// // Note that the options **MUST** be listed **in numerical order**,
490/// // otherwise the behavior will be undefined!
491/// URI_HOST => Some("example.com").into_iter(),
492/// URI_PORT => Some(1234).into_iter(),
493/// URI_PATH => vec!["a","b","c"].into_iter(),
494/// })
495/// }
496/// #
497/// # fn write_payload(&self,msg: &mut dyn MessageWrite, socket_addr: &IC::SocketAddr) -> Result<(), Error> {
498/// # Ok(())
499/// # }
500/// # fn handler(&mut self,context: Result<&IC, Error>) -> Result<ResponseStatus<()>, Error> {
501/// # context.map(|_| ResponseStatus::Done(()))
502/// # }
503/// # }
504/// ```
505#[macro_export]
506macro_rules! write_options {
507 (($msg:expr, $socket_addr:expr, $start:expr, $end:expr, $inner:expr) { $($key:expr => $viter:expr),* }) => {{
508 let mut start = $start;
509 let end = $end;
510 let inner = &$inner;
511 let msg = $msg;
512 let socket_addr = $socket_addr;
513 #[allow(unused)]
514 use $crate::option::*;
515 #[allow(unused)]
516 use std::iter::once;
517
518 $( write_options!(_internal $key, $viter, start, end, msg, socket_addr, inner); )*
519
520 inner.write_options(msg, socket_addr, start, end)
521 }};
522
523 (($msg:expr, $socket_addr:expr, $start:expr, $end:expr) { $($key:expr => $viter:expr),* }) => {{
524 let mut start = $start;
525 let end = $end;
526 let msg = $msg;
527 let _socket_addr = $socket_addr;
528 #[allow(unused)]
529 use $crate::option::*;
530 #[allow(unused)]
531 use std::iter::once;
532
533 $( write_options!(_internal $key, $viter, start, end, msg, socket_addr); )*
534
535 let _ = start;
536
537 Ok(())
538 }};
539
540 (($msg:ident, $socket_addr:ident, $start:ident, $end:ident, $inner:expr) { $($key:expr => $viter:expr),* ,}) => {
541 write_options!(($msg,$socket_addr,$start,$end,$inner){$($key=>$viter),*})
542 };
543
544 (($msg:ident, $socket_addr:ident, $start:ident, $end:ident) { $($key:expr => $viter:expr),* ,}) => {
545 write_options!(($msg,$socket_addr,$start,$end){$($key=>$viter),*})
546 };
547
548 ( _internal $key:expr, $viter:expr, $start:ident, $end:ident, $msg:ident, $socket_addr:ident, $inner:expr) => {{
549 let key = $key;
550 let mut value_iter = $viter.into_iter().peekable();
551
552 if value_iter.peek().is_some()
553 && match $start {
554 Bound::Included(b) => b <= key.0,
555 Bound::Excluded(b) => b < key.0,
556 Bound::Unbounded => true,
557 }
558 {
559 if match $end {
560 Bound::Included(b) => key.0 <= b,
561 Bound::Excluded(b) => key.0 < b,
562 Bound::Unbounded => true,
563 } {
564 $inner.write_options($msg, $socket_addr, $start, Bound::Included(key.0))?;
565 for value in value_iter {
566 $msg.insert_option(key, value)?;
567 }
568 $start = Bound::Excluded(key.0)
569 }
570 }
571 }};
572
573 ( _internal $key:expr, $viter:expr, $start:ident, $end:ident, $msg:ident, $socket_addr:ident) => {{
574 let key = $key;
575 let mut value_iter = $viter.into_iter().peekable();
576
577 if value_iter.peek().is_some()
578 && match $start {
579 Bound::Included(b) => b <= key.0,
580 Bound::Excluded(b) => b < key.0,
581 Bound::Unbounded => true,
582 }
583 {
584 if match $end {
585 Bound::Included(b) => key.0 <= b,
586 Bound::Excluded(b) => key.0 < b,
587 Bound::Unbounded => true,
588 } {
589 for value in value_iter {
590 $msg.insert_option(key, value)?;
591 }
592 $start = Bound::Excluded(key.0)
593 }
594 }
595 }};
596}
597
598/// Helper macro that provides pass-thru implementations of the timing-related methods
599/// of a [`SendDesc`].
600///
601/// This macro takes a single argument: the name of the member variable to pass along
602/// the call to.
603#[doc(hidden)]
604#[macro_export]
605macro_rules! send_desc_passthru_timing {
606 ($inner:tt) => {
607 fn delay_to_retransmit(&self, retransmits_sent: u32) -> Option<::core::time::Duration> {
608 self.$inner.delay_to_retransmit(retransmits_sent)
609 }
610 fn delay_to_restart(&self) -> Option<::core::time::Duration> {
611 self.$inner.delay_to_restart()
612 }
613 fn max_rtt(&self) -> ::core::time::Duration {
614 self.$inner.max_rtt()
615 }
616 fn transmit_wait_duration(&self) -> ::core::time::Duration {
617 self.$inner.transmit_wait_duration()
618 }
619 }
620}
621
622/// Helper macro that provides pass-thru implementation of [`SendDesc::write_options`].
623///
624/// This macro takes a single argument: the name of the member variable to pass along
625/// the call to.
626#[doc(hidden)]
627#[macro_export]
628macro_rules! send_desc_passthru_options {
629 ($inner:tt) => {
630 fn write_options(
631 &self,
632 msg: &mut dyn OptionInsert,
633 socket_addr: &IC::SocketAddr,
634 start: Bound<OptionNumber>,
635 end: Bound<OptionNumber>,
636 ) -> Result<(), Error> {
637 self.$inner.write_options(msg, socket_addr, start, end)
638 }
639 }
640}
641
642/// Helper macro that provides pass-thru implementations of [`SendDesc::handler`] and
643/// [`SendDesc::supports_option`].
644///
645/// This macro takes a single argument: the name of the member variable to pass along
646/// the call to.
647#[doc(hidden)]
648#[macro_export]
649macro_rules! send_desc_passthru_handler {
650 ($inner:tt, $rt:ty) => {
651 fn supports_option(&self, option: OptionNumber) -> bool {
652 self.$inner.supports_option(option)
653 }
654 fn handler(&mut self, context: Result<&IC, Error>) -> Result<ResponseStatus<$rt>, Error> {
655 self.$inner.handler(context)
656 }
657 };
658
659 ($inner:tt) => {
660 send_desc_passthru_handler!($inner, ());
661 }
662}
663
664/// Helper macro that provides pass-thru implementation of [`SendDesc::supports_option`].
665///
666/// This macro takes a single argument: the name of the member variable to pass along
667/// the call to.
668#[doc(hidden)]
669#[macro_export]
670macro_rules! send_desc_passthru_supports_option {
671 ($inner:tt) => {
672 fn supports_option(&self, option: OptionNumber) -> bool {
673 self.$inner.supports_option(option)
674 }
675 }
676}
677
678/// Helper macro that provides pass-thru implementation of [`SendDesc::write_payload`].
679///
680/// This macro takes a single argument: the name of the member variable to pass along
681/// the call to.
682#[doc(hidden)]
683#[macro_export]
684macro_rules! send_desc_passthru_payload {
685 ($inner:tt) => {
686 fn write_payload(
687 &self,
688 msg: &mut dyn MessageWrite,
689 socket_addr: &IC::SocketAddr,
690 ) -> Result<(), Error> {
691 self.$inner.write_payload(msg, socket_addr)
692 }
693 }
694}