jsonette/parser/mod.rs
1/*
2 * Copyright (c) 2026 DevEtte.
3 *
4 * This project is dual-licensed under both the MIT License and the
5 * Apache License, Version 2.0 (the "License"). You may not use this
6 * file except in compliance with one of these licenses.
7 *
8 * You may obtain a copy of the Licenses at:
9 * - MIT: https://opensource.org
10 * - Apache 2.0: http://apache.org
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19//! The parsing module contains the strict and tolerant JSON parsers,
20//! as well as fast diagnostics syntax checking and parsing helper utilities.
21
22pub mod strict;
23pub mod tolerant;
24pub mod utils;
25
26pub use strict::parse;
27
28use crate::json_node::JsonNode;
29use crate::types::Diagnostic;
30
31/// Tolerant parsing: Attempts to build a partial AST even if errors are present.
32/// Useful for live IDE feedback while typing, preserving as much of the tree as possible.
33///
34/// # Arguments
35///
36/// * `input` - The raw JSON string slice to parse (potentially incomplete or invalid).
37///
38/// # Returns
39///
40/// A tuple containing:
41/// * `Option<JsonNode>` - The partial AST tree if any structure could be recovered.
42/// * `Vec<Diagnostic>` - A list of errors encountered during parsing.
43pub fn tolerant_parse(input: &str) -> (Option<JsonNode>, Vec<Diagnostic>) {
44 // We opted for a hand-rolled tolerant parser (Option B) instead of tree-sitter.
45 // While tree-sitter provides excellent error recovery, it introduces a C dependency
46 // which complicates the cross-platform UniFFI build (especially for iOS/Android targets),
47 // and slightly increases binary size. A minimal hand-rolled parser is sufficient for
48 // JSON and gives us full control over `JsonNode` span generation without an intermediate AST.
49 tolerant::parse(input)
50}
51
52/// Fast validation path: parses and returns only syntax or structural errors
53/// without fully allocating the resulting AST tree.
54///
55/// # Arguments
56///
57/// * `input` - The raw JSON string slice to validate.
58///
59/// # Returns
60///
61/// A list of `Diagnostic` errors found in the input. If the input is valid JSON, this is empty.
62pub fn diagnostics(input: &str) -> Vec<Diagnostic> {
63 // Fast validation path: for M0, we simply delegate to tolerant_parse
64 // and return the extracted diagnostics. Future performance optimizations
65 // can skip AST allocation entirely if necessary.
66 tolerant_parse(input).1
67}
68
69#[cfg(test)]
70mod stub_tests {
71 use super::*;
72
73 #[test]
74 fn test_tolerant_parse_is_implemented() {
75 let (node, _) = tolerant_parse("{}");
76 assert!(node.is_some());
77 }
78
79 /// **Test Case**: Fast Validation Returns Proper Diagnostics
80 ///
81 /// ### Description
82 /// Verifies that the fast `diagnostics` validation path correctly identifies
83 /// valid and invalid JSON document syntax.
84 ///
85 /// ### Test Procedure
86 /// 1. Call `diagnostics` on a valid JSON document `{}`.
87 /// 2. Call `diagnostics` on an invalid JSON document `{`.
88 ///
89 /// ### Expected Result
90 /// The valid document returns an empty `Vec<Diagnostic>`. The invalid document
91 /// returns a non-empty `Vec<Diagnostic>`.
92 #[test]
93 fn test_diagnostics() {
94 let diags = diagnostics("{}");
95 assert!(diags.is_empty());
96
97 let diags = diagnostics("{");
98 assert!(!diags.is_empty());
99 }
100}