use alloc::vec::Vec;
use crate::{
bounds::{
big_width, day_candidates_of, is_leap, last_day, month_candidates_of, significant_range,
},
types::{Date, DateField, Edtf, Precision, Qualifier, SetElement, Year, YearKind},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Unenumerable {
Interval,
UnboundedSetElement,
YearRangeOverflow,
}
impl core::fmt::Display for Unenumerable {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
Self::Interval => "intervals denote an extent, not enumerable values",
Self::UnboundedSetElement => "'..'-open set elements denote unbounded value sets",
Self::YearRangeOverflow => "year range exceeds the computable range",
})
}
}
impl core::error::Error for Unenumerable {}
impl Edtf {
pub fn values(&self) -> Result<Values, Unenumerable> {
let state = match self {
Self::Interval(_) => return Err(Unenumerable::Interval),
Self::DateTime(dt) => State::Singleton(Some(Self::DateTime(*dt))),
Self::Date(d) => State::Date(DateValues::new(d)?),
Self::Set(s) => {
let mut queue = Vec::with_capacity(s.elements.len());
for e in &s.elements {
queue.push(match e {
SetElement::Date(d) => ElementValues::Date(DateValues::new(d)?),
SetElement::Range(a, b) => ElementValues::Range(RangeWalk::new(a, b)?),
SetElement::OnOrBefore(_) | SetElement::OnOrAfter(_) => {
return Err(Unenumerable::UnboundedSetElement);
},
});
}
State::Set { queue, idx: 0 }
},
};
Ok(Values { state })
}
}
#[derive(Debug, Clone)]
pub struct Values {
state: State,
}
#[derive(Debug, Clone)]
enum State {
Singleton(Option<Edtf>),
Date(DateValues),
Set {
queue: Vec<ElementValues>,
idx: usize,
},
}
#[derive(Debug, Clone)]
enum ElementValues {
Date(DateValues),
Range(RangeWalk),
}
impl ElementValues {
fn next(&mut self) -> Option<Date> {
match self {
Self::Date(dv) => dv.next(),
Self::Range(rw) => rw.next(),
}
}
}
impl Iterator for Values {
type Item = Edtf;
fn next(&mut self) -> Option<Edtf> {
match &mut self.state {
State::Singleton(v) => v.take(),
State::Date(dv) => dv.next().map(Edtf::Date),
State::Set { queue, idx } => loop {
let cur = queue.get_mut(*idx)?;
if let Some(d) = cur.next() {
return Some(Edtf::Date(d));
}
*idx += 1;
},
}
}
}
fn concrete_year(v: i64, qualifier: Qualifier) -> Year {
let kind = if (-9999..=9999).contains(&v) {
let mag = v.unsigned_abs();
YearKind::Standard {
negative: v < 0,
digits: [
Some((mag / 1000 % 10) as u8),
Some((mag / 100 % 10) as u8),
Some((mag / 10 % 10) as u8),
Some((mag % 10) as u8),
],
}
} else {
YearKind::Big { value: v }
};
Year {
kind,
significant_digits: None,
qualifier,
}
}
const fn concrete_field(v: u8, qualifier: Qualifier) -> DateField {
DateField {
digits: [Some(v / 10), Some(v % 10)],
qualifier,
}
}
#[derive(Debug, Clone)]
enum DateValues {
Singleton(Option<Date>),
Sweep {
cur: i64,
hi: i64,
qualifier: Qualifier,
done: bool,
},
Masked(Masked),
}
impl DateValues {
fn new(d: &Date) -> Result<Self, Unenumerable> {
if d.year.significant_digits.is_some() {
let value = d.year.value().ok_or(Unenumerable::YearRangeOverflow)?;
let (lo, hi) =
significant_range(value, d.year.significant_digits, big_width(&d.year.kind))
.ok_or(Unenumerable::YearRangeOverflow)?;
return Ok(Self::Sweep {
cur: lo,
hi,
qualifier: d.year.qualifier,
done: false,
});
}
if !d.has_unspecified() {
return Ok(Self::Singleton(Some(*d)));
}
Ok(Self::Masked(Masked::new(d)))
}
fn next(&mut self) -> Option<Date> {
match self {
Self::Singleton(d) => d.take(),
Self::Sweep {
cur,
hi,
qualifier,
done,
} => {
if *done {
return None;
}
let v = *cur;
if v == *hi {
*done = true;
} else {
*cur += 1;
}
Some(Date {
year: concrete_year(v, *qualifier),
month: None,
day: None,
})
},
Self::Masked(m) => m.next(),
}
}
}
#[derive(Debug, Clone)]
enum MaskedYear {
Fixed(i64),
Pattern([Option<u8>; 4]),
}
#[derive(Debug, Clone)]
struct Masked {
year: MaskedYear,
year_count: u32,
months: Option<Vec<u8>>,
days: Option<Vec<u8>>,
yq: Qualifier,
mq: Qualifier,
dq: Qualifier,
yi: u32,
mi: usize,
di: usize,
}
impl Masked {
fn new(d: &Date) -> Self {
let (year, year_count) = match (d.year.value(), d.year.kind) {
(Some(v), _) => (MaskedYear::Fixed(v), 1),
(None, YearKind::Standard { digits, .. }) => {
let n = u32::try_from(digits.iter().filter(|x| x.is_none()).count()).unwrap_or(4);
(MaskedYear::Pattern(digits), 10u32.pow(n))
},
(None, _) => unreachable!("only standard years carry X digits"),
};
Self {
year,
year_count,
months: d.month.map(month_candidates_of),
days: d.day.map(day_candidates_of),
yq: d.year.qualifier,
mq: d.month.map(|m| m.qualifier).unwrap_or_default(),
dq: d.day.map(|f| f.qualifier).unwrap_or_default(),
yi: 0,
mi: 0,
di: 0,
}
}
fn year_at(&self, counter: u32) -> i64 {
match self.year {
MaskedYear::Fixed(v) => v,
MaskedYear::Pattern(digits) => {
let mut rem = counter;
let mut filled = digits.map(|d| d.unwrap_or(0));
for pos in (0..4).rev() {
if digits[pos].is_none() {
filled[pos] = (rem % 10) as u8;
rem /= 10;
}
}
filled.iter().fold(0, |acc, d| acc * 10 + i64::from(*d))
},
}
}
fn next(&mut self) -> Option<Date> {
loop {
if self.yi >= self.year_count {
return None;
}
let y = self.year_at(self.yi);
let Some(months) = &self.months else {
self.yi += 1;
return Some(Date {
year: concrete_year(y, self.yq),
month: None,
day: None,
});
};
while self.mi < months.len() {
let m = months[self.mi];
let Some(days) = &self.days else {
self.mi += 1;
return Some(Date {
year: concrete_year(y, self.yq),
month: Some(concrete_field(m, self.mq)),
day: None,
});
};
while self.di < days.len() {
let dd = days[self.di];
self.di += 1;
if dd <= last_day(m, is_leap(y)) {
return Some(Date {
year: concrete_year(y, self.yq),
month: Some(concrete_field(m, self.mq)),
day: Some(concrete_field(dd, self.dq)),
});
}
}
self.di = 0;
self.mi += 1;
}
self.mi = 0;
self.yi += 1;
}
}
}
#[derive(Debug, Clone)]
struct RangeWalk {
cur: (i64, u8, u8),
end: (i64, u8, u8),
precision: Precision,
done: bool,
}
impl RangeWalk {
fn new(a: &Date, b: &Date) -> Result<Self, Unenumerable> {
let ay = a.year.value().ok_or(Unenumerable::YearRangeOverflow)?;
let by = b.year.value().ok_or(Unenumerable::YearRangeOverflow)?;
let field = |f: Option<DateField>| f.and_then(DateField::value).unwrap_or(0);
Ok(Self {
cur: (ay, field(a.month), field(a.day)),
end: (by, field(b.month), field(b.day)),
precision: a.precision(),
done: false,
})
}
fn next(&mut self) -> Option<Date> {
if self.done {
return None;
}
let (y, m, d) = self.cur;
if self.cur == self.end {
self.done = true;
} else {
self.cur = match self.precision {
Precision::Year => (y + 1, 0, 0),
Precision::Month => {
if m == 12 {
(y + 1, 1, 0)
} else {
(y, m + 1, 0)
}
},
Precision::Day => {
if d < last_day(m, is_leap(y)) {
(y, m, d + 1)
} else if m == 12 {
(y + 1, 1, 1)
} else {
(y, m + 1, 1)
}
},
Precision::Season => unreachable!("D27 rejects season range endpoints"),
};
}
let q = Qualifier::default();
Some(Date {
year: concrete_year(y, q),
month: (self.precision != Precision::Year).then(|| concrete_field(m, q)),
day: (self.precision == Precision::Day).then(|| concrete_field(d, q)),
})
}
}