diff --git a/components/datetime/benches/fixtures/tests/patterns.json b/components/datetime/benches/fixtures/tests/patterns.json index 0feb96af842..a11940396e2 100644 --- a/components/datetime/benches/fixtures/tests/patterns.json +++ b/components/datetime/benches/fixtures/tests/patterns.json @@ -20,5 +20,10 @@ "E, dd/MM", "LLL", "E, d MMM y", - "E, dd/MM/y" + "E, dd/MM/y", + "y年M月d日", + "y年M月d日EEEE", + "d בMMM y", + "H นาฬิกา mm นาที ss วินาที zzzz", + "H時mm分ss秒 zzzz" ] diff --git a/components/datetime/src/fields/length.rs b/components/datetime/src/fields/length.rs new file mode 100644 index 00000000000..82e708f1643 --- /dev/null +++ b/components/datetime/src/fields/length.rs @@ -0,0 +1,39 @@ +use std::convert::TryFrom; + +#[derive(Debug)] +pub enum LengthError { + TooLong, +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum FieldLength { + One = 1, + TwoDigit = 2, + Abbreviated = 3, + Wide = 4, + Narrow = 5, + Six = 6, +} + +macro_rules! try_field_length { + ($i:ty) => { + impl TryFrom<$i> for FieldLength { + type Error = LengthError; + + fn try_from(input: $i) -> Result { + Ok(match input { + 1 => Self::One, + 2 => Self::TwoDigit, + 3 => Self::Abbreviated, + 4 => Self::Wide, + 5 => Self::Narrow, + 6 => Self::Six, + _ => return Err(LengthError::TooLong), + }) + } + } + }; +} + +try_field_length!(u8); +try_field_length!(usize); diff --git a/components/datetime/src/fields/mod.rs b/components/datetime/src/fields/mod.rs new file mode 100644 index 00000000000..9a130ea4d6a --- /dev/null +++ b/components/datetime/src/fields/mod.rs @@ -0,0 +1,43 @@ +mod length; +mod symbols; + +pub use length::FieldLength; +pub use symbols::*; + +use std::convert::{TryFrom, TryInto}; + +#[derive(Debug)] +pub enum Error { + TooLong(FieldSymbol), +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct Field { + pub symbol: FieldSymbol, + pub length: FieldLength, +} + +impl Field {} + +impl From<(FieldSymbol, FieldLength)> for Field { + fn from(input: (FieldSymbol, FieldLength)) -> Self { + Field { + symbol: input.0, + length: input.1, + } + } +} + +impl TryFrom<(FieldSymbol, usize)> for Field { + type Error = Error; + fn try_from(input: (FieldSymbol, usize)) -> Result { + let (symbol, length) = ( + input.0, + input + .1 + .try_into() + .map_err(|_| Self::Error::TooLong(input.0))?, + ); + Ok(Field { symbol, length }) + } +} diff --git a/components/datetime/src/fields.rs b/components/datetime/src/fields/symbols.rs similarity index 71% rename from components/datetime/src/fields.rs rename to components/datetime/src/fields/symbols.rs index 07831ae744c..1e2079736ef 100644 --- a/components/datetime/src/fields.rs +++ b/components/datetime/src/fields/symbols.rs @@ -1,44 +1,10 @@ use std::convert::TryFrom; #[derive(Debug)] -pub enum Error { - Unknown, - TooLong, +pub enum SymbolError { + Unknown(u8), } -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum FieldLength { - One = 1, - TwoDigit = 2, - Abbreviated = 3, - Wide = 4, - Narrow = 5, - Six = 6, -} - -macro_rules! try_field_length { - ($i:ty) => { - impl TryFrom<$i> for FieldLength { - type Error = Error; - - fn try_from(input: $i) -> Result { - Ok(match input { - 1 => Self::One, - 2 => Self::TwoDigit, - 3 => Self::Abbreviated, - 4 => Self::Wide, - 5 => Self::Narrow, - 6 => Self::Six, - _ => return Err(Error::TooLong), - }) - } - } - }; -} - -try_field_length!(u8); -try_field_length!(usize); - #[derive(Debug, PartialEq, Clone, Copy)] pub enum FieldSymbol { Year(Year), @@ -52,7 +18,7 @@ pub enum FieldSymbol { } impl TryFrom for FieldSymbol { - type Error = Error; + type Error = SymbolError; fn try_from(b: u8) -> Result { match b { b'm' => Ok(Self::Minute), @@ -75,12 +41,12 @@ pub enum Year { } impl TryFrom for Year { - type Error = Error; + type Error = SymbolError; fn try_from(b: u8) -> Result { match b { b'y' => Ok(Self::Calendar), b'Y' => Ok(Self::WeekOf), - _ => Err(Error::Unknown), + b => Err(SymbolError::Unknown(b)), } } } @@ -98,12 +64,12 @@ pub enum Month { } impl TryFrom for Month { - type Error = Error; + type Error = SymbolError; fn try_from(b: u8) -> Result { match b { b'M' => Ok(Self::Format), b'L' => Ok(Self::StandAlone), - _ => Err(Error::Unknown), + b => Err(SymbolError::Unknown(b)), } } } @@ -123,14 +89,14 @@ pub enum Day { } impl TryFrom for Day { - type Error = Error; + type Error = SymbolError; fn try_from(b: u8) -> Result { match b { b'd' => Ok(Self::DayOfMonth), b'D' => Ok(Self::DayOfYear), b'F' => Ok(Self::DayOfWeekInMonth), b'g' => Ok(Self::ModifiedJulianDay), - _ => Err(Error::Unknown), + b => Err(SymbolError::Unknown(b)), } } } @@ -150,14 +116,14 @@ pub enum Hour { } impl TryFrom for Hour { - type Error = Error; + type Error = SymbolError; fn try_from(b: u8) -> Result { match b { b'K' => Ok(Self::H11), b'h' => Ok(Self::H12), b'H' => Ok(Self::H23), b'k' => Ok(Self::H24), - _ => Err(Error::Unknown), + b => Err(SymbolError::Unknown(b)), } } } @@ -176,13 +142,13 @@ pub enum Second { } impl TryFrom for Second { - type Error = Error; + type Error = SymbolError; fn try_from(b: u8) -> Result { match b { b's' => Ok(Self::Second), b'S' => Ok(Self::FractionalSecond), b'A' => Ok(Self::Millisecond), - _ => Err(Error::Unknown), + b => Err(SymbolError::Unknown(b)), } } } @@ -201,13 +167,13 @@ pub enum Weekday { } impl TryFrom for Weekday { - type Error = Error; + type Error = SymbolError; fn try_from(b: u8) -> Result { match b { b'E' => Ok(Self::Format), b'e' => Ok(Self::Local), b'c' => Ok(Self::StandAlone), - _ => Err(Error::Unknown), + b => Err(SymbolError::Unknown(b)), } } } @@ -224,11 +190,11 @@ pub enum DayPeriod { } impl TryFrom for DayPeriod { - type Error = Error; + type Error = SymbolError; fn try_from(b: u8) -> Result { match b { b'a' => Ok(Self::AmPm), - _ => Err(Error::Unknown), + b => Err(SymbolError::Unknown(b)), } } } @@ -238,20 +204,3 @@ impl From for FieldSymbol { FieldSymbol::DayPeriod(input) } } - -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct Field { - pub symbol: FieldSymbol, - pub length: FieldLength, -} - -impl Field {} - -impl From<(FieldSymbol, FieldLength)> for Field { - fn from(input: (FieldSymbol, FieldLength)) -> Self { - Field { - symbol: input.0, - length: input.1, - } - } -} diff --git a/components/datetime/src/pattern/error.rs b/components/datetime/src/pattern/error.rs index e550d708446..7baf4cc8c47 100644 --- a/components/datetime/src/pattern/error.rs +++ b/components/datetime/src/pattern/error.rs @@ -3,15 +3,14 @@ use crate::fields; #[derive(Debug)] pub enum Error { Unknown, - FieldTooLong, + FieldTooLong(fields::FieldSymbol), UnknownSubstitution(u8), } impl From for Error { fn from(input: fields::Error) -> Self { match input { - fields::Error::Unknown => Self::Unknown, - fields::Error::TooLong => Self::FieldTooLong, + fields::Error::TooLong(symbol) => Self::FieldTooLong(symbol), } } } diff --git a/components/datetime/src/pattern/mod.rs b/components/datetime/src/pattern/mod.rs index 4202ef9638a..e7667354670 100644 --- a/components/datetime/src/pattern/mod.rs +++ b/components/datetime/src/pattern/mod.rs @@ -92,5 +92,19 @@ mod tests { .into_iter() .collect() ); + + assert_eq!( + Pattern::from_bytes("y年M月d日").expect("Parsing pattern failed."), + vec![ + (fields::Year::Calendar.into(), FieldLength::One).into(), + "年".into(), + (fields::Month::Format.into(), FieldLength::One).into(), + "月".into(), + (fields::Day::DayOfMonth.into(), FieldLength::One).into(), + "日".into(), + ] + .into_iter() + .collect() + ); } } diff --git a/components/datetime/src/pattern/parser.rs b/components/datetime/src/pattern/parser.rs index 973787a42f0..f54fdc616bd 100644 --- a/components/datetime/src/pattern/parser.rs +++ b/components/datetime/src/pattern/parser.rs @@ -1,7 +1,7 @@ use super::error::Error; use super::{Pattern, PatternItem}; -use crate::fields::{Field, FieldLength, FieldSymbol}; -use std::convert::TryFrom; +use crate::fields::{Field, FieldSymbol}; +use std::convert::{TryFrom, TryInto}; enum Segment { Symbol { symbol: FieldSymbol, byte: u8 }, @@ -33,23 +33,21 @@ impl<'p> Parser<'p> { fn collect_item(&mut self, idx: usize) -> Result<(), Error> { match self.state.segment { - Segment::Symbol { symbol, .. } => { - self.collect_symbol(symbol, idx)? - } + Segment::Symbol { symbol, .. } => self.collect_symbol(symbol, idx)?, Segment::Literal => self.collect_literal(idx), } Ok(()) } fn collect_symbol(&mut self, symbol: FieldSymbol, idx: usize) -> Result<(), Error> { - let length = FieldLength::try_from(idx - self.state.start_idx)?; - self.items.push(Field { symbol, length }.into()); + let field: Field = (symbol, idx - self.state.start_idx).try_into()?; + self.items.push(field.into()); Ok(()) } fn collect_literal(&mut self, idx: usize) { - let slice = &self.source[self.state.start_idx..idx]; - if !slice.is_empty() { + if idx > self.state.start_idx { + let slice = &self.source[self.state.start_idx..idx]; let item = PatternItem::Literal(slice.to_string()); self.items.push(item); } @@ -69,8 +67,7 @@ impl<'p> Parser<'p> { segment: Segment::Symbol { symbol, byte: b }, start_idx: idx, }; - } else if let Segment::Symbol { symbol, .. } = self.state.segment - { + } else if let Segment::Symbol { symbol, .. } = self.state.segment { self.collect_symbol(symbol, idx)?; self.state = State { segment: Segment::Literal, @@ -100,7 +97,7 @@ impl<'p> Parser<'p> { bytes.next().ok_or(Error::Unknown)?; self.state = State { segment: Segment::Literal, - start_idx: idx + 3 + start_idx: idx + 3, }; } }