Skip to main content

chrony_confile/ast/
mod.rs

1//! Chrony configuration AST types.
2//!
3//! This module defines the core data structures representing chrony configuration files:
4//! [`ChronyConfig`], [`ConfigNode`], [`Directive`], [`DirectiveKind`] (all 96 variants),
5//! and [`DirectiveCategory`].
6//!
7//! The sub-modules group configuration types by functional area:
8//! - [`source`] -- NTP source configuration (`server`, `pool`, `peer`, `refclock`, etc.)
9//! - [`selection`] -- Source selection tuning (`minsources`, `maxdistance`, etc.)
10//! - [`clock`] -- System clock configuration (`driftfile`, `makestep`, etc.)
11//! - [`log`] -- Logging configuration (`log`, `logdir`, etc.)
12//! - [`rtc`] -- RTC configuration (`rtcfile`, `rtcdevice`, etc.)
13//! - [`ntp`] -- NTP server configuration (`port`, `bindaddress`, etc.)
14//! - [`cmd`] -- Command access configuration (`cmdport`, `cmdallow`, etc.)
15//! - [`hw`] -- Hardware timestamping configuration (`hwtimestamp`, etc.)
16//! - [`nts`] -- NTS configuration (`ntsaeads`)
17//! - [`misc`] -- Miscellaneous directives (`include`, `confdir`, `user`, etc.)
18//! - [`source_file`] -- `.sources` file parsing types
19
20pub mod source;
21pub mod selection;
22pub mod clock;
23pub mod log;
24pub mod rtc;
25pub mod ntp;
26pub mod cmd;
27pub mod hw;
28pub mod nts;
29pub mod misc;
30pub mod source_file;
31
32pub use source::*;
33pub use selection::*;
34pub use clock::*;
35pub use log::*;
36pub use rtc::*;
37pub use ntp::*;
38pub use cmd::*;
39pub use hw::*;
40pub use nts::*;
41pub use misc::*;
42pub use source_file::*;
43
44use crate::span::Span;
45
46/// Every chrony directive as a single enum variant.
47#[derive(Debug, Clone, PartialEq)]
48pub enum DirectiveKind {
49    // Source
50    Server(ServerConfig),
51    Pool(PoolConfig),
52    Peer(PeerConfig),
53    InitStepSlew(InitStepSlewConfig),
54    RefClock(RefClockConfig),
55    Manual,
56    AcquisitionPort(AcquisitionPortConfig),
57    BindAcqAddress(BindAddressConfig),
58    BindAcqDevice(BindDeviceConfig),
59    Dscp(DscpConfig),
60    DumpDir(DumpDirConfig),
61    MaxSamples(MaxSamplesConfig),
62    MinSamples(MinSamplesConfig),
63    NtsDumpDir(NtsDumpDirConfig),
64    NtsRefresh(NtsRefreshConfig),
65    NtsTrustedCerts(NtsTrustedCertsConfig),
66    NoSystemCert,
67    NoCertTimeCheck(NoCertTimeCheckConfig),
68    Refresh(RefreshConfig),
69
70    // Selection
71    AuthSelectMode(AuthSelectMode),
72    CombineLimit(CombineLimitConfig),
73    MaxDistance(MaxDistanceConfig),
74    MaxJitter(MaxJitterConfig),
75    MinSources(MinSourcesConfig),
76    ReselectDist(ReselectDistConfig),
77    StratumWeight(StratumWeightConfig),
78
79    // System clock
80    ClockPrecision(ClockPrecisionConfig),
81    CorrTimeRatio(CorrTimeRatioConfig),
82    DriftFile(DriftFileConfig),
83    FallbackDrift(FallbackDriftConfig),
84    LeapSecMode(LeapSecMode),
85    LeapSecTz(LeapSecTzConfig),
86    LeapSecList(LeapSecListConfig),
87    MakeStep(MakeStepConfig),
88    MaxChange(MaxChangeConfig),
89    MaxClockError(MaxClockErrorConfig),
90    MaxDrift(MaxDriftConfig),
91    MaxUpdateSkew(MaxUpdateSkewConfig),
92    MaxSlewRate(MaxSlewRateConfig),
93    TempComp(TempCompConfig),
94
95    // NTP server
96    Allow(AllowDenyConfig),
97    Deny(AllowDenyConfig),
98    BindAddress(BindAddressConfig),
99    BindDevice(BindDeviceConfig),
100    Broadcast(BroadcastConfig),
101    ClientLogLimit(ClientLogLimitConfig),
102    NoClientLog,
103    Local(LocalConfig),
104    NtpSignDSocket(NtpSignDSocketConfig),
105    NtsPort(NtsPortConfig),
106    NtsServerCert(NtsServerCertConfig),
107    NtsServerKey(NtsServerKeyConfig),
108    NtsProcesses(NtsProcessesConfig),
109    MaxNtsConnections(MaxNtsConnectionsConfig),
110    NtsNtpServer(NtsNtpServerConfig),
111    NtsRotate(NtsRotateConfig),
112    Port(PortConfig),
113    RateLimit(RateLimitConfig),
114    NtsRateLimit(NtsRateLimitConfig),
115    SmoothTime(SmoothTimeConfig),
116
117    // Command access
118    BindCmdAddress(BindCmdAddressConfig),
119    BindCmdDevice(BindCmdDeviceConfig),
120    CmdAllow(AllowDenyConfig),
121    CmdDeny(AllowDenyConfig),
122    CmdPort(CmdPortConfig),
123    CmdRateLimit(CmdRateLimitConfig),
124    OpenCommands(OpenCommandsConfig),
125
126    // RTC
127    HwClockFile(HwClockFileConfig),
128    RtcAutoTrim(RtcAutoTrimConfig),
129    RtcDevice(RtcDeviceConfig),
130    RtcFile(RtcFileConfig),
131    RtcOnUtc,
132    RtcSync,
133
134    // Log
135    Log(LogConfig),
136    LogBanner(LogBannerConfig),
137    LogChange(LogChangeConfig),
138    LogDir(LogDirConfig),
139
140    // Hardware timestamping
141    HwTimestamp(HwTimestampConfig),
142    HwTsTimeout(HwTsTimeoutConfig),
143    MaxTxBuffers(MaxTxBuffersConfig),
144    PtpPort(PtpPortConfig),
145    PtpDomain(PtpDomainConfig),
146
147    // NTS
148    NtsAeads(NtsAeadsConfig),
149
150    // Miscellaneous
151    ConfDir(ConfDirConfig),
152    Include(IncludeConfig),
153    SourceDir(SourceDirConfig),
154    LockAll,
155    MailOnChange(MailOnChangeConfig),
156    PidFile(PidFileConfig),
157    SchedPriority(SchedPriorityConfig),
158    User(UserConfig),
159    KeyFile(KeyFileConfig),
160    DumpOnExit,
161}
162
163/// A single chrony directive with its source location and comments.
164///
165/// Each directive carries its original source location ([`Span`]), any leading comments
166/// (comment lines immediately preceding the directive), an optional trailing comment
167/// (inline comment after the directive), and the parsed directive body ([`DirectiveKind`]).
168///
169/// Comments are preserved for lossless round-trip serialization.
170///
171/// # Examples
172///
173/// ```rust
174/// use chrony_confile::ChronyConfig;
175///
176/// let config: ChronyConfig = "# My NTP server\nserver ntp.example.com iburst  # preferred\n".parse()?;
177///
178/// let directive = config.directives().next().unwrap();
179/// assert_eq!(directive.leading_comments[0], "My NTP server");
180/// assert_eq!(directive.trailing_comment.as_deref(), Some("preferred"));
181/// # Ok::<_, chrony_confile::ParseError>(())
182/// ```
183#[derive(Debug, Clone, PartialEq)]
184pub struct Directive {
185    /// Source location of the directive.
186    pub span: Span,
187    /// Comments on lines immediately preceding the directive.
188    pub leading_comments: Vec<String>,
189    /// An inline comment after the directive, if present.
190    pub trailing_comment: Option<String>,
191    /// The parsed directive body.
192    pub kind: DirectiveKind,
193}
194
195impl Directive {
196    /// Creates a new directive with the given kind and source span.
197    pub fn new(kind: DirectiveKind, span: Span) -> Self {
198        Self { span, leading_comments: Vec::new(), trailing_comment: None, kind }
199    }
200
201    /// Builder-style method to add a leading comment.
202    #[must_use]
203    pub fn with_leading_comment(mut self, comment: impl Into<String>) -> Self {
204        self.leading_comments.push(comment.into());
205        self
206    }
207
208    /// Builder-style method to add a trailing comment.
209    #[must_use]
210    pub fn with_trailing_comment(mut self, comment: impl Into<String>) -> Self {
211        self.trailing_comment = Some(comment.into());
212        self
213    }
214}
215
216impl From<DirectiveKind> for Directive {
217    fn from(kind: DirectiveKind) -> Self {
218        Self::new(kind, Span::new(None, 0, 0, 0))
219    }
220}
221
222/// A single element in a chrony configuration file.
223///
224/// Each line in a config file is either a directive, a comment, or a blank line.
225///
226/// # Examples
227///
228/// Comments preceding a directive are attached as leading comments to that directive
229/// and do not produce a separate [`ConfigNode::Comment`]. Standalone comments (separated
230/// by blank lines) produce [`ConfigNode::Comment`] nodes.
231///
232/// ```rust
233/// use chrony_confile::{ChronyConfig, ConfigNode};
234///
235/// let config: ChronyConfig = "server ntp.example.com\n\n".parse()?;
236///
237/// assert!(matches!(config.nodes[0], ConfigNode::Directive(_)));
238/// assert!(matches!(config.nodes[1], ConfigNode::BlankLine));
239/// # Ok::<_, chrony_confile::ParseError>(())
240/// ```
241#[derive(Debug, Clone, PartialEq)]
242pub enum ConfigNode {
243    /// A parsed directive with its metadata.
244    Directive(Box<Directive>),
245    /// A comment line (excluding the leading `#` or `;`).
246    Comment(String),
247    /// An empty or whitespace-only line.
248    BlankLine,
249}
250
251impl From<Directive> for ConfigNode {
252    fn from(d: Directive) -> Self {
253        Self::Directive(Box::new(d))
254    }
255}
256
257impl From<DirectiveKind> for ConfigNode {
258    fn from(kind: DirectiveKind) -> Self {
259        Self::Directive(Box::new(Directive::from(kind)))
260    }
261}
262
263/// A complete chrony configuration file.
264///
265/// Represents the parsed content of a `chrony.conf` file (or equivalent configuration).
266/// The configuration is stored as an ordered list of [`ConfigNode`] values, preserving
267/// comments, blank lines, and directives in their original order.
268///
269/// # Parsing
270///
271/// ```rust
272/// use chrony_confile::ChronyConfig;
273///
274/// let config = ChronyConfig::parse("\
275/// server ntp.example.com iburst
276/// pool pool.ntp.org
277/// ")?;
278/// assert_eq!(config.directives().count(), 2);
279/// # Ok::<_, chrony_confile::ParseError>(())
280/// ```
281///
282/// # FromStr
283///
284/// ```rust
285/// use chrony_confile::ChronyConfig;
286///
287/// let config: ChronyConfig = "server ntp.example.com\n".parse()?;
288/// assert!(config.find("server").next().is_some());
289/// # Ok::<_, chrony_confile::ParseError>(())
290/// ```
291///
292/// # Serialization
293///
294/// ```rust
295/// use chrony_confile::ChronyConfig;
296///
297/// let input = "server ntp.example.com iburst\n";
298/// let config = ChronyConfig::parse(input)?;
299/// let output = config.to_string();
300/// assert_eq!(input, output);
301/// # Ok::<_, chrony_confile::ParseError>(())
302/// ```
303#[derive(Debug, Clone, Default, PartialEq)]
304pub struct ChronyConfig {
305    /// The ordered list of configuration nodes (directives, comments, blank lines).
306    pub nodes: Vec<ConfigNode>,
307}
308
309impl ChronyConfig {
310    /// Creates a new, empty configuration.
311    pub fn new() -> Self {
312        Self { nodes: Vec::new() }
313    }
314
315    /// Adds a directive to the configuration.
316    ///
317    /// The directive kind is converted into a full [`Directive`] with default metadata
318    /// (no source span, no comments).
319    ///
320    /// # Examples
321    ///
322    /// ```rust
323    /// use chrony_confile::{ChronyConfig, DirectiveKind};
324    ///
325    /// let mut config = ChronyConfig::new();
326    /// config.push(DirectiveKind::RtcOnUtc);
327    /// config.push(DirectiveKind::LockAll);
328    /// assert_eq!(config.directives().count(), 2);
329    /// ```
330    pub fn push(&mut self, kind: DirectiveKind) {
331        self.nodes.push(ConfigNode::Directive(Box::new(Directive::from(kind))));
332    }
333
334    /// Adds a fully-formed directive node to the configuration.
335    pub fn push_directive(&mut self, directive: Directive) {
336        self.nodes.push(ConfigNode::Directive(Box::new(directive)));
337    }
338
339    /// Adds a comment line to the configuration.
340    pub fn add_comment(&mut self, comment: impl Into<String>) {
341        self.nodes.push(ConfigNode::Comment(comment.into()));
342    }
343
344    /// Adds a blank line to the configuration.
345    pub fn add_blank(&mut self) {
346        self.nodes.push(ConfigNode::BlankLine);
347    }
348
349    /// Returns an iterator over all directives in the configuration.
350    ///
351    /// This excludes comments and blank lines; only [`ConfigNode::Directive`] nodes are yielded.
352    pub fn directives(&self) -> impl Iterator<Item = &Directive> {
353        self.nodes.iter().filter_map(|n| match n {
354            ConfigNode::Directive(d) => Some(d.as_ref()),
355            _ => None,
356        })
357    }
358
359    /// Returns a mutable iterator over all directives in the configuration.
360    ///
361    /// This allows modifying directives in place while preserving their order relative
362    /// to comments and blank lines.
363    pub fn directives_mut(&mut self) -> impl Iterator<Item = &mut Directive> {
364        self.nodes.iter_mut().filter_map(|n| match n {
365            ConfigNode::Directive(d) => Some(d.as_mut()),
366            _ => None,
367        })
368    }
369
370    /// Find all directives with the given directive name.
371    ///
372    /// The name is compared case-sensitively against the directive's canonical name
373    /// (lowercase). Common values include `"server"`, `"pool"`, `"driftfile"`,
374    /// `"allow"`, and `"deny"`.
375    ///
376    /// # Examples
377    ///
378    /// ```rust
379    /// use chrony_confile::ChronyConfig;
380    ///
381    /// let config: ChronyConfig = "\
382    /// server ntp1.example.com
383    /// server ntp2.example.com
384    /// pool pool.ntp.org
385    /// ".parse()?;
386    ///
387    /// assert_eq!(config.find("server").count(), 2);
388    /// assert_eq!(config.find("pool").count(), 1);
389    /// # Ok::<_, chrony_confile::ParseError>(())
390    /// ```
391    pub fn find(&self, name: &str) -> impl Iterator<Item = &Directive> {
392        self.directives().filter(move |d| d.kind.name() == name)
393    }
394}
395
396/// Categorizes directives by their functional area in chrony.
397///
398/// Each [`DirectiveKind`] variant maps to one category via [`DirectiveKind::category`].
399/// This is useful for grouping directives when displaying or filtering configuration.
400///
401/// # Examples
402///
403/// ```rust
404/// use chrony_confile::{DirectiveKind, DirectiveCategory, ServerConfig};
405/// use chrony_confile::values::UdpPort;
406///
407/// let kind = DirectiveKind::Server(ServerConfig::default());
408/// assert_eq!(kind.category(), DirectiveCategory::Source);
409/// ```
410#[derive(Debug, Clone, Copy, PartialEq, Eq)]
411pub enum DirectiveCategory {
412    /// NTP source configuration (`server`, `pool`, `peer`, `refclock`, etc.)
413    Source,
414    /// Source selection tuning (`minsources`, `maxdistance`, etc.)
415    Selection,
416    /// System clock configuration (`driftfile`, `makestep`, etc.)
417    SystemClock,
418    /// NTP server configuration (`allow`, `port`, `bindaddress`, etc.)
419    NtpServer,
420    /// Command access configuration (`cmdallow`, `cmdport`, etc.)
421    CommandAccess,
422    /// RTC configuration (`rtcfile`, `rtcdevice`, etc.)
423    Rtc,
424    /// Logging configuration (`log`, `logdir`, etc.)
425    Log,
426    /// Hardware timestamping configuration (`hwtimestamp`, etc.)
427    HardwareTimestamping,
428    /// NTS (Network Time Security) configuration
429    Nts,
430    /// Miscellaneous directives (`include`, `confdir`, `user`, etc.)
431    Miscellaneous,
432    /// Deprecated directives silently accepted for compatibility
433    Deprecated,
434}
435
436impl DirectiveKind {
437    /// Returns the canonical directive name (e.g. `"server"`, `"driftfile"`, `"allow"`).
438    ///
439    /// This is the lower-case name used in chrony configuration files.
440    pub fn name(&self) -> &'static str {
441        match self {
442            Self::Server(_) => "server",
443            Self::Pool(_) => "pool",
444            Self::Peer(_) => "peer",
445            Self::InitStepSlew(_) => "initstepslew",
446            Self::RefClock(_) => "refclock",
447            Self::Manual => "manual",
448            Self::AcquisitionPort(_) => "acquisitionport",
449            Self::BindAcqAddress(_) => "bindacqaddress",
450            Self::BindAcqDevice(_) => "bindacqdevice",
451            Self::Dscp(_) => "dscp",
452            Self::DumpDir(_) => "dumpdir",
453            Self::MaxSamples(_) => "maxsamples",
454            Self::MinSamples(_) => "minsamples",
455            Self::NtsDumpDir(_) => "ntsdumpdir",
456            Self::NtsRefresh(_) => "ntsrefresh",
457            Self::NtsTrustedCerts(_) => "ntstrustedcerts",
458            Self::NoSystemCert => "nosystemcert",
459            Self::NoCertTimeCheck(_) => "nocerttimecheck",
460            Self::Refresh(_) => "refresh",
461            Self::AuthSelectMode(_) => "authselectmode",
462            Self::CombineLimit(_) => "combinelimit",
463            Self::MaxDistance(_) => "maxdistance",
464            Self::MaxJitter(_) => "maxjitter",
465            Self::MinSources(_) => "minsources",
466            Self::ReselectDist(_) => "reselectdist",
467            Self::StratumWeight(_) => "stratumweight",
468            Self::ClockPrecision(_) => "clockprecision",
469            Self::CorrTimeRatio(_) => "corrtimeratio",
470            Self::DriftFile(_) => "driftfile",
471            Self::FallbackDrift(_) => "fallbackdrift",
472            Self::LeapSecMode(_) => "leapsecmode",
473            Self::LeapSecTz(_) => "leapsectz",
474            Self::LeapSecList(_) => "leapseclist",
475            Self::MakeStep(_) => "makestep",
476            Self::MaxChange(_) => "maxchange",
477            Self::MaxClockError(_) => "maxclockerror",
478            Self::MaxDrift(_) => "maxdrift",
479            Self::MaxUpdateSkew(_) => "maxupdateskew",
480            Self::MaxSlewRate(_) => "maxslewrate",
481            Self::TempComp(_) => "tempcomp",
482            Self::Allow(_) => "allow",
483            Self::Deny(_) => "deny",
484            Self::BindAddress(_) => "bindaddress",
485            Self::BindDevice(_) => "binddevice",
486            Self::Broadcast(_) => "broadcast",
487            Self::ClientLogLimit(_) => "clientloglimit",
488            Self::NoClientLog => "noclientlog",
489            Self::Local(_) => "local",
490            Self::NtpSignDSocket(_) => "ntpsigndsocket",
491            Self::NtsPort(_) => "ntsport",
492            Self::NtsServerCert(_) => "ntsservercert",
493            Self::NtsServerKey(_) => "ntsserverkey",
494            Self::NtsProcesses(_) => "ntsprocesses",
495            Self::MaxNtsConnections(_) => "maxntsconnections",
496            Self::NtsNtpServer(_) => "ntsntpserver",
497            Self::NtsRotate(_) => "ntsrotate",
498            Self::Port(_) => "port",
499            Self::RateLimit(_) => "ratelimit",
500            Self::NtsRateLimit(_) => "ntsratelimit",
501            Self::SmoothTime(_) => "smoothtime",
502            Self::BindCmdAddress(_) => "bindcmdaddress",
503            Self::BindCmdDevice(_) => "bindcmddevice",
504            Self::CmdAllow(_) => "cmdallow",
505            Self::CmdDeny(_) => "cmddeny",
506            Self::CmdPort(_) => "cmdport",
507            Self::CmdRateLimit(_) => "cmdratelimit",
508            Self::OpenCommands(_) => "opencommands",
509            Self::HwClockFile(_) => "hwclockfile",
510            Self::RtcAutoTrim(_) => "rtcautotrim",
511            Self::RtcDevice(_) => "rtcdevice",
512            Self::RtcFile(_) => "rtcfile",
513            Self::RtcOnUtc => "rtconutc",
514            Self::RtcSync => "rtcsync",
515            Self::Log(_) => "log",
516            Self::LogBanner(_) => "logbanner",
517            Self::LogChange(_) => "logchange",
518            Self::LogDir(_) => "logdir",
519            Self::HwTimestamp(_) => "hwtimestamp",
520            Self::HwTsTimeout(_) => "hwtstimeout",
521            Self::MaxTxBuffers(_) => "maxtxbuffers",
522            Self::PtpPort(_) => "ptpport",
523            Self::PtpDomain(_) => "ptpdomain",
524            Self::NtsAeads(_) => "ntsaeads",
525            Self::ConfDir(_) => "confdir",
526            Self::Include(_) => "include",
527            Self::SourceDir(_) => "sourcedir",
528            Self::LockAll => "lock_all",
529            Self::MailOnChange(_) => "mailonchange",
530            Self::PidFile(_) => "pidfile",
531            Self::SchedPriority(_) => "sched_priority",
532            Self::User(_) => "user",
533            Self::KeyFile(_) => "keyfile",
534            Self::DumpOnExit => "dumponexit",
535        }
536    }
537
538    /// Returns the category this directive belongs to.
539    ///
540    /// Categories group directives by their functional area: source configuration,
541    /// selection tuning, system clock, NTP server, command access, RTC, logging,
542    /// hardware timestamping, NTS, and miscellaneous.
543    pub fn category(&self) -> DirectiveCategory {
544        match self {
545            Self::Server(_) | Self::Pool(_) | Self::Peer(_)
546                | Self::InitStepSlew(_) | Self::RefClock(_) | Self::Manual
547                | Self::AcquisitionPort(_) | Self::BindAcqAddress(_)
548                | Self::BindAcqDevice(_) | Self::Dscp(_) | Self::DumpDir(_)
549                | Self::MaxSamples(_) | Self::MinSamples(_) | Self::NtsDumpDir(_)
550                | Self::NtsRefresh(_) | Self::NtsTrustedCerts(_)
551                | Self::NoSystemCert | Self::NoCertTimeCheck(_)
552                | Self::Refresh(_) => DirectiveCategory::Source,
553            Self::AuthSelectMode(_) | Self::CombineLimit(_)
554                | Self::MaxDistance(_) | Self::MaxJitter(_)
555                | Self::MinSources(_) | Self::ReselectDist(_)
556                | Self::StratumWeight(_) => DirectiveCategory::Selection,
557            Self::ClockPrecision(_) | Self::CorrTimeRatio(_)
558                | Self::DriftFile(_) | Self::FallbackDrift(_)
559                | Self::LeapSecMode(_) | Self::LeapSecTz(_)
560                | Self::LeapSecList(_) | Self::MakeStep(_)
561                | Self::MaxChange(_) | Self::MaxClockError(_)
562                | Self::MaxDrift(_) | Self::MaxUpdateSkew(_)
563                | Self::MaxSlewRate(_) | Self::TempComp(_) => DirectiveCategory::SystemClock,
564            Self::Allow(_) | Self::Deny(_) | Self::BindAddress(_)
565                | Self::BindDevice(_) | Self::Broadcast(_)
566                | Self::ClientLogLimit(_) | Self::NoClientLog
567                | Self::Local(_) | Self::NtpSignDSocket(_)
568                | Self::NtsPort(_) | Self::NtsServerCert(_)
569                | Self::NtsServerKey(_) | Self::NtsProcesses(_)
570                | Self::MaxNtsConnections(_) | Self::NtsNtpServer(_)
571                | Self::NtsRotate(_) | Self::Port(_) | Self::RateLimit(_)
572                | Self::NtsRateLimit(_) | Self::SmoothTime(_) => DirectiveCategory::NtpServer,
573            Self::BindCmdAddress(_) | Self::BindCmdDevice(_)
574                | Self::CmdAllow(_) | Self::CmdDeny(_)
575                | Self::CmdPort(_) | Self::CmdRateLimit(_)
576                | Self::OpenCommands(_) => DirectiveCategory::CommandAccess,
577            Self::HwClockFile(_) | Self::RtcAutoTrim(_)
578                | Self::RtcDevice(_) | Self::RtcFile(_)
579                | Self::RtcOnUtc | Self::RtcSync => DirectiveCategory::Rtc,
580            Self::Log(_) | Self::LogBanner(_) | Self::LogChange(_)
581                | Self::LogDir(_) => DirectiveCategory::Log,
582            Self::HwTimestamp(_) | Self::HwTsTimeout(_)
583                | Self::MaxTxBuffers(_) | Self::PtpPort(_)
584                | Self::PtpDomain(_) => DirectiveCategory::HardwareTimestamping,
585            Self::NtsAeads(_) => DirectiveCategory::Nts,
586            Self::ConfDir(_) | Self::Include(_) | Self::SourceDir(_)
587                | Self::LockAll | Self::MailOnChange(_) | Self::PidFile(_)
588                | Self::SchedPriority(_) | Self::User(_) | Self::KeyFile(_)
589                | Self::DumpOnExit => DirectiveCategory::Miscellaneous,
590        }
591    }
592
593    /// Returns `true` if this directive is deprecated and silently accepted for backward
594    /// compatibility.
595    pub fn is_deprecated(&self) -> bool {
596        matches!(self, Self::DumpOnExit)
597    }
598}