parsing_custom_struct/
parsing-custom-struct.rs

1#![allow(unused)]
2
3#[derive(Debug)]
4struct Person {
5  name: String,
6  age: u8,
7  alive: bool,
8}
9
10const PEOPLE: [&str; 14] = [
11  r#"{ "name": "Zen", "age": 10, "alive": true }"#,
12  "",
13  ";",
14  "0",
15  "{}",
16  r#"{ "name": 0 }"#,
17  r#"{ "name": "  " }"#,
18  r#"{ "name": "Katya" }"#,
19  r#"{ "name": "Katya", "age": null }"#,
20  r#"{ "name": "Katya", "age": 0.1 }"#,
21  r#"{ "name": "Katya", "age": -1 }"#,
22  r#"{ "name": "Katya", "age": 8 }"#,
23  r#"{ "name": "Katya", "age": 8, "alive": [] }"#,
24  r#"{ "name": "Katya", "age": 8, "alive": false }"#,
25];
26
27fn main() {
28  for json in PEOPLE {
29    match parse_person(json) {
30      Ok(p) => println!("{p:#?}"),
31      Err(err) => eprintln!("failed to parse r#\"{json}\"#: {err:?}"),
32    }
33  }
34}
35
36#[derive(Debug)]
37enum ParsePersonError {
38  JsonParse(jso::parse::Error),
39  JsonNotAnObject,
40  Custom(&'static str),
41}
42
43fn parse_person(s: &str) -> Result<Person, ParsePersonError> {
44  use ParsePersonError::*;
45
46  let json = jso::parse(s).map_err(JsonParse)?;
47  let mut o = json.obj().ok_or(JsonNotAnObject)?;
48
49  let name = o
50    .remove("name")
51    .ok_or(Custom("missing \"name\""))?
52    .str()
53    .ok_or(Custom("\"name\" is not a string"))?
54    .trim()
55    .to_string();
56
57  if name.is_empty() {
58    return Err(Custom("\"name\" empty"));
59  }
60
61  let age = o
62    .remove("age")
63    .ok_or(Custom("missing \"age\""))?
64    .num()
65    .ok_or(Custom("\"age\" is not a number"))?;
66
67  if age.fract() != 0. {
68    return Err(Custom("\"age\" must be an integer"));
69  }
70
71  if !(0.0..=255.0).contains(&age) {
72    return Err(Custom("\"age\" must be between 0-255"));
73  }
74
75  let age = age as u8;
76
77  let alive = o
78    .remove("alive")
79    .ok_or(Custom("missing \"alive\""))?
80    .bool()
81    .ok_or(Custom("\"alive\" not a boolean"))?;
82
83  Ok(Person { name, age, alive })
84}