use crate::helpers;
use crate::ParseError;
use core::convert::TryInto;
use core::fmt;
use core::num::NonZeroU8;
use crate::DateTime;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Edtf {
Date(Date),
Interval(Date, Date),
DateTime(DateTime),
}
impl Edtf {
pub fn parse(input: &str) -> Result<Self, ParseError> {
Self::parse_inner(input).and_then(Self::validate)
}
pub fn as_date(&self) -> Option<Date> {
match self {
Self::Date(d) => Some(*d),
_ => None,
}
}
pub fn as_interval(&self) -> Option<(Date, Date)> {
match self {
Self::Interval(d, d2) => Some((*d, *d2)),
_ => None,
}
}
pub fn as_datetime(&self) -> Option<DateTime> {
match self {
Self::DateTime(d) => Some(*d),
_ => None,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Date {
pub(crate) year: i32,
pub(crate) month: Option<NonZeroU8>,
pub(crate) day: Option<NonZeroU8>,
}
impl fmt::Debug for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
}
}
impl Date {
pub fn year(self) -> i32 {
self.year
}
pub fn month(self) -> u32 {
self.month.map_or(0, |x| x.get()) as u32
}
pub fn day(self) -> u32 {
self.day.map_or(0, |x| x.get()) as u32
}
pub fn parse(input: &str) -> Result<Self, ParseError> {
Self::parse_inner(input).and_then(Self::validate)
}
pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option<Self> {
Date {
year,
month: month.try_into().ok().and_then(NonZeroU8::new),
day: day.try_into().ok().and_then(NonZeroU8::new),
}
.validate()
.ok()
}
pub fn from_ymd(year: i32, month: u32, day: u32) -> Self {
Self::from_ymd_opt(year, month, day).expect("invalid or out-of-range date")
}
}
impl core::str::FromStr for Edtf {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Edtf::parse(s)
}
}
impl fmt::Display for Edtf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Date(d) => write!(f, "{}", d),
Self::Interval(d, d2) => write!(f, "{}/{}", d, d2),
Self::DateTime(dt) => write!(f, "{}", dt),
}
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Date { year, month, day } = *self;
let sign = helpers::sign_str_if_neg(year);
let year = year.abs();
write!(f, "{}{:04}", sign, year)?;
if let Some(month) = month {
write!(f, "-{:02}", month)?;
if let Some(day) = day {
write!(f, "-{:02}", day)?;
}
}
Ok(())
}
}