1use std::{borrow::Cow, fmt};
4
5use crate::error::TokenError;
6
7mod comment;
8mod parse;
9mod string;
10pub use self::{
11 parse::{parse_token, ParseRes},
12 string::StrLit,
13};
14
15struct ByteSet(&'static str);
16
17#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
18pub enum StrTag {
19 Raw,
21 Esc,
23 Txt,
29}
30
31impl StrTag {
32 const VALID: ByteSet = ByteSet("abcdefghijklmnopqrstuvwxyz0123456789-_");
33
34 fn from_prefix(buf: &[u8]) -> Option<(usize, Self)> {
35 if buf.starts_with(b"raw\"") {
36 Some((4, Self::Raw))
37 } else if buf.starts_with(b"esc\"") {
38 Some((4, Self::Esc))
39 } else if buf.starts_with(b"txt\"") {
40 Some((4, Self::Txt))
41 } else {
42 None
43 }
44 }
45}
46
47impl fmt::Display for StrTag {
48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49 match self {
50 Self::Raw => write!(f, "`#raw`"),
51 Self::Esc => write!(f, "`#esc`"),
52 Self::Txt => write!(f, "`#txt`"),
53 }
54 }
55}
56
57#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
58pub struct StrTagSuffix(Cow<'static, str>);
59
60impl StrTagSuffix {
61 pub fn len(&self) -> usize {
62 self.0.len()
63 }
64}
65
66impl StrTagSuffix {
67 pub fn new(num_hashes: usize) -> Self {
68 static HASHES: &str = "\"################################";
69 Self(if num_hashes < HASHES.len() {
70 Cow::Borrowed(&HASHES[..num_hashes + 1])
71 } else {
72 let mut s = String::with_capacity(num_hashes + 1);
73 s.push('"');
74 s.extend(std::iter::repeat('#').take(num_hashes));
75 Cow::Owned(s)
76 })
77 }
78}
79
80impl AsRef<[u8]> for StrTagSuffix {
81 fn as_ref(&self) -> &[u8] {
82 self.0.as_ref().as_ref()
83 }
84}
85
86impl fmt::Display for StrTagSuffix {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 write!(f, "`{}`", self.0)
89 }
90}
91
92#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
93pub enum Brace {
94 Curly,
96 Paren,
98 Square,
100 Angle,
102}
103
104#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
105pub enum Delim {
106 Colon,
107 Comma,
108}
109
110#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
111pub enum Literal<'a> {
112 Str(StrLit<'a>),
113 Num(&'a str),
114}
115
116#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
117pub enum Action {
118 Include,
119}
120
121#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
122pub enum Token<'a> {
123 Ident(&'a str),
124 Action(Action),
125 Literal(Literal<'a>),
126 Brace {
127 start: bool,
128 brace: Brace,
129 },
130 Delim(Delim),
131 Comment(&'a str),
132 Newline(bool),
134}
135
136impl<'a> Token<'a> {
137 pub fn token_type_name(&self) -> &'static str {
138 match self {
139 Self::Ident(_) => "identifier",
140 Self::Action(_) => "action",
141 Self::Literal(Literal::Str(..)) => "string",
142 Self::Literal(Literal::Num(..)) => "number",
143 Self::Brace {
144 start: true,
145 brace: Brace::Paren,
146 } => "`(`",
147 Self::Brace {
148 start: false,
149 brace: Brace::Paren,
150 } => "`)`",
151 Self::Brace {
152 start: true,
153 brace: Brace::Curly,
154 } => "`{`",
155 Self::Brace {
156 start: false,
157 brace: Brace::Curly,
158 } => "`}`",
159 Self::Brace {
160 start: true,
161 brace: Brace::Square,
162 } => "`[`",
163 Self::Brace {
164 start: false,
165 brace: Brace::Square,
166 } => "`]`",
167 Self::Brace {
168 start: true,
169 brace: Brace::Angle,
170 } => "`<`",
171 Self::Brace {
172 start: false,
173 brace: Brace::Angle,
174 } => "`>`",
175 Self::Delim(Delim::Comma) => "`,`",
176 Self::Delim(Delim::Colon) => "`:`",
177 Self::Newline(false) => "newline",
178 Self::Newline(true) => "end of file",
179 Self::Comment(..) => "comment",
180 }
181 }
182
183 pub fn write_token_display_name(&self, f: &mut impl fmt::Write) -> fmt::Result {
184 match self {
185 Self::Ident(value) => write!(f, "`{value}`"),
186 Self::Action(Action::Include) => write!(f, "`@include`"),
187 Self::Literal(Literal::Num(num)) => write!(f, "`{num}`"),
188 Self::Brace { .. }
189 | Self::Delim(..)
190 | Self::Newline(..)
191 | Self::Literal(Literal::Str(..))
192 | Self::Comment(..) => {
193 write!(f, "{}", self.token_type_name())
194 }
195 }
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn print_comment_token_type() {
205 let token = Token::Comment("hello");
207 assert_eq!(token.token_type_name().to_string(), "comment");
208 {
209 let mut s = String::new();
210 token.write_token_display_name(&mut s).unwrap();
211 assert_eq!(s, "comment");
212 }
213 }
214
215 #[test]
216 fn print_number_token_type() {
217 let token = Token::Literal(Literal::Num("10"));
219 assert_eq!(token.token_type_name().to_string(), "number");
220 {
221 let mut s = String::new();
222 token.write_token_display_name(&mut s).unwrap();
223 assert_eq!(s, "`10`");
224 }
225 }
226
227 #[test]
228 fn print_str_token_type() {
229 let token = Token::Literal(Literal::Str(StrLit::new(StrTag::Raw, "aa")));
231 assert_eq!(token.token_type_name().to_string(), "string");
232 {
233 let mut s = String::new();
234 token.write_token_display_name(&mut s).unwrap();
235 assert_eq!(s, "string");
236 }
237 }
238
239 #[test]
240 fn print_brace_token_type() {
241 let token = Token::Brace {
243 start: true,
244 brace: Brace::Curly,
245 };
246 assert_eq!(token.token_type_name().to_string(), "`{`");
247 {
248 let mut s = String::new();
249 token.write_token_display_name(&mut s).unwrap();
250 assert_eq!(s, "`{`");
251 }
252 }
253}