use crate::fieldsets::{self, enums::*, Combo};
use crate::options::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(
all(feature = "serde", feature = "unstable"),
derive(serde::Serialize, serde::Deserialize)
)]
#[non_exhaustive]
pub enum DateFields {
D,
MD,
YMD,
DE,
MDE,
YMDE,
E,
M,
YM,
Y,
}
impl DateFields {
pub const VALUES: &[Self] = &[
Self::D,
Self::MD,
Self::YMD,
Self::DE,
Self::MDE,
Self::YMDE,
Self::E,
Self::M,
Self::YM,
Self::Y,
];
pub fn is_calendar_period(self) -> bool {
match self {
DateFields::D => false,
DateFields::MD => false,
DateFields::YMD => false,
DateFields::DE => false,
DateFields::MDE => false,
DateFields::YMDE => false,
DateFields::E => false,
DateFields::M => true,
DateFields::YM => true,
DateFields::Y => true,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(
all(feature = "serde", feature = "unstable"),
derive(serde::Serialize, serde::Deserialize)
)]
#[non_exhaustive]
pub enum ZoneStyle {
SpecificLong,
SpecificShort,
LocalizedOffsetLong,
LocalizedOffsetShort,
GenericLong,
GenericShort,
Location,
ExemplarCity,
}
impl ZoneStyle {
pub const VALUES: &[Self] = &[
Self::SpecificLong,
Self::SpecificShort,
Self::LocalizedOffsetLong,
Self::LocalizedOffsetShort,
Self::GenericLong,
Self::GenericShort,
Self::Location,
Self::ExemplarCity,
];
}
#[derive(Debug, Clone, displaydoc::Display)]
#[ignore_extra_doc_attributes] #[non_exhaustive]
pub enum BuilderError {
MissingDateFields,
MissingTimePrecision,
MissingZoneStyle,
InvalidDateFields,
SuperfluousOptions(FieldSetBuilder),
}
impl core::error::Error for BuilderError {}
#[cfg(all(feature = "serde", feature = "unstable"))]
mod _serde {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct FieldSetBuilderHuman {
#[serde(skip_serializing_if = "Option::is_none")]
pub length: Option<Length>,
#[serde(skip_serializing_if = "Option::is_none")]
pub date_fields: Option<DateFields>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_precision: Option<TimePrecision>,
#[serde(skip_serializing_if = "Option::is_none")]
pub zone_style: Option<ZoneStyle>,
#[serde(skip_serializing_if = "Option::is_none")]
pub alignment: Option<Alignment>,
#[serde(skip_serializing_if = "Option::is_none")]
pub year_style: Option<YearStyle>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct FieldSetBuilderMachine {
pub length: Option<Length>,
pub date_fields: Option<DateFields>,
pub time_precision: Option<TimePrecision>,
pub zone_style: Option<ZoneStyle>,
pub alignment: Option<Alignment>,
pub year_style: Option<YearStyle>,
}
impl Serialize for FieldSetBuilder {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let FieldSetBuilder {
length,
date_fields,
time_precision,
zone_style,
alignment,
year_style,
} = *self;
if serializer.is_human_readable() {
FieldSetBuilderHuman {
length,
date_fields,
time_precision,
zone_style,
alignment,
year_style,
}
.serialize(serializer)
} else {
FieldSetBuilderMachine {
length,
date_fields,
time_precision,
zone_style,
alignment,
year_style,
}
.serialize(serializer)
}
}
}
impl<'de> Deserialize<'de> for FieldSetBuilder {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let FieldSetBuilderHuman {
length,
date_fields,
time_precision,
zone_style,
alignment,
year_style,
} = FieldSetBuilderHuman::deserialize(deserializer)?;
Ok(FieldSetBuilder {
length,
date_fields,
time_precision,
zone_style,
alignment,
year_style,
})
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[non_exhaustive]
pub struct FieldSetBuilder {
pub length: Option<Length>,
pub date_fields: Option<DateFields>,
pub time_precision: Option<TimePrecision>,
pub zone_style: Option<ZoneStyle>,
pub alignment: Option<Alignment>,
pub year_style: Option<YearStyle>,
}
enum DateOrCalendarPeriodFieldSet {
Date(DateFieldSet),
CalendarPeriod(CalendarPeriodFieldSet),
}
impl FieldSetBuilder {
pub fn new() -> Self {
Self::default()
}
fn build_date_or_calendar_period_without_checking_options(
&mut self,
) -> Result<DateOrCalendarPeriodFieldSet, BuilderError> {
use DateOrCalendarPeriodFieldSet::*;
let field_set = match self.date_fields.take() {
Some(DateFields::D) => Date(DateFieldSet::D(fieldsets::D::take_from_builder(self))),
Some(DateFields::MD) => Date(DateFieldSet::MD(fieldsets::MD::take_from_builder(self))),
Some(DateFields::YMD) => {
Date(DateFieldSet::YMD(fieldsets::YMD::take_from_builder(self)))
}
Some(DateFields::DE) => Date(DateFieldSet::DE(fieldsets::DE::take_from_builder(self))),
Some(DateFields::MDE) => {
Date(DateFieldSet::MDE(fieldsets::MDE::take_from_builder(self)))
}
Some(DateFields::YMDE) => {
Date(DateFieldSet::YMDE(fieldsets::YMDE::take_from_builder(self)))
}
Some(DateFields::E) => Date(DateFieldSet::E(fieldsets::E::take_from_builder(self))),
Some(DateFields::M) => CalendarPeriod(CalendarPeriodFieldSet::M(
fieldsets::M::take_from_builder(self),
)),
Some(DateFields::YM) => CalendarPeriod(CalendarPeriodFieldSet::YM(
fieldsets::YM::take_from_builder(self),
)),
Some(DateFields::Y) => CalendarPeriod(CalendarPeriodFieldSet::Y(
fieldsets::Y::take_from_builder(self),
)),
None => return Err(BuilderError::MissingDateFields),
};
Ok(field_set)
}
pub fn build_date(mut self) -> Result<DateFieldSet, BuilderError> {
let date_field_set = match self.build_date_or_calendar_period_without_checking_options()? {
DateOrCalendarPeriodFieldSet::Date(fs) => fs,
DateOrCalendarPeriodFieldSet::CalendarPeriod(_) => {
return Err(BuilderError::InvalidDateFields)
}
};
self.check_options_consumed()?;
Ok(date_field_set)
}
pub fn build_calendar_period(mut self) -> Result<CalendarPeriodFieldSet, BuilderError> {
let calendar_period_field_set = match self
.build_date_or_calendar_period_without_checking_options()?
{
DateOrCalendarPeriodFieldSet::Date(_) => return Err(BuilderError::InvalidDateFields),
DateOrCalendarPeriodFieldSet::CalendarPeriod(fs) => fs,
};
self.check_options_consumed()?;
Ok(calendar_period_field_set)
}
pub fn build_time(mut self) -> Result<TimeFieldSet, BuilderError> {
if self.time_precision.is_none() {
return Err(BuilderError::MissingTimePrecision);
}
let time_field_set = TimeFieldSet::T(fieldsets::T::take_from_builder(&mut self));
self.check_options_consumed()?;
Ok(time_field_set)
}
fn build_zone_without_checking_options(&mut self) -> Result<ZoneFieldSet, BuilderError> {
let zone_field_set = match self.zone_style.take() {
Some(ZoneStyle::SpecificShort) => {
ZoneFieldSet::SpecificShort(fieldsets::zone::SpecificShort)
}
Some(ZoneStyle::SpecificLong) => {
ZoneFieldSet::SpecificLong(fieldsets::zone::SpecificLong)
}
Some(ZoneStyle::LocalizedOffsetLong) => {
ZoneFieldSet::LocalizedOffsetLong(fieldsets::zone::LocalizedOffsetLong)
}
Some(ZoneStyle::LocalizedOffsetShort) => {
ZoneFieldSet::LocalizedOffsetShort(fieldsets::zone::LocalizedOffsetShort)
}
Some(ZoneStyle::GenericLong) => ZoneFieldSet::GenericLong(fieldsets::zone::GenericLong),
Some(ZoneStyle::GenericShort) => {
ZoneFieldSet::GenericShort(fieldsets::zone::GenericShort)
}
Some(ZoneStyle::Location) => ZoneFieldSet::Location(fieldsets::zone::Location),
Some(ZoneStyle::ExemplarCity) => {
ZoneFieldSet::ExemplarCity(fieldsets::zone::ExemplarCity)
}
None => return Err(BuilderError::MissingZoneStyle),
};
Ok(zone_field_set)
}
pub fn build_zone(mut self) -> Result<ZoneFieldSet, BuilderError> {
let zone_field_set = self.build_zone_without_checking_options()?;
self.check_options_consumed()?;
Ok(zone_field_set)
}
pub fn build_date_and_time(mut self) -> Result<DateAndTimeFieldSet, BuilderError> {
let Some(date_fields) = self.date_fields.take() else {
return Err(BuilderError::MissingDateFields);
};
if self.time_precision.is_none() {
return Err(BuilderError::MissingTimePrecision);
}
let date_and_time_field_set = match date_fields {
DateFields::D => DateAndTimeFieldSet::DT(fieldsets::DT::take_from_builder(&mut self)),
DateFields::MD => {
DateAndTimeFieldSet::MDT(fieldsets::MDT::take_from_builder(&mut self))
}
DateFields::YMD => {
DateAndTimeFieldSet::YMDT(fieldsets::YMDT::take_from_builder(&mut self))
}
DateFields::DE => {
DateAndTimeFieldSet::DET(fieldsets::DET::take_from_builder(&mut self))
}
DateFields::MDE => {
DateAndTimeFieldSet::MDET(fieldsets::MDET::take_from_builder(&mut self))
}
DateFields::YMDE => {
DateAndTimeFieldSet::YMDET(fieldsets::YMDET::take_from_builder(&mut self))
}
DateFields::E => DateAndTimeFieldSet::ET(fieldsets::ET::take_from_builder(&mut self)),
DateFields::M | DateFields::YM | DateFields::Y => {
return Err(BuilderError::InvalidDateFields)
}
};
self.check_options_consumed()?;
Ok(date_and_time_field_set)
}
pub fn build_composite_datetime(mut self) -> Result<CompositeDateTimeFieldSet, BuilderError> {
match (self.date_fields.is_some(), self.time_precision.is_some()) {
(true, false) => {
let field_set = match self
.build_date_or_calendar_period_without_checking_options()?
{
DateOrCalendarPeriodFieldSet::Date(fs) => CompositeDateTimeFieldSet::Date(fs),
DateOrCalendarPeriodFieldSet::CalendarPeriod(fs) => {
CompositeDateTimeFieldSet::CalendarPeriod(fs)
}
};
self.check_options_consumed()?;
Ok(field_set)
}
(false, true) => self.build_time().map(CompositeDateTimeFieldSet::Time),
(true, true) => self
.build_date_and_time()
.map(CompositeDateTimeFieldSet::DateTime),
(false, false) => Err(BuilderError::MissingDateFields),
}
}
pub fn build_zoned_date(mut self) -> Result<ZonedDateFieldSet, BuilderError> {
let zone_field_set = self.build_zone_without_checking_options()?;
let date_field_set = self.build_date()?;
Ok(date_field_set.with_zone(zone_field_set))
}
pub fn build_zoned_time(mut self) -> Result<ZonedTimeFieldSet, BuilderError> {
let zone_field_set = self.build_zone_without_checking_options()?;
let time_field_set = self.build_time()?;
Ok(time_field_set.with_zone(zone_field_set))
}
pub fn build_zoned_date_and_time(mut self) -> Result<ZonedDateAndTimeFieldSet, BuilderError> {
let zone_field_set = self.build_zone_without_checking_options()?;
let datetime_field_set = self.build_date_and_time()?;
Ok(datetime_field_set.with_zone(zone_field_set))
}
pub fn build_composite(mut self) -> Result<CompositeFieldSet, BuilderError> {
match (
self.date_fields.is_some(),
self.time_precision.is_some(),
self.zone_style.is_some(),
) {
(true, false, false) => {
let field_set =
match self.build_date_or_calendar_period_without_checking_options()? {
DateOrCalendarPeriodFieldSet::Date(fs) => CompositeFieldSet::Date(fs),
DateOrCalendarPeriodFieldSet::CalendarPeriod(fs) => {
CompositeFieldSet::CalendarPeriod(fs)
}
};
self.check_options_consumed()?;
Ok(field_set)
}
(false, true, false) => self.build_time().map(CompositeFieldSet::Time),
(true, true, false) => self.build_date_and_time().map(CompositeFieldSet::DateTime),
(false, false, true) => self.build_zone().map(CompositeFieldSet::Zone),
(true, false, true) => {
let zone_field_set = self.build_zone_without_checking_options()?;
let date_field_set = self.build_date()?;
Ok(CompositeFieldSet::DateZone(Combo::new(
date_field_set,
zone_field_set,
)))
}
(false, true, true) => {
let zone_field_set = self.build_zone_without_checking_options()?;
let time_field_set = self.build_time()?;
Ok(CompositeFieldSet::TimeZone(Combo::new(
time_field_set,
zone_field_set,
)))
}
(true, true, true) => {
let zone_field_set = self.build_zone_without_checking_options()?;
let date_and_time_field_set = self.build_date_and_time()?;
Ok(CompositeFieldSet::DateTimeZone(Combo::new(
date_and_time_field_set,
zone_field_set,
)))
}
(false, false, false) => Err(BuilderError::MissingDateFields),
}
}
fn check_options_consumed(self) -> Result<(), BuilderError> {
if self != Self::default() {
Err(BuilderError::SuperfluousOptions(self))
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
static DATE_FIELD_SETS: &[DateFields] = &[
DateFields::D,
DateFields::MD,
DateFields::YMD,
DateFields::DE,
DateFields::MDE,
DateFields::YMDE,
DateFields::E,
];
static CALENDAR_PERIOD_FIELD_SETS: &[DateFields] =
&[DateFields::M, DateFields::YM, DateFields::Y];
static ZONE_STYLES: &[ZoneStyle] = &[
ZoneStyle::SpecificLong,
ZoneStyle::SpecificShort,
ZoneStyle::LocalizedOffsetLong,
ZoneStyle::LocalizedOffsetShort,
ZoneStyle::GenericLong,
ZoneStyle::GenericShort,
ZoneStyle::Location,
ZoneStyle::ExemplarCity,
];
#[cfg(all(feature = "serde", feature = "unstable"))]
fn check_serde(value: &FieldSetBuilder) {
let json_str = serde_json::to_string(value).unwrap();
let json_parsed: FieldSetBuilder = serde_json::from_str(&json_str).unwrap();
assert_eq!(value, &json_parsed);
let bincode_bytes = bincode::serialize(value).unwrap();
let bincode_parsed: FieldSetBuilder = bincode::deserialize(&bincode_bytes).unwrap();
assert_eq!(value, &bincode_parsed);
}
#[test]
fn test_date_field_sets() {
for date_fields in DATE_FIELD_SETS.iter() {
let mut builder = FieldSetBuilder::new();
builder.date_fields = Some(*date_fields);
builder.clone().build_date().unwrap();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap();
builder.clone().build_composite().unwrap();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
#[test]
fn test_calendar_period_field_sets() {
for date_fields in CALENDAR_PERIOD_FIELD_SETS.iter() {
let mut builder = FieldSetBuilder::new();
builder.date_fields = Some(*date_fields);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap();
builder.clone().build_composite().unwrap();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
#[test]
fn test_time_field_sets() {
let mut builder = FieldSetBuilder::new();
builder.time_precision = Some(TimePrecision::Minute);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap();
builder.clone().build_composite().unwrap();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
#[test]
fn test_zone_field_sets() {
for zone_style in ZONE_STYLES.iter() {
let mut builder = FieldSetBuilder::new();
builder.zone_style = Some(*zone_style);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap_err();
builder.clone().build_composite().unwrap();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
#[test]
fn test_date_time_field_sets() {
for date_fields in DATE_FIELD_SETS.iter() {
let mut builder = FieldSetBuilder::new();
builder.date_fields = Some(*date_fields);
builder.time_precision = Some(TimePrecision::Minute);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap();
builder.clone().build_composite_datetime().unwrap();
builder.clone().build_composite().unwrap();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
#[test]
fn test_calendar_period_time_field_sets() {
for date_fields in CALENDAR_PERIOD_FIELD_SETS.iter() {
let mut builder = FieldSetBuilder::new();
builder.date_fields = Some(*date_fields);
builder.time_precision = Some(TimePrecision::Minute);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap_err();
builder.clone().build_composite().unwrap_err();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
#[test]
fn test_date_zone_field_sets() {
for date_fields in DATE_FIELD_SETS.iter() {
for zone_style in ZONE_STYLES.iter() {
let mut builder = FieldSetBuilder::new();
builder.date_fields = Some(*date_fields);
builder.zone_style = Some(*zone_style);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap_err();
builder.clone().build_composite().unwrap();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
}
#[test]
fn test_calendar_period_zone_field_sets() {
for date_fields in CALENDAR_PERIOD_FIELD_SETS.iter() {
for zone_style in ZONE_STYLES.iter() {
let mut builder = FieldSetBuilder::new();
builder.date_fields = Some(*date_fields);
builder.zone_style = Some(*zone_style);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap_err();
builder.clone().build_composite().unwrap_err();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
}
#[test]
fn test_time_zone_field_sets() {
for zone_style in ZONE_STYLES.iter() {
let mut builder = FieldSetBuilder::new();
builder.time_precision = Some(TimePrecision::Minute);
builder.zone_style = Some(*zone_style);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap_err();
builder.clone().build_composite().unwrap();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
#[test]
fn test_date_time_zone_field_sets() {
for date_fields in DATE_FIELD_SETS.iter() {
for zone_style in ZONE_STYLES.iter() {
let mut builder = FieldSetBuilder::new();
builder.date_fields = Some(*date_fields);
builder.time_precision = Some(TimePrecision::Minute);
builder.zone_style = Some(*zone_style);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap_err();
builder.clone().build_composite().unwrap();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
}
#[test]
fn test_calendar_period_time_zone_field_sets() {
for date_fields in CALENDAR_PERIOD_FIELD_SETS.iter() {
for zone_style in ZONE_STYLES.iter() {
let mut builder = FieldSetBuilder::new();
builder.date_fields = Some(*date_fields);
builder.time_precision = Some(TimePrecision::Minute);
builder.zone_style = Some(*zone_style);
builder.clone().build_date().unwrap_err();
builder.clone().build_calendar_period().unwrap_err();
builder.clone().build_time().unwrap_err();
builder.clone().build_zone().unwrap_err();
builder.clone().build_date_and_time().unwrap_err();
builder.clone().build_composite_datetime().unwrap_err();
builder.clone().build_composite().unwrap_err();
#[cfg(all(feature = "serde", feature = "unstable"))]
check_serde(&builder);
}
}
}
}