Skip to main content

aranya_client/
config.rs

1#![warn(missing_docs)]
2
3//! Client configurations.
4
5use core::time::Duration;
6
7use crate::{error::InvalidArg, ConfigError, Result};
8
9pub mod team;
10pub use team::*;
11
12/// Maximum sync interval of 1 year (365 days).
13///
14/// This limit prevents overflow when calculating deadlines in DelayQueue::insert(),
15/// which adds the interval to Instant::now().
16pub const MAX_SYNC_INTERVAL: Duration = Duration::from_secs(365 * 24 * 60 * 60);
17
18/// Configuration info for syncing with a peer.
19#[derive(Clone, Debug)]
20pub struct SyncPeerConfig {
21    interval: Option<Duration>,
22    sync_now: bool,
23    #[cfg(feature = "preview")]
24    sync_on_hello: bool,
25}
26
27impl SyncPeerConfig {
28    /// Creates a default [`SyncPeerConfigBuilder`].
29    pub fn builder() -> SyncPeerConfigBuilder {
30        Default::default()
31    }
32}
33
34impl From<SyncPeerConfig> for aranya_daemon_api::SyncPeerConfig {
35    fn from(value: SyncPeerConfig) -> Self {
36        Self {
37            interval: value.interval,
38            sync_now: value.sync_now,
39            #[cfg(feature = "preview")]
40            sync_on_hello: value.sync_on_hello,
41        }
42    }
43}
44
45/// Builder for a [`SyncPeerConfig`]
46#[derive(Debug)]
47pub struct SyncPeerConfigBuilder {
48    interval: Option<Duration>,
49    sync_now: bool,
50    #[cfg(feature = "preview")]
51    sync_on_hello: bool,
52}
53
54impl SyncPeerConfigBuilder {
55    /// Creates a new builder for [`SyncPeerConfig`].
56    pub fn new() -> Self {
57        Default::default()
58    }
59
60    /// Attempts to build a [`SyncPeerConfig`] using the provided parameters.
61    pub fn build(self) -> Result<SyncPeerConfig> {
62        // Check that interval doesn't exceed 1 year to prevent overflow when adding to Instant::now()
63        // in DelayQueue::insert() (which calculates deadline as current_time + interval)
64        if let Some(interval) = self.interval {
65            if interval > MAX_SYNC_INTERVAL {
66                return Err(ConfigError::InvalidArg(InvalidArg::new(
67                    "duration",
68                    "must not exceed 1 year to prevent overflow",
69                ))
70                .into());
71            }
72        }
73
74        Ok(SyncPeerConfig {
75            interval: self.interval,
76            sync_now: self.sync_now,
77            #[cfg(feature = "preview")]
78            sync_on_hello: self.sync_on_hello,
79        })
80    }
81
82    /// Sets the interval at which syncing occurs.
83    ///
84    /// The interval must be less than 1 year to prevent overflow when calculating deadlines.
85    ///
86    /// By default, the interval is not set (None), which means the peer will not be periodically synced.
87    pub fn interval(mut self, duration: Duration) -> Self {
88        self.interval = Some(duration);
89        self
90    }
91
92    /// Configures whether the peer will be scheduled for an immediate sync when added.
93    ///
94    /// By default, the peer is scheduled for an immediate sync.
95    pub fn sync_now(mut self, sync_now: bool) -> Self {
96        self.sync_now = sync_now;
97        self
98    }
99
100    /// Configures whether to automatically sync when a hello message is received from this peer
101    /// indicating they have a head that we don't have.
102    ///
103    /// By default, sync on hello is disabled.
104    #[cfg(feature = "preview")]
105    #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
106    pub fn sync_on_hello(mut self, sync_on_hello: bool) -> Self {
107        self.sync_on_hello = sync_on_hello;
108        self
109    }
110}
111
112impl Default for SyncPeerConfigBuilder {
113    fn default() -> Self {
114        Self {
115            interval: None,
116            sync_now: true,
117            #[cfg(feature = "preview")]
118            sync_on_hello: false,
119        }
120    }
121}
122
123/// Configuration for hello subscription.
124///
125/// This configures how a device subscribes to hello notifications from a sync peer.
126/// Hello notifications inform subscribers when the peer's graph has changed,
127/// enabling reactive syncing.
128#[cfg(feature = "preview")]
129#[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
130#[derive(Clone, Debug)]
131pub struct HelloSubscriptionConfig {
132    graph_change_debounce: Duration,
133    expiration: Duration,
134    periodic_interval: Duration,
135}
136
137#[cfg(feature = "preview")]
138impl HelloSubscriptionConfig {
139    /// Creates a default [`HelloSubscriptionConfigBuilder`].
140    pub fn builder() -> HelloSubscriptionConfigBuilder {
141        Default::default()
142    }
143
144    /// Debounce interval for hello notifications after sending one.
145    ///
146    /// After sending a hello notification, no further notification will be sent
147    /// for graph changes within this interval. Default is 100ms.
148    pub fn graph_change_debounce(&self) -> Duration {
149        self.graph_change_debounce
150    }
151
152    /// How long the subscription remains active.
153    pub fn expiration(&self) -> Duration {
154        self.expiration
155    }
156
157    /// Interval between periodic hello messages.
158    pub fn periodic_interval(&self) -> Duration {
159        self.periodic_interval
160    }
161}
162
163#[cfg(feature = "preview")]
164impl Default for HelloSubscriptionConfig {
165    fn default() -> Self {
166        Self {
167            graph_change_debounce: Duration::from_millis(100),
168            expiration: MAX_SYNC_INTERVAL,
169            periodic_interval: Duration::from_secs(10),
170        }
171    }
172}
173
174/// Builder for a [`HelloSubscriptionConfig`].
175#[cfg(feature = "preview")]
176#[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
177#[derive(Debug)]
178pub struct HelloSubscriptionConfigBuilder {
179    graph_change_debounce: Duration,
180    expiration: Duration,
181    periodic_interval: Duration,
182}
183
184#[cfg(feature = "preview")]
185impl HelloSubscriptionConfigBuilder {
186    /// Creates a new builder for [`HelloSubscriptionConfig`].
187    pub fn new() -> Self {
188        Default::default()
189    }
190
191    /// Attempts to build a [`HelloSubscriptionConfig`] using the provided parameters.
192    pub fn build(self) -> Result<HelloSubscriptionConfig> {
193        Ok(HelloSubscriptionConfig {
194            graph_change_debounce: self.graph_change_debounce,
195            expiration: self.expiration,
196            periodic_interval: self.periodic_interval,
197        })
198    }
199
200    /// Sets the graph_change_debounce interval for hello notifications.
201    ///
202    /// After sending a hello notification, no further notification will be sent
203    /// for graph changes within this interval. Default is 100ms.
204    pub fn graph_change_debounce(mut self, duration: Duration) -> Self {
205        self.graph_change_debounce = duration;
206        self
207    }
208
209    /// Sets how long the subscription remains active before expiring.
210    ///
211    /// After this duration, the subscription will automatically end and no more
212    /// hello notifications will be sent. Default is `MAX_SYNC_INTERVAL` (1 year).
213    pub fn expiration(mut self, duration: Duration) -> Self {
214        self.expiration = duration;
215        self
216    }
217
218    /// Sets the interval between periodic hello messages.
219    ///
220    /// Periodic hello messages are sent at this interval regardless of graph changes. Default is 10 seconds.
221    pub fn periodic_interval(mut self, interval: Duration) -> Self {
222        self.periodic_interval = interval;
223        self
224    }
225}
226
227#[cfg(feature = "preview")]
228impl Default for HelloSubscriptionConfigBuilder {
229    fn default() -> Self {
230        let config = HelloSubscriptionConfig::default();
231        Self {
232            graph_change_debounce: config.graph_change_debounce,
233            expiration: config.expiration,
234            periodic_interval: config.periodic_interval,
235        }
236    }
237}