1//! Contains common types and functions used throughout the library.
2
3use std::fmt;
4
5/// Represents a position inside some textual document.
6#[derive(Copy, Clone, PartialEq, Eq)]
7pub struct TextPosition {
8 /// Row, counting from 0
9 pub row: u64,
10 /// Column, counting from 0
11 pub column: u64,
12}
13
14impl TextPosition {
15 /// Creates a new position initialized to the beginning of the document
16 #[inline]
17 #[must_use]
18 pub const fn new() -> Self {
19 Self { row: 0, column: 0 }
20 }
21
22 /// Advances the position in a line
23 #[inline]
24 pub fn advance(&mut self, count: u8) {
25 self.column += u64::from(count);
26 }
27
28 /// Advances the position in a line to the next tab position
29 #[inline]
30 pub fn advance_to_tab(&mut self, width: u8) {
31 let width = u64::from(width);
32 self.column += width - self.column % width;
33 }
34
35 /// Advances the position to the beginning of the next line
36 #[inline]
37 pub fn new_line(&mut self) {
38 self.column = 0;
39 self.row += 1;
40 }
41}
42
43impl fmt::Debug for TextPosition {
44 #[cold]
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "{}:{}", self.row + 1, self.column + 1)
47 }
48}
49
50impl fmt::Display for TextPosition {
51 #[inline]
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{}:{}", self.row + 1, self.column + 1)
54 }
55}
56
57/// Get the position in the document corresponding to the object
58///
59/// This trait is implemented by parsers, lexers and errors.
60pub trait Position {
61 /// Returns the current position or a position corresponding to the object.
62 fn position(&self) -> TextPosition;
63}
64
65impl Position for TextPosition {
66 #[inline]
67 fn position(&self) -> TextPosition {
68 *self
69 }
70}
71
72/// XML version enumeration.
73#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
74pub enum XmlVersion {
75 /// XML version 1.0.
76 Version10,
77
78 /// XML version 1.1.
79 Version11,
80}
81
82impl fmt::Display for XmlVersion {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 match *self {
85 Self::Version10 => "1.0",
86 Self::Version11 => "1.1",
87 }.fmt(f)
88 }
89}
90
91impl fmt::Debug for XmlVersion {
92 #[cold]
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 fmt::Display::fmt(self, f)
95 }
96}
97
98/// Checks whether the given character is a white space character (`S`)
99/// as is defined by XML 1.1 specification, [section 2.3][1].
100///
101/// [1]: http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn
102#[must_use]
103#[inline]
104pub const fn is_whitespace_char(c: char) -> bool {
105 matches!(c, '\x20' | '\x0a' | '\x09' | '\x0d')
106}
107
108/// Checks whether the given string is compound only by white space
109/// characters (`S`) using the previous `is_whitespace_char` to check
110/// all characters of this string
111pub fn is_whitespace_str(s: &str) -> bool {
112 s.chars().all(is_whitespace_char)
113}
114
115/// Is it a valid character in XML 1.0
116#[must_use]
117pub const fn is_xml10_char(c: char) -> bool {
118 matches!(c, '\u{09}' | '\u{0A}' | '\u{0D}' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..)
119}
120
121/// Is it a valid character in XML 1.1
122#[must_use]
123pub const fn is_xml11_char(c: char) -> bool {
124 matches!(c, '\u{01}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..)
125}
126
127/// Is it a valid character in XML 1.1 but not part of the restricted character set
128#[must_use]
129pub const fn is_xml11_char_not_restricted(c: char) -> bool {
130 is_xml11_char(c) &&
131 !matches!(c, '\u{01}'..='\u{08}' | '\u{0B}'..='\u{0C}' | '\u{0E}'..='\u{1F}' | '\u{7F}'..='\u{84}' | '\u{86}'..='\u{9F}')
132}
133
134/// Checks whether the given character is a name start character (`NameStartChar`)
135/// as is defined by XML 1.1 specification, [section 2.3][1].
136///
137/// [1]: http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn
138#[must_use]
139pub const fn is_name_start_char(c: char) -> bool {
140 matches!(c,
141 ':' | 'A'..='Z' | '_' | 'a'..='z' |
142 '\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' |
143 '\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' |
144 '\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' |
145 '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' |
146 '\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' |
147 '\u{10000}'..='\u{EFFFF}'
148 )
149}
150
151/// Checks whether the given character is a name character (`NameChar`)
152/// as is defined by XML 1.1 specification, [section 2.3][1].
153///
154/// [1]: http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn
155#[must_use]
156pub const fn is_name_char(c: char) -> bool {
157 if is_name_start_char(c) {
158 return true;
159 }
160 matches!(c,
161 '-' | '.' | '0'..='9' | '\u{B7}' |
162 '\u{300}'..='\u{36F}' | '\u{203F}'..='\u{2040}'
163 )
164}
165