Skip to main content

varar_core/
json_value.rs

1//! A tiny recursive-descent JSON reader over the dynamic [`Value`] model.
2//!
3//! Two callers: `varar.lock.json` parsing in [`crate::drift`], and the
4//! conformance gates, which compare an artifact against a golden by CONTENT
5//! rather than by bytes — the golden is one file generated by the TypeScript
6//! reference, and key order, indentation and escaping belong to whichever
7//! writer produced it, not to the contract.
8
9use crate::value::Value;
10use std::collections::BTreeMap;
11
12/// Parses JSON text into a [`Value`], or `None` on malformed input. Integral
13/// numbers become `Value::Int`, the rest `Value::Float` — `Value`'s derived
14/// equality deliberately treats `Int(2)` and `Float(2.0)` as different.
15pub fn parse_json_value(text: &str) -> Option<Value> {
16    JsonReader::new(text).parse_whole()
17}
18
19/// A tiny recursive-descent JSON reader — enough for `varar.lock.json`, returning
20/// `None` on malformed input (Java's caught-exception → null).
21struct JsonReader {
22    chars: Vec<char>,
23    i: usize,
24}
25
26impl JsonReader {
27    fn new(text: &str) -> JsonReader {
28        JsonReader {
29            chars: text.chars().collect(),
30            i: 0,
31        }
32    }
33
34    fn parse_whole(&mut self) -> Option<Value> {
35        let v = self.value()?;
36        self.skip_ws();
37        if self.i != self.chars.len() {
38            return None;
39        }
40        Some(v)
41    }
42
43    fn value(&mut self) -> Option<Value> {
44        self.skip_ws();
45        match self.peek()? {
46            '{' => self.object(),
47            '[' => self.array(),
48            '"' => self.string().map(Value::String),
49            't' | 'f' => self.boolean(),
50            'n' => self.null(),
51            _ => self.number(),
52        }
53    }
54
55    fn object(&mut self) -> Option<Value> {
56        self.expect('{')?;
57        let mut map = BTreeMap::new();
58        self.skip_ws();
59        if self.peek()? == '}' {
60            self.i += 1;
61            return Some(Value::Map(map));
62        }
63        loop {
64            self.skip_ws();
65            let key = self.string()?;
66            self.skip_ws();
67            self.expect(':')?;
68            map.insert(key, self.value()?);
69            self.skip_ws();
70            match self.next()? {
71                '}' => return Some(Value::Map(map)),
72                ',' => {}
73                _ => return None,
74            }
75        }
76    }
77
78    fn array(&mut self) -> Option<Value> {
79        self.expect('[')?;
80        let mut list = Vec::new();
81        self.skip_ws();
82        if self.peek()? == ']' {
83            self.i += 1;
84            return Some(Value::List(list));
85        }
86        loop {
87            list.push(self.value()?);
88            self.skip_ws();
89            match self.next()? {
90                ']' => return Some(Value::List(list)),
91                ',' => {}
92                _ => return None,
93            }
94        }
95    }
96
97    fn string(&mut self) -> Option<String> {
98        self.expect('"')?;
99        let mut out = String::new();
100        loop {
101            match self.next()? {
102                '"' => return Some(out),
103                '\\' => match self.next()? {
104                    '"' => out.push('"'),
105                    '\\' => out.push('\\'),
106                    '/' => out.push('/'),
107                    'n' => out.push('\n'),
108                    'r' => out.push('\r'),
109                    't' => out.push('\t'),
110                    'b' => out.push('\u{0008}'),
111                    'f' => out.push('\u{000c}'),
112                    'u' => {
113                        let code = self.hex4()?;
114                        out.push(char::from_u32(code)?);
115                    }
116                    _ => return None,
117                },
118                c => out.push(c),
119            }
120        }
121    }
122
123    fn hex4(&mut self) -> Option<u32> {
124        if self.i + 4 > self.chars.len() {
125            return None;
126        }
127        let slice: String = self.chars[self.i..self.i + 4].iter().collect();
128        self.i += 4;
129        u32::from_str_radix(&slice, 16).ok()
130    }
131
132    fn number(&mut self) -> Option<Value> {
133        let start = self.i;
134        while self.i < self.chars.len() && "-+.eE0123456789".contains(self.chars[self.i]) {
135            self.i += 1;
136        }
137        if self.i == start {
138            return None;
139        }
140        let num: String = self.chars[start..self.i].iter().collect();
141        if num.contains(['.', 'e', 'E']) {
142            num.parse::<f64>().ok().map(Value::Float)
143        } else {
144            num.parse::<i64>().ok().map(Value::Int)
145        }
146    }
147
148    fn boolean(&mut self) -> Option<Value> {
149        if self.starts_with("true") {
150            self.i += 4;
151            Some(Value::Bool(true))
152        } else if self.starts_with("false") {
153            self.i += 5;
154            Some(Value::Bool(false))
155        } else {
156            None
157        }
158    }
159
160    fn null(&mut self) -> Option<Value> {
161        if self.starts_with("null") {
162            self.i += 4;
163            Some(Value::Null)
164        } else {
165            None
166        }
167    }
168
169    fn starts_with(&self, lit: &str) -> bool {
170        let lit: Vec<char> = lit.chars().collect();
171        self.i + lit.len() <= self.chars.len() && self.chars[self.i..self.i + lit.len()] == lit[..]
172    }
173
174    fn skip_ws(&mut self) {
175        while self.i < self.chars.len() && matches!(self.chars[self.i], ' ' | '\n' | '\r' | '\t') {
176            self.i += 1;
177        }
178    }
179
180    fn peek(&self) -> Option<char> {
181        self.chars.get(self.i).copied()
182    }
183
184    fn next(&mut self) -> Option<char> {
185        let c = self.chars.get(self.i).copied()?;
186        self.i += 1;
187        Some(c)
188    }
189
190    fn expect(&mut self, c: char) -> Option<()> {
191        if self.next()? == c { Some(()) } else { None }
192    }
193}