ddns_a/monitor/poller/
monitor.rs

1//! Polling monitor configuration.
2//!
3//! This module provides [`PollingMonitor`], the builder/configuration struct
4//! for creating polling-based IP address monitors.
5
6use super::super::DebouncePolicy;
7use super::stream::PollingStream;
8use crate::network::AddressFetcher;
9use crate::time::{Clock, SystemClock};
10use std::time::Duration;
11
12/// Polling-based IP address monitor.
13///
14/// Periodically fetches network adapter information and emits a stream
15/// of [`super::super::IpChange`] events when addresses are added or removed.
16///
17/// # Type Parameters
18///
19/// * `F` - The [`AddressFetcher`] implementation for retrieving adapter snapshots
20/// * `C` - The [`Clock`] implementation for timestamps (defaults to [`SystemClock`])
21///
22/// # Example
23///
24/// ```ignore
25/// use ddns_a::monitor::PollingMonitor;
26/// use ddns_a::time::SystemClock;
27/// use std::time::Duration;
28///
29/// let fetcher = MyFetcher::new();
30/// let monitor = PollingMonitor::new(fetcher, Duration::from_secs(60));
31///
32/// let mut stream = monitor.into_stream();
33/// while let Some(changes) = stream.next().await {
34///     for change in changes {
35///         println!("{:?}", change);
36///     }
37/// }
38/// ```
39pub struct PollingMonitor<F, C = SystemClock> {
40    fetcher: F,
41    clock: C,
42    interval: Duration,
43    debounce: Option<DebouncePolicy>,
44}
45
46impl<F> PollingMonitor<F, SystemClock>
47where
48    F: AddressFetcher,
49{
50    /// Creates a new polling monitor with system clock.
51    ///
52    /// # Arguments
53    ///
54    /// * `fetcher` - The address fetcher to use for polling
55    /// * `interval` - The interval between polls
56    #[must_use]
57    pub const fn new(fetcher: F, interval: Duration) -> Self {
58        Self::with_clock(fetcher, SystemClock, interval)
59    }
60}
61
62impl<F, C> PollingMonitor<F, C>
63where
64    F: AddressFetcher,
65    C: Clock,
66{
67    /// Creates a new polling monitor with a custom clock.
68    ///
69    /// This constructor allows injecting a mock clock for testing.
70    ///
71    /// # Arguments
72    ///
73    /// * `fetcher` - The address fetcher to use for polling
74    /// * `clock` - The clock to use for timestamps
75    /// * `interval` - The interval between polls
76    #[must_use]
77    pub const fn with_clock(fetcher: F, clock: C, interval: Duration) -> Self {
78        Self {
79            fetcher,
80            clock,
81            interval,
82            debounce: None,
83        }
84    }
85
86    /// Configures debounce policy for this monitor.
87    ///
88    /// When debounce is enabled, rapid consecutive changes within the
89    /// debounce window are merged, with cancelling changes (add then remove
90    /// of the same IP) being eliminated.
91    ///
92    /// # Arguments
93    ///
94    /// * `policy` - The debounce policy to apply
95    #[must_use]
96    pub const fn with_debounce(mut self, policy: DebouncePolicy) -> Self {
97        self.debounce = Some(policy);
98        self
99    }
100
101    /// Returns the configured polling interval.
102    #[must_use]
103    pub const fn interval(&self) -> Duration {
104        self.interval
105    }
106
107    /// Returns the configured debounce policy, if any.
108    #[must_use]
109    pub const fn debounce(&self) -> Option<&DebouncePolicy> {
110        self.debounce.as_ref()
111    }
112
113    /// Converts this monitor into a stream of IP changes.
114    ///
115    /// The returned stream will poll at the configured interval and
116    /// yield batches of [`super::super::IpChange`] events whenever addresses change.
117    ///
118    /// The stream never terminates on its own; use `take_until` with
119    /// a shutdown signal to stop it gracefully.
120    #[must_use]
121    pub fn into_stream(self) -> PollingStream<F, C> {
122        PollingStream::new(self.fetcher, self.clock, self.interval, self.debounce)
123    }
124}