use super::error::SkeletonError;
use crate::provider::fields::{self, Field, FieldLength, FieldSymbol};
use crate::provider::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<[Field; 5]>);
impl Skeleton {
pub(crate) fn fields_iter(&self) -> impl Iterator<Item = &Field> {
self.0.iter()
}
pub(crate) fn fields_len(&self) -> usize {
self.0.len()
}
pub fn as_slice(&self) -> &[Field] {
self.0.as_slice()
}
}
impl From<SmallVec<[Field; 5]>> for Skeleton {
fn from(fields: SmallVec<[Field; 5]>) -> Self {
Self(fields)
}
}
impl From<Vec<Field>> for Skeleton {
fn from(fields: Vec<Field>) -> Self {
Self(fields.into())
}
}
impl From<&[Field]> for Skeleton {
fn from(fields: &[Field]) -> Self {
Self(fields.into())
}
}
impl From<&Pattern> for Skeleton {
fn from(pattern: &Pattern) -> Self {
let mut fields: SmallVec<[Field; 5]> = SmallVec::new();
for item in pattern.items() {
if let crate::provider::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::H23),
FieldSymbol::Minute
| FieldSymbol::Second(_)
| FieldSymbol::TimeZone(_)
| FieldSymbol::DecimalSecond(_)
| 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<[Field; 5]> = SmallVec::new();
let mut iter = skeleton_string.chars().peekable();
while let Some(ch) = iter.next() {
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_symbol = if ch == 'Z' {
match field_length {
1..=3 => {
field_length = 4;
FieldSymbol::try_from('x')?
}
4 => FieldSymbol::try_from('O')?,
5 => {
field_length = 4;
FieldSymbol::try_from('X')?
}
_ => FieldSymbol::try_from(ch)?,
}
} else {
FieldSymbol::try_from(ch)?
};
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))
}
}
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(())
}
}