Skip to main content

holidays_ru/
resolved.rs

1/// Результат запроса к календарю: факт или прогноз.
2///
3/// # Семантика
4///
5/// - `Fact(T)` — данные присутствуют в официальной таблице производственного календаря.
6/// - `Predict(T)` — официальной таблицы нет, результат получен алгоритмическим прогнозом.
7///
8/// # Примеры
9///
10/// ```rust
11/// use holidays_ru::Resolved;
12///
13/// let r: Resolved<i32> = Resolved::Fact(42);
14/// assert!(r.is_fact());
15/// assert_eq!(r.value(), 42);
16///
17/// let mapped = r.map(|v| v * 2);
18/// assert_eq!(mapped, Resolved::Fact(84));
19/// ```
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum Resolved<T> {
22    /// Результат основан на официальных данных производственного календаря.
23    Fact(T),
24
25    /// Результат получен алгоритмическим прогнозом.
26    Predict(T),
27}
28
29impl<T> Resolved<T> {
30    /// Извлекает значение, отбрасывая информацию о происхождении.
31    ///
32    /// ```rust
33    /// use holidays_ru::Resolved;
34    ///
35    /// assert_eq!(Resolved::Fact(10).value(), 10);
36    /// assert_eq!(Resolved::Predict(20).value(), 20);
37    /// ```
38    #[inline]
39    pub fn value(self) -> T {
40        match self {
41            Self::Fact(value) | Self::Predict(value) => value,
42        }
43    }
44
45    /// Возвращает `true`, если результат основан на официальных данных.
46    #[inline]
47    pub const fn is_fact(&self) -> bool {
48        matches!(self, Self::Fact(_))
49    }
50
51    /// Возвращает `true`, если результат получен прогнозом.
52    #[inline]
53    pub const fn is_predict(&self) -> bool {
54        matches!(self, Self::Predict(_))
55    }
56
57    /// Применяет функцию к внутреннему значению, сохраняя обёртку `Fact`/`Predict`.
58    #[inline]
59    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Resolved<U> {
60        match self {
61            Self::Fact(value) => Resolved::Fact(f(value)),
62            Self::Predict(value) => Resolved::Predict(f(value)),
63        }
64    }
65
66    /// Возвращает внутреннее значение как ссылку.
67    #[inline]
68    pub const fn as_ref(&self) -> Resolved<&T> {
69        match self {
70            Self::Fact(value) => Resolved::Fact(value),
71            Self::Predict(value) => Resolved::Predict(value),
72        }
73    }
74}
75
76#[cfg(feature = "serde")]
77impl<T: serde::Serialize> serde::Serialize for Resolved<T> {
78    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
79        use serde::ser::SerializeStruct;
80
81        let mut s = serializer.serialize_struct("Resolved", 2)?;
82        match self {
83            Self::Fact(value) => {
84                s.serialize_field("kind", "Fact")?;
85                s.serialize_field("value", value)?;
86            }
87            Self::Predict(value) => {
88                s.serialize_field("kind", "Predict")?;
89                s.serialize_field("value", value)?;
90            }
91        }
92        s.end()
93    }
94}
95
96#[cfg(feature = "serde")]
97impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Resolved<T> {
98    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
99        #[derive(serde::Deserialize)]
100        #[serde(field_identifier, rename_all = "snake_case")]
101        enum Field {
102            Kind,
103            Value,
104        }
105
106        struct Visitor<T>(std::marker::PhantomData<T>);
107
108        impl<'de, T: serde::Deserialize<'de>> serde::de::Visitor<'de> for Visitor<T> {
109            type Value = Resolved<T>;
110
111            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
112                f.write_str("struct Resolved")
113            }
114
115            fn visit_map<A: serde::de::MapAccess<'de>>(
116                self,
117                mut map: A,
118            ) -> Result<Self::Value, A::Error> {
119                let mut kind: Option<String> = None;
120                let mut value: Option<T> = None;
121
122                while let Some(key) = map.next_key()? {
123                    match key {
124                        Field::Kind => {
125                            if kind.is_some() {
126                                return Err(serde::de::Error::duplicate_field("kind"));
127                            }
128                            kind = Some(map.next_value()?);
129                        }
130                        Field::Value => {
131                            if value.is_some() {
132                                return Err(serde::de::Error::duplicate_field("value"));
133                            }
134                            value = Some(map.next_value()?);
135                        }
136                    }
137                }
138
139                let kind = kind.ok_or_else(|| serde::de::Error::missing_field("kind"))?;
140                let value = value.ok_or_else(|| serde::de::Error::missing_field("value"))?;
141
142                match kind.as_str() {
143                    "Fact" => Ok(Resolved::Fact(value)),
144                    "Predict" => Ok(Resolved::Predict(value)),
145                    other => Err(serde::de::Error::unknown_variant(
146                        other,
147                        &["Fact", "Predict"],
148                    )),
149                }
150            }
151        }
152
153        deserializer.deserialize_struct(
154            "Resolved",
155            &["kind", "value"],
156            Visitor(std::marker::PhantomData),
157        )
158    }
159}
160
161#[cfg(all(test, feature = "serde"))]
162mod serde_tests {
163    use super::*;
164
165    #[test]
166    fn test_serde_json_fact_format() {
167        let value = Resolved::Fact(42u8);
168        let json = serde_json::to_string(&value).unwrap();
169
170        assert_eq!(json, r#"{"kind":"Fact","value":42}"#);
171        assert_eq!(serde_json::from_str::<Resolved<u8>>(&json).unwrap(), value);
172    }
173
174    #[test]
175    fn test_serde_json_predict_format() {
176        let value = Resolved::Predict(false);
177        let json = serde_json::to_string(&value).unwrap();
178
179        assert_eq!(json, r#"{"kind":"Predict","value":false}"#);
180        assert_eq!(
181            serde_json::from_str::<Resolved<bool>>(&json).unwrap(),
182            value
183        );
184    }
185}