1 //! Contains common types and functions used throughout the library.
2 
3 use std::fmt;
4 
5 /// Represents a position inside some textual document.
6 #[derive(Copy, Clone, PartialEq, Eq)]
7 pub struct TextPosition {
8     /// Row, counting from 0
9     pub row: u64,
10     /// Column, counting from 0
11     pub column: u64,
12 }
13 
14 impl TextPosition {
15     /// Creates a new position initialized to the beginning of the document
16     #[inline]
17     #[must_use]
new() -> TextPosition18     pub fn new() -> TextPosition {
19         TextPosition { row: 0, column: 0 }
20     }
21 
22     /// Advances the position in a line
23     #[inline]
advance(&mut self, count: u8)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]
advance_to_tab(&mut self, width: u8)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]
new_line(&mut self)37     pub fn new_line(&mut self) {
38         self.column = 0;
39         self.row += 1;
40     }
41 }
42 
43 impl fmt::Debug for TextPosition {
44     #[cold]
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result45     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46         write!(f, "{}:{}", self.row + 1, self.column + 1)
47     }
48 }
49 
50 impl fmt::Display for TextPosition {
51     #[inline]
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result52     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.
60 pub trait Position {
61     /// Returns the current position or a position corresponding to the object.
position(&self) -> TextPosition62     fn position(&self) -> TextPosition;
63 }
64 
65 impl Position for TextPosition {
66     #[inline]
position(&self) -> TextPosition67     fn position(&self) -> TextPosition {
68         *self
69     }
70 }
71 
72 /// XML version enumeration.
73 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
74 pub enum XmlVersion {
75     /// XML version 1.0.
76     Version10,
77 
78     /// XML version 1.1.
79     Version11,
80 }
81 
82 impl fmt::Display for XmlVersion {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result83     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84         match *self {
85             XmlVersion::Version10 => "1.0",
86             XmlVersion::Version11 => "1.1",
87         }.fmt(f)
88     }
89 }
90 
91 impl fmt::Debug for XmlVersion {
92     #[cold]
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result93     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]
is_whitespace_char(c: char) -> bool104 pub 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
is_whitespace_str(s: &str) -> bool111 pub fn is_whitespace_str(s: &str) -> bool {
112     s.chars().all(is_whitespace_char)
113 }
114 
is_xml10_char(c: char) -> bool115 #[must_use] pub fn is_xml10_char(c: char) -> bool {
116     matches!(c, '\u{09}' | '\u{0A}' | '\u{0D}' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..)
117 }
118 
is_xml11_char(c: char) -> bool119 #[must_use] pub fn is_xml11_char(c: char) -> bool {
120     matches!(c, '\u{01}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..)
121 }
122 
is_xml11_char_not_restricted(c: char) -> bool123 #[must_use] pub fn is_xml11_char_not_restricted(c: char) -> bool {
124     is_xml11_char(c) && !matches!(c, '\u{01}'..='\u{08}' | '\u{0B}'..='\u{0C}' | '\u{0E}'..='\u{1F}' | '\u{7F}'..='\u{84}' | '\u{86}'..='\u{9F}')
125 }
126 
127 /// Checks whether the given character is a name start character (`NameStartChar`)
128 /// as is defined by XML 1.1 specification, [section 2.3][1].
129 ///
130 /// [1]: http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn
131 #[must_use]
is_name_start_char(c: char) -> bool132 pub fn is_name_start_char(c: char) -> bool {
133     match c {
134         ':' | 'A'..='Z' | '_' | 'a'..='z' |
135         '\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' |
136         '\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' |
137         '\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' |
138         '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' |
139         '\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' |
140         '\u{10000}'..='\u{EFFFF}' => true,
141         _ => false
142     }
143 }
144 
145 /// Checks whether the given character is a name character (`NameChar`)
146 /// as is defined by XML 1.1 specification, [section 2.3][1].
147 ///
148 /// [1]: http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn
149 #[must_use]
is_name_char(c: char) -> bool150 pub fn is_name_char(c: char) -> bool {
151     match c {
152         _ if is_name_start_char(c) => true,
153         '-' | '.' | '0'..='9' | '\u{B7}' |
154         '\u{300}'..='\u{36F}' | '\u{203F}'..='\u{2040}' => true,
155         _ => false
156     }
157 }
158