use std::{
collections::BTreeMap,
fmt::{Debug, Display},
num::ParseIntError,
str::FromStr,
};
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use crate::ordinaries::OrdinaryValue;
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum Date {
Single {
date: DateParts,
meta: DateMeta,
},
Range {
start: DateParts,
end: DateParts,
meta: DateMeta,
},
Raw {
date: String,
meta: DateMeta,
},
Edtf {
date: String,
meta: DateMeta,
},
}
#[derive(Debug, Default, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
struct DateInternal {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
date_parts: Vec<DateParts>,
#[serde(default, skip_serializing_if = "Option::is_none")]
season: Option<Season>,
#[serde(default, skip_serializing_if = "Option::is_none")]
circa: Option<Circa>,
#[serde(default, skip_serializing_if = "Option::is_none")]
literal: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
raw: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
edtf: Option<String>,
#[serde(flatten)]
extra: BTreeMap<String, OrdinaryValue>,
}
impl Date {
pub fn meta(&self) -> &DateMeta {
match self {
Self::Single { meta, .. }
| Self::Range { meta, .. }
| Self::Raw { meta, .. }
| Self::Edtf { meta, .. } => meta,
}
}
}
impl Serialize for Date {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let meta = self.meta().clone();
let mut internal = DateInternal {
season: meta.season,
circa: meta.circa,
literal: meta.literal,
extra: meta.extra,
..Default::default()
};
match self {
Self::Single { date, .. } => {
internal.date_parts = vec![*date];
}
Self::Range { start, end, .. } => {
internal.date_parts = vec![*start, *end];
}
Self::Raw { date, .. } => {
internal.raw = Some(date.clone());
}
Self::Edtf { date, .. } => {
internal.edtf = Some(date.clone());
}
}
internal.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Date {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
D::Error: serde::de::Error,
{
let internal = DateInternal::deserialize(deserializer)?;
if internal.date_parts.len() == 1 {
Ok(Self::Single {
date: internal.date_parts[0],
meta: DateMeta::from_internal(internal),
})
} else if internal.date_parts.len() == 2 {
Ok(Self::Range {
start: internal.date_parts[0],
end: internal.date_parts[1],
meta: DateMeta::from_internal(internal),
})
} else if let Some(date) = &internal.edtf {
Ok(Self::Edtf {
date: date.clone(),
meta: DateMeta::from_internal(internal),
})
} else if let Some(date) = &internal.raw {
Ok(Self::Raw {
date: date.clone(),
meta: DateMeta::from_internal(internal),
})
} else {
Err(D::Error::custom("unknown date format".to_string()))
}
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "DatePartsInternal", into = "DatePartsInternal")]
pub struct DateParts {
pub year: i64,
pub month: Option<u8>,
pub day: Option<u8>,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
struct DatePartsInternal(
StrumI64,
#[serde(default, skip_serializing_if = "Option::is_none")] Option<StrumU8>,
#[serde(default, skip_serializing_if = "Option::is_none")] Option<StrumU8>,
);
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
enum StrumI64 {
String(String),
Num(i64),
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
enum StrumU8 {
String(String),
Num(u8),
}
impl TryFrom<StrumI64> for i64 {
type Error = ParseIntError;
fn try_from(value: StrumI64) -> Result<Self, Self::Error> {
match value {
StrumI64::String(s) => s.parse(),
StrumI64::Num(t) => Ok(t),
}
}
}
impl TryFrom<StrumU8> for u8 {
type Error = ParseIntError;
fn try_from(value: StrumU8) -> Result<Self, Self::Error> {
match value {
StrumU8::String(s) => s.parse(),
StrumU8::Num(t) => Ok(t),
}
}
}
impl TryFrom<DatePartsInternal> for DateParts {
type Error = ParseIntError;
fn try_from(
DatePartsInternal(year, month, day): DatePartsInternal,
) -> Result<Self, Self::Error> {
Ok(Self {
year: year.try_into()?,
month: month.map(|m| m.try_into()).transpose()?,
day: day.map(|d| d.try_into()).transpose()?,
})
}
}
impl From<DateParts> for DatePartsInternal {
fn from(parts: DateParts) -> Self {
Self(
StrumI64::Num(parts.year),
parts.month.map(StrumU8::Num),
parts.day.map(StrumU8::Num),
)
}
}
#[derive(Debug, Default, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct DateMeta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub season: Option<Season>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub circa: Option<Circa>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub literal: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, OrdinaryValue>,
}
impl DateMeta {
fn from_internal(internal: DateInternal) -> Self {
Self {
season: internal.season,
circa: internal.circa,
literal: internal.literal,
extra: internal.extra,
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Circa {
Arbitrary(String),
Year(i64),
Bool(bool),
}
impl Circa {
pub fn as_arbitrary(&self) -> Option<&str> {
if let Self::Arbitrary(str) = self {
Some(str.as_ref())
} else {
None
}
}
pub fn as_year(&self) -> Option<i64> {
if let Self::Year(num) = self {
Some(*num)
} else {
None
}
}
pub fn as_bool(&self) -> Option<bool> {
if let Self::Bool(b) = self {
Some(*b)
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum Season {
Spring,
Summer,
Autumn,
Winter,
}
impl Display for Season {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Spring => "spring",
Self::Summer => "summer",
Self::Autumn => "autumn",
Self::Winter => "winter",
}
)
}
}
impl FromStr for Season {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"spring" | "season-01" => Ok(Self::Spring),
"summer" | "season-02" => Ok(Self::Summer),
"autumn" | "season-03" => Ok(Self::Autumn),
"winter" | "season-04" => Ok(Self::Winter),
other => Err(format!("unknown season: {other:?}")),
}
}
}
impl Serialize for Season {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = self.to_string();
s.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Season {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Season::from_str(&s).map_err(D::Error::custom)
}
}