hml_rs/markup/event.rs
1//a Imports
2use lexer_rs::{PosnInCharStream, StreamCharSpan};
3
4use crate::names::{NSNameId, Name, NamespaceStack, Tag};
5
6//a Content
7//tp ContentType
8/// The type of the content; it may be raw or in need of expansion, or
9/// it may be whitespace (if the source is XML)
10///
11/// Raw data is a Unicode string; in XML this corresponds to a CDATA
12/// section, in HML a raw string
13///
14/// Whitespace is space, tab and newline characters only; it comes
15/// from XML source, and if whitespace is deemed important for the
16/// application (as XML allows) then it is provided as such here. It
17/// may be used in XML output, in which case it generates the same
18/// whitespace; in HML output it is turned in to plain content with
19/// escape sequences for tabs and newlines.
20///
21/// Other content needs interpretation by the event provider, unless
22/// it is to be handled unparsed by the application (this is an
23/// application choice)
24#[derive(Clone, Copy, Debug, PartialEq)]
25pub enum ContentType {
26 /// Must change to Raw
27 Raw,
28 /// Content can be intepreted (escapes/entities converted to characters/strings)
29 Interpretable,
30 /// The content is whitespace that contains only tab, space or newlines (and hence need not be interpreted)
31 Whitespace,
32}
33
34//a Event
35//tp Event
36/// A markup event
37#[derive(Debug, Copy, Clone, PartialEq, Eq)]
38pub enum EventType {
39 /// The start of a document - issued before any other events
40 StartDocument,
41 /// The end of a document - issued after all other events; all
42 /// elements must be closed, and no more events will be returned
43 EndDocument,
44 /// The start of an element: this is always paired with an [EndElement] event
45 StartElement,
46 /// The end of an element
47 EndElement,
48 /// One [String] of content for an element; will always be within an element
49 Content,
50 /// A processing instruction
51 ProcessingInstruction,
52 /// A comment consisting of a String, with new lines *between* comment lines
53 ///
54 /// There is no trailing newline unless the last line was blank
55 /// (in which case that new line separates the last-but-one-line
56 /// from an empty last line)
57 Comment,
58}
59
60//tp Event
61/// A markup event occupying a [Span] on a stream
62#[derive(Debug)]
63pub enum Event<P>
64where
65 P: PosnInCharStream,
66{
67 /// The start of the document
68 StartDocument {
69 /// File position of start of the document
70 span: StreamCharSpan<P>,
71 /// Version as an integer - 100 for 1.00, etc
72 version: usize,
73 },
74
75 /// The end of the document
76 EndDocument {
77 /// File position of end of the document
78 span: StreamCharSpan<P>,
79 },
80
81 /// Denotes a beginning of an XML element.
82 StartElement {
83 /// The span of the start element 'tag'
84 span: StreamCharSpan<P>,
85 /// The actual tag (prefix, URI, name, attributes)
86 tag: Tag,
87 },
88
89 /// Denotes an end of an XML element.
90 EndElement {
91 /// The span of the end element 'tag'
92 span: StreamCharSpan<P>,
93 /// The (prefix, URI, name) of the element (equal to the same
94 /// value as the StartElement that this closes)
95 name: Name,
96 },
97
98 /// Denotes one part of the content of an element
99 Content {
100 /// The span of the content
101 span: StreamCharSpan<P>,
102 /// The type of the content: raw, whitespace, needs unescaping
103 ctype: ContentType,
104 /// The string content
105 data: String,
106 },
107
108 /// Denotes an XML processing instruction.
109 ProcessingInstruction {
110 /// The span of the PI
111 span: StreamCharSpan<P>,
112 /// A NSNameId within the namespace that is the name of the processing instruction
113 name: NSNameId,
114 /// An optional value for the processing instruction
115 data: Option<String>,
116 },
117
118 /// Denotes a comment.
119 Comment {
120 /// The span of the comment
121 span: StreamCharSpan<P>,
122 /// One string containing *all* the lines of comment (separated by \n)
123 ///
124 /// The last line does not have \n appended, so single line comments have no newline
125 data: String,
126 /// Length of each original comment line (not including any additional \n)
127 lengths: Vec<usize>,
128 },
129}
130
131//ip Event
132impl<P> Event<P>
133where
134 P: PosnInCharStream,
135{
136 //fp start_document
137 /// Create a StartDocument event
138 pub fn start_document(span: StreamCharSpan<P>, version: usize) -> Self {
139 Self::StartDocument { span, version }
140 }
141
142 //fp end_document
143 /// Create an EndDocument event
144 pub fn end_document(span: StreamCharSpan<P>) -> Self {
145 Self::EndDocument { span }
146 }
147
148 //fp start_element
149 /// Create a StartElement event with a given [Tag]
150 pub fn start_element(span: StreamCharSpan<P>, tag: Tag) -> Self {
151 Self::StartElement { span, tag }
152 }
153
154 //fp end_element
155 /// Create an EndElement event with a given [Name]
156 pub fn end_element(span: StreamCharSpan<P>, name: Name) -> Self {
157 Self::EndElement { span, name }
158 }
159
160 //fp comment
161 /// Create an event of a vec of comment strings
162 pub fn comment(span: StreamCharSpan<P>, data: String, lengths: Vec<usize>) -> Self {
163 Self::Comment {
164 span,
165 data,
166 lengths,
167 }
168 }
169
170 //fp content
171 /// Create an event of content of the given type
172 pub fn content(span: StreamCharSpan<P>, ctype: ContentType, data: String) -> Self {
173 Self::Content { span, ctype, data }
174 }
175
176 //fp content_raw
177 /// Create an event of raw content
178 pub fn content_raw(span: StreamCharSpan<P>, data: String) -> Self {
179 Self::content(span, ContentType::Raw, data)
180 }
181
182 //fp content_int
183 /// Create an event of interpretable content
184 pub fn content_int(span: StreamCharSpan<P>, data: String) -> Self {
185 Self::content(span, ContentType::Interpretable, data)
186 }
187
188 //fp content_ws
189 /// Create an event of whitespace content
190 pub fn content_ws(span: StreamCharSpan<P>, data: String) -> Self {
191 Self::content(span, ContentType::Whitespace, data)
192 }
193
194 //mp get_type
195 /// Get the [EventType] corresponding to the [Event]
196 pub fn get_type(&self) -> EventType {
197 match self {
198 Self::StartDocument { .. } => EventType::StartDocument,
199 Self::EndDocument { .. } => EventType::EndDocument,
200 Self::StartElement { .. } => EventType::StartElement,
201 Self::EndElement { .. } => EventType::EndElement,
202 Self::Content { .. } => EventType::Content,
203 Self::ProcessingInstruction { .. } => EventType::ProcessingInstruction,
204 Self::Comment { .. } => EventType::Comment,
205 }
206 }
207
208 //mp borrow_span
209 /// Borrow the span of the event, for logging or errors etc.
210 pub fn borrow_span(&self) -> &StreamCharSpan<P> {
211 match self {
212 Self::StartDocument { span, .. } => span,
213 Self::EndDocument { span, .. } => span,
214 Self::StartElement { span, .. } => span,
215 Self::EndElement { span, .. } => span,
216 Self::Content { span, .. } => span,
217 Self::ProcessingInstruction { span, .. } => span,
218 Self::Comment { span, .. } => span,
219 }
220 }
221
222 //mp as_start_document
223 /// Return Some(version number) if the [Event] is a StartDocument
224 /// event; else return None
225 pub fn as_start_document(&self) -> Option<usize> {
226 match self {
227 Self::StartDocument { version, .. } => Some(*version),
228 _ => None,
229 }
230 }
231
232 //mp as_start_element
233 /// Return Some(Tag) if the [Event] is a StartElement
234 /// event; else return None
235 pub fn as_start_element(self) -> Option<Tag> {
236 match self {
237 Self::StartElement { tag, .. } => Some(tag),
238 _ => None,
239 }
240 }
241
242 //mp as_end_element
243 /// Return Some(Name) if the [Event] is an EndElement
244 /// event; else return None
245 pub fn as_end_element(&self) -> Option<&Name> {
246 match self {
247 Self::EndElement { name, .. } => Some(name),
248 _ => None,
249 }
250 }
251
252 //mp as_content
253 /// Return Some(ContentType, string) if the [Event] is a Content
254 /// event; else return None
255 pub fn as_content(&self) -> Option<(ContentType, &str)> {
256 match self {
257 Self::Content { ctype, data, .. } => Some((*ctype, data)),
258 _ => None,
259 }
260 }
261
262 //mp is_start_document
263 /// Return true if the Event is a StartDocument event
264 pub fn is_start_document(&self) -> bool {
265 matches!(self, Self::StartDocument { .. })
266 }
267
268 //mp is_end_document
269 /// Return true if the Event is an EndDocument event
270 pub fn is_end_document(&self) -> bool {
271 matches!(self, Self::EndDocument { .. })
272 }
273}
274
275//a If xml_rs is included
276#[cfg(feature = "xml")]
277//ip Event
278impl<P> Event<P>
279where
280 P: PosnInCharStream,
281{
282 //mp as_xml_writer
283 /// Get an [xml::writer::XmlEvent<'a>] from this Name
284 pub fn as_xml_writer<'a>(
285 &'a self,
286 ns: &'a NamespaceStack,
287 ) -> Option<xml::writer::XmlEvent<'a>> {
288 use Event::*;
289 match self {
290 StartDocument { version, .. } => {
291 let version = if *version == 100 {
292 xml::common::XmlVersion::Version10
293 } else {
294 xml::common::XmlVersion::Version11
295 };
296 Some(xml::writer::XmlEvent::StartDocument {
297 version,
298 encoding: None,
299 standalone: None,
300 })
301 }
302 StartElement { tag, .. } => {
303 let name = tag.name.as_xml_name(ns);
304 let mut x = xml::writer::XmlEvent::start_element(name);
305 for a in tag.attributes.attributes() {
306 let attr_name = a.name.as_xml_name(ns);
307 x = x.attr(attr_name, &a.value);
308 }
309 Some(x.into())
310 }
311 EndElement { .. } => Some(xml::writer::XmlEvent::end_element().into()),
312 EndDocument { .. } => None,
313 Content { data, .. } => Some(xml::writer::XmlEvent::characters(data)),
314 ProcessingInstruction { name, data, .. } => {
315 let name = ns.name_str(*name);
316 Some(xml::writer::XmlEvent::processing_instruction(
317 name,
318 data.as_ref().map(|x| x.as_str()),
319 ))
320 }
321 Comment { data, .. } => Some(xml::writer::XmlEvent::comment(data)),
322 }
323 }
324
325 //mp as_xml_reader
326 /// Get an [xml::reader::XmlEvent<'a>] from this Name
327 pub fn as_xml_reader(
328 &self,
329 ns: &NamespaceStack,
330 _fill_namespaces: bool,
331 ) -> Option<xml::reader::XmlEvent> {
332 use Event::*;
333 match self {
334 StartDocument { version, .. } => {
335 let version = if *version == 100 {
336 xml::common::XmlVersion::Version10
337 } else {
338 xml::common::XmlVersion::Version11
339 };
340 Some(xml::reader::XmlEvent::StartDocument {
341 version,
342 encoding: "UTF8".to_string(),
343 standalone: None,
344 })
345 }
346 StartElement { tag, .. } => {
347 let name = tag.name.as_xml_name(ns).to_owned();
348 let namespace = xml::namespace::Namespace::empty();
349 let mut attributes = Vec::new();
350 for a in tag.attributes.attributes() {
351 let attr_name = a.name.as_xml_name(ns).to_owned();
352 attributes.push(xml::attribute::OwnedAttribute::new(attr_name, &a.value));
353 }
354 Some(xml::reader::XmlEvent::StartElement {
355 name,
356 namespace,
357 attributes,
358 })
359 }
360 EndElement { name, .. } => Some(xml::reader::XmlEvent::EndElement {
361 name: name.as_xml_name(ns).to_owned(),
362 }),
363 EndDocument { .. } => Some(xml::reader::XmlEvent::EndDocument),
364 Content { data, .. } => Some(xml::reader::XmlEvent::Characters(data.clone())),
365 ProcessingInstruction { name, data, .. } => {
366 let name = ns.name_str(*name);
367 Some(xml::reader::XmlEvent::ProcessingInstruction {
368 name: name.to_string(),
369 data: data.clone(),
370 })
371 }
372 Comment { data, .. } => Some(xml::reader::XmlEvent::Comment(data.to_string())),
373 }
374 }
375}