Skip to content

Commit

Permalink
Implement [end] component
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Aug 27, 2023
1 parent 5b2d6f8 commit cf16454
Show file tree
Hide file tree
Showing 17 changed files with 124 additions and 19 deletions.
13 changes: 13 additions & 0 deletions tests/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ fn unexpected_trailing_characters() -> Parse {
Time::parse("a", format_description!("")).unwrap_err()
}

fn unexpected_trailing_characters_from_description() -> ParseFromDescription {
match Time::parse("0", format_description!("[end]")) {
Err(Parse::ParseFromDescription(
err @ ParseFromDescription::UnexpectedTrailingCharacters { .. },
)) => err,
_ => panic!("unexpected result"),
}
}

fn invalid_format_description() -> InvalidFormatDescription {
format_description::parse("[").unwrap_err()
}
Expand Down Expand Up @@ -95,6 +104,10 @@ fn display() {
ParseFromDescription::InvalidComponent("a"),
Parse::from(ParseFromDescription::InvalidComponent("a"))
);
assert_display_eq!(
unexpected_trailing_characters_from_description(),
Parse::from(unexpected_trailing_characters_from_description())
);
assert_display_eq!(
component_range(),
Parse::from(TryFromParsed::from(component_range()))
Expand Down
7 changes: 7 additions & 0 deletions tests/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,13 @@ fn ignore() -> time::Result<()> {
Ok(())
}

#[test]
fn end() -> time::Result<()> {
assert_eq!(Time::MIDNIGHT.format(fd!("[end]"))?, "");

Ok(())
}

#[test]
fn unix_timestamp() -> time::Result<()> {
let dt = datetime!(2009-02-13 23:31:30.123456789 UTC);
Expand Down
4 changes: 4 additions & 0 deletions tests/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ fn format_description_coverage() {
}
)))]
);
assert_eq!(
format_description!("[end]"),
&[FormatItem::Component(Component::End(modifier!(End)))]
);
}

