1pub(crate) mod lexical;
2mod number;
3mod stream;
4mod string;
5
6use core::{fmt::Display, iter::Peekable, range::Range};
7
8pub use lexical::{JsonByte, JsonChar};
9pub use stream::TokenStream;
10
11#[repr(u8)]
12#[derive(Debug, PartialEq, Eq, Clone, Copy)]
13pub enum Token {
14 OpenCurlyBrace,
15 ClosedCurlyBrace,
16 Colon,
17 Comma,
18 OpenSquareBracket,
19 ClosedSquareBracket,
20 String,
21 Mantissa,
22 Exponent,
23 Null,
24 True,
25 False,
26}
27
28impl Token {
29 pub(crate) fn is_start_of_value(self) -> bool {
30 matches!(
31 self,
32 Token::OpenCurlyBrace
33 | Token::OpenSquareBracket
34 | Token::String
35 | Token::Null
36 | Token::True
37 | Token::False
38 | Token::Mantissa
39 )
40 }
41
42 pub(crate) fn is_scalar(self) -> bool {
43 matches!(
44 self,
45 Token::String | Token::Null | Token::True | Token::False | Token::Mantissa
46 )
47 }
48}
49
50impl Display for Token {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 let val = match self {
53 Token::OpenCurlyBrace => "{",
54 Token::ClosedCurlyBrace => "}",
55 Token::Colon => ":",
56 Token::Comma => ",",
57 Token::OpenSquareBracket => "[",
58 Token::ClosedSquareBracket => "]",
59 Token::String => "string",
60 Token::Mantissa => "mantissa",
61 Token::Exponent => "exponent",
62 Token::Null => NULL,
63 Token::True => TRUE,
64 Token::False => FALSE,
65 };
66 write!(f, "`{val}`")
67 }
68}
69
70const NO_SIGNIFICANT_CHARACTERS: &str = "no significant characters";
71
72#[derive(Debug, PartialEq, Eq, Clone)]
73pub struct ErrorToken {
74 pub(crate) tag: Token,
75 content: Box<str>,
76}
77
78impl ErrorToken {
79 pub(crate) fn new(tag: Token, range: Range<usize>, source: &str) -> Self {
80 Self {
81 tag,
82 content: source[range].into(),
83 }
84 }
85
86 pub fn content(&self) -> &str {
87 &self.content
88 }
89}
90
91impl Display for ErrorToken {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 match self.tag {
94 Token::String | Token::Mantissa => write!(f, "`{}`", self.content),
95 Token::Exponent => write!(f, "`e{}`", self.content),
96 t => write!(f, "{t}"),
97 }
98 }
99}
100
101#[derive(Debug, PartialEq, Eq, Clone)]
102pub struct TokenOption(pub(crate) Option<ErrorToken>);
103
104impl Display for TokenOption {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 match &self.0 {
107 Some(t) => write!(f, "{t}"),
108 None => write!(f, "{NO_SIGNIFICANT_CHARACTERS}"),
109 }
110 }
111}
112
113impl From<Option<Token>> for TokenOption {
114 fn from(value: Option<Token>) -> Self {
115 Self(value.map(|t| ErrorToken {
116 tag: t,
117 content: "".into(),
118 }))
119 }
120}
121
122#[derive(Debug, PartialEq, Eq, Clone, Copy)]
123pub struct JsonCharOption(pub Option<JsonChar>);
124
125impl Display for JsonCharOption {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 let val = match &self.0 {
128 Some(x) => format!("`{x}`"),
129 None => NO_SIGNIFICANT_CHARACTERS.to_owned(),
130 };
131 write!(f, "{val}")
132 }
133}
134
135impl From<Option<JsonChar>> for JsonCharOption {
136 fn from(value: Option<JsonChar>) -> Self {
137 Self(value)
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub struct TokenWithContext {
143 pub token: Token,
144 pub range: Range<usize>,
145}
146
147impl TokenWithContext {
148 pub(crate) fn new(token: Token, range: Range<usize>) -> Self {
149 Self { token, range }
150 }
151
152 pub(crate) fn content_range(&self) -> Range<usize> {
153 match self.token {
154 Token::String => self.range.start + 1..self.range.end - 1,
155 _ => self.range,
156 }
157 }
158}
159
160pub(crate) const NULL: &str = "null";
161pub(crate) const FALSE: &str = "false";
162pub(crate) const TRUE: &str = "true";
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub struct CharWithContext(pub Range<usize>, pub JsonChar);
166
167impl From<(usize, char)> for CharWithContext {
168 fn from((i, c): (usize, char)) -> Self {
169 Self(i..i + c.len_utf8(), c.into())
170 }
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174pub struct ByteWithContext(pub usize, pub JsonByte);
175
176impl From<(usize, u8)> for ByteWithContext {
177 fn from((i, b): (usize, u8)) -> Self {
178 Self(i, b.into())
179 }
180}
181
182impl ByteWithContext {
183 pub(crate) fn range(&self) -> Range<usize> {
184 self.0..self.0 + 1
185 }
186
187 pub(crate) fn as_token_with_context(&self) -> Option<TokenWithContext> {
188 Some(TokenWithContext::new(self.1.as_token()?, self.range()))
189 }
190
191 pub(crate) fn as_byte(&self) -> u8 {
192 self.1.0
193 }
194}
195
196#[derive(Debug, Clone)]
197pub(crate) struct BytesWithContext<'a> {
198 input: &'a str,
199 pos: usize,
200}
201
202impl<'a> BytesWithContext<'a> {
203 pub(crate) fn new(input: &'a str, pos: usize) -> Self {
204 Self { input, pos }
205 }
206}
207
208impl Iterator for BytesWithContext<'_> {
209 type Item = ByteWithContext;
210
211 fn next(&mut self) -> Option<Self::Item> {
212 self.input.as_bytes().get(self.pos).copied().map(|byte| {
213 let start = self.pos;
214 self.pos += 1;
215 (start, byte).into()
216 })
217 }
218}
219
220pub(crate) fn current_byte_pos(
221 bytes: &mut Peekable<impl Iterator<Item = ByteWithContext>>,
222 input: &str,
223) -> usize {
224 bytes
225 .peek()
226 .map_or(input.len(), |ByteWithContext(start, _)| *start)
227}
228
229impl From<bool> for Token {
230 fn from(value: bool) -> Self {
231 if value { Token::True } else { Token::False }
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use crate::{Error, ErrorKind, Result};
239
240 fn str_to_tokens(s: &str) -> Result<Vec<TokenWithContext>> {
241 stream::TokenStream::new(s).collect()
242 }
243
244 fn ctx(token: Token, range: Range<usize>) -> TokenWithContext {
245 TokenWithContext::new(token, range)
246 }
247
248 #[test]
249 fn should_parse_single_key_object() {
250 assert_eq!(
251 str_to_tokens(r#"{"rust": "is a must"}"#).unwrap(),
252 [
253 ctx(Token::OpenCurlyBrace, 0..1),
254 ctx(Token::String, 1..7),
255 ctx(Token::Colon, 7..8),
256 ctx(Token::String, 9..20),
257 ctx(Token::ClosedCurlyBrace, 20..21),
258 ]
259 );
260 }
261
262 #[rstest_reuse::template]
263 #[rstest::rstest]
264 #[case("null", Token::Null)]
265 #[case("true", Token::True)]
266 #[case("false", Token::False)]
267 #[case("\"burger\"", Token::String)]
268 #[case(r#""\"burger\"""#, Token::String)]
269 fn primitive_template(#[case] json: &str, #[case] expected: Token) {}
270
271 #[rstest_reuse::apply(primitive_template)]
272 fn primitives(#[case] json: &str, #[case] expected: Token) {
273 assert_eq!(str_to_tokens(json), Ok(vec![ctx(expected, 0..json.len())]));
274 }
275
276 #[rstest_reuse::apply(primitive_template)]
277 fn primitive_object_value(#[case] primitive: &str, #[case] expected: Token) {
278 let json = format!(
279 r#"{{
280 "rust": {primitive}
281 }}"#
282 );
283 assert_eq!(
284 str_to_tokens(&json).unwrap(),
285 [
286 ctx(Token::OpenCurlyBrace, 0..1),
287 ctx(Token::String, 18..24),
288 ctx(Token::Colon, 24..25),
289 ctx(expected, 26..(26 + primitive.len())),
290 ctx(Token::ClosedCurlyBrace, (json.len() - 1)..json.len()),
291 ]
292 );
293 }
294
295 fn json_to_json_and_error(
296 json: &'static str,
297 kind: ErrorKind,
298 range: Option<Range<usize>>,
299 ) -> (&'static str, Error) {
300 let error = match range {
301 Some(range) => Error::new(kind, range, json),
302 None => Error::from_unterminated(kind, json),
303 };
304 (json, error)
305 }
306
307 #[rstest::rstest]
308 #[case(json_to_json_and_error(
309 "a",
310 ErrorKind::UnexpectedCharacter('a'.into()),
311 Some(0..1)
312 ))]
313 #[case(json_to_json_and_error(
314 "n",
315 ErrorKind::UnexpectedCharacter('n'.into()),
316 Some(0..1)
317 ))]
318 #[case(json_to_json_and_error(
319 r#""
320
321 ""#,
322 ErrorKind::UnexpectedControlCharacterInString('\n'.into()),
323 Some(1..2)
324 ))]
325 fn should_not_parse_invalid_syntax(#[case] (json, error): (&str, Error)) {
326 assert_eq!(str_to_tokens(json), Err(error));
327 }
328
329 #[test]
330 fn multiple_keys() {
331 assert_eq!(
332 str_to_tokens(
333 r#"{
334 "rust": "is a must",
335 "name": "ferris"
336 }"#
337 )
338 .unwrap(),
339 [
340 ctx(Token::OpenCurlyBrace, 0..1),
341 ctx(Token::String, 18..24),
342 ctx(Token::Colon, 24..25),
343 ctx(Token::String, 26..37),
344 ctx(Token::Comma, 37..38),
345 ctx(Token::String, 55..61),
346 ctx(Token::Colon, 61..62),
347 ctx(Token::String, 63..71),
348 ctx(Token::ClosedCurlyBrace, 84..85),
349 ]
350 );
351 }
352
353 #[test]
354 fn array_brackets() {
355 assert_eq!(
356 str_to_tokens("[]").unwrap(),
357 [
358 ctx(Token::OpenSquareBracket, 0..1),
359 ctx(Token::ClosedSquareBracket, 1..2),
360 ]
361 );
362 }
363
364 #[test]
365 fn token_with_context_size() {
366 assert_eq!(core::mem::size_of::<Token>(), 1);
367 assert_eq!(core::mem::size_of::<TokenWithContext>(), 24);
368 }
369}