systemd_unit_edit/
systemd_metadata.rs

1//! Systemd-specific metadata and domain knowledge
2//!
3//! This module contains information about systemd directives, their types,
4//! and how they should be merged in drop-in files.
5
6/// Check if a directive is accumulating (values should be added rather than replaced)
7///
8/// In systemd, some directives accumulate values across drop-ins (like Wants=, After=),
9/// while others replace (like Description=, Type=).
10///
11/// This is based on systemd's behavior where certain directives are list-based
12/// and should accumulate when multiple values are specified across the main unit
13/// and drop-in files.
14pub fn is_accumulating_directive(key: &str) -> bool {
15    matches!(
16        key,
17        // Unit dependencies and ordering
18        "Wants"
19            | "Requires"
20            | "Requisite"
21            | "BindsTo"
22            | "PartOf"
23            | "Upholds"
24            | "After"
25            | "Before"
26            | "Conflicts"
27            | "OnFailure"
28            | "OnSuccess"
29            | "PropagatesReloadTo"
30            | "ReloadPropagatedFrom"
31            | "PropagatesStopTo"
32            | "StopPropagatedFrom"
33            | "JoinsNamespaceOf"
34            | "RequiresMountsFor"
35            | "OnSuccessJobMode"
36            | "OnFailureJobMode"
37            // Environment
38            | "Environment"
39            | "EnvironmentFile"
40            | "PassEnvironment"
41            | "UnsetEnvironment"
42            // Execution
43            | "ExecStartPre"
44            | "ExecStartPost"
45            | "ExecCondition"
46            | "ExecReload"
47            | "ExecStop"
48            | "ExecStopPost"
49            // Groups and users
50            | "SupplementaryGroups"
51            // Paths and security
52            | "ReadWritePaths"
53            | "ReadOnlyPaths"
54            | "InaccessiblePaths"
55            | "ExecPaths"
56            | "NoExecPaths"
57            | "ExecSearchPath"
58            | "LogExtraFields"
59            | "RestrictAddressFamilies"
60            | "SystemCallFilter"
61            | "SystemCallLog"
62            | "SystemCallArchitectures"
63            | "RestrictNetworkInterfaces"
64            | "BindPaths"
65            | "BindReadOnlyPaths"
66            // Device access
67            | "DeviceAllow"
68            // Sockets
69            | "ListenStream"
70            | "ListenDatagram"
71            | "ListenSequentialPacket"
72            | "ListenFIFO"
73            | "ListenSpecial"
74            | "ListenNetlink"
75            | "ListenMessageQueue"
76            | "ListenUSBFunction"
77            // Path
78            | "PathExists"
79            | "PathExistsGlob"
80            | "PathChanged"
81            | "PathModified"
82            | "DirectoryNotEmpty"
83            // Timer
84            | "OnActiveSec"
85            | "OnBootSec"
86            | "OnStartupSec"
87            | "OnUnitActiveSec"
88            | "OnUnitInactiveSec"
89            | "OnCalendar"
90    )
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_accumulating_directives() {
99        assert!(is_accumulating_directive("Wants"));
100        assert!(is_accumulating_directive("After"));
101        assert!(is_accumulating_directive("Requires"));
102        assert!(is_accumulating_directive("Environment"));
103    }
104
105    #[test]
106    fn test_non_accumulating_directives() {
107        assert!(!is_accumulating_directive("Description"));
108        assert!(!is_accumulating_directive("Type"));
109        assert!(!is_accumulating_directive("ExecStart"));
110        assert!(!is_accumulating_directive("User"));
111    }
112}