1use std::{
4 collections::BTreeMap,
5 fmt::{Debug, Display},
6 num::ParseIntError,
7 str::FromStr,
8};
9
10use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
11
12use crate::ordinaries::OrdinaryValue;
13
14#[derive(Debug, Clone, Hash, Eq, PartialEq)]
36pub enum Date {
37 Single {
39 date: DateParts,
41
42 meta: DateMeta,
44 },
45
46 Range {
48 start: DateParts,
50
51 end: DateParts,
53
54 meta: DateMeta,
56 },
57
58 Raw {
60 date: String,
62
63 meta: DateMeta,
65 },
66
67 Edtf {
69 date: String,
71
72 meta: DateMeta,
74 },
75}
76
77#[derive(Debug, Default, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
78#[serde(rename_all = "kebab-case")]
79struct DateInternal {
80 #[serde(default, skip_serializing_if = "Vec::is_empty")]
81 date_parts: Vec<DateParts>,
82
83 #[serde(default, skip_serializing_if = "Option::is_none")]
84 season: Option<Season>,
85
86 #[serde(default, skip_serializing_if = "Option::is_none")]
87 circa: Option<Circa>,
88
89 #[serde(default, skip_serializing_if = "Option::is_none")]
90 literal: Option<String>,
91
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 raw: Option<String>,
94
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 edtf: Option<String>,
97
98 #[serde(flatten)]
99 extra: BTreeMap<String, OrdinaryValue>,
100}
101
102impl Date {
103 pub fn meta(&self) -> &DateMeta {
105 match self {
106 Self::Single { meta, .. }
107 | Self::Range { meta, .. }
108 | Self::Raw { meta, .. }
109 | Self::Edtf { meta, .. } => meta,
110 }
111 }
112}
113
114impl Serialize for Date {
115 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116 where
117 S: Serializer,
118 {
119 let meta = self.meta().clone();
120 let mut internal = DateInternal {
121 season: meta.season,
122 circa: meta.circa,
123 literal: meta.literal,
124 extra: meta.extra,
125 ..Default::default()
126 };
127
128 match self {
129 Self::Single { date, .. } => {
130 internal.date_parts = vec![*date];
131 }
132 Self::Range { start, end, .. } => {
133 internal.date_parts = vec![*start, *end];
134 }
135 Self::Raw { date, .. } => {
136 internal.raw = Some(date.clone());
137 }
138 Self::Edtf { date, .. } => {
139 internal.edtf = Some(date.clone());
140 }
141 }
142
143 internal.serialize(serializer)
144 }
145}
146
147impl<'de> Deserialize<'de> for Date {
148 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
149 where
150 D: Deserializer<'de>,
151 D::Error: serde::de::Error,
152 {
153 let internal = DateInternal::deserialize(deserializer)?;
154
155 if internal.date_parts.len() == 1 {
156 Ok(Self::Single {
157 date: internal.date_parts[0],
158 meta: DateMeta::from_internal(internal),
159 })
160 } else if internal.date_parts.len() == 2 {
161 Ok(Self::Range {
162 start: internal.date_parts[0],
163 end: internal.date_parts[1],
164 meta: DateMeta::from_internal(internal),
165 })
166 } else if let Some(date) = &internal.edtf {
167 Ok(Self::Edtf {
168 date: date.clone(),
169 meta: DateMeta::from_internal(internal),
170 })
171 } else if let Some(date) = &internal.raw {
172 Ok(Self::Raw {
173 date: date.clone(),
174 meta: DateMeta::from_internal(internal),
175 })
176 } else {
177 Err(D::Error::custom("unknown date format".to_string()))
178 }
179 }
180}
181
182#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
186#[serde(try_from = "DatePartsInternal", into = "DatePartsInternal")]
187pub struct DateParts {
188 pub year: i64,
190
191 pub month: Option<u8>,
193
194 pub day: Option<u8>,
196}
197
198#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
199struct DatePartsInternal(
200 StrumI64,
201 #[serde(default, skip_serializing_if = "Option::is_none")] Option<StrumU8>,
202 #[serde(default, skip_serializing_if = "Option::is_none")] Option<StrumU8>,
203);
204
205#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
206#[serde(untagged)]
207enum StrumI64 {
208 String(String),
209 Num(i64),
210}
211
212#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
213#[serde(untagged)]
214enum StrumU8 {
215 String(String),
216 Num(u8),
217}
218
219impl TryFrom<StrumI64> for i64 {
220 type Error = ParseIntError;
221
222 fn try_from(value: StrumI64) -> Result<Self, Self::Error> {
223 match value {
224 StrumI64::String(s) => s.parse(),
225 StrumI64::Num(t) => Ok(t),
226 }
227 }
228}
229
230impl TryFrom<StrumU8> for u8 {
231 type Error = ParseIntError;
232
233 fn try_from(value: StrumU8) -> Result<Self, Self::Error> {
234 match value {
235 StrumU8::String(s) => s.parse(),
236 StrumU8::Num(t) => Ok(t),
237 }
238 }
239}
240
241impl TryFrom<DatePartsInternal> for DateParts {
242 type Error = ParseIntError;
243
244 fn try_from(
245 DatePartsInternal(year, month, day): DatePartsInternal,
246 ) -> Result<Self, Self::Error> {
247 Ok(Self {
248 year: year.try_into()?,
249 month: month.map(|m| m.try_into()).transpose()?,
250 day: day.map(|d| d.try_into()).transpose()?,
251 })
252 }
253}
254
255impl From<DateParts> for DatePartsInternal {
256 fn from(parts: DateParts) -> Self {
257 Self(
258 StrumI64::Num(parts.year),
259 parts.month.map(StrumU8::Num),
260 parts.day.map(StrumU8::Num),
261 )
262 }
263}
264
265#[derive(Debug, Default, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
266pub struct DateMeta {
268 #[serde(default, skip_serializing_if = "Option::is_none")]
270 pub season: Option<Season>,
271
272 #[serde(default, skip_serializing_if = "Option::is_none")]
278 pub circa: Option<Circa>, #[serde(default, skip_serializing_if = "Option::is_none")]
282 pub literal: Option<String>,
283
284 #[serde(flatten)]
286 pub extra: BTreeMap<String, OrdinaryValue>,
287}
288
289impl DateMeta {
290 fn from_internal(internal: DateInternal) -> Self {
291 Self {
292 season: internal.season,
293 circa: internal.circa,
294 literal: internal.literal,
295 extra: internal.extra,
296 }
297 }
298}
299
300#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
307#[serde(untagged)]
308pub enum Circa {
309 Arbitrary(String),
311
312 Year(i64),
314
315 Bool(bool),
317}
318
319impl Circa {
320 pub fn as_arbitrary(&self) -> Option<&str> {
322 if let Self::Arbitrary(str) = self {
323 Some(str.as_ref())
324 } else {
325 None
326 }
327 }
328
329 pub fn as_year(&self) -> Option<i64> {
331 if let Self::Year(num) = self {
332 Some(*num)
333 } else {
334 None
335 }
336 }
337
338 pub fn as_bool(&self) -> Option<bool> {
340 if let Self::Bool(b) = self {
341 Some(*b)
342 } else {
343 None
344 }
345 }
346}
347
348#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
353pub enum Season {
354 Spring,
356
357 Summer,
359
360 Autumn,
362
363 Winter,
365}
366
367impl Display for Season {
368 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369 write!(
370 f,
371 "{}",
372 match self {
373 Self::Spring => "spring",
374 Self::Summer => "summer",
375 Self::Autumn => "autumn",
376 Self::Winter => "winter",
377 }
378 )
379 }
380}
381
382impl FromStr for Season {
383 type Err = String;
384
385 fn from_str(s: &str) -> Result<Self, Self::Err> {
386 match s.to_lowercase().as_str() {
387 "spring" | "season-01" => Ok(Self::Spring),
388 "summer" | "season-02" => Ok(Self::Summer),
389 "autumn" | "season-03" => Ok(Self::Autumn),
390 "winter" | "season-04" => Ok(Self::Winter),
391 other => Err(format!("unknown season: {other:?}")),
392 }
393 }
394}
395
396impl Serialize for Season {
397 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
398 where
399 S: Serializer,
400 {
401 let s = self.to_string();
402 s.serialize(serializer)
403 }
404}
405
406impl<'de> Deserialize<'de> for Season {
407 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
408 where
409 D: Deserializer<'de>,
410 {
411 let s = String::deserialize(deserializer)?;
412 Season::from_str(&s).map_err(D::Error::custom)
413 }
414}