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}