1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Json parser
//!
//! # Example
//! ```
//! use json::Json;
//!
//! let j = Json::deserialize(r#"{
//!     "array" : [ 1, 2, "3", null ],
//!     "true" : true,
//!     "nested" : {
//!         "inner" : []
//!     }
//! }"#).unwrap();
//!
//! let Json::Object(map) = j else { panic!() };
//! assert!(
//!     matches!(
//!         map.get("true"),
//!         Some(Json::True)));
//! ```

use std::{borrow::Cow, collections::HashMap, fmt::{Display, Write}};

mod lexer;
mod parser;

#[cfg(feature = "bindings")]
pub mod export;

pub type Result<T> = std::result::Result<T,Cow<'static,str>>;

/// Represents a JSON object
#[derive(Debug)]
pub enum Json {
    Array(Vec<Json>),
    Object(HashMap<String,Json>),
    String(String),
    Number(f64),
    True, False, Null,
}

/// Configures the JSON parser
pub struct JsonConfig {
    /// Max depth for nested objects
    pub max_depth: u32,
    /// Recover from errors.
    /// For example, trailing commas on objects
    /// are not allowed, but this flag makes
    /// the parser skip them.
    pub recover_from_errors: bool,
}

const DEFAULT_CONFIG: JsonConfig = JsonConfig {
    max_depth: 500,
    recover_from_errors: false,
};

impl Default for JsonConfig {
    fn default() -> Self { DEFAULT_CONFIG }
}

macro_rules! deserialize {
    ($text:ident, $conf:ident) => {
        {
            let mut tokens = lexer::tokenize($text)?;
            parser::parse(&mut tokens, $conf)
        }
    };
}

impl Json {
    /// Deserializes the given string into a [Json] object
    pub fn deserialize(text: &str) -> Result<Json> {
        deserialize!(text, DEFAULT_CONFIG)
    }
    /// Deserializes the given string into a [Json] object
    /// using the given [JsonConfig]
    pub fn deserialize_with_config(text: &str, conf: JsonConfig) -> Result<Json> {
        deserialize!(text, conf)
    }
    /// Serializes the JSON object into a string
    pub fn serialize(&self, out: &mut dyn Write) -> std::fmt::Result {
        match self {
            Json::Array(elements) => {
                out.write_char('[')?;
                for i in 0..elements.len() {
                    elements[i].serialize(out)?;
                    if i < elements.len() -1 {
                        out.write_char(',')?;
                    }
                }
                out.write_char(']')?;
            },
            Json::Object(obj) => {
                out.write_char('{')?;
                let mut first = true;
                for (k,v) in obj {
                    if !first {
                        out.write_char(',')?;
                    }
                    first = false;
                    out.write_str(k)?;
                    out.write_char(':')?;
                    v.serialize(out)?;
                }
                out.write_char('}')?;
            },
            Json::String(s) => { write!(out, "\"{s}\"")?; },
            Json::Number(n) => { write!(out, "{n}")?; },
            Json::True => { out.write_str("true")? },
            Json::False => { out.write_str("false")? },
            Json::Null => { out.write_str("null")? },
        }
        Ok(())
    }
}

impl Display for Json {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut string = String::new();
        self.serialize(&mut string).unwrap();
        write!(f, "{}", string)
    }
}