#[test]
Expand Down
10 changes: 6 additions & 4 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,13 @@ require_all_features! {

/// Construct a non-exhaustive modifier.
macro_rules! modifier {
($name:ident {
$($field:ident $(: $value:expr)?),+ $(,)?
}) => {{
($name:ident $({
$($field:ident $(: $value:expr)?),* $(,)?
})?) => {{
// Needed for when there are no fields.
#[allow(unused_mut)]
let mut value = ::time::format_description::modifier::$name::default();
$(value.$field = modifier!(@value $field $($value)?);)+
$($(value.$field = modifier!(@value $field $($value)?);)*)?
value
}};

Expand Down
2 changes: 1 addition & 1 deletion tests/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ fn size() {
assert_size!(error::Format, 24, 24);
assert_size!(error::InvalidFormatDescription, 48, 48);
assert_size!(error::Parse, 48, 48);
assert_size!(error::ParseFromDescription, 16, 24);
assert_size!(error::ParseFromDescription, 24, 24);
assert_size!(error::TryFromParsed, 48, 48);
assert_size!(Component, 6, 6); // TODO Size is 4 starting with rustc 1.71.
assert_size!(FormatItem<'_>, 24, 24);
Expand Down
4 changes: 4 additions & 0 deletions tests/parse_format_description.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ fn simple_component() {
}
)))])
);
assert_eq!(
format_description::parse("[end]"),
Ok(vec![FormatItem::Component(Component::End(modifier!(End)))])
);
assert_eq!(
format_description::parse("[hour]"),
Ok(vec![FormatItem::Component(Component::Hour(modifier!(
Expand Down
33 changes: 33 additions & 0 deletions tests/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1645,3 +1645,36 @@ fn issue_601() {

assert_eq!(date, datetime!(2009-02-13 23:31:30.123 +00:00:00));
}

#[test]
fn end() -> time::Result<()> {
let mut parsed = Parsed::new();
let remaining_input = parsed.parse_item(
b"",
&FormatItem::Component(Component::End(modifier::End::default())),
);
assert_eq!(remaining_input, Ok(b"".as_slice()));

assert_eq!(
Time::parse("00:00", &fd::parse("[hour]:[minute][end]")?),
Ok(time!(0:00))
);
assert_eq!(
Time::parse(
"00:00:00",
&fd::parse_owned::<2>("[hour]:[minute][optional [[end]]]:[second]")?
),
Ok(time!(0:00))
);
assert!(matches!(
Time::parse(
"00:00:00",
&fd::parse_owned::<2>("[hour]:[minute][end]:[second]")?
),
Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::UnexpectedTrailingCharacters { .. }
))
));

Ok(())
}
2 changes: 2 additions & 0 deletions time-macros/src/format_description/format_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ macro_rules! component_definition {
_component_span: Span,
) -> Result<Self, Error>
{
#[allow(unused_mut)]
let mut this = Self {
$($field: None),*
};
Expand Down Expand Up @@ -212,6 +213,7 @@ component_definition! {
Day = "day" {
padding = "padding": Option<Padding> => padding,
},
End = "end" {},
Hour = "hour" {
padding = "padding": Option<Padding> => padding,
base = "repr": Option<HourBase> => is_12_hour_clock,
Expand Down
1 change: 1 addition & 0 deletions time-macros/src/format_description/public/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ declare_component! {
OffsetSecond
Ignore
UnixTimestamp
End
}
12 changes: 8 additions & 4 deletions time-macros/src/format_description/public/modifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ macro_rules! to_tokens {
$struct_vis:vis struct $struct_name:ident {$(
$(#[$field_attr:meta])*
$field_vis:vis $field_name:ident : $field_ty:ty
),+ $(,)?}
),* $(,)?}
) => {
$(#[$struct_attr])*
$struct_vis struct $struct_name {$(
$(#[$field_attr])*
$field_vis $field_name: $field_ty
),+}
),*}

impl ToTokenTree for $struct_name {
fn into_token_tree(self) -> TokenTree {
let mut tokens = TokenStream::new();
let Self {$($field_name),+} = self;
let Self {$($field_name),*} = self;

quote_append! { tokens
let mut value = ::time::format_description::modifier::$struct_name::default();
Expand All @@ -30,7 +30,7 @@ macro_rules! to_tokens {
quote_append!(tokens value.$field_name =);
$field_name.append_to(&mut tokens);
quote_append!(tokens ;);
)+
)*
quote_append!(tokens value);

proc_macro::TokenTree::Group(proc_macro::Group::new(
Expand Down Expand Up @@ -245,3 +245,7 @@ to_tokens! {
pub(crate) sign_is_mandatory: bool,
}
}

to_tokens! {
pub(crate) struct End {}
}
5 changes: 5 additions & 0 deletions time/src/error/parse_from_description.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub enum ParseFromDescription {
InvalidLiteral,
/// A dynamic component was not valid.
InvalidComponent(&'static str),
#[non_exhaustive]
UnexpectedTrailingCharacters,
}

impl fmt::Display for ParseFromDescription {
Expand All @@ -22,6 +24,9 @@ impl fmt::Display for ParseFromDescription {
Self::InvalidComponent(name) => {
write!(f, "the '{name}' component could not be parsed")
}
Self::UnexpectedTrailingCharacters => {
f.write_str("unexpected trailing characters; the end of input was expected")
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions time/src/format_description/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ pub enum Component {
Ignore(modifier::Ignore),
/// A Unix timestamp.
UnixTimestamp(modifier::UnixTimestamp),
/// The end of input. Parsing this component will fail if there is any input remaining. This
/// component neither affects formatting nor consumes any input when parsing.
End(modifier::End),
}
9 changes: 9 additions & 0 deletions time/src/format_description/modifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@ pub struct UnixTimestamp {
pub sign_is_mandatory: bool,
}

/// The end of input.
///
/// There is currently not customization for this modifier.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct End;

/// Generate the provided code if and only if `pub` is present.
macro_rules! if_pub {
(pub $(#[$attr:meta])*; $($x:tt)*) => {
Expand Down Expand Up @@ -406,4 +413,6 @@ impl_const_default! {
precision: UnixTimestampPrecision::Second,
sign_is_mandatory: false,
};
/// Creates a modifier used to represent the end of input.
@pub End => End;
}
3 changes: 3 additions & 0 deletions time/src/format_description/parse/format_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ macro_rules! component_definition {
_component_span: Span,
) -> Result<Self, Error>
{
// rustc will complain if the modifier is empty.
#[allow(unused_mut)]
let mut this = Self {
$($field: None),*
};
Expand Down Expand Up @@ -280,6 +282,7 @@ component_definition! {
Day = "day" {
padding = "padding": Option<Padding> => padding,
},
End = "end" {},
Hour = "hour" {
padding = "padding": Option<Padding> => padding,
base = "repr": Option<HourBase> => is_12_hour_clock,
Expand Down
1 change: 1 addition & 0 deletions time/src/formatting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ pub(crate) fn format_component(
(UnixTimestamp(modifier), Some(date), Some(time), Some(offset)) => {
fmt_unix_timestamp(output, date, time, offset, modifier)?
}
(End(modifier::End {}), ..) => 0,
_ => return Err(error::Format::InsufficientTypeInformation),
})
}
Expand Down
12 changes: 12 additions & 0 deletions time/src/parsing/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,15 @@ pub(crate) fn parse_unix_timestamp(
_ => Some(ParsedItem(input, nano_timestamp as _)),
}
}

/// Parse the `end` component, which represents the end of input. If any input is remaining, `None`
/// is returned.
pub(crate) const fn parse_end(input: &[u8], end: modifier::End) -> Option<ParsedItem<'_, ()>> {
let modifier::End {} = end;

if input.is_empty() {
Some(ParsedItem(input, ()))
} else {
None
}
}
22 changes: 12 additions & 10 deletions time/src/parsing/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ use crate::convert::{Day, Hour, Minute, Nanosecond, Second};
use crate::date::{MAX_YEAR, MIN_YEAR};
use crate::date_time::{maybe_offset_from_offset, offset_kind, DateTime, MaybeOffset};
use crate::error::TryFromParsed::InsufficientInformation;
use crate::format_description::modifier::{WeekNumberRepr, YearRepr};
#[cfg(feature = "alloc")]
use crate::format_description::OwnedFormatItem;
use crate::format_description::{Component, FormatItem};
use crate::format_description::{modifier, Component, FormatItem};
use crate::parsing::component::{
parse_day, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour,
parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second,
parse_subsecond, parse_unix_timestamp, parse_week_number, parse_weekday, parse_year, Period,
};
Expand Down Expand Up @@ -276,11 +275,11 @@ impl Parsed {
let ParsedItem(remaining, value) =
parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
match modifiers.repr {
WeekNumberRepr::Iso => {
modifier::WeekNumberRepr::Iso => {
NonZeroU8::new(value).and_then(|value| self.set_iso_week_number(value))
}
WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
WeekNumberRepr::Monday => self.set_monday_week_number(value),
modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value),
}
.ok_or(InvalidComponent("week number"))?;
Ok(remaining)
Expand All @@ -289,10 +288,10 @@ impl Parsed {
let ParsedItem(remaining, value) =
parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
match (modifiers.iso_week_based, modifiers.repr) {
(false, YearRepr::Full) => self.set_year(value),
(false, YearRepr::LastTwo) => self.set_year_last_two(value as _),
(true, YearRepr::Full) => self.set_iso_year(value),
(true, YearRepr::LastTwo) => self.set_iso_year_last_two(value as _),
(false, modifier::YearRepr::Full) => self.set_year(value),
(false, modifier::YearRepr::LastTwo) => self.set_year_last_two(value as _),
(true, modifier::YearRepr::Full) => self.set_iso_year(value),
(true, modifier::YearRepr::LastTwo) => self.set_iso_year_last_two(value as _),
}
.ok_or(InvalidComponent("year"))?;
Ok(remaining)
Expand Down Expand Up @@ -349,6 +348,9 @@ impl Parsed {
parsed.consume_value(|value| self.set_unix_timestamp_nanos(value))
})
.ok_or(InvalidComponent("unix_timestamp")),
Component::End(modifiers) => parse_end(input, modifiers)
.map(ParsedItem::<()>::into_inner)
.ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters),
}
}
}
Expand Down

0 comments on commit cf16454

Please sign in to comment.