Skip to main content

ixdtf/parsers/
annotations.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! Parsing for `TimeZoneAnnotations` and `KeyValueAnnotations`.
6
7use crate::{
8    assert_syntax,
9    encoding::EncodingType,
10    parsers::{
11        grammar::{
12            is_a_key_char, is_a_key_leading_char, is_annotation_close,
13            is_annotation_key_value_separator, is_annotation_open, is_annotation_value_component,
14            is_critical_flag, is_hyphen,
15        },
16        timezone, Cursor,
17    },
18    records::{Annotation, TimeZoneAnnotation},
19    ParseError, ParserResult,
20};
21
22/// Strictly a parsing intermediary for the checking the common annotation backing.
23pub(crate) struct AnnotationSet<'a, T: EncodingType> {
24    pub(crate) tz: Option<TimeZoneAnnotation<'a, T>>,
25    pub(crate) calendar: Option<&'a [T::CodeUnit]>,
26}
27
28/// Parse a `TimeZoneAnnotation` `Annotations` set
29pub(crate) fn parse_annotation_set<'a, T: EncodingType>(
30    cursor: &mut Cursor<'a, T>,
31    handler: impl FnMut(Annotation<'a, T>) -> Option<Annotation<'a, T>>,
32) -> ParserResult<AnnotationSet<'a, T>> {
33    // Parse an optional TimeZoneAnnotation
34    let tz_annotation = timezone::parse_ambiguous_tz_annotation(cursor)?;
35
36    // Parse any `Annotations`
37    let annotations = cursor.check_or(false, is_annotation_open)?;
38
39    if annotations {
40        let calendar = parse_annotations(cursor, handler)?;
41        return Ok(AnnotationSet {
42            tz: tz_annotation,
43            calendar,
44        });
45    }
46
47    Ok(AnnotationSet {
48        tz: tz_annotation,
49        calendar: None,
50    })
51}
52
53/// Parse any number of `KeyValueAnnotation`s
54pub(crate) fn parse_annotations<'a, T: EncodingType>(
55    cursor: &mut Cursor<'a, T>,
56    mut handler: impl FnMut(Annotation<'a, T>) -> Option<Annotation<'a, T>>,
57) -> ParserResult<Option<&'a [T::CodeUnit]>> {
58    let mut calendar: Option<Annotation<'a, T>> = None;
59
60    while cursor.check_or(false, is_annotation_open)? {
61        let annotation = handler(parse_kv_annotation(cursor)?);
62
63        match annotation {
64            // Check if the key is the registered key "u-ca".
65            Some(kv) if T::check_calendar_key(kv.key) => {
66                // Check the calendar
67                match calendar {
68                    Some(calendar)
69                        // if calendars do not match and one of them is critical
70                        if calendar.value != kv.value && (calendar.critical || kv.critical) =>
71                    {
72                        return Err(ParseError::CriticalDuplicateCalendar)
73                    }
74                    // If there is not yet a calendar, save it.
75                    None => {
76                        calendar = Some(kv);
77                    }
78                    _ => {}
79                }
80            }
81            Some(unknown_kv) if unknown_kv.critical => {
82                // Throw an error on any unrecognized annotations that are marked as critical.
83                return Err(ParseError::UnrecognizedCritical);
84            }
85            _ => {}
86        }
87    }
88
89    Ok(calendar.map(|a| a.value))
90}
91
92/// Parse an annotation with an `AnnotationKey`=`AnnotationValue` pair.
93fn parse_kv_annotation<'a, T: EncodingType>(
94    cursor: &mut Cursor<'a, T>,
95) -> ParserResult<Annotation<'a, T>> {
96    assert_syntax!(
97        is_annotation_open(cursor.next_or(ParseError::AnnotationOpen)?),
98        AnnotationOpen
99    );
100
101    let critical = cursor.check_or(false, is_critical_flag)?;
102    cursor.advance_if(critical);
103
104    // Parse AnnotationKey.
105    let annotation_key = parse_annotation_key(cursor)?;
106    assert_syntax!(
107        is_annotation_key_value_separator(cursor.next_or(ParseError::AnnotationKeyValueSeparator)?),
108        AnnotationKeyValueSeparator,
109    );
110
111    // Parse AnnotationValue.
112    let annotation_value = parse_annotation_value(cursor)?;
113    assert_syntax!(
114        is_annotation_close(cursor.next_or(ParseError::AnnotationClose)?),
115        AnnotationClose
116    );
117
118    Ok(Annotation {
119        critical,
120        key: annotation_key,
121        value: annotation_value,
122    })
123}
124
125/// Parse an `AnnotationKey`.
126fn parse_annotation_key<'a, T: EncodingType>(
127    cursor: &mut Cursor<'a, T>,
128) -> ParserResult<&'a [T::CodeUnit]> {
129    let key_start = cursor.pos();
130    assert_syntax!(
131        is_a_key_leading_char(cursor.next_or(ParseError::AnnotationKeyLeadingChar)?),
132        AnnotationKeyLeadingChar,
133    );
134
135    while let Some(potential_key_char) = cursor.next()? {
136        // End of key.
137        if cursor.check_or(false, is_annotation_key_value_separator)? {
138            // Return found key
139            return cursor
140                .slice(key_start, cursor.pos())
141                .ok_or(ParseError::ImplAssert);
142        }
143
144        assert_syntax!(is_a_key_char(potential_key_char), AnnotationKeyChar);
145    }
146
147    Err(ParseError::AnnotationChar)
148}
149
150/// Parse an `AnnotationValue`.
151fn parse_annotation_value<'a, T: EncodingType>(
152    cursor: &mut Cursor<'a, T>,
153) -> ParserResult<&'a [T::CodeUnit]> {
154    let value_start = cursor.pos();
155    cursor.advance();
156    while let Some(potential_value_char) = cursor.next()? {
157        if cursor.check_or(false, is_annotation_close)? {
158            // Return the determined AnnotationValue.
159            return cursor
160                .slice(value_start, cursor.pos())
161                .ok_or(ParseError::ImplAssert);
162        }
163
164        if is_hyphen(potential_value_char) {
165            assert_syntax!(
166                cursor.peek()?.is_some_and(is_annotation_value_component),
167                AnnotationValueCharPostHyphen,
168            );
169            cursor.advance();
170            continue;
171        }
172
173        assert_syntax!(
174            is_annotation_value_component(potential_value_char),
175            AnnotationValueChar,
176        );
177    }
178
179    Err(ParseError::AnnotationValueChar)
180}