-
Notifications
You must be signed in to change notification settings - Fork 185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Date, Time, and DateTime from str impls via IXDTF #5260
Merged
Merged
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
e6c0e2d
Add initial ixdtf integration to icu::calendar
sffc 59b40a7
Add AnyCalendar support
sffc e2a012e
Refactor
sffc 70047f5
fmt
sffc 4b361d1
Add Time and DateTime
sffc 05f5fa9
Add utf8 functions
sffc de60458
Use it in datetime test
sffc 97663dd
Add impl FromStr
sffc de0a56e
features
sffc 5594033
Add diplomat wrappers
sffc 444c064
Remove js-specific rename
sffc b937e49
diplomat-gen
sffc 2d9a022
Add ixdtf dep
sffc e8b63bb
fmt
sffc ce2ba13
features
sffc e3c9962
msrv?
sffc 15543e8
rust_link
sffc 504d026
fmt, diplomat-gen
sffc 1f5b4c9
Update components/calendar/src/ixdtf.rs
sffc 87299ff
MissingFields
sffc 9d7e12a
remove public From impls
sffc 32eac8f
Fix diplomat link and regen
sffc 8800afd
Merge remote-tracking branch 'upstream/main' into ixdtf-calendar-inte…
sffc e47fe2a
diplomat-gen
sffc a84b333
More rust links
sffc d51d245
Remove icu_capi ixdtf dep
sffc bb88ea6
Merge branch 'main' into ixdtf-calendar-integration
sffc 78a6947
Rust renames
sffc 49c4152
icu_capi error rename
sffc 0eb45b1
from_string
sffc 3f4c0ac
Fix rust_link
sffc d54fa6e
diplomat-gen
sffc 78df9bf
Add missing files from diplomat-gen
sffc c3d8f08
Merge remote-tracking branch 'upstream/main' into ixdtf-calendar-inte…
sffc 752151f
Fix rust_link again
sffc 6ae5f22
diplomat-gen
sffc 67f6e9c
cargo fmt
sffc f87d82e
Update components/calendar/src/ixdtf.rs
sffc cebaca9
ParseError
sffc 8bf9e89
diplomat-gen
sffc 9303143
oops, CalendarParseError
sffc b607aca
diplomat-gen
sffc cfc0b1a
Merge remote-tracking branch 'upstream/main' into ixdtf-calendar-inte…
sffc b851b04
Update attrs according to what @Manishearth says I should do
sffc db13889
diplomat-gen
sffc f9c04f8
Remove redundant all()
sffc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,301 @@ | ||
// This file is part of ICU4X. For terms of use, please see the file | ||
// called LICENSE at the top level of the ICU4X source tree | ||
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). | ||
|
||
use core::str::FromStr; | ||
|
||
use crate::{AnyCalendar, Date, DateTime, Iso, RangeError, Time}; | ||
use ixdtf::parsers::records::IxdtfParseRecord; | ||
use ixdtf::parsers::IxdtfParser; | ||
use ixdtf::ParserError; | ||
|
||
/// An error returned from parsing an IXDTF string to an `icu_calendar` type. | ||
#[derive(Debug)] | ||
#[non_exhaustive] | ||
pub enum ParseError { | ||
/// Syntax error in the IXDTF string. | ||
Syntax(ParserError), | ||
/// Value is out of range. | ||
Range(RangeError), | ||
/// The IXDTF is missing fields required for parsing into the chosen type. | ||
MissingFields, | ||
/// The IXDTF specifies an unknown calendar. | ||
UnknownCalendar, | ||
} | ||
|
||
impl From<RangeError> for ParseError { | ||
fn from(value: RangeError) -> Self { | ||
Self::Range(value) | ||
} | ||
} | ||
|
||
impl From<ParserError> for ParseError { | ||
fn from(value: ParserError) -> Self { | ||
Self::Syntax(value) | ||
} | ||
} | ||
|
||
impl AnyCalendar { | ||
#[cfg(feature = "compiled_data")] | ||
fn try_from_ixdtf_record(ixdtf_record: &IxdtfParseRecord) -> Result<Self, ParseError> { | ||
let calendar_id = ixdtf_record.calendar.unwrap_or(b"iso"); | ||
let calendar_kind = crate::AnyCalendarKind::get_for_bcp47_bytes(calendar_id) | ||
.ok_or(ParseError::UnknownCalendar)?; | ||
let calendar = AnyCalendar::new(calendar_kind); | ||
Ok(calendar) | ||
} | ||
} | ||
|
||
impl Date<Iso> { | ||
/// Creates a [`Date`] in the ISO-8601 calendar from an IXDTF syntax string. | ||
/// | ||
/// Ignores any calendar annotations in the string. | ||
/// | ||
/// ✨ *Enabled with the `ixdtf` Cargo feature.* | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use icu::calendar::Date; | ||
/// | ||
/// let date = Date::try_iso_from_str("2024-07-17").unwrap(); | ||
/// | ||
/// assert_eq!(date.year().number, 2024); | ||
/// assert_eq!( | ||
/// date.month().code, | ||
/// icu::calendar::types::MonthCode(tinystr::tinystr!(4, "M07")) | ||
/// ); | ||
/// assert_eq!(date.day_of_month().0, 17); | ||
/// ``` | ||
pub fn try_iso_from_str(ixdtf_str: &str) -> Result<Self, ParseError> { | ||
Self::try_iso_from_utf8(ixdtf_str.as_bytes()) | ||
} | ||
|
||
/// Creates a [`Date`] in the ISO-8601 calendar from an IXDTF syntax string. | ||
/// | ||
/// See [`Self::try_iso_from_str()`]. | ||
/// | ||
/// ✨ *Enabled with the `ixdtf` Cargo feature.* | ||
pub fn try_iso_from_utf8(ixdtf_str: &[u8]) -> Result<Self, ParseError> { | ||
let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse()?; | ||
Self::try_from_ixdtf_record(&ixdtf_record) | ||
} | ||
|
||
fn try_from_ixdtf_record(ixdtf_record: &IxdtfParseRecord) -> Result<Self, ParseError> { | ||
let date_record = ixdtf_record.date.ok_or(ParseError::MissingFields)?; | ||
let date = Self::try_new_iso_date(date_record.year, date_record.month, date_record.day)?; | ||
Ok(date) | ||
} | ||
} | ||
|
||
impl FromStr for Date<Iso> { | ||
type Err = ParseError; | ||
fn from_str(ixdtf_str: &str) -> Result<Self, Self::Err> { | ||
Self::try_iso_from_str(ixdtf_str) | ||
} | ||
} | ||
|
||
impl Date<AnyCalendar> { | ||
/// Creates a [`Date`] in any calendar from an IXDTF syntax string with compiled data. | ||
/// | ||
/// ✨ *Enabled with the `compiled_data` and `ixdtf` Cargo features.* | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use icu::calendar::Date; | ||
/// | ||
/// let date = Date::try_from_str("2024-07-17[u-ca=hebrew]").unwrap(); | ||
/// | ||
/// assert_eq!(date.year().number, 5784); | ||
/// assert_eq!( | ||
/// date.month().code, | ||
/// icu::calendar::types::MonthCode(tinystr::tinystr!(4, "M10")) | ||
/// ); | ||
/// assert_eq!(date.day_of_month().0, 11); | ||
/// ``` | ||
#[cfg(feature = "compiled_data")] | ||
pub fn try_from_str(ixdtf_str: &str) -> Result<Self, ParseError> { | ||
Self::try_from_utf8(ixdtf_str.as_bytes()) | ||
} | ||
|
||
/// Creates a [`Date`] in any calendar from an IXDTF syntax string with compiled data. | ||
/// | ||
/// ✨ *Enabled with the `compiled_data` and `ixdtf` Cargo features.* | ||
/// | ||
/// See [`Self::try_from_str()`]. | ||
#[cfg(feature = "compiled_data")] | ||
pub fn try_from_utf8(ixdtf_str: &[u8]) -> Result<Self, ParseError> { | ||
let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse()?; | ||
let iso_date = Date::<Iso>::try_from_ixdtf_record(&ixdtf_record)?; | ||
let calendar = AnyCalendar::try_from_ixdtf_record(&ixdtf_record)?; | ||
let date = iso_date.to_any().to_calendar(calendar); | ||
Ok(date) | ||
} | ||
} | ||
|
||
#[cfg(feature = "compiled_data")] | ||
impl FromStr for Date<AnyCalendar> { | ||
type Err = ParseError; | ||
fn from_str(ixdtf_str: &str) -> Result<Self, Self::Err> { | ||
Self::try_from_str(ixdtf_str) | ||
} | ||
} | ||
|
||
impl Time { | ||
/// Creates a [`Time`] from an IXDTF syntax string of a time. | ||
/// | ||
/// Does not support parsing an IXDTF string with a date and time; for that, use [`DateTime`]. | ||
/// | ||
/// ✨ *Enabled with the `ixdtf` Cargo feature.* | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use icu::calendar::Time; | ||
/// | ||
/// let time = Time::try_from_str("16:01:17.045").unwrap(); | ||
/// | ||
/// assert_eq!(time.hour.number(), 16); | ||
/// assert_eq!(time.minute.number(), 1); | ||
/// assert_eq!(time.second.number(), 17); | ||
/// assert_eq!(time.nanosecond.number(), 45000000); | ||
/// ``` | ||
pub fn try_from_str(ixdtf_str: &str) -> Result<Self, ParseError> { | ||
Self::try_from_utf8(ixdtf_str.as_bytes()) | ||
} | ||
|
||
/// Creates a [`Time`] in the ISO-8601 calendar from an IXDTF syntax string. | ||
/// | ||
/// ✨ *Enabled with the `ixdtf` Cargo feature.* | ||
/// | ||
/// See [`Self::try_from_str()`]. | ||
pub fn try_from_utf8(ixdtf_str: &[u8]) -> Result<Self, ParseError> { | ||
let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse_time()?; | ||
Self::try_from_ixdtf_record(&ixdtf_record) | ||
} | ||
|
||
fn try_from_ixdtf_record(ixdtf_record: &IxdtfParseRecord) -> Result<Self, ParseError> { | ||
let time_record = ixdtf_record.time.ok_or(ParseError::MissingFields)?; | ||
let time = Self::try_new( | ||
time_record.hour, | ||
time_record.minute, | ||
time_record.second, | ||
time_record.nanosecond, | ||
)?; | ||
Ok(time) | ||
} | ||
} | ||
|
||
impl FromStr for Time { | ||
type Err = ParseError; | ||
fn from_str(ixdtf_str: &str) -> Result<Self, Self::Err> { | ||
Self::try_from_str(ixdtf_str) | ||
} | ||
} | ||
|
||
impl DateTime<Iso> { | ||
/// Creates a [`DateTime`] in the ISO-8601 calendar from an IXDTF syntax string. | ||
/// | ||
/// Ignores any calendar annotations in the string. | ||
/// | ||
/// ✨ *Enabled with the `ixdtf` Cargo feature.* | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use icu::calendar::DateTime; | ||
/// | ||
/// let datetime = DateTime::try_iso_from_str("2024-07-17T16:01:17.045").unwrap(); | ||
/// | ||
/// assert_eq!(datetime.date.year().number, 2024); | ||
/// assert_eq!( | ||
/// datetime.date.month().code, | ||
/// icu::calendar::types::MonthCode(tinystr::tinystr!(4, "M07")) | ||
/// ); | ||
/// assert_eq!(datetime.date.day_of_month().0, 17); | ||
/// | ||
/// assert_eq!(datetime.time.hour.number(), 16); | ||
/// assert_eq!(datetime.time.minute.number(), 1); | ||
/// assert_eq!(datetime.time.second.number(), 17); | ||
/// assert_eq!(datetime.time.nanosecond.number(), 45000000); | ||
/// ``` | ||
pub fn try_iso_from_str(ixdtf_str: &str) -> Result<Self, ParseError> { | ||
Self::try_iso_from_utf8(ixdtf_str.as_bytes()) | ||
} | ||
|
||
/// Creates a [`DateTime`] in the ISO-8601 calendar from an IXDTF syntax string. | ||
/// | ||
/// ✨ *Enabled with the `ixdtf` Cargo feature.* | ||
/// | ||
/// See [`Self::try_iso_from_str()`]. | ||
pub fn try_iso_from_utf8(ixdtf_str: &[u8]) -> Result<Self, ParseError> { | ||
let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse()?; | ||
Self::try_from_ixdtf_record(&ixdtf_record) | ||
} | ||
|
||
fn try_from_ixdtf_record(ixdtf_record: &IxdtfParseRecord) -> Result<Self, ParseError> { | ||
let date = Date::<Iso>::try_from_ixdtf_record(ixdtf_record)?; | ||
let time = Time::try_from_ixdtf_record(ixdtf_record)?; | ||
Ok(Self::new(date, time)) | ||
} | ||
} | ||
|
||
impl FromStr for DateTime<Iso> { | ||
type Err = ParseError; | ||
fn from_str(ixdtf_str: &str) -> Result<Self, Self::Err> { | ||
Self::try_iso_from_str(ixdtf_str) | ||
} | ||
} | ||
|
||
impl DateTime<AnyCalendar> { | ||
/// Creates a [`DateTime`] in any calendar from an IXDTF syntax string with compiled data. | ||
/// | ||
/// ✨ *Enabled with the `compiled_data` and `ixdtf` Cargo features.* | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use icu::calendar::DateTime; | ||
/// | ||
/// let datetime = DateTime::try_from_str("2024-07-17T16:01:17.045[u-ca=hebrew]").unwrap(); | ||
/// | ||
/// assert_eq!(datetime.date.year().number, 5784); | ||
/// assert_eq!( | ||
/// datetime.date.month().code, | ||
/// icu::calendar::types::MonthCode(tinystr::tinystr!(4, "M10")) | ||
/// ); | ||
/// assert_eq!(datetime.date.day_of_month().0, 11); | ||
/// | ||
/// assert_eq!(datetime.time.hour.number(), 16); | ||
/// assert_eq!(datetime.time.minute.number(), 1); | ||
/// assert_eq!(datetime.time.second.number(), 17); | ||
/// assert_eq!(datetime.time.nanosecond.number(), 45000000); | ||
/// ``` | ||
#[cfg(feature = "compiled_data")] | ||
pub fn try_from_str(ixdtf_str: &str) -> Result<Self, ParseError> { | ||
Self::try_from_utf8(ixdtf_str.as_bytes()) | ||
} | ||
|
||
/// Creates a [`DateTime`] in any calendar from an IXDTF syntax string with compiled data. | ||
/// | ||
/// See [`Self::try_from_str()`]. | ||
/// | ||
/// ✨ *Enabled with the `compiled_data` and `ixdtf` Cargo features.* | ||
#[cfg(feature = "compiled_data")] | ||
pub fn try_from_utf8(ixdtf_str: &[u8]) -> Result<Self, ParseError> { | ||
let ixdtf_record = IxdtfParser::from_utf8(ixdtf_str).parse()?; | ||
let iso_datetime = DateTime::<Iso>::try_from_ixdtf_record(&ixdtf_record)?; | ||
let calendar = AnyCalendar::try_from_ixdtf_record(&ixdtf_record)?; | ||
let datetime = iso_datetime.to_any().to_calendar(calendar); | ||
Ok(datetime) | ||
} | ||
} | ||
|
||
#[cfg(feature = "compiled_data")] | ||
impl FromStr for DateTime<AnyCalendar> { | ||
type Err = ParseError; | ||
fn from_str(ixdtf_str: &str) -> Result<Self, Self::Err> { | ||
Self::try_from_str(ixdtf_str) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
follow-up: this should also be
ParseError
instead ofParserError
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#2127 (comment)