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 #[must_use]
106 pub fn as_str(self) -> &'static str {
107 match self {
108 Self::Version10 => "1.0",
109 Self::Version11 => "1.1",
110 }
111 }
112}
113
114impl fmt::Display for XmlVersion {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 self.as_str().fmt(f)
117 }
118}
119
120impl fmt::Debug for XmlVersion {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 fmt::Display::fmt(self, f)
123 }
124}
125
126#[must_use]
131#[inline]
132pub const fn is_whitespace_char(c: char) -> bool {
133 matches!(c, '\x20' | '\x0a' | '\x09' | '\x0d')
134}
135
136pub(crate) fn is_pubid_char(c: char) -> bool {
138 matches!(c, '\x20' | '\x0D' | '\x0A' | 'a'..='z' | 'A'..='Z' | '0'..='9' |
139 '-' | '\'' | '(' | ')' | '+' | ',' | '.' | '/' | ':' | '=' | '?' | ';' |
140 '!' | '*' | '#' | '@' | '$' | '_' | '%')
141}
142
143pub fn is_whitespace_str(s: &str) -> bool {
147 s.chars().all(is_whitespace_char)
148}
149
150#[must_use]
152pub const fn is_xml10_char(c: char) -> bool {
153 matches!(c, '\u{09}' | '\u{0A}' | '\u{0D}' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..)
154}
155
156#[must_use]
158pub const fn is_xml11_char(c: char) -> bool {
159 matches!(c, '\u{01}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..)
160}
161
162#[must_use]
164pub const fn is_xml11_char_not_restricted(c: char) -> bool {
165 is_xml11_char(c) &&
166 !matches!(c, '\u{01}'..='\u{08}' | '\u{0B}'..='\u{0C}' | '\u{0E}'..='\u{1F}' | '\u{7F}'..='\u{84}' | '\u{86}'..='\u{9F}')
167}
168
169#[must_use]
174pub const fn is_name_start_char(c: char) -> bool {
175 matches!(c,
176 ':' | 'A'..='Z' | '_' | 'a'..='z' |
177 '\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' |
178 '\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' |
179 '\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' |
180 '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' |
181 '\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' |
182 '\u{10000}'..='\u{EFFFF}'
183 )
184}
185
186#[must_use]
191pub const fn is_name_char(c: char) -> bool {
192 if is_name_start_char(c) {
193 return true;
194 }
195 matches!(c,
196 '-' | '.' | '0'..='9' | '\u{B7}' |
197 '\u{300}'..='\u{36F}' | '\u{203F}'..='\u{2040}'
198 )
199}