Skip to main content

fjson_core/
lib.rs

1use std::collections::HashMap;
2
3/// Parses a JSON string and automatically corrects
4/// common issues (e.g., missing brackets, invalid numbers, incomplete literals).
5///
6/// Also deserializes everything by default (root and nested).
7///
8/// Returns a formatted, valid JSON string.
9pub fn fix(input: impl Into<String>) -> String {
10    let json = Parser::new(input).parse_and_fix();
11    json.deserialize_all().stringify(0)
12}
13
14struct Parser {
15    chars: Vec<char>,
16    i: usize,
17}
18
19impl Parser {
20    pub fn new(input: impl Into<String>) -> Self {
21        Self {
22            chars: input.into().trim().chars().collect(),
23            i: 0,
24        }
25    }
26
27    fn peek(&mut self) -> Option<char> {
28        self.chars.get(self.i).copied()
29    }
30
31    fn next(&mut self) -> Option<char> {
32        let c = self.peek();
33        if c.is_some() {
34            self.i += 1;
35        }
36        c
37    }
38
39    fn skip_whitespace(&mut self) {
40        while let Some(c) = self.peek()
41            && c.is_whitespace()
42        {
43            self.next();
44        }
45    }
46
47    pub fn parse_and_fix(&mut self) -> Json {
48        self.parse_value()
49    }
50
51    fn parse_value(&mut self) -> Json {
52        self.skip_whitespace();
53
54        if let Some(c) = self.peek() {
55            match c {
56                'n' | 'N' | 't' | 'T' | 'f' | 'F' => self.parse_static(),
57
58                val if val.is_ascii_digit() || val == '-' || val == '.' => self.parse_number(),
59
60                '"' => self.parse_string(),
61
62                '[' => self.parse_array(),
63
64                '{' => self.parse_object(),
65
66                _ => {
67                    self.next();
68                    Json::Null
69                }
70            }
71        } else {
72            Json::Null
73        }
74    }
75
76    fn parse_static(&mut self) -> Json {
77        match self.next().unwrap().to_ascii_lowercase() {
78            'n' => Json::Null,
79            't' => Json::True,
80            _ => Json::False,
81        }
82    }
83
84    fn parse_number(&mut self) -> Json {
85        let mut lex = String::new();
86
87        if let Some('-') = self.peek() {
88            lex.push('-');
89            self.next();
90
91            while let Some('-') = self.peek() {
92                self.next();
93            }
94        }
95
96        if let Some('.') = self.peek() {
97            lex.push('0');
98        }
99
100        // leading 0
101        if let Some('0') = self.peek() {
102            lex.push('0');
103            self.next();
104
105            while let Some(c) = self.peek() {
106                if c == '0' {
107                    self.next();
108                } else {
109                    if c.is_ascii_digit() {
110                        lex.pop();
111                    }
112                    break;
113                }
114            }
115        }
116
117        // integer
118        while let Some(c) = self.peek()
119            && c.is_ascii_digit()
120        {
121            lex.push(c);
122            self.next();
123        }
124
125        // float
126        if let Some('.') = self.peek() {
127            lex.push('.');
128            self.next();
129
130            let mut count = 0;
131            while let Some(c) = self.peek()
132                && c.is_ascii_digit()
133            {
134                lex.push(c);
135                self.next();
136                count += 1;
137            }
138
139            if count == 0 {
140                lex.push('0');
141            }
142        }
143
144        // expoent
145        if let Some(c) = self.peek()
146            && (c == 'e' || c == 'E')
147        {
148            lex.push(c);
149            self.next();
150
151            if let Some(sign) = self.peek()
152                && (sign == '-' || sign == '+')
153            {
154                lex.push(sign);
155                self.next();
156            }
157
158            let mut count = 0;
159            while let Some(c) = self.peek()
160                && c.is_ascii_digit()
161            {
162                lex.push(c);
163                self.next();
164                count += 1;
165            }
166
167            if count == 0 {
168                if lex.ends_with('-') || lex.ends_with('+') {
169                    lex.pop();
170                }
171                lex.pop();
172            }
173        }
174
175        if lex == "-" {
176            lex.push('0');
177        }
178
179        Json::Number(lex)
180    }
181
182    fn parse_string(&mut self) -> Json {
183        let mut lex = String::new();
184
185        self.next();
186        while let Some(c) = self.next() {
187            match c {
188                '"' => {
189                    break;
190                }
191
192                '\\' => {
193                    if let Some(esc) = self.next() {
194                        match esc {
195                            '"' => lex.push('"'),
196                            '\\' => lex.push('\\'),
197                            '/' => lex.push('/'),
198                            'b' => lex.push('\u{0008}'),
199                            'f' => lex.push('\u{000C}'),
200                            'n' => lex.push('\n'),
201                            'r' => lex.push('\r'),
202                            't' => lex.push('\t'),
203
204                            'u' => {
205                                if let Some(c) = self.parse_unicode_escape() {
206                                    lex.push(c);
207                                }
208                            }
209
210                            ch => {
211                                lex.push(ch);
212                            }
213                        }
214                    }
215                }
216
217                _ => {
218                    lex.push(c);
219                }
220            }
221        }
222
223        Json::String(lex)
224    }
225
226    fn parse_unicode_escape(&mut self) -> Option<char> {
227        let mut hex = String::new();
228        for _ in 0..4 {
229            if let Some(c) = self.next() {
230                hex.push(c);
231            } else {
232                return None;
233            }
234        }
235
236        let code = u32::from_str_radix(&hex, 16).ok()?;
237
238        if let Some(ch) = char::from_u32(code) {
239            Some(ch)
240        } else if (0xD800..=0xDBFF).contains(&code) {
241            if let (Some('\\'), Some('u')) = (self.next(), self.next()) {
242                let mut low_hex = String::new();
243                for _ in 0..4 {
244                    if let Some(c) = self.next() {
245                        low_hex.push(c);
246                    } else {
247                        return None;
248                    }
249                }
250
251                let low_code = u32::from_str_radix(&low_hex, 16).ok()?;
252
253                if (0xDC00..=0xDFFF).contains(&low_code) {
254                    let full_code = 0x10000 + ((code - 0xD800) << 10) + (low_code - 0xDC00);
255
256                    char::from_u32(full_code)
257                } else {
258                    None
259                }
260            } else {
261                None
262            }
263        } else {
264            None
265        }
266    }
267
268    fn parse_array(&mut self) -> Json {
269        let mut arr = Vec::new();
270
271        self.next();
272
273        loop {
274            self.skip_whitespace();
275
276            match self.peek() {
277                Some(']') => {
278                    self.next();
279                    break;
280                }
281
282                Some(',') => {
283                    self.next();
284                    continue;
285                }
286
287                Some(_) => {
288                    arr.push(self.parse_value());
289
290                    self.skip_whitespace();
291
292                    while let Some(c) = self.peek() {
293                        match c {
294                            ']' | ',' => {
295                                break;
296                            }
297
298                            _ => {
299                                self.next();
300                            }
301                        }
302                    }
303                }
304
305                None => {
306                    break;
307                }
308            }
309        }
310
311        Json::Array(arr)
312    }
313
314    fn parse_object(&mut self) -> Json {
315        let mut obj = HashMap::new();
316        let mut order = Vec::new();
317
318        self.next();
319
320        loop {
321            self.skip_whitespace();
322
323            match self.peek() {
324                Some('}') => {
325                    self.next();
326                    break;
327                }
328
329                Some('"') => {
330                    let key = match self.parse_string() {
331                        Json::String(s) => s,
332                        _ => unreachable!(),
333                    };
334
335                    self.skip_whitespace();
336
337                    if self.peek() == Some(':') {
338                        self.next();
339                    }
340
341                    if obj.contains_key(&key)
342                        && let Some(pos) = order.iter().position(|k| k == &key)
343                    {
344                        order.remove(pos);
345                    }
346
347                    obj.insert(key.clone(), self.parse_value());
348                    order.push(key);
349                }
350
351                None => {
352                    break;
353                }
354
355                _ => {
356                    self.next();
357                }
358            }
359        }
360
361        Json::Object((obj, order))
362    }
363}
364
365fn escape_string(s: &str) -> String {
366    let mut result = String::with_capacity(s.len());
367    for c in s.chars() {
368        match c {
369            '"' => result.push_str("\\\""),
370            '\\' => result.push_str("\\\\"),
371            '\n' => result.push_str("\\n"),
372            '\r' => result.push_str("\\r"),
373            '\t' => result.push_str("\\t"),
374            '\u{0008}' => result.push_str("\\b"),
375            '\u{000C}' => result.push_str("\\f"),
376            ch if ch < ' ' => {
377                result.push_str(&format!("\\u{:04x}", ch as u32));
378            }
379            ch => result.push(ch),
380        }
381    }
382    result
383}
384
385enum Json {
386    Null,
387    True,
388    False,
389    Number(String),
390    String(String),
391    Array(Vec<Json>),
392    Object((HashMap<String, Json>, Vec<String>)),
393}
394
395impl Json {
396    pub fn deserialize_all(self) -> Json {
397        match self {
398            Self::String(val) => {
399                let trimmed = val.trim();
400
401                if trimmed.starts_with('{') || trimmed.starts_with('[') {
402                    Parser::new(trimmed).parse_and_fix().deserialize_all()
403                } else {
404                    Json::String(val)
405                }
406            }
407
408            Json::Array(arr) => Json::Array(arr.into_iter().map(|v| v.deserialize_all()).collect()),
409
410            Json::Object((obj, order)) => Json::Object((
411                obj.into_iter()
412                    .map(|(k, v)| (k, v.deserialize_all()))
413                    .collect(),
414                order,
415            )),
416
417            other => other,
418        }
419    }
420
421    pub fn stringify(&self, tabs: usize) -> String {
422        const TAB: &str = "   ";
423
424        match self {
425            Self::Null => "null".to_string(),
426            Self::True => "true".to_string(),
427            Self::False => "false".to_string(),
428
429            Self::Number(val) => val.clone(),
430            Self::String(val) => format!("\"{}\"", escape_string(val)),
431
432            Self::Array(arr) => {
433                if arr.is_empty() {
434                    return "[]".to_string();
435                }
436
437                let mut result = String::from("[\n");
438                let tab_str = TAB.repeat(tabs + 1);
439
440                for val in arr {
441                    result.push_str(&format!("{}{},\n", tab_str, val.stringify(tabs + 1)));
442                }
443
444                result.truncate(result.len() - 2);
445
446                result.push('\n');
447                result.push_str(&TAB.repeat(tabs));
448                result.push(']');
449                result
450            }
451
452            Self::Object((obj, order)) => {
453                if obj.is_empty() {
454                    return "{}".to_string();
455                }
456
457                let mut result = String::from("{\n");
458                let tab_str = TAB.repeat(tabs + 1);
459
460                for key in order {
461                    if let Some(val) = obj.get(key) {
462                        result.push_str(&format!(
463                            "{}\"{}\": {},\n",
464                            tab_str,
465                            escape_string(key),
466                            val.stringify(tabs + 1)
467                        ));
468                    }
469                }
470
471                result.truncate(result.len() - 2);
472
473                result.push('\n');
474                result.push_str(&TAB.repeat(tabs));
475                result.push('}');
476                result
477            }
478        }
479    }
480}
481
482#[cfg(test)]
483mod tests;