Skip to main content

chrony_confile/builder/
mod.rs

1//! Builder pattern for programmatic configuration generation.
2//!
3//! Provides [`ServerBuilder`] and [`PoolBuilder`] for constructing `server` and `pool`
4//! directives with type-safe method chaining. All options are validated at call time
5//! via the bounded integer newtypes from [`values`](crate::values).
6
7use crate::ast::*;
8use crate::span::Span;
9use crate::values::*;
10
11/// Builder for creating `server` directives programmatically.
12///
13/// Provides a type-safe, chainable API for constructing [`DirectiveKind::Server`] values
14/// with all supported options. Every option is validated at call time via the bounded
15/// integer newtypes from [`crate::values`].
16///
17/// # Examples
18///
19/// ```rust
20/// use chrony_confile::builder::ServerBuilder;
21/// use chrony_confile::values::{PollInterval, UdpPort};
22///
23/// let directive = ServerBuilder::new("ntp.example.com")
24///     .iburst()
25///     .prefer()
26///     .minpoll(PollInterval::new(4)?)
27///     .maxpoll(PollInterval::new(10)?)
28///     .build();
29///
30/// assert_eq!(directive.kind.name(), "server");
31/// # Ok::<_, Box<dyn std::error::Error>>(())
32/// ```
33#[derive(Debug, Clone)]
34pub struct ServerBuilder {
35    pub(crate) config: ServerConfig,
36}
37
38impl ServerBuilder {
39    /// Creates a new builder for the given hostname.
40    ///
41    /// All other options are initialized to chrony defaults.
42    #[must_use]
43    pub fn new(hostname: impl Into<String>) -> Self {
44        Self {
45            config: ServerConfig {
46                hostname: hostname.into(),
47                ..Default::default()
48            },
49        }
50    }
51
52    /// Enables the `iburst` option (initial burst of quick measurements).
53    #[must_use]
54    pub fn iburst(mut self) -> Self {
55        self.config.iburst = true;
56        self
57    }
58
59    /// Enables the `burst` option (always burst measurements).
60    #[must_use]
61    pub fn burst(mut self) -> Self {
62        self.config.burst = true;
63        self
64    }
65
66    /// Sets the `prefer` selection option.
67    #[must_use]
68    pub fn prefer(mut self) -> Self {
69        self.config.select_opts.set(SelectOptions::PREFER);
70        self
71    }
72
73    /// Sets the `noselect` selection option.
74    #[must_use]
75    pub fn noselect(mut self) -> Self {
76        self.config.select_opts.set(SelectOptions::NOSELECT);
77        self
78    }
79
80    /// Sets the `trust` selection option.
81    #[must_use]
82    pub fn trust(mut self) -> Self {
83        self.config.select_opts.set(SelectOptions::TRUST);
84        self
85    }
86
87    /// Sets the `require` selection option.
88    #[must_use]
89    pub fn require(mut self) -> Self {
90        self.config.select_opts.set(SelectOptions::REQUIRE);
91        self
92    }
93
94    /// Enables NTS (Network Time Security) for this source.
95    #[must_use]
96    pub fn nts(mut self) -> Self {
97        self.config.nts = true;
98        self
99    }
100
101    /// Marks the source as initially offline.
102    #[must_use]
103    pub fn offline(mut self) -> Self {
104        self.config.connectivity = SourceConnectivity::Offline;
105        self
106    }
107
108    /// Sets the minimum polling interval (as a power of 2 in seconds).
109    #[must_use]
110    pub fn minpoll(mut self, poll: PollInterval) -> Self {
111        self.config.minpoll = poll;
112        self
113    }
114
115    /// Sets the maximum polling interval (as a power of 2 in seconds).
116    #[must_use]
117    pub fn maxpoll(mut self, poll: PollInterval) -> Self {
118        self.config.maxpoll = poll;
119        self
120    }
121
122    /// Sets the UDP port for this source.
123    #[must_use]
124    pub fn port(mut self, port: UdpPort) -> Self {
125        self.config.port = port;
126        self
127    }
128
129    /// NTS port for the server.
130    #[must_use]
131    pub fn ntsport(mut self, port: UdpPort) -> Self {
132        self.config.nts_port = port;
133        self
134    }
135
136    /// Sets the maximum expected round-trip delay (seconds).
137    #[must_use]
138    pub fn maxdelay(mut self, delay: f64) -> Self {
139        self.config.max_delay = delay;
140        self
141    }
142
143    /// Max delay ratio.
144    #[must_use]
145    pub fn maxdelayratio(mut self, ratio: f64) -> Self {
146        self.config.max_delay_ratio = ratio;
147        self
148    }
149
150    /// Max delay dev ratio.
151    #[must_use]
152    pub fn maxdelaydevratio(mut self, ratio: f64) -> Self {
153        self.config.max_delay_dev_ratio = ratio;
154        self
155    }
156
157    /// Max delay quantile.
158    #[must_use]
159    pub fn maxdelayquant(mut self, quant: f64) -> Self {
160        self.config.max_delay_quant = quant;
161        self
162    }
163
164    /// Min delay.
165    #[must_use]
166    pub fn mindelay(mut self, delay: f64) -> Self {
167        self.config.min_delay = delay;
168        self
169    }
170
171    /// Asymmetry.
172    #[must_use]
173    pub fn asymmetry(mut self, asym: f64) -> Self {
174        self.config.asymmetry = asym;
175        self
176    }
177
178    /// Auto offline mode.
179    #[must_use]
180    pub fn auto_offline(mut self) -> Self {
181        self.config.auto_offline = true;
182        self
183    }
184
185    /// Interleaved mode (xleave).
186    #[must_use]
187    pub fn xleave(mut self) -> Self {
188        self.config.interleaved = true;
189        self
190    }
191
192    /// Copy peer measurement statistics.
193    #[must_use]
194    pub fn copy(mut self) -> Self {
195        self.config.copy = true;
196        self
197    }
198
199    /// Presend poll interval.
200    #[must_use]
201    pub fn presend(mut self, poll: PollInterval) -> Self {
202        self.config.presend = poll;
203        self
204    }
205
206    /// Filter value.
207    #[must_use]
208    pub fn filter(mut self, n: u32) -> Self {
209        self.config.filter = Some(n);
210        self
211    }
212
213    /// Address family.
214    #[must_use]
215    pub fn family(mut self, family: AddressFamily) -> Self {
216        self.config.family = family;
217        self
218    }
219
220    /// Extension field ID (hex).
221    #[must_use]
222    pub fn extfield(mut self, id: u32) -> Self {
223        self.config.ext_fields = id;
224        self
225    }
226
227    /// Sets the time offset for this source (seconds).
228    #[must_use]
229    pub fn offset(mut self, offset: f64) -> Self {
230        self.config.offset = offset;
231        self
232    }
233
234    /// Sets the NTP protocol version for this source.
235    #[must_use]
236    pub fn version(mut self, version: NtpVersion) -> Self {
237        self.config.version = Some(version);
238        self
239    }
240
241    /// Sets the authentication key ID for this source.
242    #[must_use]
243    pub fn key(mut self, key_id: u32) -> Self {
244        self.config.authkey = key_id;
245        self
246    }
247
248    /// Sets the authentication key ID (alias for `key`).
249    #[must_use]
250    pub fn authkey(mut self, key_id: u32) -> Self {
251        self.config.authkey = key_id;
252        self
253    }
254
255    /// Sets the minimum number of samples to keep.
256    #[must_use]
257    pub fn minsamples(mut self, n: u32) -> Self {
258        self.config.min_samples = Some(n);
259        self
260    }
261
262    /// Sets the maximum number of samples to keep.
263    #[must_use]
264    pub fn maxsamples(mut self, n: u32) -> Self {
265        self.config.max_samples = Some(n);
266        self
267    }
268
269    /// Sets the poll target number of samples.
270    #[must_use]
271    pub fn polltarget(mut self, target: PollTarget) -> Self {
272        self.config.poll_target = target;
273        self
274    }
275
276    /// Sets the minimum stratum for this source.
277    #[must_use]
278    pub fn minstratum(mut self, stratum: Stratum) -> Self {
279        self.config.min_stratum = stratum;
280        self
281    }
282
283    /// Sets the certificate set ID for NTS.
284    #[must_use]
285    pub fn certset(mut self, id: u32) -> Self {
286        self.config.cert_set = id;
287        self
288    }
289
290    /// Builds and returns a `server` directive with the configured options.
291    #[must_use]
292    pub fn build(self) -> Box<Directive> {
293        Box::new(Directive::new(
294            DirectiveKind::Server(self.config),
295            Span::new(None, 0, 0, 0),
296        ))
297    }
298}
299
300/// Builder for creating `pool` directives.
301#[derive(Debug, Clone)]
302pub struct PoolBuilder {
303    config: PoolConfig,
304}
305
306impl PoolBuilder {
307    /// Creates a new builder for the given pool hostname.
308    ///
309    /// All other options are initialized to chrony defaults.
310    #[must_use]
311    pub fn new(hostname: impl Into<String>) -> Self {
312        Self {
313            config: PoolConfig {
314                source: ServerConfig {
315                    hostname: hostname.into(),
316                    ..Default::default()
317                },
318                max_sources: 4,
319            },
320        }
321    }
322
323    /// Enables the `iburst` option (initial burst of quick measurements).
324    #[must_use]
325    pub fn iburst(mut self) -> Self {
326        self.config.source.iburst = true;
327        self
328    }
329
330    #[must_use]
331    pub fn burst(mut self) -> Self {
332        self.config.source.burst = true;
333        self
334    }
335
336    #[must_use]
337    pub fn prefer(mut self) -> Self {
338        self.config.source.select_opts.set(SelectOptions::PREFER);
339        self
340    }
341
342    #[must_use]
343    pub fn noselect(mut self) -> Self {
344        self.config.source.select_opts.set(SelectOptions::NOSELECT);
345        self
346    }
347
348    #[must_use]
349    pub fn trust(mut self) -> Self {
350        self.config.source.select_opts.set(SelectOptions::TRUST);
351        self
352    }
353
354    #[must_use]
355    pub fn require(mut self) -> Self {
356        self.config.source.select_opts.set(SelectOptions::REQUIRE);
357        self
358    }
359
360    #[must_use]
361    pub fn nts(mut self) -> Self {
362        self.config.source.nts = true;
363        self
364    }
365
366    #[must_use]
367    pub fn offline(mut self) -> Self {
368        self.config.source.connectivity = SourceConnectivity::Offline;
369        self
370    }
371
372    #[must_use]
373    pub fn minpoll(mut self, poll: PollInterval) -> Self {
374        self.config.source.minpoll = poll;
375        self
376    }
377
378    #[must_use]
379    pub fn maxpoll(mut self, poll: PollInterval) -> Self {
380        self.config.source.maxpoll = poll;
381        self
382    }
383
384    #[must_use]
385    pub fn port(mut self, port: UdpPort) -> Self {
386        self.config.source.port = port;
387        self
388    }
389
390    #[must_use]
391    pub fn maxdelay(mut self, delay: f64) -> Self {
392        self.config.source.max_delay = delay;
393        self
394    }
395
396    #[must_use]
397    pub fn offset(mut self, offset: f64) -> Self {
398        self.config.source.offset = offset;
399        self
400    }
401
402    #[must_use]
403    pub fn version(mut self, version: NtpVersion) -> Self {
404        self.config.source.version = Some(version);
405        self
406    }
407
408    #[must_use]
409    pub fn key(mut self, key_id: u32) -> Self {
410        self.config.source.authkey = key_id;
411        self
412    }
413
414    /// Sets the minimum number of samples to keep.
415    #[must_use]
416    pub fn minsamples(mut self, n: u32) -> Self {
417        self.config.source.min_samples = Some(n);
418        self
419    }
420
421    /// Sets the maximum number of samples to keep.
422    #[must_use]
423    pub fn maxsamples(mut self, n: u32) -> Self {
424        self.config.source.max_samples = Some(n);
425        self
426    }
427
428    /// Sets the poll target number of samples.
429    #[must_use]
430    pub fn polltarget(mut self, target: PollTarget) -> Self {
431        self.config.source.poll_target = target;
432        self
433    }
434
435    /// Sets the maximum number of sources from this pool.
436    #[must_use]
437    pub fn maxsources(mut self, n: u32) -> Self {
438        self.config.max_sources = n;
439        self
440    }
441
442    /// Builds and returns a `pool` directive with the configured options.
443    #[must_use]
444    pub fn build(self) -> Box<Directive> {
445        Box::new(Directive::new(
446            DirectiveKind::Pool(self.config),
447            Span::new(None, 0, 0, 0),
448        ))
449    }
450}