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.boolean("ok", true).number("line", 3).nothing("error");
173 assert_eq!(object.finish(), "{\"ok\":true,\"line\":3,\"error\":null}");
174 }
175}
176
177#[derive(Debug, Clone, PartialEq)]
179pub enum Read {
180 Null,
182 Bool(bool),
184 Number(String),
188 Text(String),
190 Array(Vec<Read>),
192 Object(BTreeMap<String, Read>),
194}
195
196impl Read {
197 pub fn get(&self, name: &str) -> Option<&Read> {
199 match self {
200 Read::Object(members) => members.get(name),
201 _ => None,
202 }
203 }
204
205 pub fn at(&self, place: usize) -> Option<&Read> {
207 match self {
208 Read::Array(values) => values.get(place),
209 _ => None,
210 }
211 }
212
213 pub fn array(&self) -> &[Read] {
215 match self {
216 Read::Array(values) => values,
217 _ => &[],
218 }
219 }
220
221 pub fn names(&self) -> Vec<&str> {
223 match self {
224 Read::Object(members) => members.keys().map(String::as_str).collect(),
225 _ => Vec::new(),
226 }
227 }
228
229 pub fn as_str(&self) -> Option<&str> {
231 match self {
232 Read::Text(text) => Some(text),
233 _ => None,
234 }
235 }
236
237 pub fn field_text(&self, name: &str) -> Option<&str> {
239 self.get(name).and_then(Read::as_str)
240 }
241
242 pub fn as_i64(&self) -> Option<i64> {
244 match self {
245 Read::Number(text) => text.parse().ok(),
246 _ => None,
247 }
248 }
249
250 pub fn as_bool(&self) -> Option<bool> {
252 match self {
253 Read::Bool(value) => Some(*value),
254 _ => None,
255 }
256 }
257
258 pub fn is_number(&self) -> bool {
260 matches!(self, Read::Number(_))
261 }
262
263 pub fn is_string(&self) -> bool {
265 matches!(self, Read::Text(_))
266 }
267
268 pub fn is_null(&self) -> bool {
270 matches!(self, Read::Null)
271 }
272}
273
274#[derive(Debug, Clone, PartialEq, Eq)]
276pub struct ReadError {
277 pub reason: String,
279 pub at: usize,
281}
282
283impl std::fmt::Display for ReadError {
284 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285 write!(formatter, "{} at byte {}", self.reason, self.at)
286 }
287}
288
289impl std::error::Error for ReadError {}
290
291pub fn read(text: &str) -> Result<Read, ReadError> {
293 let mut parser = Reader {
294 bytes: text.as_bytes(),
295 at: 0,
296 };
297 let value = parser.value()?;
298 parser.space();
299 if parser.at != parser.bytes.len() {
300 return Err(parser.the_error("the document ends before its value does"));
301 }
302 Ok(value)
303}
304
305struct Reader<'a> {
307 bytes: &'a [u8],
308 at: usize,
309}
310
311impl Reader<'_> {
312 fn the_error(&self, reason: &str) -> ReadError {
314 ReadError {
315 reason: reason.to_string(),
316 at: self.at,
317 }
318 }
319
320 fn space(&mut self) {
322 while let Some(byte) = self.bytes.get(self.at) {
323 if matches!(byte, b' ' | b'\t' | b'\n' | b'\r') {
324 self.at += 1;
325 } else {
326 break;
327 }
328 }
329 }
330
331 fn value(&mut self) -> Result<Read, ReadError> {
333 self.space();
334 match self.bytes.get(self.at) {
335 Some(b'{') => self.object(),
336 Some(b'[') => self.array(),
337 Some(b'"') => Ok(Read::Text(self.text()?)),
338 Some(b't') => self.word("true", Read::Bool(true)),
339 Some(b'f') => self.word("false", Read::Bool(false)),
340 Some(b'n') => self.word("null", Read::Null),
341 Some(byte) if *byte == b'-' || byte.is_ascii_digit() => self.number(),
342 Some(_) => Err(self.the_error("the document holds a character that begins no value")),
343 None => Err(self.the_error("the document ends where a value was wanted")),
344 }
345 }
346
347 fn word(&mut self, word: &str, answer: Read) -> Result<Read, ReadError> {
349 if self.bytes[self.at..].starts_with(word.as_bytes()) {
350 self.at += word.len();
351 Ok(answer)
352 } else {
353 Err(self.the_error("the document holds a word that is not a value of the format"))
354 }
355 }
356
357 fn object(&mut self) -> Result<Read, ReadError> {
359 self.at += 1;
360 let mut members = BTreeMap::new();
361 self.space();
362 if self.bytes.get(self.at) == Some(&b'}') {
363 self.at += 1;
364 return Ok(Read::Object(members));
365 }
366 loop {
367 self.space();
368 if self.bytes.get(self.at) != Some(&b'"') {
369 return Err(self.the_error("a name of an object is not a text"));
370 }
371 let name = self.text()?;
372 self.space();
373 if self.bytes.get(self.at) != Some(&b':') {
374 return Err(self.the_error("a name of an object is not followed by a colon"));
375 }
376 self.at += 1;
377 let value = self.value()?;
378 members.insert(name, value);
379 self.space();
380 match self.bytes.get(self.at) {
381 Some(b',') => self.at += 1,
382 Some(b'}') => {
383 self.at += 1;
384 return Ok(Read::Object(members));
385 }
386 _ => return Err(self.the_error("an object ends without its closing brace")),
387 }
388 }
389 }
390
391 fn array(&mut self) -> Result<Read, ReadError> {
393 self.at += 1;
394 let mut values = Vec::new();
395 self.space();
396 if self.bytes.get(self.at) == Some(&b']') {
397 self.at += 1;
398 return Ok(Read::Array(values));
399 }
400 loop {
401 values.push(self.value()?);
402 self.space();
403 match self.bytes.get(self.at) {
404 Some(b',') => self.at += 1,
405 Some(b']') => {
406 self.at += 1;
407 return Ok(Read::Array(values));
408 }
409 _ => return Err(self.the_error("an array ends without its closing bracket")),
410 }
411 }
412 }
413
414 fn text(&mut self) -> Result<String, ReadError> {
416 self.at += 1;
417 let mut out = String::new();
418 loop {
419 let byte = *self
420 .bytes
421 .get(self.at)
422 .ok_or_else(|| self.the_error("a text ends before its closing quote"))?;
423 match byte {
424 b'"' => {
425 self.at += 1;
426 return Ok(out);
427 }
428 b'\\' => {
429 self.at += 1;
430 let escape = *self
431 .bytes
432 .get(self.at)
433 .ok_or_else(|| self.the_error("a text ends in the middle of an escape"))?;
434 self.at += 1;
435 match escape {
436 b'"' => out.push('"'),
437 b'\\' => out.push('\\'),
438 b'/' => out.push('/'),
439 b'b' => out.push('\u{08}'),
440 b'f' => out.push('\u{0C}'),
441 b'n' => out.push('\n'),
442 b'r' => out.push('\r'),
443 b't' => out.push('\t'),
444 b'u' => {
445 let place = self.at;
446 let digits = self
447 .bytes
448 .get(place..place + 4)
449 .ok_or_else(|| self.the_error("an escape of four digits is short"))?;
450 let hex = std::str::from_utf8(digits)
451 .map_err(|_| self.the_error("an escape is not four digits"))?;
452 let code = u32::from_str_radix(hex, 16)
453 .map_err(|_| self.the_error("an escape is not four digits"))?;
454 self.at += 4;
455 out.push(char::from_u32(code).unwrap_or('\u{FFFD}'));
456 }
457 _ => return Err(self.the_error("a text holds an escape the format does not define")),
458 }
459 }
460 other => {
461 let rest = std::str::from_utf8(&self.bytes[self.at..])
464 .map_err(|_| self.the_error("a text is not a text of the format"))?;
465 let letter = rest
466 .chars()
467 .next()
468 .ok_or_else(|| self.the_error("a text ends where a letter was wanted"))?;
469 out.push(letter);
470 self.at += letter.len_utf8();
471 if other == 0 {
472 return Err(self.the_error("a text holds a byte that is not of the format"));
473 }
474 }
475 }
476 }
477 }
478
479 fn number(&mut self) -> Result<Read, ReadError> {
481 let start = self.at;
482 if self.bytes.get(self.at) == Some(&b'-') {
483 self.at += 1;
484 }
485 while let Some(byte) = self.bytes.get(self.at) {
486 if byte.is_ascii_digit() || matches!(byte, b'.' | b'e' | b'E' | b'+' | b'-') {
487 self.at += 1;
488 } else {
489 break;
490 }
491 }
492 let text = std::str::from_utf8(&self.bytes[start..self.at])
493 .map_err(|_| self.the_error("a number is not a number of the format"))?;
494 if text.is_empty() || text == "-" {
495 return Err(self.the_error("a number holds no digits"));
496 }
497 Ok(Read::Number(text.to_string()))
498 }
499}