jsonc_parser/
ast.rs

1use super::common::Range;
2use super::common::Ranged;
3use std::borrow::Cow;
4
5/// JSON value.
6#[derive(Debug, PartialEq, Clone)]
7pub enum Value<'a> {
8  StringLit(StringLit<'a>),
9  NumberLit(NumberLit<'a>),
10  BooleanLit(BooleanLit),
11  Object(Object<'a>),
12  Array(Array<'a>),
13  NullKeyword(NullKeyword),
14}
15
16impl<'a> Value<'a> {
17  pub fn as_string_lit(&self) -> Option<&StringLit<'a>> {
18    match self {
19      Value::StringLit(node) => Some(node),
20      _ => None,
21    }
22  }
23
24  pub fn as_number_lit(&self) -> Option<&NumberLit<'a>> {
25    match self {
26      Value::NumberLit(node) => Some(node),
27      _ => None,
28    }
29  }
30
31  pub fn as_boolean_lit(&self) -> Option<&BooleanLit> {
32    match self {
33      Value::BooleanLit(node) => Some(node),
34      _ => None,
35    }
36  }
37
38  pub fn as_object(&self) -> Option<&Object<'a>> {
39    match self {
40      Value::Object(node) => Some(node),
41      _ => None,
42    }
43  }
44
45  pub fn as_array(&self) -> Option<&Array<'a>> {
46    match self {
47      Value::Array(node) => Some(node),
48      _ => None,
49    }
50  }
51
52  pub fn as_null_keyword(&self) -> Option<&NullKeyword> {
53    match self {
54      Value::NullKeyword(node) => Some(node),
55      _ => None,
56    }
57  }
58}
59
60#[cfg(feature = "serde")]
61impl<'a> From<Value<'a>> for serde_json::Value {
62  fn from(value: Value<'a>) -> Self {
63    use std::str::FromStr;
64    match value {
65      Value::Array(arr) => {
66        let vec = arr.elements.into_iter().map(|v| v.into()).collect();
67        serde_json::Value::Array(vec)
68      }
69      Value::BooleanLit(b) => serde_json::Value::Bool(b.value),
70      Value::NullKeyword(_) => serde_json::Value::Null,
71      Value::NumberLit(num) => {
72        // check if this is a hexadecimal literal (0x or 0X prefix)
73        let num_str = num.value.trim_start_matches(['-', '+']);
74        if num_str.len() > 2 && (num_str.starts_with("0x") || num_str.starts_with("0X")) {
75          // Parse hexadecimal and convert to decimal
76          let hex_part = &num_str[2..];
77          match i64::from_str_radix(hex_part, 16) {
78            Ok(decimal_value) => {
79              let final_value = if num.value.starts_with('-') {
80                -decimal_value
81              } else {
82                decimal_value
83              };
84              serde_json::Value::Number(serde_json::Number::from(final_value))
85            }
86            Err(_) => serde_json::Value::String(num.value.to_string()),
87          }
88        } else {
89          // standard decimal number
90          let num_for_parsing = num.value.trim_start_matches('+');
91          match serde_json::Number::from_str(num_for_parsing) {
92            Ok(number) => serde_json::Value::Number(number),
93            Err(_) => serde_json::Value::String(num.value.to_string()),
94          }
95        }
96      }
97      Value::Object(obj) => {
98        let mut map = serde_json::map::Map::new();
99        for prop in obj.properties {
100          map.insert(prop.name.into_string(), prop.value.into());
101        }
102        serde_json::Value::Object(map)
103      }
104      Value::StringLit(s) => serde_json::Value::String(s.value.into_owned()),
105    }
106  }
107}
108
109/// Node that can appear in the AST.
110#[derive(Debug, PartialEq, Clone, Copy)]
111pub enum Node<'a, 'b> {
112  StringLit(&'b StringLit<'a>),
113  NumberLit(&'b NumberLit<'a>),
114  BooleanLit(&'b BooleanLit),
115  Object(&'b Object<'a>),
116  ObjectProp(&'b ObjectProp<'a>),
117  Array(&'b Array<'a>),
118  NullKeyword(&'b NullKeyword),
119  WordLit(&'b WordLit<'a>),
120}
121
122impl<'a, 'b> Node<'a, 'b> {
123  /// Gets the node kind.
124  pub fn kind(&self) -> NodeKind {
125    match self {
126      Node::StringLit(_) => NodeKind::StringLit,
127      Node::NumberLit(_) => NodeKind::NumberLit,
128      Node::BooleanLit(_) => NodeKind::BooleanLit,
129      Node::Object(_) => NodeKind::Object,
130      Node::ObjectProp(_) => NodeKind::ObjectProp,
131      Node::Array(_) => NodeKind::Array,
132      Node::NullKeyword(_) => NodeKind::NullKeyword,
133      Node::WordLit(_) => NodeKind::WordLit,
134    }
135  }
136
137  pub fn as_string_lit(&self) -> Option<&'b StringLit<'a>> {
138    match self {
139      Node::StringLit(node) => Some(node),
140      _ => None,
141    }
142  }
143
144  pub fn as_number_lit(&self) -> Option<&'b NumberLit<'a>> {
145    match self {
146      Node::NumberLit(node) => Some(node),
147      _ => None,
148    }
149  }
150
151  pub fn as_boolean_lit(&self) -> Option<&'b BooleanLit> {
152    match self {
153      Node::BooleanLit(node) => Some(node),
154      _ => None,
155    }
156  }
157
158  pub fn as_object(&self) -> Option<&'b Object<'a>> {
159    match self {
160      Node::Object(node) => Some(node),
161      _ => None,
162    }
163  }
164
165  pub fn as_object_prop(&self) -> Option<&'b ObjectProp<'a>> {
166    match self {
167      Node::ObjectProp(node) => Some(node),
168      _ => None,
169    }
170  }
171
172  pub fn as_array(&self) -> Option<&'b Array<'a>> {
173    match self {
174      Node::Array(node) => Some(node),
175      _ => None,
176    }
177  }
178
179  pub fn as_null_keyword(&self) -> Option<&'b NullKeyword> {
180    match self {
181      Node::NullKeyword(node) => Some(node),
182      _ => None,
183    }
184  }
185
186  pub fn as_word_lit(&self) -> Option<&'b WordLit<'a>> {
187    match self {
188      Node::WordLit(node) => Some(node),
189      _ => None,
190    }
191  }
192}
193
194/// Kind of AST node.
195#[derive(Debug, PartialEq, Clone, Copy)]
196pub enum NodeKind {
197  StringLit,
198  NumberLit,
199  BooleanLit,
200  Object,
201  ObjectProp,
202  Array,
203  NullKeyword,
204  WordLit,
205}
206
207/// Node surrounded in double quotes (ex. `"my string"`).
208#[derive(Debug, PartialEq, Clone)]
209pub struct StringLit<'a> {
210  pub range: Range,
211  pub value: Cow<'a, str>,
212}
213
214/// A string that's not in quotes.
215/// Usually the appearance of this would be a parsing error.
216#[derive(Debug, PartialEq, Clone)]
217pub struct WordLit<'a> {
218  pub range: Range,
219  pub value: &'a str,
220}
221
222/// Represents a number (ex. `123`, `99.99`, `-1.2e+2`).
223#[derive(Debug, PartialEq, Clone)]
224pub struct NumberLit<'a> {
225  pub range: Range,
226  pub value: &'a str,
227}
228
229/// Represents a boolean (ex. `true` or `false`).
230#[derive(Debug, PartialEq, Clone)]
231pub struct BooleanLit {
232  pub range: Range,
233  pub value: bool,
234}
235
236/// Represents the null keyword (ex. `null`).
237#[derive(Debug, PartialEq, Clone)]
238pub struct NullKeyword {
239  pub range: Range,
240}
241
242/// Represents an object that may contain properties (ex. `{}`, `{ "prop": 4 }`).
243#[derive(Debug, PartialEq, Clone)]
244pub struct Object<'a> {
245  pub range: Range,
246  pub properties: Vec<ObjectProp<'a>>,
247}
248
249macro_rules! generate_take {
250  ($self:ident, $name:ident, $value_type:ident) => {
251    // there must be some better code that could be written here...
252    if let Some(pos) = $self.properties.iter().position(|p| p.name.as_str() == $name) {
253      if let Value::$value_type(_) = &$self.properties[pos].value {
254        if let Value::$value_type(node) = $self.properties.remove(pos).value {
255          Some(node)
256        } else {
257          None
258        }
259      } else {
260        None
261      }
262    } else {
263      None
264    }
265  };
266}
267
268macro_rules! generate_get {
269  ($self:ident, $name:ident, $value_type:ident) => {
270    $self
271      .properties
272      .iter()
273      .filter(|p| p.name.as_str() == $name)
274      .map(|p| {
275        if let Value::$value_type(node) = &p.value {
276          Some(node)
277        } else {
278          None
279        }
280      })
281      .next()
282      .flatten()
283  };
284}
285
286impl<'a> Object<'a> {
287  /// Gets a property value in the object by its name.
288  pub fn get(&self, name: &str) -> Option<&ObjectProp<'a>> {
289    self.properties.iter().find(|p| p.name.as_str() == name)
290  }
291
292  /// Gets a string property value from the object by name.
293  /// Returns `None` when not a string or it doesn't exist.
294  pub fn get_string(&self, name: &str) -> Option<&StringLit<'a>> {
295    generate_get!(self, name, StringLit)
296  }
297
298  /// Gets a number property value from the object by name.
299  /// Returns `None` when not a number or it doesn't exist.
300  pub fn get_number(&self, name: &str) -> Option<&NumberLit<'a>> {
301    generate_get!(self, name, NumberLit)
302  }
303
304  /// Gets a boolean property value from the object by name.
305  /// Returns `None` when not a boolean or it doesn't exist.
306  pub fn get_boolean(&self, name: &str) -> Option<&BooleanLit> {
307    generate_get!(self, name, BooleanLit)
308  }
309
310  /// Gets an object property value from the object by name.
311  /// Returns `None` when not an object or it doesn't exist.
312  pub fn get_object(&self, name: &str) -> Option<&Object<'a>> {
313    generate_get!(self, name, Object)
314  }
315
316  /// Gets an array property value from the object by name.
317  /// Returns `None` when not an array or it doesn't exist.
318  pub fn get_array(&self, name: &str) -> Option<&Array<'a>> {
319    generate_get!(self, name, Array)
320  }
321
322  /// Takes a value from the object by name.
323  /// Returns `None` when it doesn't exist.
324  pub fn take(&mut self, name: &str) -> Option<ObjectProp<'a>> {
325    if let Some(pos) = self.properties.iter().position(|p| p.name.as_str() == name) {
326      Some(self.properties.remove(pos))
327    } else {
328      None
329    }
330  }
331
332  /// Takes a string property value from the object by name.
333  /// Returns `None` when not a string or it doesn't exist.
334  pub fn take_string(&mut self, name: &str) -> Option<StringLit<'a>> {
335    generate_take!(self, name, StringLit)
336  }
337
338  /// Takes a number property value from the object by name.
339  /// Returns `None` when not a number or it doesn't exist.
340  pub fn take_number(&mut self, name: &str) -> Option<NumberLit<'a>> {
341    generate_take!(self, name, NumberLit)
342  }
343
344  /// Takes a boolean property value from the object by name.
345  /// Returns `None` when not a boolean or it doesn't exist.
346  pub fn take_boolean(&mut self, name: &str) -> Option<BooleanLit> {
347    generate_take!(self, name, BooleanLit)
348  }
349
350  /// Takes an object property value from the object by name.
351  /// Returns `None` when not an object or it doesn't exist.
352  pub fn take_object(&mut self, name: &str) -> Option<Object<'a>> {
353    generate_take!(self, name, Object)
354  }
355
356  /// Takes an array property value from the object by name.
357  /// Returns `None` when not an array or it doesn't exist.
358  pub fn take_array(&mut self, name: &str) -> Option<Array<'a>> {
359    generate_take!(self, name, Array)
360  }
361}
362
363/// Represents an object property (ex. `"prop": []`).
364#[derive(Debug, PartialEq, Clone)]
365pub struct ObjectProp<'a> {
366  pub range: Range,
367  pub name: ObjectPropName<'a>,
368  pub value: Value<'a>,
369}
370
371/// Represents an object property name that may or may not be in quotes.
372#[derive(Debug, PartialEq, Clone)]
373pub enum ObjectPropName<'a> {
374  String(StringLit<'a>),
375  Word(WordLit<'a>),
376}
377
378impl<'a> ObjectPropName<'a> {
379  /// Converts the object property name into a string.
380  pub fn into_string(self) -> String {
381    match self {
382      ObjectPropName::String(lit) => lit.value.into_owned(),
383      ObjectPropName::Word(lit) => lit.value.to_string(),
384    }
385  }
386
387  /// Gets the object property name as a string reference.
388  pub fn as_str(&'a self) -> &'a str {
389    match self {
390      ObjectPropName::String(lit) => lit.value.as_ref(),
391      ObjectPropName::Word(lit) => lit.value,
392    }
393  }
394}
395
396/// Represents an array that may contain elements (ex. `[]`, `[5, 6]`).
397#[derive(Debug, PartialEq, Clone)]
398pub struct Array<'a> {
399  pub range: Range,
400  pub elements: Vec<Value<'a>>,
401}
402
403/// Kind of JSONC comment.
404#[derive(Debug, PartialEq, Clone)]
405pub enum CommentKind {
406  Line,
407  Block,
408}
409
410/// JSONC comment.
411#[derive(Debug, PartialEq, Clone)]
412pub enum Comment<'a> {
413  Line(CommentLine<'a>),
414  Block(CommentBlock<'a>),
415}
416
417impl<'a> Comment<'a> {
418  /// Gets the text of the comment.
419  pub fn text(&self) -> &'a str {
420    match self {
421      Comment::Line(line) => line.text,
422      Comment::Block(line) => line.text,
423    }
424  }
425
426  /// Gets the comment kind.
427  pub fn kind(&self) -> CommentKind {
428    match self {
429      Comment::Line(_) => CommentKind::Line,
430      Comment::Block(_) => CommentKind::Block,
431    }
432  }
433}
434
435impl<'a> Ranged for Comment<'a> {
436  fn range(&self) -> Range {
437    match self {
438      Comment::Line(line) => line.range(),
439      Comment::Block(line) => line.range(),
440    }
441  }
442}
443
444/// Represents a comment line (ex. `// my comment`).
445#[derive(Debug, PartialEq, Clone)]
446pub struct CommentLine<'a> {
447  pub range: Range,
448  pub text: &'a str,
449}
450
451/// Represents a comment block (ex. `/* my comment */`).
452#[derive(Debug, PartialEq, Clone)]
453pub struct CommentBlock<'a> {
454  pub range: Range,
455  pub text: &'a str,
456}
457
458// Object Property Name
459
460impl<'a, 'b> From<&'b ObjectPropName<'a>> for Node<'a, 'b> {
461  fn from(object_prop_name: &'b ObjectPropName<'a>) -> Node<'a, 'b> {
462    match object_prop_name {
463      ObjectPropName::String(lit) => lit.into(),
464      ObjectPropName::Word(lit) => lit.into(),
465    }
466  }
467}
468
469impl<'a> Ranged for ObjectPropName<'a> {
470  fn range(&self) -> Range {
471    match self {
472      ObjectPropName::String(lit) => lit.range(),
473      ObjectPropName::Word(lit) => lit.range(),
474    }
475  }
476}
477
478// Implement Traits
479
480macro_rules! impl_ranged {
481  ($($node_name:ident),*) => {
482    $(
483      impl Ranged for $node_name {
484        fn range(&self) -> Range {
485            self.range
486        }
487      }
488    )*
489  };
490}
491
492impl_ranged![BooleanLit, NullKeyword];
493
494macro_rules! impl_ranged_lifetime {
495  ($($node_name:ident),*) => {
496    $(
497      impl<'a> Ranged for $node_name<'a> {
498        fn range(&self) -> Range {
499            self.range
500        }
501      }
502    )*
503  };
504}
505
506impl_ranged_lifetime![
507  WordLit,
508  Object,
509  ObjectProp,
510  Array,
511  CommentLine,
512  CommentBlock,
513  NumberLit,
514  StringLit
515];
516
517impl<'a> Ranged for Value<'a> {
518  fn range(&self) -> Range {
519    match self {
520      Value::Array(node) => node.range(),
521      Value::BooleanLit(node) => node.range(),
522      Value::NullKeyword(node) => node.range(),
523      Value::NumberLit(node) => node.range(),
524      Value::Object(node) => node.range(),
525      Value::StringLit(node) => node.range(),
526    }
527  }
528}
529
530impl<'a, 'b> Ranged for Node<'a, 'b> {
531  fn range(&self) -> Range {
532    match self {
533      Node::StringLit(node) => node.range(),
534      Node::NumberLit(node) => node.range(),
535      Node::BooleanLit(node) => node.range(),
536      Node::NullKeyword(node) => node.range(),
537      Node::WordLit(node) => node.range(),
538      Node::Array(node) => node.range(),
539      Node::Object(node) => node.range(),
540      Node::ObjectProp(node) => node.range(),
541    }
542  }
543}
544
545macro_rules! generate_node {
546    ($($node_name:ident),*) => {
547        $(
548        impl<'a, 'b> From<&'b $node_name> for Node<'a, 'b> {
549            fn from(node: &'b $node_name) -> Node<'a, 'b> {
550                Node::$node_name(node)
551            }
552        }
553        )*
554    };
555}
556
557generate_node![BooleanLit, NullKeyword];
558
559macro_rules! generate_node_lifetime {
560    ($($node_name:ident),*) => {
561
562        $(
563        impl<'a, 'b> From<&'b $node_name<'a>> for Node<'a, 'b> {
564            fn from(node: &'b $node_name<'a>) -> Node<'a, 'b> {
565                Node::$node_name(node)
566            }
567        }
568        )*
569    };
570}
571
572generate_node_lifetime![WordLit, Object, ObjectProp, Array, NumberLit, StringLit];
573
574impl<'a, 'b> From<&'b Value<'a>> for Node<'a, 'b> {
575  fn from(value: &'b Value<'a>) -> Node<'a, 'b> {
576    match value {
577      Value::Array(node) => Node::Array(node),
578      Value::BooleanLit(node) => Node::BooleanLit(node),
579      Value::NullKeyword(node) => Node::NullKeyword(node),
580      Value::NumberLit(node) => Node::NumberLit(node),
581      Value::Object(node) => Node::Object(node),
582      Value::StringLit(node) => Node::StringLit(node),
583    }
584  }
585}
586
587#[cfg(test)]
588mod test {
589  use super::*;
590  use crate::ParseOptions;
591  use crate::parse_to_ast;
592
593  #[test]
594  fn it_should_take() {
595    let ast = parse_to_ast(
596      "{'prop': 'asdf', 'other': 'text'}",
597      &Default::default(),
598      &ParseOptions::default(),
599    )
600    .unwrap();
601    let mut obj = match ast.value {
602      Some(Value::Object(obj)) => obj,
603      _ => unreachable!(),
604    };
605
606    assert_eq!(obj.properties.len(), 2);
607    assert_eq!(obj.take_string("asdf"), None);
608    assert_eq!(obj.properties.len(), 2);
609    assert_eq!(obj.take_number("prop"), None);
610    assert_eq!(obj.properties.len(), 2);
611    assert!(obj.take_string("prop").is_some());
612    assert_eq!(obj.properties.len(), 1);
613    assert_eq!(obj.take("something"), None);
614    assert_eq!(obj.properties.len(), 1);
615    assert!(obj.take("other").is_some());
616    assert_eq!(obj.properties.len(), 0);
617  }
618
619  #[test]
620  fn it_should_get() {
621    let ast = parse_to_ast("{'prop': 'asdf'}", &Default::default(), &ParseOptions::default()).unwrap();
622    let obj = match ast.value {
623      Some(Value::Object(obj)) => obj,
624      _ => unreachable!(),
625    };
626
627    assert_eq!(obj.properties.len(), 1);
628    assert_eq!(obj.get_string("asdf"), None);
629    assert!(obj.get_string("prop").is_some());
630    assert_eq!(obj.get("asdf"), None);
631    assert_eq!(obj.properties.len(), 1);
632  }
633
634  #[cfg(feature = "serde")]
635  #[test]
636  fn it_should_coerce_to_serde_value() {
637    let ast = parse_to_ast(
638      r#"{"prop":[true,1,null,"str"]}"#,
639      &Default::default(),
640      &ParseOptions::default(),
641    )
642    .unwrap();
643    let value = ast.value.unwrap();
644    let serde_value: serde_json::Value = value.into();
645
646    assert_eq!(
647      serde_value,
648      serde_json::json!({
649        "prop": [
650          true,
651          1,
652          null,
653          "str"
654        ]
655      })
656    );
657  }
658
659  #[cfg(feature = "serde")]
660  #[test]
661  fn handle_weird_data() {
662    let ast = parse_to_ast(
663      r#"{eyyyyyyy:6yy:6000e000615yyyk:6}"#,
664      &Default::default(),
665      &ParseOptions::default(),
666    )
667    .unwrap();
668    let value = ast.value.unwrap();
669    let serde_value: serde_json::Value = value.into();
670
671    assert_eq!(
672      serde_value,
673      // this output is fine because the input is bad
674      serde_json::json!({
675        "eyyyyyyy": 6,
676        "yy": "6000e000615",
677        "yyyk": 6
678      })
679    );
680  }
681}