ixdtf/parsers/
annotations.rs1use 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
22pub(crate) struct AnnotationSet<'a, T: EncodingType> {
24 pub(crate) tz: Option<TimeZoneAnnotation<'a, T>>,
25 pub(crate) calendar: Option<&'a [T::CodeUnit]>,
26}
27
28pub(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 let tz_annotation = timezone::parse_ambiguous_tz_annotation(cursor)?;
35
36 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
53pub(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 Some(kv) if T::check_calendar_key(kv.key) => {
66 match calendar {
68 Some(calendar)
69 if calendar.value != kv.value && (calendar.critical || kv.critical) =>
71 {
72 return Err(ParseError::CriticalDuplicateCalendar)
73 }
74 None => {
76 calendar = Some(kv);
77 }
78 _ => {}
79 }
80 }
81 Some(unknown_kv) if unknown_kv.critical => {
82 return Err(ParseError::UnrecognizedCritical);
84 }
85 _ => {}
86 }
87 }
88
89 Ok(calendar.map(|a| a.value))
90}
91
92fn 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 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 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
125fn 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 cursor.next_if(is_a_key_leading_char)?.is_some(),
132 AnnotationKeyLeadingChar,
133 );
134
135 while let Some(potential_key_char) =
136 cursor.next_if(|ch| !is_annotation_key_value_separator(ch))?
137 {
138 assert_syntax!(is_a_key_char(potential_key_char), AnnotationKeyChar);
139 }
140
141 cursor
142 .slice(key_start, cursor.pos())
143 .ok_or(ParseError::ImplAssert)
144}
145
146fn parse_annotation_value<'a, T: EncodingType>(
148 cursor: &mut Cursor<'a, T>,
149) -> ParserResult<&'a [T::CodeUnit]> {
150 let value_start = cursor.pos();
151 assert_syntax!(
152 cursor.next_if(is_annotation_value_component)?.is_some(),
153 AnnotationValueChar,
154 );
155 while let Some(potential_value_char) = cursor.next_if(|ch| !is_annotation_close(ch))? {
156 if is_hyphen(potential_value_char) {
157 assert_syntax!(
158 cursor.next_if(is_annotation_value_component)?.is_some(),
159 AnnotationValueCharPostHyphen,
160 );
161 continue;
162 }
163
164 assert_syntax!(
165 is_annotation_value_component(potential_value_char),
166 AnnotationValueChar,
167 );
168 }
169
170 cursor
171 .slice(value_start, cursor.pos())
172 .ok_or(ParseError::ImplAssert)
173}