Skip to main content

oak_json/ast/
mod.rs

1#![doc = include_str!("readme.md")]
2use core::range::Range;
3use oak_core::source::{SourceBuffer, ToSource};
4
5#[cfg(feature = "oak-pretty-print")]
6use oak_pretty_print::{AsDocument, doc as pp_doc};
7
8/// The root node of a JSON AST.
9#[derive(Clone, Debug, PartialEq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct JsonRoot {
12    /// The top-level value of the JSON document.
13    pub value: JsonValueNode,
14}
15
16impl ToSource for JsonRoot {
17    fn to_source(&self, buffer: &mut SourceBuffer) {
18        self.value.to_source(buffer)
19    }
20}
21
22/// Represents any valid JSON value.
23#[derive(Clone, Debug, PartialEq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[cfg_attr(feature = "oak-pretty-print", derive(AsDocument))]
26pub enum JsonValueNode {
27    /// A JSON object (collection of key-value pairs).
28    Object(JsonObject),
29    /// A JSON array (ordered list of values).
30    Array(JsonArray),
31    /// A JSON string.
32    String(JsonString),
33    /// A JSON number (represented as f64).
34    Number(JsonNumber),
35    /// A JSON boolean (true or false).
36    Boolean(JsonBoolean),
37    /// A JSON null value.
38    Null(JsonNull),
39}
40
41impl JsonValueNode {
42    /// Returns the value as a string slice, if it is a string.
43    pub fn as_str(&self) -> Option<&str> {
44        match self {
45            JsonValueNode::String(s) => Some(&s.value),
46            _ => None,
47        }
48    }
49
50    /// Returns the value as an `f64`, if it is a number.
51    pub fn as_f64(&self) -> Option<f64> {
52        match self {
53            JsonValueNode::Number(n) => Some(n.value),
54            _ => None,
55        }
56    }
57
58    /// Returns the value as a `bool`, if it is a boolean.
59    pub fn as_bool(&self) -> Option<bool> {
60        match self {
61            JsonValueNode::Boolean(b) => Some(b.value),
62            _ => None,
63        }
64    }
65
66    /// Returns the value as a `u64`, if it is a number.
67    pub fn as_u64(&self) -> Option<u64> {
68        self.as_f64().map(|f| f as u64)
69    }
70
71    /// Returns the value as a reference to a `JsonArray`, if it is an array.
72    pub fn as_array(&self) -> Option<&JsonArray> {
73        match self {
74            JsonValueNode::Array(a) => Some(a),
75            _ => None,
76        }
77    }
78
79    /// Returns the value as a reference to a `JsonObject`, if it is an object.
80    pub fn as_object(&self) -> Option<&JsonObject> {
81        match self {
82            JsonValueNode::Object(o) => Some(o),
83            _ => None,
84        }
85    }
86
87    /// Gets a value from the object by key, if this value is an object.
88    pub fn get(&self, key: &str) -> Option<&JsonValueNode> {
89        match self {
90            JsonValueNode::Object(o) => o.get(key),
91            _ => None,
92        }
93    }
94
95    /// Converts the JSON value to its string representation.
96    pub fn to_string(&self) -> String {
97        match self {
98            JsonValueNode::Null(_) => "null".to_string(),
99            JsonValueNode::Boolean(b) => b.value.to_string(),
100            JsonValueNode::Number(n) => {
101                // Format as integer if possible
102                if n.value.fract() == 0.0 { (n.value as i64).to_string() } else { n.value.to_string() }
103            }
104            JsonValueNode::String(s) => format!("\"{}\"", s.value),
105            JsonValueNode::Array(a) => {
106                let elements: Vec<String> = a.elements.iter().map(|e| e.to_string()).collect();
107                format!("[{}]", elements.join(","))
108            }
109            JsonValueNode::Object(o) => {
110                let fields: Vec<String> = o.fields.iter().map(|f| format!("\"{}\":{}", f.name.value, f.value.to_string())).collect();
111                format!("{{{}}}", fields.join(","))
112            }
113        }
114    }
115}
116
117impl std::fmt::Display for JsonValueNode {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        write!(f, "{}", self.to_string())
120    }
121}
122
123impl From<&str> for JsonValueNode {
124    fn from(s: &str) -> Self {
125        JsonValueNode::String(JsonString { value: s.to_string(), span: (0..0).into() })
126    }
127}
128
129impl From<String> for JsonValueNode {
130    fn from(s: String) -> Self {
131        JsonValueNode::String(JsonString { value: s, span: (0..0).into() })
132    }
133}
134
135impl From<f64> for JsonValueNode {
136    fn from(f: f64) -> Self {
137        JsonValueNode::Number(JsonNumber { value: f, span: (0..0).into() })
138    }
139}
140
141impl From<u64> for JsonValueNode {
142    fn from(u: u64) -> Self {
143        JsonValueNode::Number(JsonNumber { value: u as f64, span: (0..0).into() })
144    }
145}
146
147impl From<i32> for JsonValueNode {
148    fn from(i: i32) -> Self {
149        JsonValueNode::Number(JsonNumber { value: i as f64, span: (0..0).into() })
150    }
151}
152
153impl From<i64> for JsonValueNode {
154    fn from(i: i64) -> Self {
155        JsonValueNode::Number(JsonNumber { value: i as f64, span: (0..0).into() })
156    }
157}
158
159impl From<usize> for JsonValueNode {
160    fn from(u: usize) -> Self {
161        JsonValueNode::Number(JsonNumber { value: u as f64, span: (0..0).into() })
162    }
163}
164
165impl From<bool> for JsonValueNode {
166    fn from(b: bool) -> Self {
167        JsonValueNode::Boolean(JsonBoolean { value: b, span: (0..0).into() })
168    }
169}
170
171impl From<u32> for JsonValueNode {
172    fn from(value: u32) -> Self {
173        JsonValueNode::Number(JsonNumber { value: value as f64, span: (0..0).into() })
174    }
175}
176
177impl From<f32> for JsonValueNode {
178    fn from(value: f32) -> Self {
179        JsonValueNode::Number(JsonNumber { value: value as f64, span: (0..0).into() })
180    }
181}
182
183impl<T: Into<JsonValueNode>> From<Option<T>> for JsonValueNode {
184    fn from(value: Option<T>) -> Self {
185        match value {
186            Some(v) => v.into(),
187            None => JsonValueNode::Null(JsonNull { span: (0..0).into() }),
188        }
189    }
190}
191
192impl From<()> for JsonValueNode {
193    fn from(_: ()) -> Self {
194        JsonValueNode::Null(JsonNull { span: (0..0).into() })
195    }
196}
197
198impl From<Vec<JsonValueNode>> for JsonValueNode {
199    fn from(elements: Vec<JsonValueNode>) -> Self {
200        JsonValueNode::Array(JsonArray { elements, span: (0..0).into() })
201    }
202}
203
204impl From<std::collections::HashMap<String, JsonValueNode>> for JsonValueNode {
205    fn from(fields: std::collections::HashMap<String, JsonValueNode>) -> Self {
206        let mut fields_vec: Vec<JsonField> = fields.into_iter().map(|(k, v)| JsonField { name: JsonString { value: k, span: (0..0).into() }, value: v, span: (0..0).into() }).collect();
207        // Sort fields by name for consistent output
208        fields_vec.sort_by(|a, b| a.name.value.cmp(&b.name.value));
209        JsonValueNode::Object(JsonObject { fields: fields_vec, span: (0..0).into() })
210    }
211}
212
213impl<T: Into<JsonValueNode>> FromIterator<T> for JsonValueNode {
214    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
215        JsonValueNode::Array(JsonArray { elements: iter.into_iter().map(Into::into).collect(), span: (0..0).into() })
216    }
217}
218
219impl ToSource for JsonValueNode {
220    fn to_source(&self, buffer: &mut SourceBuffer) {
221        match self {
222            JsonValueNode::Object(v) => v.to_source(buffer),
223            JsonValueNode::Array(v) => v.to_source(buffer),
224            JsonValueNode::String(v) => v.to_source(buffer),
225            JsonValueNode::Number(v) => v.to_source(buffer),
226            JsonValueNode::Boolean(v) => v.to_source(buffer),
227            JsonValueNode::Null(v) => v.to_source(buffer),
228        }
229    }
230}
231
232/// Represents a JSON object.
233#[derive(Clone, Debug, PartialEq)]
234#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
235#[cfg_attr(feature = "oak-pretty-print", derive(AsDocument))]
236#[cfg_attr(feature = "oak-pretty-print", oak(doc = 
237    if _self.fields.is_empty() {
238        pp_doc!("{}")
239    } else {
240        pp_doc!(group( [
241            "{",
242            indent( [line, join(_self.fields.iter(), [",", line])]),
243            line,
244            "}"
245        ]))
246    }
247))]
248pub struct JsonObject {
249    /// The fields (key-value pairs) of the object.
250    pub fields: Vec<JsonField>,
251    /// The source range of the object.
252    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
253    pub span: Range<usize>,
254}
255
256impl JsonObject {
257    /// Returns the value associated with the given key, if it exists.
258    pub fn get(&self, key: &str) -> Option<&JsonValueNode> {
259        self.fields.iter().find(|f| f.name.value == key).map(|f| &f.value)
260    }
261}
262
263impl ToSource for JsonObject {
264    fn to_source(&self, buffer: &mut SourceBuffer) {
265        buffer.push("{");
266        for (i, field) in self.fields.iter().enumerate() {
267            if i > 0 {
268                buffer.push(",")
269            }
270            field.to_source(buffer)
271        }
272        buffer.push("}")
273    }
274}
275
276/// Represents a single field (key-value pair) in a JSON object.
277#[derive(Clone, Debug, PartialEq)]
278#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
279#[cfg_attr(feature = "oak-pretty-print", derive(AsDocument))]
280#[cfg_attr(feature = "oak-pretty-print", oak(doc = [self.name.as_document(params), ": ", self.value.as_document(params)]))]
281pub struct JsonField {
282    /// The name (key) of the field.
283    pub name: JsonString,
284    /// The value of the field.
285    pub value: JsonValueNode,
286    /// The source range of the field.
287    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
288    pub span: Range<usize>,
289}
290
291impl ToSource for JsonField {
292    fn to_source(&self, buffer: &mut SourceBuffer) {
293        self.name.to_source(buffer);
294        buffer.push(":");
295        self.value.to_source(buffer)
296    }
297}
298
299/// Represents a JSON array.
300#[derive(Clone, Debug, PartialEq)]
301#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
302#[cfg_attr(feature = "oak-pretty-print", derive(AsDocument))]
303#[cfg_attr(feature = "oak-pretty-print", oak(doc = 
304    if _self.elements.is_empty() {
305        pp_doc!("[]")
306    } else {
307        pp_doc!(group( [
308            "[",
309            indent( [line, join(_self.elements.iter(), [",", line])]),
310            line,
311            "]"
312        ]))
313    }
314))]
315pub struct JsonArray {
316    /// The elements of the array.
317    pub elements: Vec<JsonValueNode>,
318    /// The source range of the array.
319    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
320    pub span: Range<usize>,
321}
322
323impl ToSource for JsonArray {
324    fn to_source(&self, buffer: &mut SourceBuffer) {
325        buffer.push("[");
326        for (i, element) in self.elements.iter().enumerate() {
327            if i > 0 {
328                buffer.push(",")
329            }
330            element.to_source(buffer)
331        }
332        buffer.push("]")
333    }
334}
335
336/// Represents a JSON string literal.
337#[derive(Clone, Debug, PartialEq)]
338#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
339#[cfg_attr(feature = "oak-pretty-print", derive(AsDocument))]
340#[cfg_attr(feature = "oak-pretty-print", oak(doc = format!("\"{}\"", self.value)))]
341pub struct JsonString {
342    /// The string content (without quotes).
343    pub value: String,
344    /// The source range of the string literal.
345    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
346    pub span: Range<usize>,
347}
348
349impl ToSource for JsonString {
350    fn to_source(&self, buffer: &mut SourceBuffer) {
351        buffer.push("\"");
352        buffer.push(&self.value);
353        buffer.push("\"")
354    }
355}
356
357/// Represents a JSON number literal.
358#[derive(Clone, Debug, PartialEq)]
359#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
360#[cfg_attr(feature = "oak-pretty-print", derive(AsDocument))]
361#[cfg_attr(feature = "oak-pretty-print", oak(doc = self.value.to_string()))]
362pub struct JsonNumber {
363    /// The numeric value.
364    pub value: f64,
365    /// The source range of the number literal.
366    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
367    pub span: Range<usize>,
368}
369
370impl ToSource for JsonNumber {
371    fn to_source(&self, buffer: &mut SourceBuffer) {
372        buffer.push(&self.value.to_string())
373    }
374}
375
376/// Represents a JSON boolean literal.
377#[derive(Clone, Debug, PartialEq)]
378#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
379#[cfg_attr(feature = "oak-pretty-print", derive(AsDocument))]
380#[cfg_attr(feature = "oak-pretty-print", oak(doc = if self.value { "true" } else { "false" }))]
381pub struct JsonBoolean {
382    /// The boolean value.
383    pub value: bool,
384    /// The source range of the boolean literal.
385    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
386    pub span: Range<usize>,
387}
388
389impl ToSource for JsonBoolean {
390    fn to_source(&self, buffer: &mut SourceBuffer) {
391        buffer.push(if self.value { "true" } else { "false" })
392    }
393}
394
395/// Represents a JSON null literal.
396#[derive(Clone, Debug, PartialEq)]
397#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
398#[cfg_attr(feature = "oak-pretty-print", derive(AsDocument))]
399#[cfg_attr(feature = "oak-pretty-print", oak(doc = "null"))]
400pub struct JsonNull {
401    /// The source range of the null literal.
402    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
403    pub span: Range<usize>,
404}
405
406impl ToSource for JsonNull {
407    fn to_source(&self, buffer: &mut SourceBuffer) {
408        buffer.push("null")
409    }
410}