1use std::fmt;
4
5#[derive(Copy, Clone, PartialEq, Eq)]
7pub struct TextPosition {
8 #[doc(hidden)]
9 pub row: u64,
10
11 #[doc(hidden)]
12 pub column: u64,
13}
14
15impl TextPosition {
16 #[inline]
18 #[must_use]
19 pub const fn new() -> Self {
20 Self { row: 0, column: 0 }
21 }
22
23 #[inline]
25 pub fn advance(&mut self, count: u8) {
26 self.column += u64::from(count);
27 }
28
29 #[doc(hidden)]
30 #[deprecated]
31 pub fn advance_to_tab(&mut self, width: u8) {
32 let width = u64::from(width);
33 self.column += width - self.column % width;
34 }
35
36 #[inline]
38 pub fn new_line(&mut self) {
39 self.column = 0;
40 self.row += 1;
41 }
42
43 #[must_use]
45 pub fn row(&self) -> u64 {
46 self.row
47 }
48
49 #[must_use]
51 pub fn column(&self) -> u64 {
52 self.column
53 }
54}
55
56impl fmt::Debug for TextPosition {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 fmt::Display::fmt(self, f)
59 }
60}
61
62impl fmt::Display for TextPosition {
63 #[inline]
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "{}:{}", self.row + 1, self.column + 1)
66 }
67}
68
69pub trait Position {
73 fn position(&self) -> TextPosition;
75}
76
77impl Position for TextPosition {
78 #[inline]
79 fn position(&self) -> TextPosition {
80 *self
81 }
82}
83
84#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
86pub enum XmlVersion {
87 Version10,
92
93 Version11,
95}
96
97impl XmlVersion {
98 pub fn as_str(self) -> &'static str {
106 match self {
107 Self::Version10 => "1.0",
108 Self::Version11 => "1.1",
109 }
110 }
111}
112
113impl fmt::Display for XmlVersion {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 self.as_str().fmt(f)
116 }
117}
118
119impl fmt::Debug for XmlVersion {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 fmt::Display::fmt(self, f)
122 }
123}
124
125#[must_use]
130#[inline]
131pub const fn is_whitespace_char(c: char) -> bool {
132 matches!(c, '\x20' | '\x0a' | '\x09' | '\x0d')
133}
134
135pub (crate) fn is_pubid_char(c: char) -> bool {
137 matches!(c, '\x20' | '\x0D' | '\x0A' | 'a'..='z' | 'A'..='Z' | '0'..='9' |
138 '-' | '\'' | '(' | ')' | '+' | ',' | '.' | '/' | ':' | '=' | '?' | ';' |
139 '!' | '*' | '#' | '@' | '$' | '_' | '%')
140}
141
142pub fn is_whitespace_str(s: &str) -> bool {
146 s.chars().all(is_whitespace_char)
147}
148
149#[must_use]
151pub const fn is_xml10_char(c: char) -> bool {
152 matches!(c, '\u{09}' | '\u{0A}' | '\u{0D}' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..)
153}
154
155#[must_use]
157pub const fn is_xml11_char(c: char) -> bool {
158 matches!(c, '\u{01}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..)
159}
160
161#[must_use]
163pub const fn is_xml11_char_not_restricted(c: char) -> bool {
164 is_xml11_char(c) &&
165 !matches!(c, '\u{01}'..='\u{08}' | '\u{0B}'..='\u{0C}' | '\u{0E}'..='\u{1F}' | '\u{7F}'..='\u{84}' | '\u{86}'..='\u{9F}')
166}
167
168#[must_use]
173pub const fn is_name_start_char(c: char) -> bool {
174 matches!(c,
175 ':' | 'A'..='Z' | '_' | 'a'..='z' |
176 '\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' |
177 '\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' |
178 '\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' |
179 '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' |
180 '\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' |
181 '\u{10000}'..='\u{EFFFF}'
182 )
183}
184
185#[must_use]
190pub const fn is_name_char(c: char) -> bool {
191 if is_name_start_char(c) {
192 return true;
193 }
194 matches!(c,
195 '-' | '.' | '0'..='9' | '\u{B7}' |
196 '\u{300}'..='\u{36F}' | '\u{203F}'..='\u{2040}'
197 )
198}