use super::error::SkeletonError;
use crate::fields::{self, Field, FieldLength, FieldSymbol};
#[cfg(feature = "datagen")]
use crate::pattern::reference::Pattern;
use alloc::vec::Vec;
use core::convert::TryFrom;
use smallvec::SmallVec;
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd)]
pub struct Skeleton(pub(crate) SmallVec<[fields::Field; 5]>);
impl Skeleton {
pub(crate) fn fields_iter<'a>(&'a self) -> impl Iterator<Item = &Field> + 'a {
self.0.iter()
}
pub(crate) fn fields_len(&self) -> usize {
self.0.len()
}
pub fn as_slice(&self) -> &[fields::Field] {
self.0.as_slice()
}
}
impl From<SmallVec<[fields::Field; 5]>> for Skeleton {
fn from(fields: SmallVec<[fields::Field; 5]>) -> Self {
Self(fields)
}
}
impl From<Vec<fields::Field>> for Skeleton {
fn from(fields: Vec<fields::Field>) -> Self {
Self(fields.into())
}
}
#[doc(hidden)]
#[cfg(feature = "datagen")]
impl From<&Pattern> for Skeleton {
fn from(pattern: &Pattern) -> Self {
let mut fields: SmallVec<[fields::Field; 5]> = SmallVec::new();
for item in pattern.items() {
if let crate::pattern::PatternItem::Field(field) = item {
let mut field = *field;
field.symbol = match field.symbol {
FieldSymbol::Month(_) => FieldSymbol::Month(fields::Month::Format),
FieldSymbol::Weekday(_) => FieldSymbol::Weekday(fields::Weekday::Format),
FieldSymbol::DayPeriod(fields::DayPeriod::AmPm)
| FieldSymbol::DayPeriod(fields::DayPeriod::NoonMidnight) => continue,
FieldSymbol::Hour(fields::Hour::H11) | FieldSymbol::Hour(fields::Hour::H12) => {
FieldSymbol::Hour(fields::Hour::H12)
}
FieldSymbol::Hour(fields::Hour::H23) | FieldSymbol::Hour(fields::Hour::H24) => {
FieldSymbol::Hour(fields::Hour::H23)
}
FieldSymbol::Minute
| FieldSymbol::Second(_)
| FieldSymbol::TimeZone(_)
| FieldSymbol::Era
| FieldSymbol::Year(_)
| FieldSymbol::Week(_)
| FieldSymbol::Day(_) => field.symbol,
};
if let Err(pos) = fields.binary_search(&field) {
fields.insert(pos, field)
}
}
}
Self(fields)
}
}
impl TryFrom<&str> for Skeleton {
type Error = SkeletonError;
fn try_from(skeleton_string: &str) -> Result<Self, Self::Error> {
let mut fields: SmallVec<[fields::Field; 5]> = SmallVec::new();
let mut iter = skeleton_string.chars().peekable();
while let Some(ch) = iter.next() {
let field_symbol = FieldSymbol::try_from(ch)?;
let mut field_length: u8 = 1;
while let Some(next_ch) = iter.peek() {
if *next_ch != ch {
break;
}
field_length += 1;
iter.next();
}
let field = Field::from((field_symbol, FieldLength::from_idx(field_length)?));
match fields.binary_search(&field) {
Ok(_) => return Err(SkeletonError::DuplicateField),
Err(pos) => fields.insert(pos, field),
}
}
Ok(Self::from(fields))
}
}
#[cfg(feature = "datagen")]
impl core::fmt::Display for Skeleton {
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
use core::fmt::Write;
for field in self.fields_iter() {
let ch: char = field.symbol.into();
for _ in 0..field.length.to_len() {
formatter.write_char(ch)?;
}
}
Ok(())
}
}