xml_no_std/reader/parser/
inside_declaration.rs

1extern crate alloc;
2
3use alloc::string::ToString;
4
5use crate::common::{is_whitespace_char, XmlVersion};
6use crate::reader::error::SyntaxError;
7use crate::reader::events::XmlEvent;
8use crate::reader::lexer::Token;
9use crate::util::Encoding;
10
11use super::{
12    DeclarationSubstate, Encountered, PullParser, QualifiedNameTarget, Result, State,
13    DEFAULT_VERSION,
14};
15
16impl PullParser {
17    #[inline(never)]
18    fn emit_start_document(&mut self) -> Option<Result> {
19        debug_assert!(self.encountered == Encountered::None);
20        self.encountered = Encountered::Declaration;
21
22        let version = self.data.version;
23        let encoding = self.data.take_encoding();
24        let standalone = self.data.standalone;
25
26        if let Some(new_encoding) = encoding.as_deref() {
27            let new_encoding = match new_encoding.parse() {
28                Ok(e) => e,
29                Err(_) if self.config.ignore_invalid_encoding_declarations => Encoding::Latin1,
30                Err(_) => return Some(self.error(SyntaxError::UnsupportedEncoding(new_encoding.into()))),
31            };
32            let current_encoding = self.lexer.encoding();
33            if current_encoding != new_encoding {
34                let set = match (current_encoding, new_encoding) {
35                    (Encoding::Unknown | Encoding::Default, new) if new != Encoding::Utf16 => new,
36                    (Encoding::Utf16Be | Encoding::Utf16Le, Encoding::Utf16) => current_encoding,
37                    _ if self.config.ignore_invalid_encoding_declarations => current_encoding,
38                    _ => return Some(self.error(SyntaxError::ConflictingEncoding(new_encoding, current_encoding))),
39                };
40                self.lexer.set_encoding(set);
41            }
42        }
43
44        let current_encoding = self.lexer.encoding();
45        self.into_state_emit(State::OutsideTag, Ok(XmlEvent::StartDocument {
46            version: version.unwrap_or(DEFAULT_VERSION),
47            encoding: encoding.unwrap_or_else(move || current_encoding.to_string()),
48            standalone
49        }))
50    }
51
52    // TODO: remove redundancy via macros or extra methods
53    pub fn inside_declaration(&mut self, t: Token, s: DeclarationSubstate) -> Option<Result> {
54
55        match s {
56            DeclarationSubstate::BeforeVersion => match t {
57                Token::Character('v') => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::InsideVersion)),
58                Token::Character(c) if is_whitespace_char(c) => None,  // continue
59                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
60            },
61
62            DeclarationSubstate::InsideVersion => self.read_qualified_name(t, QualifiedNameTarget::AttributeNameTarget, |this, token, name| {
63                match &*name.local_name {
64                    "ersion" if name.namespace.is_none() =>
65                        this.into_state_continue(State::InsideDeclaration(
66                            if token == Token::EqualsSign {
67                                DeclarationSubstate::InsideVersionValue
68                            } else {
69                                DeclarationSubstate::AfterVersion
70                            }
71                        )),
72                    _ => Some(this.error(SyntaxError::UnexpectedNameInsideXml(name.to_string().into()))),
73                }
74            }),
75
76            DeclarationSubstate::AfterVersion => match t {
77                Token::EqualsSign => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::InsideVersionValue)),
78                Token::Character(c) if is_whitespace_char(c) => None,
79                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
80            },
81
82            DeclarationSubstate::InsideVersionValue => self.read_attribute_value(t, |this, value| {
83                this.data.version = match &*value {
84                    "1.0" => Some(XmlVersion::Version10),
85                    "1.1" => Some(XmlVersion::Version11),
86                    _     => None
87                };
88                if this.data.version.is_some() {
89                    this.into_state_continue(State::InsideDeclaration(DeclarationSubstate::AfterVersionValue))
90                } else {
91                    Some(this.error(SyntaxError::UnexpectedXmlVersion(value.into())))
92                }
93            }),
94
95            DeclarationSubstate::AfterVersionValue => match t {
96                Token::Character(c) if is_whitespace_char(c) => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::BeforeEncoding)),
97                Token::ProcessingInstructionEnd => self.emit_start_document(),
98                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
99            },
100
101            DeclarationSubstate::BeforeEncoding => match t {
102                Token::Character('e') => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::InsideEncoding)),
103                Token::Character('s') => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::InsideStandaloneDecl)),
104                Token::ProcessingInstructionEnd => self.emit_start_document(),
105                Token::Character(c) if is_whitespace_char(c) => None,  // skip whitespace
106                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
107            },
108
109            DeclarationSubstate::InsideEncoding => self.read_qualified_name(t, QualifiedNameTarget::AttributeNameTarget, |this, token, name| {
110                match &*name.local_name {
111                    "ncoding" if name.namespace.is_none() =>
112                        this.into_state_continue(State::InsideDeclaration(
113                            if token == Token::EqualsSign { DeclarationSubstate::InsideEncodingValue } else { DeclarationSubstate::AfterEncoding }
114                        )),
115                    _ => Some(this.error(SyntaxError::UnexpectedName(name.to_string().into())))
116                }
117            }),
118
119            DeclarationSubstate::AfterEncoding => match t {
120                Token::EqualsSign => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::InsideEncodingValue)),
121                Token::Character(c) if is_whitespace_char(c) => None,
122                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
123            },
124
125            DeclarationSubstate::InsideEncodingValue => self.read_attribute_value(t, |this, value| {
126                this.data.encoding = Some(value);
127                this.into_state_continue(State::InsideDeclaration(DeclarationSubstate::AfterEncodingValue))
128            }),
129
130            DeclarationSubstate::AfterEncodingValue => match t {
131                Token::Character(c) if is_whitespace_char(c) => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::BeforeStandaloneDecl)),
132                Token::ProcessingInstructionEnd => self.emit_start_document(),
133                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
134            },
135
136            DeclarationSubstate::BeforeStandaloneDecl => match t {
137                Token::Character('s') => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::InsideStandaloneDecl)),
138                Token::ProcessingInstructionEnd => self.emit_start_document(),
139                Token::Character(c) if is_whitespace_char(c) => None, // skip whitespace
140                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
141            },
142
143            DeclarationSubstate::InsideStandaloneDecl => self.read_qualified_name(t, QualifiedNameTarget::AttributeNameTarget, |this, token, name| {
144                match &*name.local_name {
145                    "tandalone" if name.namespace.is_none() =>
146                        this.into_state_continue(State::InsideDeclaration(
147                            if token == Token::EqualsSign {
148                                DeclarationSubstate::InsideStandaloneDeclValue
149                            } else {
150                                DeclarationSubstate::AfterStandaloneDecl
151                            }
152                        )),
153                    _ => Some(this.error(SyntaxError::UnexpectedName(name.to_string().into()))),
154                }
155            }),
156
157            DeclarationSubstate::AfterStandaloneDecl => match t {
158                Token::EqualsSign => self.into_state_continue(State::InsideDeclaration(DeclarationSubstate::InsideStandaloneDeclValue)),
159                Token::Character(c) if is_whitespace_char(c) => None,
160                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
161            },
162
163            DeclarationSubstate::InsideStandaloneDeclValue => self.read_attribute_value(t, |this, value| {
164                let standalone = match &*value {
165                    "yes" => Some(true),
166                    "no"  => Some(false),
167                    _     => None
168                };
169                if standalone.is_some() {
170                    this.data.standalone = standalone;
171                    this.into_state_continue(State::InsideDeclaration(DeclarationSubstate::AfterStandaloneDeclValue))
172                } else {
173                    Some(this.error(SyntaxError::InvalidStandaloneDeclaration(value.into())))
174                }
175            }),
176
177            DeclarationSubstate::AfterStandaloneDeclValue => match t {
178                Token::ProcessingInstructionEnd => self.emit_start_document(),
179                Token::Character(c) if is_whitespace_char(c) => None, // skip whitespace
180                _ => Some(self.error(SyntaxError::UnexpectedToken(t))),
181            },
182        }
183    }
184}