1use core::str::FromStr;
6
7use crate::{AsCalendar, Calendar, Date, Iso, RangeError};
8use icu_locale_core::preferences::extensions::unicode::keywords::CalendarAlgorithm;
9use ixdtf::encoding::Utf8;
10use ixdtf::parsers::IxdtfParser;
11use ixdtf::records::IxdtfParseRecord;
12use ixdtf::ParseError as Rfc9557Error;
13
14#[derive(Debug, displaydoc::Display)]
16#[non_exhaustive]
17pub enum ParseError {
18 #[displaydoc("Syntax error in the RFC 9557 string: {0}")]
20 Syntax(Rfc9557Error),
21 #[displaydoc("Value out of range: {0}")]
23 Range(RangeError),
24 MissingFields,
26 UnknownCalendar,
28 #[displaydoc("Expected calendar {0:?} but found calendar {1:?}")]
30 MismatchedCalendar(CalendarAlgorithm, CalendarAlgorithm),
31}
32
33impl From<RangeError> for ParseError {
34 fn from(value: RangeError) -> Self {
35 Self::Range(value)
36 }
37}
38
39impl From<Rfc9557Error> for ParseError {
40 fn from(value: Rfc9557Error) -> Self {
41 Self::Syntax(value)
42 }
43}
44
45impl FromStr for Date<Iso> {
46 type Err = ParseError;
47 fn from_str(rfc_9557_str: &str) -> Result<Self, Self::Err> {
48 Self::try_from_str(rfc_9557_str, Iso)
49 }
50}
51
52impl<A: AsCalendar> Date<A> {
53 pub fn try_from_str(rfc_9557_str: &str, calendar: A) -> Result<Self, ParseError> {
76 Self::try_from_utf8(rfc_9557_str.as_bytes(), calendar)
77 }
78
79 pub fn try_from_utf8(rfc_9557_str: &[u8], calendar: A) -> Result<Self, ParseError> {
88 let ixdtf_record = IxdtfParser::from_utf8(rfc_9557_str).parse()?;
89 Self::try_from_ixdtf_record(&ixdtf_record, calendar)
90 }
91
92 #[doc(hidden)]
93 pub fn try_from_ixdtf_record(
94 ixdtf_record: &IxdtfParseRecord<'_, Utf8>,
95 calendar: A,
96 ) -> Result<Self, ParseError> {
97 let date_record = ixdtf_record.date.ok_or(ParseError::MissingFields)?;
98 let iso = Date::try_new_iso(date_record.year, date_record.month, date_record.day)?;
99
100 if let Some(ixdtf_calendar) = ixdtf_record.calendar {
101 if let Some(expected_calendar) = calendar.as_calendar().calendar_algorithm() {
102 if let Some(parsed_calendar) =
103 icu_locale_core::extensions::unicode::Value::try_from_utf8(ixdtf_calendar)
104 .ok()
105 .and_then(|v| CalendarAlgorithm::try_from(&v).ok())
106 {
107 if parsed_calendar != expected_calendar {
108 return Err(ParseError::MismatchedCalendar(
109 expected_calendar,
110 parsed_calendar,
111 ));
112 }
113 }
114 }
115 }
116 Ok(iso.to_calendar(calendar))
117 }
118}