Skip to main content

cyclonedds/
listener.rs

1//! Listener types for reacting to [`status events`](crate::Status) on
2//! [`entities`](crate::entity::Entity).
3//!
4//! Each entity type has a corresponding listener struct that holds optional
5//! callbacks for the status events it can produce. Callbacks are plain function
6//! pointers and are registered via chainable `with_*` methods.
7//!
8//! The listener structure mimics the DDS entity hierarchy. [`Listener`] is the
9//! top-level type attached to a [`Participant`](crate::Participant) and
10//! composes [`SubscriberListener`] and [`PublisherListener`]. Entity-specific
11//! listeners ([`ReaderListener`] and [`WriterListener`]) are attached directly
12//! to their respective entities.
13//!
14//! ```text
15//! ╭───────────────────────╮          ╭─────────────────────────────────────╮
16//! │        Entity         │          │               Listener              │
17//! ╰───────────────────────╯          ╰─────────────────────────────────────╯
18//!  
19//!     Domain
20//!       │
21//!  Participant ··················································· Listener
22//!       ├─ Topic<T> ······························ TopicListener<T>  ─┤
23//!       ├─ Subscriber ··························· SubscriberListener ─┤
24//!       │     └─ Reader<T> ··········· ReaderListener<T> ───┘         │
25//!       └─ Publisher ····························· PublisherListener ─┘
26//!            └─ Writer<T> ············ WriterListener<T> ───┘
27//! ```
28//!
29//! Listeners can be set at any level of the entity hierarchy. A listener set on
30//! a [`Participant`](crate::Participant) will have its callbacks inherited by
31//! child entities of that participant.
32//!
33//! Alternatively, a higher-level listener can also be passed directly to the
34//! child entity's builder (as each listener type implements [`AsRef`] for the
35//! listener types below it in the hierarchy). As a result, a single
36//! [`Listener`] can be reused across multiple entity builders without
37//! constructing separate listeners for each level.
38//!
39//! ```
40//! use cyclonedds::{Domain, Listener, Participant, Subscriber};
41//!
42//! let domain = Domain::default();
43//!
44//! // Create a participant listener with the subscriber callbacks configured.
45//! let listener = Listener::new().with_subscriber(|s| {
46//!     s.with_data_on_readers(|subscriber| {
47//!         println!("{subscriber:?} has data");
48//!     })
49//! });
50//!
51//! // Create a participant with the listener.
52//! let participant = Participant::builder(&domain)
53//!     .with_listener(&listener)
54//!     .build()?;
55//!
56//! // Subscribers created under the participant will inherit the `data_on_readers`
57//! // callback.
58//! let subscriber = Subscriber::new(&participant)?;
59//!
60//! // This subscriber is explicitly created with the subscriber portion of the
61//! // `listener`.
62//! let subscriber = Subscriber::builder(&participant)
63//!     .with_listener(&listener)
64//!     .build()?;
65//!
66//! # Ok::<_, cyclonedds::Error>(())
67//! ```
68//!
69//! Each callback fires when its corresponding [`Status`](crate::Status)
70//! condition is triggered. Most callbacks receive a status value from the
71//! [`status`](crate::status) module carrying event-specific detail such as
72//! counts and last-instance handles.
73//!
74//! # Warning
75//!
76//! <div class="warning">
77//!
78//! **Unstable:** The full DDS listener hierarchy, where [`TopicListener<T>`]
79//! composes under [`Listener`] and [`ReaderListener<T>`] and
80//! [`WriterListener<T>`] compose under [`SubscriberListener`] and
81//! [`PublisherListener`], respectively, is not yet implemented.
82//!
83//! The [`Listener`], [`SubscriberListener`], and [`PublisherListener`] may
84//! propagate to many [`Topic<T>`](crate::Topic), [`Reader<T>`](crate::Reader),
85//! and [`Writer<T>`](crate::Writer) that all have different types for `<T>`. As
86//! a result, one of two obvious solutions presents itself:
87//!
88//! - Allow these higher-level types to only have callbacks of effectively [`std::any::Any`] and
89//!   require the callback to attempt to convert. This maps the most correctly onto how the API is
90//!   designed in the specification but would greatly complicate the internal dispatching of these
91//!   listeners.
92//!
93//! - Maintain a typed registry of all the different types of callbacks that are attached on the
94//!   higher-level untyped subscribers and then add code to check if the event that fired
95//!   corresponds to a type whose callback was registered. This would work but introduces semantics
96//!   that do not match the other DDS implementations.
97//!
98//! </div>
99//!
100//! # Examples
101//!
102//! ```
103//! use cyclonedds::entity::Entity;
104//! use cyclonedds::{Reader, ReaderListener, Topic, TopicListener, Writer, WriterListener};
105//! # #[derive(
106//! #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
107//! # )]
108//! # struct Data {
109//! #     x: i32,
110//! # }
111//! # let domain = cyclonedds::Domain::default();
112//! # let participant = cyclonedds::Participant::new(&domain)?;
113//!
114//! let topic = Topic::<Data>::builder(&participant, "Example")
115//!     .with_listener(
116//!         TopicListener::new().with_inconsistent_topic(|topic, inconsistent_topic| {
117//!             println!(
118//!                 "{topic:?} inconsistent topic: {} just encountered, {} encountered in total",
119//!                 inconsistent_topic.total.delta, inconsistent_topic.total.count
120//!             )
121//!         }),
122//!     )
123//!     .build()?;
124//!
125//! let reader = Reader::builder(&topic)
126//!     .with_listener(
127//!         ReaderListener::new()
128//!             .with_subscription_matched(|reader, subscription_matched| {
129//!                 println!("{reader:?} had a subscription match: {subscription_matched:?}")
130//!             })
131//!             .with_sample_lost(|reader, sample_lost| {
132//!                 println!(
133//!                     "{reader:?} lost samples: {} just lost, {} lost in total",
134//!                     sample_lost.total.delta, sample_lost.total.count
135//!                 )
136//!             }),
137//!     )
138//!     .build()?;
139//!
140//! let writer = Writer::builder(&topic)
141//!     .with_listener(
142//!         WriterListener::new()
143//!             .with_publication_matched(|writer, publication_matched| {
144//!                 println!("{writer:?} has a publication match: {publication_matched:?}")
145//!             })
146//!             .with_liveliness_lost(|writer, liveliness_lost| {
147//!                 println!(
148//!                     "{writer:?} liveliness lost: {} just lost, {} lost in total",
149//!                     liveliness_lost.total.delta, liveliness_lost.total.count
150//!                 )
151//!             }),
152//!     )
153//!     .build()?;
154//! # Ok::<_, cyclonedds::Error>(())
155//! ```
156
157use crate::Result;
158use crate::internal::ffi;
159use crate::internal::traits::AsFfi;
160use crate::status::{
161    InconsistentTopic, LivelinessChanged, LivelinessLost, OfferedDeadlineMissed,
162    OfferedIncompatibleQoS, PublicationMatched, RequestedDeadlineMissed, RequestedIncompatibleQoS,
163    SampleLost, SampleRejected, SubscriptionMatched,
164};
165
166/// Listener attached to a [`Participant`](crate::Participant).
167///
168/// In the DDS entity hierarchy this composes [`SubscriberListener`],
169/// [`PublisherListener`], and [`TopicListener`]. When attached to a
170/// participant, entities created under it inherit any of the configured
171/// callbacks that apply to that entity type.
172///
173/// # Examples
174///
175/// ```
176/// use cyclonedds::{Domain, Listener, Participant, Subscriber};
177///
178/// let domain = Domain::default();
179/// let listener = Listener::new().with_subscriber(|subscriber_listener| {
180///     subscriber_listener
181///         .with_data_on_readers(|subscriber| println!("{subscriber:?} has data on readers"))
182/// });
183/// let participant = Participant::builder(&domain)
184///     .with_listener(&listener)
185///     .build()?;
186///
187/// // This subscriber inherits the callbacks set on the `participant` via the `listener`.
188/// let subscriber = Subscriber::new(&participant)?;
189///
190/// // This subscriber will have the subscriber subset associated with the `listener` directly
191/// // applied to it.
192/// let subscriber = Subscriber::builder(&participant)
193///     .with_listener(&listener)
194///     .build()?;
195/// # Ok::<_, cyclonedds::Error>(())
196/// ```
197#[derive(Debug, Default, Clone, Copy)]
198pub struct Listener {
199    // topic: TopicListener<T>,
200    subscriber: SubscriberListener,
201    publisher: PublisherListener,
202}
203
204/// Listener attached to a [`Topic<T>`](crate::Topic<T>).
205#[derive(Debug, Clone, Copy)]
206pub struct TopicListener<T>
207where
208    T: crate::Topicable,
209{
210    inconsistent_topic: Option<fn(&crate::Topic<'_, '_, T>, InconsistentTopic)>,
211}
212
213/// Listener attached to a [`Subscriber`](crate::Subscriber).
214///
215/// <div class="warning">
216///
217/// Currently [`SubscriberListener`] is missing its configuration for composing
218/// a [`ReaderListener<T>`] under this non-generic type. See the [module-level
219/// warning](crate::listener#warning) for more detail.
220///
221/// </div>
222#[derive(Debug, Default, Clone, Copy)]
223pub struct SubscriberListener {
224    data_on_readers: Option<fn(&crate::Subscriber<'_, '_>)>,
225    // ///
226    // pub reader: ReaderListener<T>,
227}
228
229/// Listener attached to a [`Reader<T>`](crate::Reader<T>).
230#[derive(Debug, Clone, Copy)]
231pub struct ReaderListener<T>
232where
233    T: crate::Topicable,
234{
235    sample_lost: Option<fn(&crate::Reader<'_, '_, '_, T>, SampleLost)>,
236    data_available: Option<fn(&crate::Reader<'_, '_, '_, T>)>,
237    sample_rejected: Option<fn(&crate::Reader<'_, '_, '_, T>, SampleRejected)>,
238    liveliness_changed: Option<fn(&crate::Reader<'_, '_, '_, T>, LivelinessChanged)>,
239    requested_deadline_missed: Option<fn(&crate::Reader<'_, '_, '_, T>, RequestedDeadlineMissed)>,
240    requested_incompatible_qos: Option<fn(&crate::Reader<'_, '_, '_, T>, RequestedIncompatibleQoS)>,
241    subscription_matched: Option<fn(&crate::Reader<'_, '_, '_, T>, SubscriptionMatched)>,
242}
243
244/// Listener attached to a [`Publisher`](crate::Publisher).
245///
246/// <div class="warning">
247///
248/// Currently [`PublisherListener`] has no registered callbacks pending a
249/// solution for composing [`WriterListener<T>`] under this non-generic type.
250/// See the [module-level warning](crate::listener#warning) for more detail.
251///
252/// </div>
253#[derive(Debug, Default, Clone, Copy)]
254pub struct PublisherListener {
255    // ///
256    // pub writer: WriterListener<T>,
257}
258
259/// Listener attached to a [`Writer<T>`](crate::Writer<T>).
260#[derive(Debug, Clone, Copy)]
261pub struct WriterListener<T>
262where
263    T: crate::Topicable,
264{
265    liveliness_lost: Option<fn(&crate::Writer<'_, '_, '_, T>, LivelinessLost)>,
266    offered_deadline_missed: Option<fn(&crate::Writer<'_, '_, '_, T>, OfferedDeadlineMissed)>,
267    offered_incompatible_qos: Option<fn(&crate::Writer<'_, '_, '_, T>, OfferedIncompatibleQoS)>,
268    publication_matched: Option<fn(&crate::Writer<'_, '_, '_, T>, PublicationMatched)>,
269}
270
271impl<T> Default for TopicListener<T>
272where
273    T: crate::Topicable,
274{
275    fn default() -> Self {
276        Self {
277            inconsistent_topic: Option::default(),
278        }
279    }
280}
281
282impl<T> Default for ReaderListener<T>
283where
284    T: crate::Topicable,
285{
286    fn default() -> Self {
287        Self {
288            sample_lost: Option::default(),
289            data_available: Option::default(),
290            sample_rejected: Option::default(),
291            liveliness_changed: Option::default(),
292            requested_deadline_missed: Option::default(),
293            requested_incompatible_qos: Option::default(),
294            subscription_matched: Option::default(),
295        }
296    }
297}
298
299impl<T> Default for WriterListener<T>
300where
301    T: crate::Topicable,
302{
303    fn default() -> Self {
304        Self {
305            liveliness_lost: Option::default(),
306            offered_deadline_missed: Option::default(),
307            offered_incompatible_qos: Option::default(),
308            publication_matched: Option::default(),
309        }
310    }
311}
312
313impl Listener {
314    /// Creates a new [`Listener`] with no callbacks registered.
315    ///
316    /// # Examples
317    ///
318    /// ```
319    /// use cyclonedds::Listener;
320    ///
321    /// let listener = Listener::new();
322    /// ```
323    #[must_use]
324    pub fn new() -> Self {
325        Self::default()
326    }
327
328    // ///
329    // pub fn with_topic(mut self, setter: fn(TopicListener<T>) -> TopicListener<T>)
330    // -> Self {     self.topic = setter(self.topic);
331    //     self
332    // }
333
334    /// Configures the [`SubscriberListener`] via a setter callback.
335    ///
336    /// # Examples
337    ///
338    /// ```
339    /// use cyclonedds::Listener;
340    ///
341    /// let listener = Listener::new().with_subscriber(|s| {
342    ///     s.with_data_on_readers(|subscriber| {
343    ///         println!("data available on a reader");
344    ///     })
345    /// });
346    /// ```
347    #[must_use]
348    pub fn with_subscriber(mut self, setter: fn(SubscriberListener) -> SubscriberListener) -> Self {
349        self.subscriber = setter(self.subscriber);
350        self
351    }
352
353    /// Configures the [`PublisherListener`] via a setter callback.
354    ///
355    /// # Examples
356    ///
357    /// <div class="warning">
358    ///
359    /// This example does not compile because the [`PublisherListener`] does not
360    /// have its `with_writer::<T>` setter yet. This is due to the fact that
361    /// the higher-level listeners are untyped in `<T>` but the lower-level
362    /// listeners are typed in `<T>` and a solution for crossing this boundary
363    /// still needs to be worked out.
364    ///
365    /// See the [module-level warning](crate::listener#warning) for more detail.
366    ///
367    /// </div>
368    ///
369    /// ```ignore
370    /// use cyclonedds::Listener;
371    ///
372    /// let listener = Listener::new().with_publisher(|p| {
373    ///     p.with_writer(|w| {
374    ///         w.with_publication_matched(|writer, publication_matched| {
375    ///             println!("{writer:?} has publication match: {publication_matched:?}")
376    ///         })
377    ///     })
378    /// });
379    /// ```
380    #[must_use]
381    pub fn with_publisher(mut self, setter: fn(PublisherListener) -> PublisherListener) -> Self {
382        self.publisher = setter(self.publisher);
383        self
384    }
385
386    #[inline]
387    pub(crate) fn apply_listener_ffi(self, listener: &mut ffi::Listener) {
388        // self.topic.apply_listener_ffi(listener);
389        self.subscriber.apply_listener_ffi(listener);
390        self.publisher.apply_listener_ffi(listener);
391    }
392}
393
394impl AsFfi for Listener {
395    type Target<'a> = Result<ffi::Listener>;
396
397    #[inline]
398    fn as_ffi(&self) -> Self::Target<'_> {
399        ffi::Listener::new().map(|mut listener| {
400            self.apply_listener_ffi(&mut listener);
401            listener
402        })
403    }
404}
405
406impl<T> TopicListener<T>
407where
408    T: crate::Topicable,
409{
410    /// Creates a new [`TopicListener<T>`] with no callbacks registered.
411    ///
412    /// # Examples
413    ///
414    /// ```
415    /// use cyclonedds::TopicListener;
416    /// # #[derive(
417    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
418    /// # )]
419    /// # struct Data {
420    /// #     x: i32,
421    /// # }
422    ///
423    /// let listener = TopicListener::<Data>::new();
424    /// ```
425    #[must_use]
426    pub fn new() -> Self {
427        Self::default()
428    }
429
430    /// Sets a callback for the
431    /// [`InconsistentTopic` status event](crate::Status::InconsistentTopic).
432    ///
433    /// The callback receives an
434    /// [`InconsistentTopic` metadata struct](InconsistentTopic).
435    ///
436    /// Fired when a remote topic is discovered with the same name but an
437    /// incompatible type or [`QoS`](crate::QoS).
438    ///
439    /// # Examples
440    ///
441    /// ```
442    /// use cyclonedds::listener::TopicListener;
443    /// # #[derive(
444    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
445    /// # )]
446    /// # struct Data {
447    /// #     x: i32,
448    /// # }
449    ///
450    /// let listener =
451    ///     TopicListener::<Data>::new().with_inconsistent_topic(|topic, inconsistent_topic| {
452    ///         println!("inconsistent topic: {inconsistent_topic:?}");
453    ///     });
454    /// ```
455    #[must_use]
456    pub fn with_inconsistent_topic(
457        mut self,
458        callback: fn(&crate::Topic<'_, '_, T>, InconsistentTopic),
459    ) -> Self {
460        self.inconsistent_topic = Some(callback);
461        self
462    }
463
464    #[inline]
465    pub(crate) fn apply_listener_ffi(&self, listener: &mut ffi::Listener) {
466        if let Some(callback) = self.inconsistent_topic {
467            ffi::dds_listener_set_inconsistent_topic(listener, callback);
468        }
469    }
470}
471
472impl<T> AsFfi for TopicListener<T>
473where
474    T: crate::Topicable,
475{
476    type Target<'a>
477        = Result<ffi::Listener>
478    where
479        T: 'a;
480
481    #[inline]
482    fn as_ffi(&self) -> Self::Target<'_> {
483        ffi::Listener::new().map(|mut listener| {
484            self.apply_listener_ffi(&mut listener);
485            listener
486        })
487    }
488}
489
490impl SubscriberListener {
491    /// Creates a new [`SubscriberListener`] with no callbacks registered.
492    ///
493    /// # Examples
494    ///
495    /// ```
496    /// use cyclonedds::SubscriberListener;
497    ///
498    /// let listener = SubscriberListener::new();
499    /// ```
500    #[must_use]
501    pub fn new() -> Self {
502        Self::default()
503    }
504
505    // ///
506    // pub fn with_reader(mut self, setter: fn(ReaderListener<T>) ->
507    // ReaderListener<T>) -> Self {     self.reader = setter(self.reader);
508    //     self
509    // }
510
511    /// Sets a callback for the [`DataOnReaders` status
512    /// event](crate::Status::DataOnReaders).
513    ///
514    /// Fired when new data is available on one or more readers belonging to
515    /// this subscriber.
516    ///
517    /// # Examples
518    ///
519    /// ```
520    /// use cyclonedds::SubscriberListener;
521    ///
522    /// let listener = SubscriberListener::new().with_data_on_readers(|subscriber| {
523    ///     println!("data available on {subscriber:?}");
524    /// });
525    /// ```
526    #[must_use]
527    pub fn with_data_on_readers(mut self, callback: fn(&crate::Subscriber<'_, '_>)) -> Self {
528        self.data_on_readers = Some(callback);
529        self
530    }
531
532    #[inline]
533    pub(crate) fn apply_listener_ffi(self, listener: &mut ffi::Listener) {
534        if let Some(callback) = self.data_on_readers {
535            ffi::dds_listener_set_data_on_readers(listener, callback);
536        }
537        // self.reader.apply_listener_ffi(listener);
538    }
539}
540
541impl AsFfi for SubscriberListener {
542    type Target<'a> = Result<ffi::Listener>;
543
544    #[inline]
545    fn as_ffi(&self) -> Self::Target<'_> {
546        ffi::Listener::new().map(|mut listener| {
547            self.apply_listener_ffi(&mut listener);
548            listener
549        })
550    }
551}
552
553impl PublisherListener {
554    /// Creates a new [`PublisherListener`] with no callbacks registered.
555    ///
556    /// # Examples
557    ///
558    /// ```
559    /// use cyclonedds::PublisherListener;
560    ///
561    /// let listener = PublisherListener::new();
562    /// ```
563    #[must_use]
564    pub fn new() -> Self {
565        Self::default()
566    }
567
568    // ///
569    // pub fn with_writer(mut self, setter: fn(WriterListener<T>) ->
570    // WriterListener<T>) -> Self {     self.writer = setter(self.writer);
571    //     self
572    // }
573
574    #[inline]
575    pub(crate) const fn apply_listener_ffi(self, listener: &mut ffi::Listener) {
576        let _ = self;
577        let _ = listener;
578        // self.writer.apply_listener_ffi(listener);
579    }
580}
581
582impl AsFfi for PublisherListener {
583    type Target<'a> = Result<ffi::Listener>;
584
585    #[inline]
586    fn as_ffi(&self) -> Self::Target<'_> {
587        ffi::Listener::new().map(|mut listener| {
588            self.apply_listener_ffi(&mut listener);
589            listener
590        })
591    }
592}
593
594impl<T> ReaderListener<T>
595where
596    T: crate::Topicable,
597{
598    /// Creates a new [`ReaderListener<T>`] with no callbacks registered.
599    ///
600    /// # Examples
601    ///
602    /// ```
603    /// use cyclonedds::listener::ReaderListener;
604    /// # #[derive(
605    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
606    /// # )]
607    /// # struct Data {
608    /// #     x: i32,
609    /// # }
610    ///
611    /// let listener = ReaderListener::<Data>::new();
612    /// ```
613    #[must_use]
614    pub fn new() -> Self {
615        Self::default()
616    }
617
618    /// Sets a callback for the [`SampleLost` status
619    /// event](crate::Status::SampleLost).
620    ///
621    /// The callback receives a [`SampleLost` metadata struct](SampleLost).
622    ///
623    /// Fired when a sample is lost, meaning it was never received by this
624    /// reader due to resource limits or [`QoS`](crate::QoS) constraints.
625    ///
626    /// # Examples
627    ///
628    /// ```
629    /// use cyclonedds::listener::ReaderListener;
630    /// # #[derive(
631    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
632    /// # )]
633    /// # struct Data {
634    /// #     x: i32,
635    /// # }
636    ///
637    /// let listener = ReaderListener::<Data>::new().with_sample_lost(|reader, sample_lost| {
638    ///     println!("samples lost: {}", sample_lost.total.count);
639    /// });
640    /// ```
641    #[must_use]
642    pub fn with_sample_lost(
643        mut self,
644        callback: fn(&crate::Reader<'_, '_, '_, T>, SampleLost),
645    ) -> Self {
646        self.sample_lost = Some(callback);
647        self
648    }
649
650    /// Sets a callback for the [`DataAvailable` status
651    /// event](crate::Status::DataAvailable).
652    ///
653    /// Fired when new data is available to be [`peeked`](crate::Reader::peek),
654    /// [`read`](crate::Reader::read), or [`taken`](crate::Reader::take) from
655    /// this reader.
656    ///
657    /// # Examples
658    ///
659    /// ```
660    /// use cyclonedds::listener::ReaderListener;
661    /// # #[derive(
662    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
663    /// # )]
664    /// # struct Data {
665    /// #     x: i32,
666    /// # }
667    ///
668    /// let listener = ReaderListener::<Data>::new().with_data_available(|reader| {
669    ///     println!("data available on {reader:?}");
670    /// });
671    /// ```
672    #[must_use]
673    pub fn with_data_available(mut self, callback: fn(&crate::Reader<'_, '_, '_, T>)) -> Self {
674        self.data_available = Some(callback);
675        self
676    }
677
678    /// Sets a callback for the
679    /// [`SampleRejected` status event](crate::Status::SampleRejected).
680    ///
681    /// The callback receives a [`SampleRejected` metadata
682    /// struct](SampleRejected).
683    ///
684    /// Fired when an incoming sample is rejected due to
685    /// [`ResourceLimits`](crate::qos::policy::ResourceLimits).
686    ///
687    /// # Examples
688    ///
689    /// ```
690    /// use cyclonedds::listener::ReaderListener;
691    /// # #[derive(
692    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
693    /// # )]
694    /// # struct Data {
695    /// #     x: i32,
696    /// # }
697    /// let listener = ReaderListener::<Data>::new().with_sample_rejected(|reader, status| {
698    ///     println!("sample rejected: {status:?}");
699    /// });
700    /// ```
701    #[must_use]
702    pub fn with_sample_rejected(
703        mut self,
704        callback: fn(&crate::Reader<'_, '_, '_, T>, SampleRejected),
705    ) -> Self {
706        self.sample_rejected = Some(callback);
707        self
708    }
709
710    /// Sets a callback for the
711    /// [`LivelinessChanged` status event](crate::Status::LivelinessChanged).
712    ///
713    /// The callback receives a
714    /// [`LivelinessChanged` metadata struct](LivelinessChanged).
715    ///
716    /// Fired when the [`Liveliness`](crate::qos::policy::Liveliness) of a
717    /// matched writer changes, i.e. a writer becomes active or inactive.
718    ///
719    /// # Examples
720    ///
721    /// ```
722    /// use cyclonedds::listener::ReaderListener;
723    /// # #[derive(
724    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
725    /// # )]
726    /// # struct Data {
727    /// #     x: i32,
728    /// # }
729    ///
730    /// let listener =
731    ///     ReaderListener::<Data>::new().with_liveliness_changed(|reader, liveliness_changed| {
732    ///         println!("active writers: {}", liveliness_changed.alive.count);
733    ///     });
734    /// ```
735    #[must_use]
736    pub fn with_liveliness_changed(
737        mut self,
738        callback: fn(&crate::Reader<'_, '_, '_, T>, LivelinessChanged),
739    ) -> Self {
740        self.liveliness_changed = Some(callback);
741        self
742    }
743
744    /// Sets a callback for the
745    /// [`RequestedDeadlineMissed` status
746    /// event](crate::Status::RequestedDeadlineMissed).
747    ///
748    /// The callback receives a
749    /// [`RequestedDeadlineMissed` metadata struct](RequestedDeadlineMissed).
750    ///
751    /// Fired when a sample is not received within the
752    /// [`Deadline`](crate::qos::policy::Deadline) period offered by a matched
753    /// writer.
754    ///
755    /// # Examples
756    ///
757    /// ```
758    /// use cyclonedds::listener::ReaderListener;
759    /// # #[derive(
760    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
761    /// # )]
762    /// # struct Data {
763    /// #     x: i32,
764    /// # }
765    ///
766    /// let listener = ReaderListener::<Data>::new().with_requested_deadline_missed(
767    ///     |reader, requested_deadline_missed| {
768    ///         println!("deadline missed: {}", requested_deadline_missed.total.count);
769    ///     },
770    /// );
771    /// ```
772    #[must_use]
773    pub fn with_requested_deadline_missed(
774        mut self,
775        callback: fn(&crate::Reader<'_, '_, '_, T>, RequestedDeadlineMissed),
776    ) -> Self {
777        self.requested_deadline_missed = Some(callback);
778        self
779    }
780
781    /// Sets a callback for the
782    /// [`RequestedIncompatibleQoS` status
783    /// event](crate::Status::RequestedIncompatibleQoS).
784    ///
785    /// The callback receives a
786    /// [`RequestedIncompatibleQoS` metadata struct](RequestedIncompatibleQoS).
787    ///
788    /// Fired when a writer is discovered whose offered [`QoS`](crate::QoS) is
789    /// incompatible with this reader's requested [`QoS`](crate::QoS).
790    ///
791    /// # Examples
792    ///
793    /// ```
794    /// use cyclonedds::listener::ReaderListener;
795    /// # #[derive(
796    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
797    /// # )]
798    /// # struct Data {
799    /// #     x: i32,
800    /// # }
801    ///
802    /// let listener = ReaderListener::<Data>::new().with_requested_incompatible_qos(
803    ///     |reader, requested_incompatible_qos| {
804    ///         println!("incompatible QoS: {requested_incompatible_qos:?}");
805    ///     },
806    /// );
807    /// ```
808    #[must_use]
809    pub fn with_requested_incompatible_qos(
810        mut self,
811        callback: fn(&crate::Reader<'_, '_, '_, T>, RequestedIncompatibleQoS),
812    ) -> Self {
813        self.requested_incompatible_qos = Some(callback);
814        self
815    }
816
817    /// Sets a callback for the
818    /// [`SubscriptionMatched` status event](crate::Status::SubscriptionMatched)
819    /// status event.
820    ///
821    /// The callback receives a
822    /// [`SubscriptionMatched` metadata struct](SubscriptionMatched).
823    ///
824    /// Fired when a writer matching this reader's topic and [`QoS`](crate::QoS)
825    /// is discovered or lost.
826    ///
827    /// # Examples
828    ///
829    /// ```
830    /// use cyclonedds::listener::ReaderListener;
831    /// # #[derive(
832    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
833    /// # )]
834    /// # struct Data {
835    /// #     x: i32,
836    /// # }
837    ///
838    /// let listener =
839    ///     ReaderListener::<Data>::new().with_subscription_matched(|reader, subscription_matched| {
840    ///         println!("matched writers: {}", subscription_matched.current.count);
841    ///     });
842    /// ```
843    #[must_use]
844    pub fn with_subscription_matched(
845        mut self,
846        callback: fn(&crate::Reader<'_, '_, '_, T>, SubscriptionMatched),
847    ) -> Self {
848        self.subscription_matched = Some(callback);
849        self
850    }
851
852    #[inline]
853    pub(crate) fn apply_listener_ffi(&self, listener: &mut ffi::Listener) {
854        if let Some(callback) = self.sample_lost {
855            ffi::dds_listener_set_sample_lost(listener, callback);
856        }
857        if let Some(callback) = self.data_available {
858            ffi::dds_listener_set_data_available(listener, callback);
859        }
860        if let Some(callback) = self.sample_rejected {
861            ffi::dds_listener_set_sample_rejected(listener, callback);
862        }
863        if let Some(callback) = self.liveliness_changed {
864            ffi::dds_listener_set_liveliness_changed(listener, callback);
865        }
866        if let Some(callback) = self.requested_deadline_missed {
867            ffi::dds_listener_set_requested_deadline_missed(listener, callback);
868        }
869        if let Some(callback) = self.requested_incompatible_qos {
870            ffi::dds_listener_set_requested_incompatible_qos(listener, callback);
871        }
872        if let Some(callback) = self.subscription_matched {
873            ffi::dds_listener_set_subscription_matched(listener, callback);
874        }
875    }
876}
877
878impl<T> AsFfi for ReaderListener<T>
879where
880    T: crate::Topicable,
881{
882    type Target<'a>
883        = Result<ffi::Listener>
884    where
885        T: 'a;
886
887    #[inline]
888    fn as_ffi(&self) -> Self::Target<'_> {
889        ffi::Listener::new().map(|mut listener| {
890            self.apply_listener_ffi(&mut listener);
891            listener
892        })
893    }
894}
895
896impl<T> WriterListener<T>
897where
898    T: crate::Topicable,
899{
900    /// Creates a new [`WriterListener<T>`] with no callbacks registered.
901    ///
902    /// # Examples
903    ///
904    /// ```
905    /// use cyclonedds::TopicListener;
906    /// # #[derive(
907    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
908    /// # )]
909    /// # struct Data {
910    /// #     x: i32,
911    /// # }
912    ///
913    /// let listener = TopicListener::<Data>::new();
914    /// ```
915    #[must_use]
916    pub fn new() -> Self {
917        Self::default()
918    }
919
920    /// Sets a callback for the
921    /// [`LivelinessLost` status event](crate::Status::LivelinessLost).
922    ///
923    /// The callback receives a [`LivelinessLost` metadata
924    /// struct](LivelinessLost).
925    ///
926    /// Fired when the writer fails to meet its
927    /// [`Liveliness`](crate::qos::policy::Liveliness) policy and is considered
928    /// inactive by matched readers.
929    ///
930    /// # Examples
931    ///
932    /// ```
933    /// use cyclonedds::listener::WriterListener;
934    /// # #[derive(
935    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
936    /// # )]
937    /// # struct Data {
938    /// #     x: i32,
939    /// # }
940    ///
941    /// let listener = WriterListener::<Data>::new().with_liveliness_lost(|writer, liveliness_lost| {
942    ///     println!(
943    ///         "{writer:?} liveliness lost: {}",
944    ///         liveliness_lost.total.count
945    ///     );
946    /// });
947    /// ```
948    #[must_use]
949    pub fn with_liveliness_lost(
950        mut self,
951        callback: fn(&crate::Writer<'_, '_, '_, T>, LivelinessLost),
952    ) -> Self {
953        self.liveliness_lost = Some(callback);
954        self
955    }
956
957    /// Sets a callback for the
958    /// [`OfferedDeadlineMissed` status
959    /// event](crate::Status::OfferedDeadlineMissed) status event.
960    ///
961    /// The callback receives an
962    /// [`OfferedDeadlineMissed` metadata struct](OfferedDeadlineMissed).
963    ///
964    /// Fired when the writer fails to write a new sample within its offered
965    /// [`Deadline`](crate::qos::policy::Deadline) period for one or more
966    /// instances.
967    ///
968    /// # Examples
969    ///
970    /// ```
971    /// use cyclonedds::listener::WriterListener;
972    /// # #[derive(
973    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
974    /// # )]
975    /// # struct Data {
976    /// #     x: i32,
977    /// # }
978    ///
979    /// let listener = WriterListener::<Data>::new().with_offered_deadline_missed(
980    ///     |writer, offered_deadline_missed| {
981    ///         println!(
982    ///             "{writer:?} deadline missed: {}",
983    ///             offered_deadline_missed.total.count
984    ///         );
985    ///     },
986    /// );
987    /// ```
988    #[must_use]
989    pub fn with_offered_deadline_missed(
990        mut self,
991        callback: fn(&crate::Writer<'_, '_, '_, T>, OfferedDeadlineMissed),
992    ) -> Self {
993        self.offered_deadline_missed = Some(callback);
994        self
995    }
996
997    /// Sets a callback for the
998    /// [`OfferedIncompatibleQoS` status
999    /// event](crate::Status::OfferedIncompatibleQoS) status event.
1000    ///
1001    /// The callback receives an
1002    /// [`OfferedIncompatibleQoS` metadata struct](OfferedIncompatibleQoS).
1003    ///
1004    /// Fired when a reader is discovered whose requested [`QoS`](crate::QoS) is
1005    /// incompatible with this writer's offered [`QoS`](crate::QoS).
1006    ///
1007    /// # Examples
1008    ///
1009    /// ```
1010    /// use cyclonedds::listener::WriterListener;
1011    /// # #[derive(
1012    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
1013    /// # )]
1014    /// # struct Data {
1015    /// #     x: i32,
1016    /// # }
1017    ///
1018    /// let listener = WriterListener::<Data>::new().with_offered_incompatible_qos(
1019    ///     |writer, offered_incompatible_qos| {
1020    ///         println!("{writer:?} discovered incompatible QoS: {offered_incompatible_qos:?}");
1021    ///     },
1022    /// );
1023    /// ```
1024    #[must_use]
1025    pub fn with_offered_incompatible_qos(
1026        mut self,
1027        callback: fn(&crate::Writer<'_, '_, '_, T>, OfferedIncompatibleQoS),
1028    ) -> Self {
1029        self.offered_incompatible_qos = Some(callback);
1030        self
1031    }
1032
1033    /// Sets a callback for the
1034    /// [`PublicationMatched` status event](crate::Status::PublicationMatched).
1035    ///
1036    /// The callback receives a
1037    /// [`PublicationMatched` metadata struct](PublicationMatched).
1038    ///
1039    /// Fired when a reader matching this writer's topic and [`QoS`](crate::QoS)
1040    /// is discovered.
1041    ///
1042    /// # Examples
1043    ///
1044    /// ```
1045    /// use cyclonedds::listener::WriterListener;
1046    /// # #[derive(
1047    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
1048    /// # )]
1049    /// # struct Data {
1050    /// #     x: i32,
1051    /// # }
1052    ///
1053    /// let listener = WriterListener::<Data>::new().with_publication_matched(|writer, status| {
1054    ///     println!("{writer:?} matched readers: {}", status.current.count);
1055    /// });
1056    /// ```
1057    #[must_use]
1058    pub fn with_publication_matched(
1059        mut self,
1060        callback: fn(&crate::Writer<'_, '_, '_, T>, PublicationMatched),
1061    ) -> Self
1062    where
1063        T: crate::Topicable,
1064    {
1065        self.publication_matched = Some(callback);
1066        self
1067    }
1068
1069    #[inline]
1070    pub(crate) fn apply_listener_ffi(&self, listener: &mut ffi::Listener) {
1071        if let Some(callback) = self.liveliness_lost {
1072            ffi::dds_listener_set_liveliness_lost(listener, callback);
1073        }
1074        if let Some(callback) = self.offered_deadline_missed {
1075            ffi::dds_listener_set_offered_deadline_missed(listener, callback);
1076        }
1077        if let Some(callback) = self.offered_incompatible_qos {
1078            ffi::dds_listener_set_offered_incompatible_qos(listener, callback);
1079        }
1080        if let Some(callback) = self.publication_matched {
1081            ffi::dds_listener_set_publication_matched(listener, callback);
1082        }
1083    }
1084}
1085
1086impl<T> AsFfi for WriterListener<T>
1087where
1088    T: crate::Topicable,
1089{
1090    type Target<'a>
1091        = Result<ffi::Listener>
1092    where
1093        T: 'a;
1094
1095    #[inline]
1096    fn as_ffi(&self) -> Self::Target<'_> {
1097        ffi::Listener::new().map(|mut listener| {
1098            self.apply_listener_ffi(&mut listener);
1099            listener
1100        })
1101    }
1102}
1103
1104impl<T> AsRef<ReaderListener<T>> for ReaderListener<T>
1105where
1106    T: crate::Topicable,
1107{
1108    fn as_ref(&self) -> &ReaderListener<T> {
1109        self
1110    }
1111}
1112impl<T> AsRef<WriterListener<T>> for WriterListener<T>
1113where
1114    T: crate::Topicable,
1115{
1116    fn as_ref(&self) -> &WriterListener<T> {
1117        self
1118    }
1119}
1120impl AsRef<SubscriberListener> for SubscriberListener {
1121    fn as_ref(&self) -> &SubscriberListener {
1122        self
1123    }
1124}
1125impl AsRef<PublisherListener> for PublisherListener {
1126    fn as_ref(&self) -> &PublisherListener {
1127        self
1128    }
1129}
1130impl<T> AsRef<TopicListener<T>> for TopicListener<T>
1131where
1132    T: crate::Topicable,
1133{
1134    fn as_ref(&self) -> &TopicListener<T> {
1135        self
1136    }
1137}
1138impl AsRef<Listener> for Listener {
1139    fn as_ref(&self) -> &Listener {
1140        self
1141    }
1142}
1143
1144// impl<T> AsRef<ReaderListener<T>> for Listener<T> {
1145//     fn as_ref(&self) -> &ReaderListener<T> {
1146//         &self.subscriber.reader
1147//     }
1148// }
1149// impl<T> AsRef<WriterListener<T>> for Listener<T> {
1150//     fn as_ref(&self) -> &WriterListener<T> {
1151//         &self.publisher.writer
1152//     }
1153// }
1154impl AsRef<SubscriberListener> for Listener {
1155    fn as_ref(&self) -> &SubscriberListener {
1156        &self.subscriber
1157    }
1158}
1159impl AsRef<PublisherListener> for Listener {
1160    fn as_ref(&self) -> &PublisherListener {
1161        &self.publisher
1162    }
1163}
1164// impl<T> AsRef<TopicListener<T>> for Listener<T> {
1165//     fn as_ref(&self) -> &TopicListener<T> {
1166//         &self.topic
1167//     }
1168// }
1169
1170// impl<T> AsRef<ReaderListener<T>> for SubscriberListener<T> {
1171//     fn as_ref(&self) -> &ReaderListener<T> {
1172//         &self.reader
1173//     }
1174// }
1175// impl<T> AsRef<WriterListener<T>> for PublisherListener<T> {
1176//     fn as_ref(&self) -> &WriterListener<T> {
1177//         &self.writer
1178//     }
1179// }
1180
1181#[cfg(test)]
1182mod tests {
1183    use super::*;
1184    use crate::Topicable;
1185
1186    fn receive_listener<L>(listener: L)
1187    where
1188        L: AsRef<Listener>,
1189    {
1190        let _ = listener.as_ref();
1191    }
1192
1193    fn receive_topic_listener<L, T>(listener: L)
1194    where
1195        L: AsRef<TopicListener<T>>,
1196        T: crate::Topicable,
1197    {
1198        let _ = listener.as_ref();
1199    }
1200
1201    fn receive_subscriber_listener<L>(listener: L)
1202    where
1203        L: AsRef<SubscriberListener>,
1204    {
1205        let _ = listener.as_ref();
1206    }
1207
1208    fn receive_publisher_listener<L>(listener: L)
1209    where
1210        L: AsRef<PublisherListener>,
1211    {
1212        let _ = listener.as_ref();
1213    }
1214
1215    fn receive_reader_listener<L, T>(listener: L)
1216    where
1217        L: AsRef<ReaderListener<T>>,
1218        T: crate::Topicable,
1219    {
1220        let _ = listener.as_ref();
1221    }
1222
1223    fn receive_writer_listener<L, T>(listener: L)
1224    where
1225        L: AsRef<WriterListener<T>>,
1226        T: crate::Topicable,
1227    {
1228        let _ = listener.as_ref();
1229    }
1230
1231    #[test]
1232    fn test_listener_create() {
1233        let listener = Listener::new()
1234            // .with_topic(|topic| topic.with_inconsistent_topic(|_, _| ()))
1235            .with_subscriber(|subscriber| {
1236                subscriber.with_data_on_readers(|_| ())
1237                // .with_reader(|reader| {
1238                //     reader
1239                //         .with_data_available(|_| ())
1240                //         .with_liveliness_changed(|_, _| ())
1241                //         .with_requested_deadline_missed(|_, _| ())
1242                //         .with_requested_incompatible_qos(|_, _| ())
1243                //         .with_sample_lost(|_, _| ())
1244                //         .with_sample_rejected(|_, _| ())
1245                //         .with_subscription_matched(|_, _| ())
1246                // })
1247            })
1248            .with_publisher(|publisher| {
1249                publisher
1250                //     .with_writer(|writer| {
1251                //     writer
1252                //         .with_liveliness_lost(|_, _| ())
1253                //         .with_offered_deadline_missed(|_, _| ())
1254                //         .with_offered_incompatible_qos(|_, _| ())
1255                //         .with_publication_matched(|_, _| ())
1256                // })
1257            });
1258        let topic_listener =
1259            TopicListener::<crate::tests::topic::Data>::new().with_inconsistent_topic(|_, _| ());
1260        let subscriber_listener = SubscriberListener::new()
1261            .with_data_on_readers(|_| ())
1262        // .with_reader(|reader| {
1263        //     reader
1264        //         .with_data_available(|_| ())
1265        //         .with_liveliness_changed(|_, _| ())
1266        //         .with_requested_deadline_missed(|_, _| ())
1267        //         .with_requested_incompatible_qos(|_, _| ())
1268        //         .with_sample_lost(|_, _| ())
1269        //         .with_sample_rejected(|_, _| ())
1270        //         .with_subscription_matched(|_, _| ())
1271        // })
1272            ;
1273        let publisher_listener =
1274            PublisherListener::new()
1275        // .with_writer(|writer| {
1276        //     writer
1277        //         .with_liveliness_lost(|_, _| ())
1278        //         .with_offered_deadline_missed(|_, _| ())
1279        //         .with_offered_incompatible_qos(|_, _| ())
1280        //         .with_publication_matched(|_, _| ())
1281        // })
1282            ;
1283        let reader_listener = ReaderListener::<crate::tests::topic::Data>::new()
1284            .with_data_available(|_| ())
1285            .with_liveliness_changed(|_, _| ())
1286            .with_requested_deadline_missed(|_, _| ())
1287            .with_requested_incompatible_qos(|_, _| ())
1288            .with_sample_lost(|_, _| ())
1289            .with_sample_rejected(|_, _| ())
1290            .with_subscription_matched(|_, _| ());
1291        let writer_listener = WriterListener::<crate::tests::topic::Data>::new()
1292            .with_liveliness_lost(|_, _| ())
1293            .with_offered_deadline_missed(|_, _| ())
1294            .with_offered_incompatible_qos(|_, _| ())
1295            .with_publication_matched(|_, _| ());
1296
1297        receive_listener(listener);
1298
1299        receive_topic_listener(&topic_listener);
1300        // receive_topic_listener(&listener);
1301
1302        receive_subscriber_listener(subscriber_listener);
1303        receive_subscriber_listener(listener);
1304
1305        receive_publisher_listener(publisher_listener);
1306        receive_publisher_listener(listener);
1307
1308        receive_reader_listener(&reader_listener);
1309        // receive_reader_listener(&subscriber_listener);
1310        // receive_reader_listener(&listener);
1311
1312        receive_writer_listener(&writer_listener);
1313        // receive_writer_listener(&publisher_listener);
1314        // receive_writer_listener(&listener);
1315    }
1316
1317    #[test]
1318    fn test_subscriber_listener_callbacks() {
1319        #[derive(Debug, PartialEq)]
1320        struct Triggered {
1321            data_on_readers: u32,
1322        }
1323
1324        static TRIGGERED: std::sync::Mutex<Triggered> =
1325            std::sync::Mutex::new(Triggered { data_on_readers: 0 });
1326
1327        let domain_id = crate::tests::domain::unique_id();
1328        let topic_name = crate::tests::topic::unique_name();
1329        let domain = crate::Domain::new(domain_id).unwrap();
1330
1331        let participant = crate::Participant::new(&domain).unwrap();
1332        let topic =
1333            crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
1334        let subscriber = crate::Subscriber::builder(&participant)
1335            .with_listener(
1336                crate::SubscriberListener::new().with_data_on_readers(|_subscriber| {
1337                    TRIGGERED.lock().unwrap().data_on_readers += 1;
1338                }),
1339            )
1340            .build()
1341            .unwrap();
1342        let reader = crate::Reader::builder(&topic)
1343            .with_subscriber(&subscriber)
1344            .build()
1345            .unwrap();
1346        let writer = crate::Writer::new(&topic).unwrap();
1347
1348        let sample = crate::tests::topic::Data::default();
1349        writer.write(&sample).unwrap();
1350
1351        let samples = reader.read().unwrap();
1352        assert_eq!(samples.len(), 1);
1353
1354        assert_eq!(*samples[0], sample);
1355
1356        assert_eq!(*TRIGGERED.lock().unwrap(), Triggered { data_on_readers: 1 });
1357    }
1358
1359    #[test]
1360    fn test_publisher_listener_callbacks() {
1361        let domain_id = crate::tests::domain::unique_id();
1362        let topic_name = crate::tests::topic::unique_name();
1363        let domain = crate::Domain::new(domain_id).unwrap();
1364
1365        let participant = crate::Participant::new(&domain).unwrap();
1366        let topic =
1367            crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
1368        let publisher = crate::Publisher::builder(&participant)
1369            .with_listener(crate::PublisherListener::new())
1370            .build()
1371            .unwrap();
1372        let reader = crate::Reader::new(&topic).unwrap();
1373        let writer = crate::Writer::builder(&topic)
1374            .with_publisher(&publisher)
1375            .build()
1376            .unwrap();
1377
1378        let sample = crate::tests::topic::Data::default();
1379        writer.write(&sample).unwrap();
1380
1381        let samples = reader.read().unwrap();
1382        assert_eq!(samples.len(), 1);
1383
1384        assert_eq!(*samples[0], sample);
1385    }
1386
1387    #[test]
1388    fn test_reader_listener_callbacks() {
1389        #[derive(Debug, PartialEq)]
1390        struct Triggered {
1391            requested_incompatible_qos: u32,
1392            requested_deadline_missed: bool,
1393            sample_rejected: u32,
1394            data_available: u32,
1395            subscription_matched: u32,
1396            liveliness_changed: u32,
1397            sample_lost: u32,
1398        }
1399
1400        static TRIGGERED: std::sync::Mutex<Triggered> = std::sync::Mutex::new(Triggered {
1401            requested_incompatible_qos: 0,
1402            requested_deadline_missed: false,
1403            sample_rejected: 0,
1404            data_available: 0,
1405            subscription_matched: 0,
1406            liveliness_changed: 0,
1407            sample_lost: 0,
1408        });
1409
1410        let domain_id = crate::tests::domain::unique_id();
1411        let topic_name = crate::tests::topic::unique_name();
1412        let domain = crate::Domain::new(domain_id).unwrap();
1413
1414        let participant = crate::Participant::new(&domain).unwrap();
1415        let qos = crate::QoS::new()
1416            .with_destination_order(crate::qos::policy::DestinationOrder::BySourceTimestamp);
1417        let topic = crate::Topic::<crate::tests::topic::Data>::builder(&participant, &topic_name)
1418            .with_qos(&qos)
1419            .build()
1420            .unwrap();
1421
1422        {
1423            let _writer = crate::Writer::new(&topic).unwrap();
1424            let _reader = crate::Reader::builder(&topic)
1425                .with_qos(
1426                    &crate::QoS::new().with_durability(crate::qos::policy::Durability::Persistent),
1427                )
1428                .with_listener(
1429                    crate::ReaderListener::new().with_requested_incompatible_qos(
1430                        |_reader, _metadata| {
1431                            TRIGGERED.lock().unwrap().requested_incompatible_qos += 1;
1432                        },
1433                    ),
1434                )
1435                .build()
1436                .unwrap();
1437        }
1438
1439        {
1440            let qos = crate::QoS::new().with_deadline(crate::qos::policy::Deadline {
1441                period: crate::Duration::from_nanos(1_000_000),
1442            });
1443            let reader = crate::Reader::builder(&topic)
1444                .with_qos(&qos)
1445                .with_listener(crate::ReaderListener::new().with_requested_deadline_missed(
1446                    |_reader, _metadata| {
1447                        TRIGGERED.lock().unwrap().requested_deadline_missed |= true;
1448                    },
1449                ))
1450                .build()
1451                .unwrap();
1452            let writer = crate::Writer::builder(&topic)
1453                .with_qos(&qos)
1454                .build()
1455                .unwrap();
1456
1457            let sample = crate::tests::topic::Data::default();
1458            writer.write(&sample).unwrap();
1459
1460            let samples = reader.take().unwrap();
1461            assert_eq!(samples.len(), 1);
1462            assert_eq!(*samples[0], sample);
1463
1464            while !TRIGGERED.lock().unwrap().requested_deadline_missed {
1465                std::thread::sleep(std::time::Duration::from_nanos(50));
1466            }
1467        }
1468
1469        {
1470            let reader = crate::Reader::builder(&topic)
1471                .with_qos(&crate::QoS::new().with_resource_limits(
1472                    crate::qos::policy::ResourceLimits {
1473                        max_samples: crate::qos::policy::ResourceLimit::Unlimited,
1474                        max_instances: crate::qos::policy::ResourceLimit::Limited(1),
1475                        max_samples_per_instance: crate::qos::policy::ResourceLimit::Unlimited,
1476                    },
1477                ))
1478                .with_listener(crate::ReaderListener::new().with_sample_rejected(
1479                    |_reader, _metadata| {
1480                        TRIGGERED.lock().unwrap().sample_rejected += 1;
1481                    },
1482                ))
1483                .build()
1484                .unwrap();
1485            let writer = crate::Writer::new(&topic).unwrap();
1486
1487            let sample = crate::tests::topic::Data {
1488                x: 1,
1489                y: 2,
1490                ..crate::tests::topic::Data::default()
1491            };
1492            writer.write(&sample).unwrap();
1493            writer
1494                .write(&crate::tests::topic::Data {
1495                    x: 2,
1496                    y: 3,
1497                    ..crate::tests::topic::Data::default()
1498                })
1499                .unwrap();
1500
1501            let samples = reader.take().unwrap();
1502            assert_eq!(samples.len(), 1);
1503            assert_eq!(*samples[0], sample);
1504        }
1505
1506        {
1507            let reader = crate::Reader::builder(&topic)
1508                .with_listener(
1509                    crate::ReaderListener::new()
1510                        .with_data_available(|_reader| {
1511                            TRIGGERED.lock().unwrap().data_available += 1;
1512                        })
1513                        .with_subscription_matched(|_reader, _matched| {
1514                            TRIGGERED.lock().unwrap().subscription_matched += 1;
1515                        })
1516                        .with_liveliness_changed(|_reader, _changed| {
1517                            TRIGGERED.lock().unwrap().liveliness_changed += 1;
1518                        })
1519                        .with_sample_lost(|_reader, _metadata| {
1520                            TRIGGERED.lock().unwrap().sample_lost += 1;
1521                        }),
1522                )
1523                .build()
1524                .unwrap();
1525            let writer = crate::Writer::new(&topic).unwrap();
1526
1527            let sample = crate::tests::topic::Data::default();
1528            writer.write(&sample).unwrap();
1529
1530            let key = sample.as_key();
1531            writer
1532                .unregister_instance_with_timestamp(
1533                    &key,
1534                    (std::time::SystemTime::now() - std::time::Duration::from_secs(1))
1535                        .try_into()
1536                        .unwrap(),
1537                )
1538                .unwrap();
1539
1540            let samples = reader.take().unwrap();
1541            assert_eq!(samples.len(), 1);
1542
1543            assert_eq!(*samples[0], sample);
1544
1545            assert_eq!(
1546                *TRIGGERED.lock().unwrap(),
1547                Triggered {
1548                    requested_incompatible_qos: 1,
1549                    requested_deadline_missed: true,
1550                    sample_rejected: 1,
1551                    data_available: 2,
1552                    subscription_matched: 1,
1553                    liveliness_changed: 1,
1554                    sample_lost: 1,
1555                }
1556            );
1557        }
1558    }
1559
1560    #[test]
1561    fn test_writer_listener_callbacks() {
1562        #[derive(Debug, PartialEq)]
1563        struct Triggered {
1564            liveliness_lost: bool,
1565            offered_deadline_missed: bool,
1566            offered_incompatible_qos: u32,
1567            publication_matched: u32,
1568        }
1569
1570        static TRIGGERED: std::sync::Mutex<Triggered> = std::sync::Mutex::new(Triggered {
1571            liveliness_lost: false,
1572            offered_deadline_missed: false,
1573            offered_incompatible_qos: 0,
1574            publication_matched: 0,
1575        });
1576
1577        let domain_id = crate::tests::domain::unique_id();
1578        let topic_name = crate::tests::topic::unique_name();
1579        let domain = crate::Domain::new(domain_id).unwrap();
1580
1581        let participant = crate::Participant::new(&domain).unwrap();
1582        let topic =
1583            crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
1584
1585        {
1586            let _reader = crate::Reader::builder(&topic)
1587                .with_qos(
1588                    &crate::QoS::new().with_durability(crate::qos::policy::Durability::Persistent),
1589                )
1590                .build()
1591                .unwrap();
1592            let _writer = crate::Writer::builder(&topic)
1593                .with_listener(crate::WriterListener::new().with_offered_incompatible_qos(
1594                    |_writer, _metadata| {
1595                        TRIGGERED.lock().unwrap().offered_incompatible_qos += 1;
1596                    },
1597                ))
1598                .build()
1599                .unwrap();
1600        }
1601
1602        {
1603            let qos = crate::QoS::new().with_deadline(crate::qos::policy::Deadline {
1604                period: crate::Duration::from_nanos(1_000_000),
1605            });
1606            let writer = crate::Writer::builder(&topic)
1607                .with_qos(&qos)
1608                .with_listener(crate::WriterListener::new().with_offered_deadline_missed(
1609                    |_writer, _metadata| {
1610                        TRIGGERED.lock().unwrap().offered_deadline_missed |= true;
1611                    },
1612                ))
1613                .build()
1614                .unwrap();
1615            let reader = crate::Reader::builder(&topic)
1616                .with_qos(&qos)
1617                .build()
1618                .unwrap();
1619
1620            let sample = crate::tests::topic::Data::default();
1621            writer.write(&sample).unwrap();
1622
1623            let samples = reader.take().unwrap();
1624            assert_eq!(samples.len(), 1);
1625            assert_eq!(*samples[0], sample);
1626
1627            while !TRIGGERED.lock().unwrap().offered_deadline_missed {
1628                std::thread::sleep(std::time::Duration::from_nanos(50));
1629            }
1630        }
1631
1632        {
1633            let writer = crate::Writer::builder(&topic)
1634                .with_listener(
1635                    crate::WriterListener::new()
1636                        .with_liveliness_lost(|_writer, _metadata| {
1637                            TRIGGERED.lock().unwrap().liveliness_lost |= true;
1638                        })
1639                        .with_publication_matched(|_writer, _metadata| {
1640                            TRIGGERED.lock().unwrap().publication_matched += 1;
1641                        }),
1642                )
1643                .with_qos(&crate::QoS::new().with_liveliness(
1644                    crate::qos::policy::Liveliness::ManualByParticipant {
1645                        lease_duration: crate::Duration::from_nanos(1_000_000),
1646                    },
1647                ))
1648                .build()
1649                .unwrap();
1650
1651            let reader = crate::Reader::new(&topic).unwrap();
1652
1653            let sample = crate::tests::topic::Data::default();
1654            writer.write(&sample).unwrap();
1655
1656            let key = sample.as_key();
1657            writer
1658                .unregister_instance_with_timestamp(
1659                    &key,
1660                    (std::time::SystemTime::now() - std::time::Duration::from_secs(1))
1661                        .try_into()
1662                        .unwrap(),
1663                )
1664                .unwrap();
1665
1666            let samples = reader.take().unwrap();
1667            assert_eq!(samples.len(), 1);
1668
1669            assert_eq!(*samples[0], sample);
1670
1671            while !TRIGGERED.lock().unwrap().liveliness_lost {
1672                std::thread::sleep(std::time::Duration::from_nanos(50));
1673            }
1674        }
1675
1676        assert_eq!(
1677            *TRIGGERED.lock().unwrap(),
1678            Triggered {
1679                liveliness_lost: true,
1680                offered_deadline_missed: true,
1681                offered_incompatible_qos: 1,
1682                publication_matched: 2,
1683            }
1684        );
1685    }
1686}