use crate::{common::is_valid_complete_date, ParseError};
use core::num::NonZeroU8;
mod parser;
use crate::common::{UnvalidatedTime, UnvalidatedTz};
use crate::{DateComplete, DateTime, Time, TzOffset};
use parser::ParsedEdtf;
pub mod api;
use api::*;
impl Edtf {
fn validate(parsed: ParsedEdtf) -> Result<Self, ParseError> {
let edtf = match parsed {
ParsedEdtf::Date(d) => Edtf::Date(d.validate()?),
ParsedEdtf::Interval(d, d2) => Edtf::Interval(d.validate()?, d2.validate()?),
ParsedEdtf::DateTime(d, t) => Edtf::DateTime(DateTime::validate(d, t)?),
};
Ok(edtf)
}
}
impl DateComplete {
pub(crate) fn validate(self) -> Result<Self, ParseError> {
let Self { year, month, day } = self;
let v = is_valid_complete_date(year, month.get(), day.get())?;
Ok(v)
}
}
impl DateTime {
pub(crate) fn validate(date: DateComplete, time: UnvalidatedTime) -> Result<Self, ParseError> {
let date = date.validate()?;
let time = time.validate()?;
Ok(DateTime { date, time })
}
}
impl UnvalidatedTz {
fn validate(self) -> Result<TzOffset, ParseError> {
match self {
Self::Unspecified => Ok(TzOffset::Unspecified),
Self::Utc => Ok(TzOffset::Utc),
Self::Hours { positive, hh } => {
let sign = if positive { 1 } else { -1 };
if hh > 23 {
return Err(ParseError::OutOfRange);
}
Ok(TzOffset::Hours(sign * hh as i32))
}
Self::HoursMinutes { positive, hh, mm } => {
if hh > 23 || mm > 59 {
return Err(ParseError::OutOfRange);
}
let sign = if positive { 1 } else { -1 };
let mins = 60 * hh as i32 + mm as i32;
Ok(TzOffset::Minutes(sign * mins))
}
}
}
}
impl UnvalidatedTime {
pub(crate) fn validate(self) -> Result<Time, ParseError> {
let Self { hh, mm, ss, tz } = self;
let tz = tz.validate()?;
if hh > 23 || mm > 59 || ss > 60 {
return Err(ParseError::OutOfRange);
}
if ss == 60 && !(hh == 23 && mm == 59) {
return Err(ParseError::OutOfRange);
}
Ok(Time { hh, mm, ss, tz })
}
}
impl Date {
fn new_unvalidated(year: i32, month: Option<NonZeroU8>, day: Option<NonZeroU8>) -> Self {
Date { year, month, day }
}
fn validate(self) -> Result<Self, ParseError> {
if self.year > 9999 || self.year < 0 {
return Err(ParseError::OutOfRange);
}
if let Some(m) = self.month.map(NonZeroU8::get) {
if let Some(d) = self.day.map(NonZeroU8::get) {
let _complete = is_valid_complete_date(self.year, m, d)?;
} else if m > 12 {
return Err(ParseError::OutOfRange);
}
} else if self.day.is_some() {
return Err(ParseError::OutOfRange);
}
Ok(self)
}
}
#[cfg(feature = "chrono")]
fn fixed_offset_from(positive: bool, hh: u8, mm: u8) -> Option<chrono::FixedOffset> {
let secs = 3600 * hh as i32 + 60 * mm as i32;
if positive {
chrono::FixedOffset::east_opt(secs)
} else {
chrono::FixedOffset::west_opt(secs)
}
}