Skip to main content

statime_config/tree/
section.rs

1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2
3use crate::{
4    ConfigError,
5    tree::{
6        empty::EffectivelyUnset,
7        merge::{Merge, MergeContext, OriginId},
8        partial_value::PartialValue,
9        path::ConfigPath,
10    },
11};
12
13/// Sections are merge-able partial values within a configuration tree.
14#[derive(Default, Debug, Clone, PartialEq, Eq)]
15pub enum Section<T> {
16    #[default]
17    Unset,
18    Set(T),
19}
20
21impl<T> Section<T> {
22    pub fn is_unset(&self) -> bool {
23        matches!(self, Section::Unset)
24    }
25}
26
27impl<'de, T> Deserialize<'de> for Section<T>
28where
29    T: Deserialize<'de>,
30{
31    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
32    where
33        D: Deserializer<'de>,
34    {
35        T::deserialize(deserializer).map(Self::Set)
36    }
37}
38
39impl<T> Serialize for Section<T>
40where
41    T: Serialize,
42{
43    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
44    where
45        S: Serializer,
46    {
47        match self {
48            Self::Set(value) => value.serialize(serializer),
49            // note: sections should be marked to be skipped and should never serialize to none
50            Self::Unset => serializer.serialize_none(),
51        }
52    }
53}
54
55/// A section says nothing when it is absent, but also when it is present with
56/// every descendant setting unset.
57impl<T> EffectivelyUnset for Section<T>
58where
59    T: EffectivelyUnset,
60{
61    fn is_effectively_unset(&self) -> bool {
62        match self {
63            Section::Unset => true,
64            Section::Set(value) => value.is_effectively_unset(),
65        }
66    }
67}
68
69impl<T> PartialValue for Section<T>
70where
71    T: PartialValue + Default,
72{
73    type Resolved = T::Resolved;
74
75    /// A section records no origin of its own, and visits its children.
76    fn attribute(&mut self, origin: OriginId) {
77        if let Section::Set(value) = self {
78            value.attribute(origin);
79        }
80    }
81
82    /// An absent section still has to be visited: a document that never
83    /// mentions a section should still get the defaults of everything inside
84    /// it.
85    fn apply_defaults(&mut self) {
86        if self.is_unset() {
87            *self = Section::Set(T::default());
88        }
89
90        if let Section::Set(value) = self {
91            value.apply_defaults();
92        }
93    }
94
95    /// A section resolves to whatever its contents resolve to. An unset one
96    /// only reaches this point when resolving without defaulting first, and
97    /// resolving the empty struct still reports any required value it misses.
98    fn resolve(self, path: &mut ConfigPath) -> Result<Self::Resolved, ConfigError> {
99        match self {
100            Section::Set(value) => value.resolve(path),
101            Section::Unset => T::default().resolve(path),
102        }
103    }
104}
105
106/// A section is a recursive merge boundary: two documents mentioning the same
107/// table is never a conflict by itself, so the policy is only consulted by the
108/// settings underneath.
109impl<T> Merge for Section<T>
110where
111    T: Merge,
112{
113    fn merge(&mut self, incoming: Self, context: &mut MergeContext<'_>) -> Result<(), ConfigError> {
114        let Section::Set(incoming) = incoming else {
115            return Ok(());
116        };
117
118        match self {
119            Section::Set(existing) => existing.merge(incoming, context),
120            Section::Unset => {
121                *self = Section::Set(incoming);
122                Ok(())
123            }
124        }
125    }
126}