1use std::collections::BTreeMap;
18use std::fmt::Write as _;
19
20pub struct Object {
25 text: String,
26 first: bool,
27}
28
29impl Object {
30 pub fn new(capacity: usize) -> Object {
32 Object {
33 text: String::with_capacity(capacity),
34 first: true,
35 }
36 }
37
38 pub fn field<F>(&mut self, name: &str, value: F) -> &mut Object
44 where
45 F: FnOnce(&mut String),
46 {
47 if self.first {
48 self.text.push('{');
49 self.first = false;
50 } else {
51 self.text.push(',');
52 }
53 write_name(&mut self.text, name);
54 self.text.push(':');
55 value(&mut self.text);
56 self
57 }
58
59 pub fn text(&mut self, name: &str, value: &str) -> &mut Object {
61 self.field(name, |out| write_text(out, value))
62 }
63
64 pub fn number(&mut self, name: &str, value: i64) -> &mut Object {
66 self.field(name, |out| {
67 let _ = write!(out, "{value}");
68 })
69 }
70
71 pub fn boolean(&mut self, name: &str, value: bool) -> &mut Object {
73 self.field(name, |out| {
74 out.push_str(if value { "true" } else { "false" });
75 })
76 }
77
78 pub fn nothing(&mut self, name: &str) -> &mut Object {
80 self.field(name, |out| out.push_str("null"))
81 }
82
83 pub fn optional_number(&mut self, name: &str, value: Option<i64>) -> &mut Object {
86 match value {
87 Some(value) => self.number(name, value),
88 None => self.nothing(name),
89 }
90 }
91
92 pub fn texts(&mut self, name: &str, values: &[String]) -> &mut Object {
94 self.field(name, |out| {
95 out.push('[');
96 for (index, value) in values.iter().enumerate() {
97 if index > 0 {
98 out.push(',');
99 }
100 write_text(out, value);
101 }
102 out.push(']');
103 })
104 }
105
106 pub fn finish(self) -> String {
108 if self.first {
109 "{}".to_string()
110 } else {
111 self.text + "}"
112 }
113 }
114}
115
116fn write_name(out: &mut String, name: &str) {
118 write_text(out, name);
119}
120
121pub fn write_text(out: &mut String, value: &str) {
129 out.push('"');
130 for letter in value.chars() {
131 match letter {
132 '"' => out.push_str("\\\""),
133 '\\' => out.push_str("\\\\"),
134 '\n' => out.push_str("\\n"),
135 '\r' => out.push_str("\\r"),
136 '\t' => out.push_str("\\t"),
137 '\u{08}' => out.push_str("\\b"),
138 '\u{0C}' => out.push_str("\\f"),
139 other if (other as u32) < 0x20 => {
140 let _ = write!(out, "\\u{:04x}", other as u32);
141 }
142 other => out.push(other),
143 }
144 }
145 out.push('"');
146}
147
148#[cfg(test)]
149mod tests {
150 use super::{Object, write_text};
151
152 #[test]
155 fn a_text_is_quoted_and_escaped() {
156 let mut out = String::new();
157 write_text(&mut out, "a\"b\\c");
158 assert_eq!(out, "\"a\\\"b\\\\c\"");
159 }
160
161 #[test]
163 fn an_object_with_no_field_is_empty() {
164 assert_eq!(Object::new(8).finish(), "{}");
165 }
166
167 #[test]
170 fn the_fields_are_written_in_the_order_they_arrive() {
171 let mut object = Object::new(32);
172 object
173 .boolean("ok", true)
174 .number("line", 3)
175 .nothing("error");
176 assert_eq!(object.finish(), "{\"ok\":true,\"line\":3,\"error\":null}");
177 }
178}
179
180#[derive(Debug, Clone, PartialEq)]
182pub enum Read {
183 Null,
185 Bool(bool),
187 Number(String),
191 Text(String),
193 Array(Vec<Read>),
195 Object(BTreeMap<String, Read>),
197}
198
199impl Read {
200 pub fn get(&self, name: &str) -> Option<&Read> {
202 match self {
203 Read::Object(members) => members.get(name),
204 _ => None,
205 }
206 }
207
208 pub fn at(&self, place: usize) -> Option<&Read> {
210 match self {
211 Read::Array(values) => values.get(place),
212 _ => None,
213 }
214 }
215
216 pub fn array(&self) -> &[Read] {
218 match self {
219 Read::Array(values) => values,
220 _ => &[],
221 }
222 }
223
224 pub fn names(&self) -> Vec<&str> {
226 match self {
227 Read::Object(members) => members.keys().map(String::as_str).collect(),
228 _ => Vec::new(),
229 }
230 }
231
232 pub fn as_str(&self) -> Option<&str> {
234 match self {
235 Read::Text(text) => Some(text),
236 _ => None,
237 }
238 }
239
240 pub fn field_text(&self, name: &str) -> Option<&str> {
242 self.get(name).and_then(Read::as_str)
243 }
244
245 pub fn as_i64(&self) -> Option<i64> {
247 match self {
248 Read::Number(text) => text.parse().ok(),
249 _ => None,
250 }
251 }
252
253 pub fn as_bool(&self) -> Option<bool> {
255 match self {
256 Read::Bool(value) => Some(*value),
257 _ => None,
258 }
259 }
260
261 pub fn is_number(&self) -> bool {
263 matches!(self, Read::Number(_))
264 }
265
266 pub fn is_string(&self) -> bool {
268 matches!(self, Read::Text(_))
269 }
270
271 pub fn is_null(&self) -> bool {
273 matches!(self, Read::Null)
274 }
275}
276
277#[derive(Debug, Clone, PartialEq, Eq)]
279pub struct ReadError {
280 pub reason: String,
282 pub at: usize,
284}
285
286impl std::fmt::Display for ReadError {
287 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 write!(formatter, "{} at byte {}", self.reason, self.at)
289 }
290}
291
292impl std::error::Error for ReadError {}
293
294pub fn read(text: &str) -> Result<Read, ReadError> {
296 let mut parser = Reader {
297 bytes: text.as_bytes(),
298 at: 0,
299 };
300 let value = parser.value()?;
301 parser.space();
302 if parser.at != parser.bytes.len() {
303 return Err(parser.the_error("the document ends before its value does"));
304 }
305 Ok(value)
306}
307
308struct Reader<'a> {
310 bytes: &'a [u8],
311 at: usize,
312}
313
314impl Reader<'_> {
315 fn the_error(&self, reason: &str) -> ReadError {
317 ReadError {
318 reason: reason.to_string(),
319 at: self.at,
320 }
321 }
322
323 fn space(&mut self) {
325 while let Some(byte) = self.bytes.get(self.at) {
326 if matches!(byte, b' ' | b'\t' | b'\n' | b'\r') {
327 self.at += 1;
328 } else {
329 break;
330 }
331 }
332 }
333
334 fn value(&mut self) -> Result<Read, ReadError> {
336 self.space();
337 match self.bytes.get(self.at) {
338 Some(b'{') => self.object(),
339 Some(b'[') => self.array(),
340 Some(b'"') => Ok(Read::Text(self.text()?)),
341 Some(b't') => self.word("true", Read::Bool(true)),
342 Some(b'f') => self.word("false", Read::Bool(false)),
343 Some(b'n') => self.word("null", Read::Null),
344 Some(byte) if *byte == b'-' || byte.is_ascii_digit() => self.number(),
345 Some(_) => Err(self.the_error("the document holds a character that begins no value")),
346 None => Err(self.the_error("the document ends where a value was wanted")),
347 }
348 }
349
350 fn word(&mut self, word: &str, answer: Read) -> Result<Read, ReadError> {
352 if self.bytes[self.at..].starts_with(word.as_bytes()) {
353 self.at += word.len();
354 Ok(answer)
355 } else {
356 Err(self.the_error("the document holds a word that is not a value of the format"))
357 }
358 }
359
360 fn object(&mut self) -> Result<Read, ReadError> {
362 self.at += 1;
363 let mut members = BTreeMap::new();
364 self.space();
365 if self.bytes.get(self.at) == Some(&b'}') {
366 self.at += 1;
367 return Ok(Read::Object(members));
368 }
369 loop {
370 self.space();
371 if self.bytes.get(self.at) != Some(&b'"') {
372 return Err(self.the_error("a name of an object is not a text"));
373 }
374 let name = self.text()?;
375 self.space();
376 if self.bytes.get(self.at) != Some(&b':') {
377 return Err(self.the_error("a name of an object is not followed by a colon"));
378 }
379 self.at += 1;
380 let value = self.value()?;
381 members.insert(name, value);
382 self.space();
383 match self.bytes.get(self.at) {
384 Some(b',') => self.at += 1,
385 Some(b'}') => {
386 self.at += 1;
387 return Ok(Read::Object(members));
388 }
389 _ => return Err(self.the_error("an object ends without its closing brace")),
390 }
391 }
392 }
393
394 fn array(&mut self) -> Result<Read, ReadError> {
396 self.at += 1;
397 let mut values = Vec::new();
398 self.space();
399 if self.bytes.get(self.at) == Some(&b']') {
400 self.at += 1;
401 return Ok(Read::Array(values));
402 }
403 loop {
404 values.push(self.value()?);
405 self.space();
406 match self.bytes.get(self.at) {
407 Some(b',') => self.at += 1,
408 Some(b']') => {
409 self.at += 1;
410 return Ok(Read::Array(values));
411 }
412 _ => return Err(self.the_error("an array ends without its closing bracket")),
413 }
414 }
415 }
416
417 fn text(&mut self) -> Result<String, ReadError> {
419 self.at += 1;
420 let mut out = String::new();
421 loop {
422 let byte = *self
423 .bytes
424 .get(self.at)
425 .ok_or_else(|| self.the_error("a text ends before its closing quote"))?;
426 match byte {
427 b'"' => {
428 self.at += 1;
429 return Ok(out);
430 }
431 b'\\' => {
432 self.at += 1;
433 let escape = *self
434 .bytes
435 .get(self.at)
436 .ok_or_else(|| self.the_error("a text ends in the middle of an escape"))?;
437 self.at += 1;
438 match escape {
439 b'"' => out.push('"'),
440 b'\\' => out.push('\\'),
441 b'/' => out.push('/'),
442 b'b' => out.push('\u{08}'),
443 b'f' => out.push('\u{0C}'),
444 b'n' => out.push('\n'),
445 b'r' => out.push('\r'),
446 b't' => out.push('\t'),
447 b'u' => {
448 let place = self.at;
449 let digits = self.bytes.get(place..place + 4).ok_or_else(|| {
450 self.the_error("an escape of four digits is short")
451 })?;
452 let hex = std::str::from_utf8(digits)
453 .map_err(|_| self.the_error("an escape is not four digits"))?;
454 let code = u32::from_str_radix(hex, 16)
455 .map_err(|_| self.the_error("an escape is not four digits"))?;
456 self.at += 4;
457 out.push(char::from_u32(code).unwrap_or('\u{FFFD}'));
458 }
459 _ => {
460 return Err(
461 self.the_error("a text holds an escape the format does not define")
462 );
463 }
464 }
465 }
466 other => {
467 let rest = std::str::from_utf8(&self.bytes[self.at..])
470 .map_err(|_| self.the_error("a text is not a text of the format"))?;
471 let letter = rest
472 .chars()
473 .next()
474 .ok_or_else(|| self.the_error("a text ends where a letter was wanted"))?;
475 out.push(letter);
476 self.at += letter.len_utf8();
477 if other == 0 {
478 return Err(self.the_error("a text holds a byte that is not of the format"));
479 }
480 }
481 }
482 }
483 }
484
485 fn number(&mut self) -> Result<Read, ReadError> {
487 let start = self.at;
488 if self.bytes.get(self.at) == Some(&b'-') {
489 self.at += 1;
490 }
491 while let Some(byte) = self.bytes.get(self.at) {
492 if byte.is_ascii_digit() || matches!(byte, b'.' | b'e' | b'E' | b'+' | b'-') {
493 self.at += 1;
494 } else {
495 break;
496 }
497 }
498 let text = std::str::from_utf8(&self.bytes[start..self.at])
499 .map_err(|_| self.the_error("a number is not a number of the format"))?;
500 if text.is_empty() || text == "-" {
501 return Err(self.the_error("a number holds no digits"));
502 }
503 Ok(Read::Number(text.to_string()))
504 }
505}