Skip to main content

ironwork_rt/
xml.rs

1//! The events XML PARSE reports under XMLPARSE(XMLSS) (Language Reference SC27-8713-03, pp. 29-33,
2//! 489-494; Programming Guide SC27-8714-03, pp. 649-653). The document may arrive in segments: markup
3//! is held until it is complete, while character content, comments and processing-instruction data
4//! that a segment ends inside are reported in parts.
5
6use std::collections::VecDeque;
7
8pub mod generate;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum EventKind {
12    StartOfDocument,
13    VersionInformation,
14    EncodingDeclaration,
15    StandaloneDeclaration,
16    DocumentTypeDeclaration,
17    Comment,
18    ProcessingInstructionTarget,
19    ProcessingInstructionData,
20    StartOfElement,
21    NamespaceDeclaration,
22    AttributeName,
23    AttributeCharacters,
24    AttributeNationalCharacter,
25    ContentCharacters,
26    ContentNationalCharacter,
27    StartOfCdataSection,
28    EndOfCdataSection,
29    EndOfElement,
30    UnresolvedReference,
31    EndOfInput,
32    EndOfDocument,
33    Exception,
34}
35
36impl EventKind {
37    /// The XML-EVENT value.
38    pub fn name(self) -> &'static str {
39        match self {
40            Self::StartOfDocument => "START-OF-DOCUMENT",
41            Self::VersionInformation => "VERSION-INFORMATION",
42            Self::EncodingDeclaration => "ENCODING-DECLARATION",
43            Self::StandaloneDeclaration => "STANDALONE-DECLARATION",
44            Self::DocumentTypeDeclaration => "DOCUMENT-TYPE-DECLARATION",
45            Self::Comment => "COMMENT",
46            Self::ProcessingInstructionTarget => "PROCESSING-INSTRUCTION-TARGET",
47            Self::ProcessingInstructionData => "PROCESSING-INSTRUCTION-DATA",
48            Self::StartOfElement => "START-OF-ELEMENT",
49            Self::NamespaceDeclaration => "NAMESPACE-DECLARATION",
50            Self::AttributeName => "ATTRIBUTE-NAME",
51            Self::AttributeCharacters => "ATTRIBUTE-CHARACTERS",
52            Self::AttributeNationalCharacter => "ATTRIBUTE-NATIONAL-CHARACTER",
53            Self::ContentCharacters => "CONTENT-CHARACTERS",
54            Self::ContentNationalCharacter => "CONTENT-NATIONAL-CHARACTER",
55            Self::StartOfCdataSection => "START-OF-CDATA-SECTION",
56            Self::EndOfCdataSection => "END-OF-CDATA-SECTION",
57            Self::EndOfElement => "END-OF-ELEMENT",
58            Self::UnresolvedReference => "UNRESOLVED-REFERENCE",
59            Self::EndOfInput => "END-OF-INPUT",
60            Self::EndOfDocument => "END-OF-DOCUMENT",
61            Self::Exception => "EXCEPTION",
62        }
63    }
64}
65
66#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct Event {
68    pub kind: EventKind,
69    /// XML-TEXT or XML-NTEXT.
70    pub text: String,
71    /// XML-NAMESPACE: the identifier bound to the name's prefix, or to the default namespace.
72    pub namespace: String,
73    /// XML-NAMESPACE-PREFIX.
74    pub prefix: String,
75    /// XML-INFORMATION: 1 when attribute or content characters are complete, 2 when more follow,
76    /// 0 for every other event (p. 34).
77    pub information: i32,
78    /// XML-CODE for an EXCEPTION the parse goes on from when the processing procedure sets XML-CODE
79    /// to zero: an undeclared prefix's warning.
80    pub code: i32,
81}
82
83impl Event {
84    fn new(kind: EventKind, text: impl Into<String>) -> Self {
85        Self { kind, text: text.into(), namespace: String::new(), prefix: String::new(), information: 0, code: 0 }
86    }
87
88    /// The warning for a name with an undeclared prefix, which XML-TEXT holds whole (Programming
89    /// Guide SC27-8714-03, pp. 647, 656-658).
90    fn undeclared(why: Why, qualified: &str) -> Self {
91        Self { code: why.code(), ..Self::new(EventKind::Exception, qualified) }
92    }
93
94    fn characters(kind: EventKind, text: String, complete: bool) -> Self {
95        Self { information: if complete { 1 } else { 2 }, ..Self::new(kind, text) }
96    }
97}
98
99#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub enum Why {
101    UnexpectedEnd,
102    InvalidCharacter,
103    MismatchedEndTag,
104    DuplicateAttribute,
105    UndeclaredElementPrefix,
106    UndeclaredAttributePrefix,
107    UndeclaredEntity,
108    BadReference,
109    ContentAfterRoot,
110    SecondRoot,
111    NoRoot,
112    BadDeclaration,
113    BadComment,
114    LessThanInAttribute,
115}
116
117impl Why {
118    /// XML-CODE: z/OS XML System Services' return code 12 (not well formed) and its reason code,
119    /// or return code 4 and Enterprise COBOL's own reason for an undeclared prefix (Programming
120    /// Guide SC27-8714-03, pp. 645, 809; z/OS XML System Services User's Guide SA38-0681-50,
121    /// Appendix B; assumption C118).
122    pub fn code(self) -> i32 {
123        let not_well_formed = |reason: i32| 0x000C_0000 | reason;
124        match self {
125            Why::UnexpectedEnd => not_well_formed(0x2004),
126            Why::NoRoot => not_well_formed(0x2019),
127            Why::DuplicateAttribute => not_well_formed(0x3000),
128            Why::BadComment => not_well_formed(0x3008),
129            Why::LessThanInAttribute => not_well_formed(0x3022),
130            Why::BadReference => not_well_formed(0x3028),
131            Why::MismatchedEndTag => not_well_formed(0x3035),
132            Why::BadDeclaration => not_well_formed(0x3060),
133            Why::UndeclaredEntity => not_well_formed(0x3061),
134            Why::InvalidCharacter | Why::ContentAfterRoot => not_well_formed(0x3062),
135            Why::SecondRoot => not_well_formed(0x3065),
136            Why::UndeclaredAttributePrefix => 0x0004_0800,
137            Why::UndeclaredElementPrefix => 0x0004_0801,
138        }
139    }
140}
141
142/// Where the document stops being well formed: the offset, in characters from the start of the
143/// document, of the first character in error, or of its end.
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
145pub struct Malformed {
146    pub offset: usize,
147    pub why: Why,
148}
149
150#[derive(Clone, Debug, PartialEq, Eq)]
151pub enum Step {
152    Event(Event),
153    /// The segment is used up and the document is not finished: END-OF-INPUT.
154    EndOfInput,
155    Error(Malformed),
156    /// END-OF-DOCUMENT has been reported.
157    Done,
158}
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq)]
161enum Place {
162    Start,
163    Prolog,
164    Content,
165    Epilogue,
166    Finished,
167}
168
169/// A construct a segment ended inside, whose text is being reported in parts.
170#[derive(Clone, Debug, PartialEq, Eq)]
171enum Open {
172    Comment,
173    /// A processing instruction's target, and whether part of its data has been reported: the
174    /// target is reported again before each later part (p. 32).
175    Instruction(String, bool),
176    Cdata,
177}
178
179struct Element {
180    qualified: String,
181    local: String,
182    prefix: String,
183    namespace: String,
184    bindings: usize,
185}
186
187pub const XML_NAMESPACE: &str = "http://www.w3.org/XML/1998/namespace";
188
189pub struct Scanner<'r> {
190    buf: Vec<char>,
191    at: usize,
192    consumed: usize,
193    place: Place,
194    open: Option<Open>,
195    elements: Vec<Element>,
196    /// Namespace bindings in scope, innermost last: prefix ("" for the default) and identifier.
197    bindings: Vec<(String, String)>,
198    pending: VecDeque<Event>,
199    representable: &'r dyn Fn(char) -> bool,
200    last_segment: bool,
201    doctype: bool,
202    standalone_no: bool,
203}
204
205type Scan<T> = Result<T, Why>;
206
207/// Not enough input to finish a construct.
208const MORE: Why = Why::UnexpectedEnd;
209
210fn is_space(c: char) -> bool {
211    matches!(c, ' ' | '\t' | '\n' | '\r')
212}
213
214fn name_start(c: char) -> bool {
215    c.is_ascii_alphabetic() || c == '_' || c == ':' || c as u32 >= 0xC0
216}
217
218fn name_char(c: char) -> bool {
219    name_start(c) || c.is_ascii_digit() || c == '-' || c == '.' || c == '\u{B7}'
220}
221
222fn xml_char(c: u32) -> bool {
223    matches!(c, 0x9 | 0xA | 0xD | 0x20..=0xD7FF | 0xE000..=0xFFFD | 0x10000..=0x10FFFF)
224}
225
226fn split(qualified: &str) -> (String, String) {
227    match qualified.split_once(':') {
228        Some((prefix, local)) => (prefix.to_owned(), local.to_owned()),
229        None => (String::new(), qualified.to_owned()),
230    }
231}
232
233impl<'r> Scanner<'r> {
234    /// `representable(c)` says whether the document's code page holds c: a character reference
235    /// to one it does not is reported as a NATIONAL-CHARACTER event.
236    pub fn new(segment: &str, representable: &'r dyn Fn(char) -> bool) -> Self {
237        Self {
238            buf: segment.chars().collect(),
239            at: 0,
240            consumed: 0,
241            place: Place::Start,
242            open: None,
243            elements: Vec::new(),
244            bindings: vec![("xml".into(), XML_NAMESPACE.into())],
245            pending: VecDeque::new(),
246            representable,
247            last_segment: false,
248            doctype: false,
249            standalone_no: false,
250        }
251    }
252
253    /// The next segment, after END-OF-INPUT, joining what the last one left unfinished.
254    pub fn feed(&mut self, segment: &str) {
255        self.consumed += self.at;
256        self.buf.drain(..self.at);
257        self.at = 0;
258        self.buf.extend(segment.chars());
259    }
260
261    /// No segment follows: what is unfinished is an error.
262    pub fn finish(&mut self) {
263        self.last_segment = true;
264    }
265
266    fn peek(&self, ahead: usize) -> Option<char> {
267        self.buf.get(self.at + ahead).copied()
268    }
269
270    fn starts(&self, s: &str) -> bool {
271        let chars: Vec<char> = s.chars().collect();
272        self.buf[self.at..].starts_with(&chars)
273    }
274
275    /// Whether the rest of the buffer could still be the start of `s`.
276    fn could_start(&self, s: &str) -> bool {
277        let rest = &self.buf[self.at..];
278        rest.len() < s.chars().count() && s.chars().zip(rest).all(|(a, &b)| a == b)
279    }
280
281    fn find(&self, from: usize, s: &str) -> Option<usize> {
282        let chars: Vec<char> = s.chars().collect();
283        (from..=self.buf.len().saturating_sub(chars.len())).find(|&i| self.buf[i..].starts_with(&chars))
284    }
285
286    fn fail(&self, at: usize, why: Why) -> Step {
287        Step::Error(Malformed { offset: self.consumed + at, why })
288    }
289
290    pub fn advance(&mut self) -> Step {
291        if let Some(e) = self.pending.pop_front() {
292            return Step::Event(e);
293        }
294        if self.place == Place::Start {
295            self.place = Place::Prolog;
296            return Step::Event(Event::new(EventKind::StartOfDocument, ""));
297        }
298        if self.place == Place::Finished {
299            return Step::Done;
300        }
301        let before = self.at;
302        match self.step() {
303            Ok(()) => match self.pending.pop_front() {
304                Some(e) => Step::Event(e),
305                None if self.place == Place::Finished => Step::Event(Event::new(EventKind::EndOfDocument, "")),
306                None => self.advance(),
307            },
308            Err(MORE) => {
309                self.at = before;
310                self.at_end()
311            }
312            Err(why) => self.fail(self.at, why),
313        }
314    }
315
316    /// The segment is used up, or holds only part of a construct.
317    fn at_end(&mut self) -> Step {
318        if self.place == Place::Epilogue && self.buf[self.at..].iter().all(|&c| is_space(c)) {
319            self.at = self.buf.len();
320            self.place = Place::Finished;
321            return Step::Event(Event::new(EventKind::EndOfDocument, ""));
322        }
323        if !self.last_segment {
324            return Step::EndOfInput;
325        }
326        let why = if self.place == Place::Prolog && self.open.is_none() && self.buf[self.at..].iter().all(|&c| is_space(c)) { Why::NoRoot } else { Why::UnexpectedEnd };
327        Step::Error(Malformed { offset: self.consumed + self.buf.len(), why })
328    }
329
330    /// Scans one construct, queueing its events; `Err(MORE)` leaves the cursor where it was.
331    fn step(&mut self) -> Scan<()> {
332        if let Some(open) = self.open.clone() {
333            return self.continue_open(open);
334        }
335        while self.place != Place::Content && self.peek(0).is_some_and(is_space) {
336            self.at += 1;
337        }
338        let Some(c) = self.peek(0) else { return Err(MORE) };
339        if c != '<' {
340            if self.place == Place::Content {
341                return self.content();
342            }
343            return Err(if self.place == Place::Epilogue { Why::ContentAfterRoot } else { Why::InvalidCharacter });
344        }
345        if self.starts("<?xml") && self.peek(5).is_some_and(|c| is_space(c) || c == '?') && self.consumed == 0 && self.at == 0 {
346            return self.declaration();
347        }
348        if self.could_start("<!--") || self.could_start("<![CDATA[") || self.could_start("<!DOCTYPE") || self.buf.len() - self.at < 2 {
349            return Err(MORE);
350        }
351        if self.starts("<!--") {
352            self.at += 4;
353            self.open = Some(Open::Comment);
354            return Ok(());
355        }
356        if self.starts("<![CDATA[") {
357            if self.place != Place::Content {
358                return Err(Why::InvalidCharacter);
359            }
360            if self.peek(9).is_none() {
361                return Err(MORE);
362            }
363            self.at += 9;
364            self.pending.push_back(Event::new(EventKind::StartOfCdataSection, ""));
365            self.open = Some(Open::Cdata);
366            return Ok(());
367        }
368        if self.starts("<!DOCTYPE") {
369            return self.doctype();
370        }
371        if self.starts("<?") {
372            return self.instruction();
373        }
374        if self.starts("</") {
375            return self.end_tag();
376        }
377        if self.place == Place::Epilogue {
378            return Err(Why::SecondRoot);
379        }
380        self.start_tag()
381    }
382
383    fn name(&mut self) -> Scan<String> {
384        let start = self.at;
385        match self.peek(0) {
386            None => return Err(MORE),
387            Some(c) if !name_start(c) => return Err(Why::InvalidCharacter),
388            _ => {}
389        }
390        while self.peek(0).is_some_and(name_char) {
391            self.at += 1;
392        }
393        if self.at == self.buf.len() {
394            return Err(MORE);
395        }
396        let name: String = self.buf[start..self.at].iter().collect();
397        if name.matches(':').count() > 1 || name.starts_with(':') || name.ends_with(':') {
398            self.at = start;
399            return Err(Why::InvalidCharacter);
400        }
401        Ok(name)
402    }
403
404    fn spaces(&mut self) -> bool {
405        let start = self.at;
406        while self.peek(0).is_some_and(is_space) {
407            self.at += 1;
408        }
409        self.at > start
410    }
411
412    fn expect(&mut self, c: char) -> Scan<()> {
413        match self.peek(0) {
414            None => Err(MORE),
415            Some(d) if d == c => {
416                self.at += 1;
417                Ok(())
418            }
419            Some(_) => Err(Why::InvalidCharacter),
420        }
421    }
422
423    /// A reference after '&': the character it stands for, or `Err(name)` for an entity that is
424    /// not one of the five predefined.
425    fn reference(&mut self) -> Scan<Result<char, String>> {
426        let end = self.find(self.at, ";").ok_or(MORE)?;
427        let body: String = self.buf[self.at + 1..end].iter().collect();
428        let value = if let Some(hex) = body.strip_prefix("#x") {
429            u32::from_str_radix(hex, 16).ok().filter(|&v| xml_char(v) && !hex.is_empty()).and_then(char::from_u32).map(Ok).ok_or(Why::BadReference)?
430        } else if let Some(dec) = body.strip_prefix('#') {
431            dec.parse::<u32>().ok().filter(|&v| xml_char(v)).and_then(char::from_u32).map(Ok).ok_or(Why::BadReference)?
432        } else {
433            match body.as_str() {
434                "lt" => Ok('<'),
435                "gt" => Ok('>'),
436                "amp" => Ok('&'),
437                "apos" => Ok('\''),
438                "quot" => Ok('"'),
439                name if !name.is_empty() && name.chars().next().is_some_and(name_start) && name.chars().all(name_char) => Err(name.to_owned()),
440                _ => return Err(Why::BadReference),
441            }
442        };
443        self.at = end + 1;
444        Ok(value)
445    }
446
447    fn declaration(&mut self) -> Scan<()> {
448        let end = self.find(self.at, "?>").ok_or(MORE)?;
449        self.at += 5;
450        let mut seen = Vec::new();
451        loop {
452            self.spaces();
453            if self.at >= end {
454                break;
455            }
456            let name = self.name().map_err(|_| Why::BadDeclaration)?;
457            self.spaces();
458            self.expect('=').map_err(|_| Why::BadDeclaration)?;
459            self.spaces();
460            let quote = self.peek(0).filter(|&q| q == '"' || q == '\'').ok_or(Why::BadDeclaration)?;
461            let close = self.find(self.at + 1, &quote.to_string()).filter(|&c| c < end).ok_or(Why::BadDeclaration)?;
462            let value: String = self.buf[self.at + 1..close].iter().collect();
463            self.at = close + 1;
464            let kind = match name.as_str() {
465                "version" if seen.is_empty() => EventKind::VersionInformation,
466                "encoding" if seen == [EventKind::VersionInformation] => EventKind::EncodingDeclaration,
467                "standalone" if seen.first() == Some(&EventKind::VersionInformation) && !seen.contains(&EventKind::StandaloneDeclaration) && (value == "yes" || value == "no") => {
468                    self.standalone_no = value == "no";
469                    EventKind::StandaloneDeclaration
470                }
471                _ => return Err(Why::BadDeclaration),
472            };
473            seen.push(kind);
474            self.pending.push_back(Event::new(kind, value));
475        }
476        if seen.first() != Some(&EventKind::VersionInformation) {
477            return Err(Why::BadDeclaration);
478        }
479        self.at = end + 2;
480        Ok(())
481    }
482
483    fn doctype(&mut self) -> Scan<()> {
484        if self.place != Place::Prolog || self.doctype {
485            return Err(Why::InvalidCharacter);
486        }
487        let mut depth = 0i32;
488        let mut quote = None;
489        let mut i = self.at + 9;
490        let end = loop {
491            let c = *self.buf.get(i).ok_or(MORE)?;
492            match (quote, c) {
493                (Some(q), c) if c == q => quote = None,
494                (Some(_), _) => {}
495                (None, '"' | '\'') => quote = Some(c),
496                (None, '[') => depth += 1,
497                (None, ']') => depth -= 1,
498                (None, '>') if depth == 0 => break i,
499                _ => {}
500            }
501            i += 1;
502        };
503        self.at += 9;
504        if !self.spaces() {
505            return Err(Why::InvalidCharacter);
506        }
507        let root = self.name()?;
508        self.at = end + 1;
509        self.doctype = true;
510        self.pending.push_back(Event::new(EventKind::DocumentTypeDeclaration, root));
511        Ok(())
512    }
513
514    fn instruction(&mut self) -> Scan<()> {
515        self.at += 2;
516        let start = self.at;
517        let target = self.name()?;
518        if target.eq_ignore_ascii_case("xml") {
519            self.at = start;
520            return Err(Why::BadDeclaration);
521        }
522        match self.peek(0) {
523            None => return Err(MORE),
524            Some('?') if self.peek(1).is_none() => return Err(MORE),
525            Some('?') if self.peek(1) == Some('>') => {}
526            Some(c) if is_space(c) => {}
527            Some(_) => return Err(Why::InvalidCharacter),
528        }
529        self.spaces();
530        self.open = Some(Open::Instruction(target.clone(), false));
531        self.pending.push_back(Event::new(EventKind::ProcessingInstructionTarget, target));
532        Ok(())
533    }
534
535    /// Text up to `terminator`, or, when the segment ends first, what it holds short of a possible
536    /// start of the terminator.
537    fn open_text(&mut self, terminator: &str) -> (String, bool) {
538        match self.find(self.at, terminator) {
539            Some(end) => {
540                let text: String = self.buf[self.at..end].iter().collect();
541                self.at = end + terminator.chars().count();
542                (text, true)
543            }
544            None => {
545                let rest = &self.buf[self.at..];
546                let keep = (1..terminator.chars().count()).rev().find(|&n| n <= rest.len() && terminator.chars().take(n).eq(rest[rest.len() - n..].iter().copied())).unwrap_or(0);
547                let end = self.buf.len() - keep;
548                let text: String = self.buf[self.at..end].iter().collect();
549                self.at = end;
550                (text, false)
551            }
552        }
553    }
554
555    fn continue_open(&mut self, open: Open) -> Scan<()> {
556        let (terminator, kind) = match &open {
557            Open::Comment => ("-->", EventKind::Comment),
558            Open::Instruction(..) => ("?>", EventKind::ProcessingInstructionData),
559            Open::Cdata => ("]]>", EventKind::ContentCharacters),
560        };
561        let (text, complete) = self.open_text(terminator);
562        if open == Open::Comment && text.contains("--") {
563            return Err(Why::BadComment);
564        }
565        if !complete && text.is_empty() {
566            return Err(MORE);
567        }
568        if let Open::Instruction(target, true) = &open {
569            self.pending.push_back(Event::new(EventKind::ProcessingInstructionTarget, target.clone()));
570        }
571        if !text.is_empty() || kind == EventKind::Comment || kind == EventKind::ProcessingInstructionData {
572            let mut event = Event::new(kind, text);
573            if kind == EventKind::ContentCharacters {
574                event.information = if complete { 1 } else { 2 };
575            }
576            self.pending.push_back(event);
577        }
578        if complete {
579            self.open = None;
580            if open == Open::Cdata {
581                self.pending.push_back(Event::new(EventKind::EndOfCdataSection, ""));
582            }
583        } else if let Open::Instruction(target, _) = open {
584            self.open = Some(Open::Instruction(target, true));
585        }
586        Ok(())
587    }
588
589    /// A run of character content, its references resolved, up to markup or the segment's end.
590    fn content(&mut self) -> Scan<()> {
591        let mut run = String::new();
592        while let Some(c) = self.peek(0) {
593            match c {
594                '<' => break,
595                '&' => {
596                    let mark = self.at;
597                    match self.reference() {
598                        Ok(Ok(ch)) if (self.representable)(ch) || ch.is_ascii() => run.push(ch),
599                        Ok(Ok(ch)) => {
600                            if !run.is_empty() {
601                                self.pending.push_back(Event::characters(EventKind::ContentCharacters, std::mem::take(&mut run), true));
602                            }
603                            self.pending.push_back(Event::new(EventKind::ContentNationalCharacter, ch.to_string()));
604                        }
605                        Ok(Err(name)) if self.doctype && self.standalone_no => {
606                            if !run.is_empty() {
607                                self.pending.push_back(Event::characters(EventKind::ContentCharacters, std::mem::take(&mut run), true));
608                            }
609                            self.pending.push_back(Event::new(EventKind::UnresolvedReference, name));
610                        }
611                        Ok(Err(_)) => {
612                            self.at = mark;
613                            return Err(Why::UndeclaredEntity);
614                        }
615                        Err(MORE) => {
616                            self.at = mark;
617                            break;
618                        }
619                        Err(why) => {
620                            self.at = mark;
621                            return Err(why);
622                        }
623                    }
624                }
625                '\r' => {
626                    run.push('\n');
627                    self.at += 1;
628                    if self.peek(0) == Some('\n') {
629                        self.at += 1;
630                    }
631                }
632                c if !xml_char(c as u32) => return Err(Why::InvalidCharacter),
633                c => {
634                    run.push(c);
635                    self.at += 1;
636                }
637            }
638        }
639        let complete = self.peek(0) == Some('<');
640        if run.is_empty() && self.pending.is_empty() {
641            return Err(MORE);
642        }
643        if !run.is_empty() {
644            self.pending.push_back(Event::characters(EventKind::ContentCharacters, run, complete));
645        }
646        Ok(())
647    }
648
649    fn bound(&self, prefix: &str) -> Option<&str> {
650        self.bindings.iter().rev().find(|(p, _)| p == prefix).map(|(_, uri)| uri.as_str())
651    }
652
653    fn start_tag(&mut self) -> Scan<()> {
654        self.at += 1;
655        let qualified = self.name()?;
656        let mut attributes: Vec<(String, usize, String, Vec<Event>)> = Vec::new();
657        let empty = loop {
658            let spaced = self.spaces();
659            match self.peek(0) {
660                None => return Err(MORE),
661                Some('>') => {
662                    self.at += 1;
663                    break false;
664                }
665                Some('/') => {
666                    self.at += 1;
667                    self.expect('>')?;
668                    break true;
669                }
670                Some(_) if !spaced => return Err(Why::InvalidCharacter),
671                Some(_) => {}
672            }
673            let name_at = self.at;
674            let name = self.name()?;
675            self.spaces();
676            self.expect('=')?;
677            self.spaces();
678            let quote = match self.peek(0) {
679                None => return Err(MORE),
680                Some(q @ ('"' | '\'')) => q,
681                Some(_) => return Err(Why::InvalidCharacter),
682            };
683            self.at += 1;
684            let (value, parts) = self.attribute_value(quote)?;
685            if attributes.iter().any(|(n, ..)| *n == name) {
686                self.at = name_at;
687                return Err(Why::DuplicateAttribute);
688            }
689            attributes.push((name, name_at, value, parts));
690        };
691        let scope = self.bindings.len();
692        let mut declarations = Vec::new();
693        for (name, _, value, _) in &attributes {
694            let prefix = match name.as_str() {
695                "xmlns" => Some(String::new()),
696                n => n.strip_prefix("xmlns:").map(str::to_owned),
697            };
698            if let Some(prefix) = prefix {
699                self.bindings.push((prefix.clone(), value.clone()));
700                declarations.push(Event { namespace: value.clone(), prefix, ..Event::new(EventKind::NamespaceDeclaration, "") });
701            }
702        }
703        let (prefix, local) = split(&qualified);
704        let mut events = Vec::new();
705        let namespace = match self.bound(&prefix) {
706            Some(uri) => uri.to_owned(),
707            None => {
708                if !prefix.is_empty() {
709                    events.push(Event::undeclared(Why::UndeclaredElementPrefix, &qualified));
710                }
711                String::new()
712            }
713        };
714        events.push(Event { namespace: namespace.clone(), prefix: prefix.clone(), ..Event::new(EventKind::StartOfElement, local.clone()) });
715        events.extend(declarations);
716        for (name, _, _, parts) in attributes {
717            if name == "xmlns" || name.starts_with("xmlns:") {
718                continue;
719            }
720            let (attribute_prefix, attribute_local) = split(&name);
721            let attribute_namespace = if attribute_prefix.is_empty() {
722                String::new()
723            } else {
724                match self.bound(&attribute_prefix) {
725                    Some(uri) => uri.to_owned(),
726                    None => {
727                        events.push(Event::undeclared(Why::UndeclaredAttributePrefix, &name));
728                        String::new()
729                    }
730                }
731            };
732            events.push(Event { namespace: attribute_namespace, prefix: attribute_prefix, ..Event::new(EventKind::AttributeName, attribute_local) });
733            events.extend(parts);
734        }
735        if self.place == Place::Epilogue {
736            return Err(Why::ContentAfterRoot);
737        }
738        self.place = Place::Content;
739        if empty {
740            events.push(Event { namespace, prefix, ..Event::new(EventKind::EndOfElement, local) });
741            self.bindings.truncate(scope);
742            if self.elements.is_empty() {
743                self.place = Place::Epilogue;
744            }
745        } else {
746            self.elements.push(Element { qualified, local, prefix, namespace, bindings: scope });
747        }
748        self.pending.extend(events);
749        Ok(())
750    }
751
752    /// An attribute value after its opening quote: the value, and its ATTRIBUTE-CHARACTERS and
753    /// ATTRIBUTE-NATIONAL-CHARACTER events.
754    fn attribute_value(&mut self, quote: char) -> Scan<(String, Vec<Event>)> {
755        let (mut value, mut run, mut parts) = (String::new(), String::new(), Vec::new());
756        loop {
757            let Some(c) = self.peek(0) else { return Err(MORE) };
758            match c {
759                c if c == quote => {
760                    self.at += 1;
761                    break;
762                }
763                '<' => return Err(Why::LessThanInAttribute),
764                '&' => match self.reference()? {
765                    Ok(ch) if (self.representable)(ch) || ch.is_ascii() => {
766                        run.push(ch);
767                        value.push(ch);
768                    }
769                    Ok(ch) => {
770                        if !run.is_empty() {
771                            parts.push(Event::characters(EventKind::AttributeCharacters, std::mem::take(&mut run), true));
772                        }
773                        parts.push(Event::new(EventKind::AttributeNationalCharacter, ch.to_string()));
774                        value.push(ch);
775                    }
776                    Err(_) => return Err(Why::UndeclaredEntity),
777                },
778                '\t' | '\n' | '\r' => {
779                    run.push(' ');
780                    value.push(' ');
781                    self.at += 1;
782                }
783                c if !xml_char(c as u32) => return Err(Why::InvalidCharacter),
784                c => {
785                    run.push(c);
786                    value.push(c);
787                    self.at += 1;
788                }
789            }
790        }
791        if !run.is_empty() || parts.is_empty() {
792            parts.push(Event::characters(EventKind::AttributeCharacters, run, true));
793        }
794        Ok((value, parts))
795    }
796
797    fn end_tag(&mut self) -> Scan<()> {
798        let start = self.at;
799        self.at += 2;
800        let qualified = self.name()?;
801        self.spaces();
802        self.expect('>')?;
803        match self.elements.last() {
804            Some(e) if e.qualified == qualified => {}
805            _ => {
806                self.at = start;
807                return Err(Why::MismatchedEndTag);
808            }
809        }
810        let e = self.elements.pop().expect("checked above");
811        self.bindings.truncate(e.bindings);
812        self.pending.push_back(Event { namespace: e.namespace, prefix: e.prefix, ..Event::new(EventKind::EndOfElement, e.local) });
813        if self.elements.is_empty() {
814            self.place = Place::Epilogue;
815        }
816        Ok(())
817    }
818}
819
820#[cfg(test)]
821mod tests;