Skip to main content

jsonc_parser/cst/
mod.rs

1//! CST for manipulating JSONC.
2//!
3//! Unlike the AST, this keeps every comment and every piece of whitespace, so a document can be
4//! edited and written back out with everything the author wrote still in place.
5//!
6//! # Example
7//!
8//! ```
9//! use jsonc_parser::cst::CstRootNode;
10//! use jsonc_parser::ParseOptions;
11//! use jsonc_parser::json;
12//!
13//! let json_text = r#"{
14//!   // comment
15//!   "data": 123
16//! }"#;
17//!
18//! let root = CstRootNode::parse(json_text, &ParseOptions::default()).unwrap();
19//! let root_obj = root.object_value_or_set();
20//!
21//! root_obj.get("data").unwrap().set_value(json!({
22//!   "nested": true
23//! }));
24//! root_obj.append("new_key", json!([456, 789, false]));
25//!
26//! assert_eq!(root.to_string(), r#"{
27//!   // comment
28//!   "data": {
29//!     "nested": true
30//!   },
31//!   "new_key": [456, 789, false]
32//! }"#);
33//! ```
34//!
35
36use std::cell::RefCell;
37use std::cmp::Ordering;
38use std::collections::VecDeque;
39use std::fmt::Display;
40use std::iter::Peekable;
41use std::ops::Range;
42use std::rc::Rc;
43use std::rc::Weak;
44
45use super::common::Ranged;
46use crate::ParseOptions;
47use crate::ast;
48use crate::errors::ParseError;
49use crate::parse_to_ast;
50use crate::string::ParseStringErrorKind;
51
52mod input;
53
54pub use input::*;
55
56macro_rules! add_root_node_method {
57  () => {
58    /// Gets the root node.
59    ///
60    /// Returns `None` if this node has become disconnected from
61    /// the tree by being removed.
62    pub fn root_node(&self) -> Option<CstRootNode> {
63      self
64        .ancestors()
65        .filter_map(|parent| match parent {
66          CstContainerNode::Root(node) => Some(node),
67          _ => None,
68        })
69        .next()
70    }
71  };
72}
73
74macro_rules! add_parent_info_methods {
75  () => {
76    /// Parent of the node.
77    ///
78    /// Returns `None` if this node has become disconnected from
79    /// the tree by being removed.
80    pub fn parent(&self) -> Option<CstContainerNode> {
81      self.parent_info().map(|p| p.parent.as_container_node())
82    }
83
84    /// An iterator of ancestors of this node.
85    pub fn ancestors(&self) -> impl Iterator<Item = CstContainerNode> {
86      AncestorIterator::new(self.clone().into())
87    }
88
89    /// Current child index of the node within the children of the
90    /// parent node.
91    pub fn child_index(&self) -> usize {
92      self.parent_info().map(|p| p.child_index).unwrap_or(0)
93    }
94
95    /// Node that comes before this one that shares the same parent.
96    pub fn previous_sibling(&self) -> Option<CstNode> {
97      let parent_info = self.parent_info()?;
98      if parent_info.child_index == 0 {
99        return None;
100      }
101      parent_info
102        .parent
103        .as_container_node()
104        .child_at_index(parent_info.child_index - 1)
105    }
106
107    /// Siblings coming before this node. This does not
108    /// include cousins.
109    pub fn previous_siblings(&self) -> impl Iterator<Item = CstNode> {
110      PreviousSiblingIterator::new(self.clone().into())
111    }
112
113    /// Node that comes after this one that shares the same parent.
114    pub fn next_sibling(&self) -> Option<CstNode> {
115      let parent_info = self.parent_info()?;
116      parent_info
117        .parent
118        .as_container_node()
119        .child_at_index(parent_info.child_index + 1)
120    }
121
122    /// Siblings coming after this node. This does not
123    /// include cousins.
124    pub fn next_siblings(&self) -> impl Iterator<Item = CstNode> {
125      NextSiblingIterator::new(self.clone().into())
126    }
127
128    /// Returns the indentation text if it can be determined.
129    pub fn indent_text(&self) -> Option<String> {
130      indent_text(&self.clone().into())
131    }
132
133    /// Whether a blank line separates this node, and the comments written above it, from
134    /// whatever came before them.
135    pub fn has_blank_line_before(&self) -> bool {
136      has_blank_line_before(&self.clone().into())
137    }
138
139    /// Gets the trailing comma token of the node, if it exists.
140    pub fn trailing_comma(&self) -> Option<CstToken> {
141      find_trailing_comma(&self.clone().into())
142    }
143
144    /// Infers if the node or appropriate ancestor uses trailing commas.
145    pub fn uses_trailing_commas(&self) -> bool {
146      uses_trailing_commas(self.clone().into())
147    }
148  };
149}
150
151/// Whether a blank line separates the node, and the comments written above it, from what came
152/// before them.
153fn has_blank_line_before(node: &CstNode) -> bool {
154  has_blank_line(node.previous_siblings().take_while(|n| n.is_trivia()))
155}
156
157fn find_trailing_comma(node: &CstNode) -> Option<CstToken> {
158  for next_sibling in node.next_siblings() {
159    match next_sibling {
160      CstNode::Container(_) => return None,
161      CstNode::Leaf(leaf) => match leaf {
162        CstLeafNode::BooleanLit(_)
163        | CstLeafNode::NullKeyword(_)
164        | CstLeafNode::NumberLit(_)
165        | CstLeafNode::StringLit(_)
166        | CstLeafNode::WordLit(_) => return None,
167        CstLeafNode::Token(token) => {
168          if token.value() == ',' {
169            return Some(token);
170          } else {
171            return None;
172          }
173        }
174        CstLeafNode::Whitespace(_) | CstLeafNode::Newline(_) | CstLeafNode::Comment(_) => {
175          // skip over
176        }
177      },
178    }
179  }
180
181  None
182}
183
184macro_rules! add_parent_methods {
185  () => {
186    add_parent_info_methods!();
187
188    fn parent_info(&self) -> Option<ParentInfo> {
189      self.0.borrow().parent.clone()
190    }
191
192    fn set_parent(&self, parent: Option<ParentInfo>) {
193      self.0.borrow_mut().parent = parent;
194    }
195  };
196}
197
198macro_rules! impl_from_leaf_or_container {
199  ($node_name:ident, $variant:ident, $leaf_or_container:ident, $leaf_or_container_variant:ident) => {
200    impl From<$node_name> for CstNode {
201      fn from(value: $node_name) -> Self {
202        CstNode::$leaf_or_container_variant($leaf_or_container::$variant(value))
203      }
204    }
205
206    impl From<$node_name> for $leaf_or_container {
207      fn from(value: $node_name) -> Self {
208        $leaf_or_container::$variant(value)
209      }
210    }
211  };
212}
213
214macro_rules! impl_container_methods {
215  ($node_name:ident, $variant:ident) => {
216    impl_from_leaf_or_container!($node_name, $variant, CstContainerNode, Container);
217
218    impl $node_name {
219      add_parent_methods!();
220
221      /// Children of the current node.
222      pub fn children(&self) -> Vec<CstNode> {
223        self.0.borrow().value.clone()
224      }
225
226      /// Children of the current node excluding comments, whitespace, newlines, and tokens.
227      pub fn children_exclude_trivia_and_tokens(&self) -> Vec<CstNode> {
228        self
229          .0
230          .borrow()
231          .value
232          .iter()
233          .filter(|n| !n.is_trivia() && !n.is_token())
234          .cloned()
235          .collect()
236      }
237
238      /// Gets the child at the specified index.
239      pub fn child_at_index(&self, index: usize) -> Option<CstNode> {
240        self.0.borrow().value.get(index).cloned()
241      }
242
243      fn remove_child_set_no_parent(&self, index: usize) {
244        let mut inner = self.0.borrow_mut();
245        if index < inner.value.len() {
246          let container = self.clone().into();
247          let child = inner.value.remove(index);
248          child.set_parent(None);
249
250          // update the index of the remaining children
251          for index in index..inner.value.len() {
252            inner.value[index].set_parent(Some(ParentInfo {
253              parent: WeakParent::from_container(&container),
254              child_index: index,
255            }));
256          }
257        }
258      }
259    }
260  };
261}
262
263macro_rules! impl_leaf_methods {
264  ($node_name:ident, $variant:ident) => {
265    impl_from_leaf_or_container!($node_name, $variant, CstLeafNode, Leaf);
266
267    impl $node_name {
268      add_parent_methods!();
269      add_root_node_method!();
270    }
271  };
272}
273
274#[derive(Debug, Clone)]
275enum WeakParent {
276  Root(Weak<CstRootNodeInner>),
277  Object(Weak<CstObjectInner>),
278  ObjectProp(Weak<CstObjectPropInner>),
279  Array(Weak<CstArrayInner>),
280}
281
282impl WeakParent {
283  pub fn from_container(container: &CstContainerNode) -> Self {
284    match container {
285      CstContainerNode::Root(node) => WeakParent::Root(Rc::downgrade(&node.0)),
286      CstContainerNode::Object(node) => WeakParent::Object(Rc::downgrade(&node.0)),
287      CstContainerNode::ObjectProp(node) => WeakParent::ObjectProp(Rc::downgrade(&node.0)),
288      CstContainerNode::Array(node) => WeakParent::Array(Rc::downgrade(&node.0)),
289    }
290  }
291
292  pub fn as_container_node(&self) -> CstContainerNode {
293    // It's much better to panic here to let the developer know an ancestor has been
294    // lost due to being dropped because if we did something like returning None then
295    // it might create strange bugs that are hard to track down.
296    const PANIC_MSG: &str = "Programming error. Ensure you keep around the RootNode for the duration of using the CST.";
297    match self {
298      WeakParent::Root(weak) => CstRootNode(weak.upgrade().expect(PANIC_MSG)).into(),
299      WeakParent::Object(weak) => CstObject(weak.upgrade().expect(PANIC_MSG)).into(),
300      WeakParent::ObjectProp(weak) => CstObjectProp(weak.upgrade().expect(PANIC_MSG)).into(),
301      WeakParent::Array(weak) => CstArray(weak.upgrade().expect(PANIC_MSG)).into(),
302    }
303  }
304}
305
306#[derive(Clone, Debug)]
307struct ParentInfo {
308  pub parent: WeakParent,
309  pub child_index: usize,
310}
311
312#[derive(Debug)]
313struct CstValueInner<T> {
314  parent: Option<ParentInfo>,
315  value: T,
316}
317
318impl<T> CstValueInner<T> {
319  fn new(value: T) -> Rc<RefCell<Self>> {
320    Rc::new(RefCell::new(CstValueInner { parent: None, value }))
321  }
322}
323
324type CstChildrenInner = CstValueInner<Vec<CstNode>>;
325
326/// All the different kinds of nodes that can appear in the CST.
327#[derive(Debug, Clone)]
328pub enum CstNode {
329  Container(CstContainerNode),
330  Leaf(CstLeafNode),
331}
332
333impl CstNode {
334  add_parent_info_methods!();
335  add_root_node_method!();
336
337  /// Gets if this node is comments, whitespace, newlines, or a non-literal token (ex. brace, colon).
338  pub fn is_trivia(&self) -> bool {
339    match self {
340      CstNode::Leaf(leaf) => match leaf {
341        CstLeafNode::BooleanLit(_)
342        | CstLeafNode::NullKeyword(_)
343        | CstLeafNode::NumberLit(_)
344        | CstLeafNode::StringLit(_)
345        | CstLeafNode::Token(_)
346        | CstLeafNode::WordLit(_) => false,
347        CstLeafNode::Whitespace(_) | CstLeafNode::Newline(_) | CstLeafNode::Comment(_) => true,
348      },
349      CstNode::Container(_) => false,
350    }
351  }
352
353  /// Comments that become before this one on the same line.
354  pub fn leading_comments_same_line(&self) -> impl Iterator<Item = CstComment> {
355    self
356      .previous_siblings()
357      .take_while(|n| n.is_whitespace() || n.is_comment())
358      .filter_map(|n| match n {
359        CstNode::Leaf(CstLeafNode::Comment(comment)) => Some(comment.clone()),
360        _ => None,
361      })
362  }
363
364  /// Comments that come after this one on the same line.
365  ///
366  /// Only returns owned trailing comments on the same line and not if owned by the next node.
367  pub fn trailing_comments_same_line(&self) -> impl Iterator<Item = CstComment> {
368    // ensure the trailing comments are owned
369    for sibling in self.next_siblings() {
370      if sibling.is_newline() {
371        break;
372      } else if !sibling.is_comment() && !sibling.is_whitespace() {
373        return Box::new(std::iter::empty()) as Box<dyn Iterator<Item = CstComment>>;
374      }
375    }
376
377    Box::new(
378      self
379        .next_siblings()
380        .take_while(|n| n.is_whitespace() || n.is_comment())
381        .filter_map(|n| match n {
382          CstNode::Leaf(CstLeafNode::Comment(comment)) => Some(comment.clone()),
383          _ => None,
384        }),
385    )
386  }
387
388  /// If this node is a newline.
389  pub fn is_newline(&self) -> bool {
390    matches!(self, CstNode::Leaf(CstLeafNode::Newline(_)))
391  }
392
393  /// If this node is a comma.
394  pub fn is_comma(&self) -> bool {
395    match self {
396      CstNode::Leaf(CstLeafNode::Token(t)) => t.value() == ',',
397      _ => false,
398    }
399  }
400
401  /// If this node is a comment.
402  pub fn is_comment(&self) -> bool {
403    matches!(self, CstNode::Leaf(CstLeafNode::Comment(_)))
404  }
405
406  /// If this node is a token.
407  pub fn is_token(&self) -> bool {
408    matches!(self, CstNode::Leaf(CstLeafNode::Token(_)))
409  }
410
411  /// If this node is whitespace.
412  pub fn is_whitespace(&self) -> bool {
413    matches!(self, CstNode::Leaf(CstLeafNode::Whitespace(_)))
414  }
415
416  /// Token char of the node if it's a token.
417  pub fn token_char(&self) -> Option<char> {
418    match self {
419      CstNode::Leaf(CstLeafNode::Token(token)) => Some(token.value()),
420      _ => None,
421    }
422  }
423
424  /// Children of this node.
425  pub fn children(&self) -> Vec<CstNode> {
426    match self {
427      CstNode::Container(n) => n.children(),
428      CstNode::Leaf(_) => Vec::new(),
429    }
430  }
431
432  /// Children of the current node excluding comments, whitespace, newlines, and tokens.
433  pub fn children_exclude_trivia_and_tokens(&self) -> Vec<CstNode> {
434    match self {
435      CstNode::Container(n) => n.children_exclude_trivia_and_tokens(),
436      CstNode::Leaf(_) => Vec::new(),
437    }
438  }
439
440  /// Child at the specified index.
441  pub fn child_at_index(&self, index: usize) -> Option<CstNode> {
442    match self {
443      CstNode::Container(n) => n.child_at_index(index),
444      CstNode::Leaf(_) => None,
445    }
446  }
447
448  /// Gets the array element index of this node if its parent is an array.
449  ///
450  /// Returns `None` when the parent is not an array.
451  pub fn element_index(&self) -> Option<usize> {
452    let child_index = self.child_index();
453    let array = self.parent()?.as_array()?;
454    array.elements().iter().position(|p| p.child_index() == child_index)
455  }
456
457  /// Node if it's the root node.
458  pub fn as_root_node(&self) -> Option<CstRootNode> {
459    match self {
460      CstNode::Container(CstContainerNode::Root(node)) => Some(node.clone()),
461      _ => None,
462    }
463  }
464
465  /// Node if it's an object.
466  pub fn as_object(&self) -> Option<CstObject> {
467    match self {
468      // doesn't return a reference so this is easier to use
469      CstNode::Container(CstContainerNode::Object(node)) => Some(node.clone()),
470      _ => None,
471    }
472  }
473
474  /// Node if it's an array.
475  pub fn as_array(&self) -> Option<CstArray> {
476    match self {
477      CstNode::Container(CstContainerNode::Array(node)) => Some(node.clone()),
478      _ => None,
479    }
480  }
481
482  /// Node if it's an object property.
483  pub fn as_object_prop(&self) -> Option<CstObjectProp> {
484    match self {
485      CstNode::Container(CstContainerNode::ObjectProp(node)) => Some(node.clone()),
486      _ => None,
487    }
488  }
489
490  /// Node if it's a boolean literal.
491  pub fn as_boolean_lit(&self) -> Option<CstBooleanLit> {
492    match self {
493      CstNode::Leaf(CstLeafNode::BooleanLit(node)) => Some(node.clone()),
494      _ => None,
495    }
496  }
497
498  /// Node if it's a null keyword.
499  pub fn as_null_keyword(&self) -> Option<CstNullKeyword> {
500    match self {
501      CstNode::Leaf(CstLeafNode::NullKeyword(node)) => Some(node.clone()),
502      _ => None,
503    }
504  }
505
506  /// Node if it's a number literal.
507  pub fn as_number_lit(&self) -> Option<CstNumberLit> {
508    match self {
509      CstNode::Leaf(CstLeafNode::NumberLit(node)) => Some(node.clone()),
510      _ => None,
511    }
512  }
513
514  /// Node if it's a string literal.
515  pub fn as_string_lit(&self) -> Option<CstStringLit> {
516    match self {
517      CstNode::Leaf(CstLeafNode::StringLit(node)) => Some(node.clone()),
518      _ => None,
519    }
520  }
521
522  /// Node if it's a word literal.
523  pub fn as_word_lit(&self) -> Option<CstWordLit> {
524    match self {
525      CstNode::Leaf(CstLeafNode::WordLit(node)) => Some(node.clone()),
526      _ => None,
527    }
528  }
529
530  /// Node if it's a token.
531  pub fn as_token(&self) -> Option<CstToken> {
532    match self {
533      CstNode::Leaf(CstLeafNode::Token(node)) => Some(node.clone()),
534      _ => None,
535    }
536  }
537
538  /// Node if it's a newline.
539  pub fn as_newline(&self) -> Option<CstNewline> {
540    match self {
541      CstNode::Leaf(CstLeafNode::Newline(node)) => Some(node.clone()),
542      _ => None,
543    }
544  }
545
546  /// Node if it's whitespace.
547  pub fn as_whitespace(&self) -> Option<CstWhitespace> {
548    match self {
549      CstNode::Leaf(CstLeafNode::Whitespace(node)) => Some(node.clone()),
550      _ => None,
551    }
552  }
553
554  /// Node if it's a comment.
555  pub fn as_comment(&self) -> Option<CstComment> {
556    match self {
557      CstNode::Leaf(CstLeafNode::Comment(node)) => Some(node.clone()),
558      _ => None,
559    }
560  }
561
562  /// Removes the node from the JSON.
563  ///
564  /// Note: Removing certain nodes may cause syntax errors.
565  pub fn remove(self) {
566    match self {
567      CstNode::Container(n) => n.remove(),
568      CstNode::Leaf(n) => n.remove(),
569    }
570  }
571
572  fn parent_info(&self) -> Option<ParentInfo> {
573    match self {
574      CstNode::Container(node) => node.parent_info(),
575      CstNode::Leaf(node) => node.parent_info(),
576    }
577  }
578
579  fn set_parent(&self, parent: Option<ParentInfo>) {
580    match self {
581      CstNode::Container(node) => node.set_parent(parent),
582      CstNode::Leaf(node) => node.set_parent(parent),
583    }
584  }
585
586  /// Removes the node from the tree without making adjustments to any siblings.
587  fn remove_raw(self) {
588    let Some(parent_info) = self.parent_info() else {
589      return; // already removed
590    };
591    parent_info
592      .parent
593      .as_container_node()
594      .remove_child_set_no_parent(parent_info.child_index);
595  }
596
597  /// Converts a CST node to a `serde_json::Value`.
598  ///
599  /// This method extracts the actual value from the CST node, ignoring
600  /// trivia (comments, whitespace, etc.).
601  ///
602  /// Returns `None` if the node is trivia or cannot be converted to a value.
603  ///
604  /// # Example
605  ///
606  /// ```
607  /// use jsonc_parser::cst::CstRootNode;
608  /// use jsonc_parser::ParseOptions;
609  ///
610  /// let json_text = r#"{ "test": 5 } // comment"#;
611  /// let root = CstRootNode::parse(json_text, &ParseOptions::default()).unwrap();
612  ///
613  /// if let Some(value_node) = root.value() {
614  ///   let json_value = value_node.to_serde_value().unwrap();
615  ///   println!("{}", json_value);
616  /// }
617  /// ```
618  #[cfg(feature = "serde_json")]
619  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
620    match self {
621      CstNode::Container(container) => container.to_serde_value(),
622      CstNode::Leaf(leaf) => leaf.to_serde_value(),
623    }
624  }
625}
626
627impl Display for CstNode {
628  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
629    match self {
630      CstNode::Container(node) => node.fmt(f),
631      CstNode::Leaf(node) => node.fmt(f),
632    }
633  }
634}
635
636#[derive(Default, Debug, Clone)]
637struct StyleInfo {
638  pub uses_trailing_commas: bool,
639  pub newline_kind: CstNewlineKind,
640}
641
642/// Enumeration of a node that has children.
643#[derive(Debug, Clone)]
644pub enum CstContainerNode {
645  Root(CstRootNode),
646  Array(CstArray),
647  Object(CstObject),
648  ObjectProp(CstObjectProp),
649}
650
651impl CstContainerNode {
652  add_parent_info_methods!();
653  add_root_node_method!();
654
655  /// If this is the root node.
656  pub fn is_root(&self) -> bool {
657    matches!(self, CstContainerNode::Root(_))
658  }
659
660  /// If this is an array node.
661  pub fn is_array(&self) -> bool {
662    matches!(self, CstContainerNode::Array(_))
663  }
664
665  /// If this is an object node.
666  pub fn is_object(&self) -> bool {
667    matches!(self, CstContainerNode::Object(_))
668  }
669
670  /// If this is an object property node.
671  pub fn is_object_prop(&self) -> bool {
672    matches!(self, CstContainerNode::ObjectProp(_))
673  }
674
675  /// Node if it's the root node.
676  pub fn as_root(&self) -> Option<CstRootNode> {
677    match self {
678      CstContainerNode::Root(node) => Some(node.clone()),
679      _ => None,
680    }
681  }
682
683  /// Node if it's an array.
684  pub fn as_array(&self) -> Option<CstArray> {
685    match self {
686      CstContainerNode::Array(node) => Some(node.clone()),
687      _ => None,
688    }
689  }
690
691  /// Node if it's an object.
692  pub fn as_object(&self) -> Option<CstObject> {
693    match self {
694      CstContainerNode::Object(node) => Some(node.clone()),
695      _ => None,
696    }
697  }
698
699  /// Node if it's an object property.
700  pub fn as_object_prop(&self) -> Option<CstObjectProp> {
701    match self {
702      CstContainerNode::ObjectProp(node) => Some(node.clone()),
703      _ => None,
704    }
705  }
706
707  /// Children of the node.
708  pub fn children(&self) -> Vec<CstNode> {
709    match self {
710      CstContainerNode::Root(n) => n.children(),
711      CstContainerNode::Object(n) => n.children(),
712      CstContainerNode::ObjectProp(n) => n.children(),
713      CstContainerNode::Array(n) => n.children(),
714    }
715  }
716
717  /// Children of the current node excluding comments, whitespace, newlines, and tokens.
718  pub fn children_exclude_trivia_and_tokens(&self) -> Vec<CstNode> {
719    match self {
720      CstContainerNode::Root(n) => n.children_exclude_trivia_and_tokens(),
721      CstContainerNode::Object(n) => n.children_exclude_trivia_and_tokens(),
722      CstContainerNode::ObjectProp(n) => n.children_exclude_trivia_and_tokens(),
723      CstContainerNode::Array(n) => n.children_exclude_trivia_and_tokens(),
724    }
725  }
726
727  /// Child at the specified index.
728  pub fn child_at_index(&self, index: usize) -> Option<CstNode> {
729    match self {
730      CstContainerNode::Root(node) => node.child_at_index(index),
731      CstContainerNode::Object(node) => node.child_at_index(index),
732      CstContainerNode::ObjectProp(node) => node.child_at_index(index),
733      CstContainerNode::Array(node) => node.child_at_index(index),
734    }
735  }
736
737  fn remove_child_set_no_parent(&self, index: usize) {
738    match self {
739      CstContainerNode::Root(n) => n.remove_child_set_no_parent(index),
740      CstContainerNode::Object(n) => n.remove_child_set_no_parent(index),
741      CstContainerNode::ObjectProp(n) => n.remove_child_set_no_parent(index),
742      CstContainerNode::Array(n) => n.remove_child_set_no_parent(index),
743    }
744  }
745
746  /// Removes the node from the JSON.
747  pub fn remove(self) {
748    match self {
749      CstContainerNode::Root(n) => n.clear_children(),
750      CstContainerNode::Object(n) => n.remove(),
751      CstContainerNode::ObjectProp(n) => n.remove(),
752      CstContainerNode::Array(n) => n.remove(),
753    }
754  }
755
756  fn parent_info(&self) -> Option<ParentInfo> {
757    match self {
758      CstContainerNode::Root(node) => node.parent_info(),
759      CstContainerNode::Object(node) => node.parent_info(),
760      CstContainerNode::ObjectProp(node) => node.parent_info(),
761      CstContainerNode::Array(node) => node.parent_info(),
762    }
763  }
764
765  fn set_parent(&self, parent: Option<ParentInfo>) {
766    match self {
767      CstContainerNode::Root(node) => node.set_parent(parent),
768      CstContainerNode::Object(node) => node.set_parent(parent),
769      CstContainerNode::ObjectProp(node) => node.set_parent(parent),
770      CstContainerNode::Array(node) => node.set_parent(parent),
771    }
772  }
773
774  #[inline(always)]
775  fn raw_append_child(&self, child: CstNode) {
776    self.raw_insert_child(None, child);
777  }
778
779  #[inline(always)]
780  fn raw_insert_child(&self, index: Option<&mut usize>, child: CstNode) {
781    self.raw_insert_children(index, vec![child]);
782  }
783
784  #[inline(always)]
785  fn raw_append_children(&self, children: Vec<CstNode>) {
786    self.raw_insert_children(None, children);
787  }
788
789  /// Replaces every child of this container, reparenting the new children.
790  fn raw_set_children(&self, children: Vec<CstNode>) {
791    let weak_parent = WeakParent::from_container(self);
792    let mut container = match self {
793      CstContainerNode::Root(node) => node.0.borrow_mut(),
794      CstContainerNode::Object(node) => node.0.borrow_mut(),
795      CstContainerNode::ObjectProp(node) => node.0.borrow_mut(),
796      CstContainerNode::Array(node) => node.0.borrow_mut(),
797    };
798    // a child that isn't in the new list has left the tree, so it loses its parent
799    for child in &container.value {
800      child.set_parent(None);
801    }
802    container.value = children;
803    for (i, child) in container.value.iter().enumerate() {
804      child.set_parent(Some(ParentInfo {
805        parent: weak_parent.clone(),
806        child_index: i,
807      }));
808    }
809  }
810
811  fn raw_insert_children(&self, index: Option<&mut usize>, children: Vec<CstNode>) {
812    if children.is_empty() {
813      return;
814    }
815
816    let weak_parent = WeakParent::from_container(self);
817    let mut container = match self {
818      CstContainerNode::Root(node) => node.0.borrow_mut(),
819      CstContainerNode::Object(node) => node.0.borrow_mut(),
820      CstContainerNode::ObjectProp(node) => node.0.borrow_mut(),
821      CstContainerNode::Array(node) => node.0.borrow_mut(),
822    };
823    let insert_index = index.as_ref().map(|i| **i).unwrap_or(container.value.len());
824    if let Some(i) = index {
825      *i += children.len();
826    }
827    container.value.splice(insert_index..insert_index, children);
828
829    // update the child index of all the nodes
830    for (i, child) in container.value.iter().enumerate().skip(insert_index) {
831      child.set_parent(Some(ParentInfo {
832        parent: weak_parent.clone(),
833        child_index: i,
834      }));
835    }
836  }
837
838  fn raw_insert_value_with_internal_indent(
839    &self,
840    insert_index: Option<&mut usize>,
841    value: InsertValue,
842    style_info: &StyleInfo,
843    indents: &Indents,
844  ) {
845    match value {
846      InsertValue::Value(value) => {
847        let is_multiline = value.force_multiline();
848        match value {
849          CstInputValue::Null => {
850            self.raw_insert_child(insert_index, CstLeafNode::NullKeyword(CstNullKeyword::new()).into());
851          }
852          CstInputValue::Bool(value) => {
853            self.raw_insert_child(insert_index, CstLeafNode::BooleanLit(CstBooleanLit::new(value)).into());
854          }
855          CstInputValue::Number(value) => {
856            self.raw_insert_child(insert_index, CstLeafNode::NumberLit(CstNumberLit::new(value)).into());
857          }
858          CstInputValue::String(value) => {
859            self.raw_insert_child(
860              insert_index,
861              CstLeafNode::StringLit(CstStringLit::new_escaped(&value)).into(),
862            );
863          }
864          CstInputValue::Array(elements) => {
865            let array_node: CstContainerNode = CstArray::new_no_tokens().into();
866            self.raw_insert_child(insert_index, array_node.clone().into());
867
868            array_node.raw_append_child(CstToken::new('[').into());
869            if !elements.is_empty() {
870              let indents = indents.indent();
871              let mut elements = elements.into_iter().peekable();
872              while let Some(value) = elements.next() {
873                if is_multiline {
874                  array_node.raw_insert_children(
875                    None,
876                    vec![
877                      CstNewline::new(style_info.newline_kind).into(),
878                      CstWhitespace::new(indents.current_indent.clone()).into(),
879                    ],
880                  );
881                }
882
883                array_node.raw_insert_value_with_internal_indent(None, InsertValue::Value(value), style_info, &indents);
884
885                if style_info.uses_trailing_commas && is_multiline || elements.peek().is_some() {
886                  if is_multiline {
887                    array_node.raw_append_child(CstToken::new(',').into());
888                  } else {
889                    array_node.raw_insert_children(
890                      None,
891                      vec![CstToken::new(',').into(), CstWhitespace::new(" ".to_string()).into()],
892                    );
893                  }
894                }
895              }
896            }
897
898            if is_multiline {
899              array_node.raw_append_children(vec![
900                CstNewline::new(style_info.newline_kind).into(),
901                CstWhitespace::new(indents.current_indent.clone()).into(),
902              ]);
903            }
904
905            array_node.raw_append_child(CstToken::new(']').into());
906          }
907          CstInputValue::Object(properties) => {
908            let object_node: CstContainerNode = CstObject::new_no_tokens().into();
909            self.raw_insert_child(insert_index, object_node.clone().into());
910
911            object_node.raw_append_child(CstToken::new('{').into());
912
913            if !properties.is_empty() {
914              {
915                let indents = indents.indent();
916                let mut properties = properties.into_iter().peekable();
917                while let Some((prop_name, value)) = properties.next() {
918                  object_node.raw_append_child(CstNewline::new(style_info.newline_kind).into());
919                  object_node.raw_append_child(CstWhitespace::new(indents.current_indent.clone()).into());
920                  object_node.raw_insert_value_with_internal_indent(
921                    None,
922                    InsertValue::Property(&prop_name, value),
923                    style_info,
924                    &indents,
925                  );
926                  if style_info.uses_trailing_commas || properties.peek().is_some() {
927                    object_node.raw_append_child(CstToken::new(',').into());
928                  }
929                }
930              }
931
932              object_node.raw_append_children(vec![
933                CstNewline::new(style_info.newline_kind).into(),
934                CstWhitespace::new(indents.current_indent.clone()).into(),
935              ]);
936            }
937
938            object_node.raw_append_child(CstToken::new('}').into());
939          }
940        }
941      }
942      InsertValue::Property(prop_name, value) => {
943        let prop = CstContainerNode::ObjectProp(CstObjectProp::new());
944        self.raw_insert_child(insert_index, prop.clone().into());
945        prop.raw_insert_children(
946          None,
947          vec![
948            CstStringLit::new_escaped(prop_name).into(),
949            CstToken::new(':').into(),
950            CstWhitespace::new(" ".to_string()).into(),
951          ],
952        );
953        prop.raw_insert_value_with_internal_indent(None, InsertValue::Value(value), style_info, indents);
954      }
955    }
956  }
957
958  /// Converts a CST container node to a `serde_json::Value`.
959  ///
960  /// Returns `None` if the node cannot be converted to a value.
961  #[cfg(feature = "serde_json")]
962  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
963    match self {
964      CstContainerNode::Root(node) => node.to_serde_value(),
965      CstContainerNode::Array(node) => node.to_serde_value(),
966      CstContainerNode::Object(node) => node.to_serde_value(),
967      CstContainerNode::ObjectProp(node) => node.to_serde_value(),
968    }
969  }
970}
971
972impl From<CstContainerNode> for CstNode {
973  fn from(value: CstContainerNode) -> Self {
974    CstNode::Container(value)
975  }
976}
977
978impl Display for CstContainerNode {
979  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
980    match self {
981      CstContainerNode::Root(node) => node.fmt(f),
982      CstContainerNode::Object(node) => node.fmt(f),
983      CstContainerNode::ObjectProp(node) => node.fmt(f),
984      CstContainerNode::Array(node) => node.fmt(f),
985    }
986  }
987}
988
989/// Enumeration of a node that has no children.
990#[derive(Debug, Clone)]
991pub enum CstLeafNode {
992  BooleanLit(CstBooleanLit),
993  NullKeyword(CstNullKeyword),
994  NumberLit(CstNumberLit),
995  StringLit(CstStringLit),
996  WordLit(CstWordLit),
997  Token(CstToken),
998  Whitespace(CstWhitespace),
999  Newline(CstNewline),
1000  Comment(CstComment),
1001}
1002
1003impl CstLeafNode {
1004  add_parent_info_methods!();
1005  add_root_node_method!();
1006
1007  /// Removes the node from the JSON.
1008  pub fn remove(self) {
1009    match self {
1010      CstLeafNode::BooleanLit(n) => n.remove(),
1011      CstLeafNode::NullKeyword(n) => n.remove(),
1012      CstLeafNode::NumberLit(n) => n.remove(),
1013      CstLeafNode::StringLit(n) => n.remove(),
1014      CstLeafNode::WordLit(n) => n.remove(),
1015      CstLeafNode::Token(n) => n.remove(),
1016      CstLeafNode::Whitespace(n) => n.remove(),
1017      CstLeafNode::Newline(n) => n.remove(),
1018      CstLeafNode::Comment(n) => n.remove(),
1019    }
1020  }
1021
1022  fn parent_info(&self) -> Option<ParentInfo> {
1023    match self {
1024      CstLeafNode::BooleanLit(node) => node.parent_info(),
1025      CstLeafNode::NullKeyword(node) => node.parent_info(),
1026      CstLeafNode::NumberLit(node) => node.parent_info(),
1027      CstLeafNode::StringLit(node) => node.parent_info(),
1028      CstLeafNode::WordLit(node) => node.parent_info(),
1029      CstLeafNode::Token(node) => node.parent_info(),
1030      CstLeafNode::Whitespace(node) => node.parent_info(),
1031      CstLeafNode::Newline(node) => node.parent_info(),
1032      CstLeafNode::Comment(node) => node.parent_info(),
1033    }
1034  }
1035
1036  fn set_parent(&self, parent: Option<ParentInfo>) {
1037    match self {
1038      CstLeafNode::BooleanLit(node) => node.set_parent(parent),
1039      CstLeafNode::NullKeyword(node) => node.set_parent(parent),
1040      CstLeafNode::NumberLit(node) => node.set_parent(parent),
1041      CstLeafNode::StringLit(node) => node.set_parent(parent),
1042      CstLeafNode::WordLit(node) => node.set_parent(parent),
1043      CstLeafNode::Token(node) => node.set_parent(parent),
1044      CstLeafNode::Whitespace(node) => node.set_parent(parent),
1045      CstLeafNode::Newline(node) => node.set_parent(parent),
1046      CstLeafNode::Comment(node) => node.set_parent(parent),
1047    }
1048  }
1049
1050  /// Converts a CST leaf node to a `serde_json::Value`.
1051  ///
1052  /// Returns `None` if the node is trivia or cannot be converted to a value.
1053  #[cfg(feature = "serde_json")]
1054  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
1055    match self {
1056      CstLeafNode::BooleanLit(node) => node.to_serde_value(),
1057      CstLeafNode::NullKeyword(node) => node.to_serde_value(),
1058      CstLeafNode::NumberLit(node) => node.to_serde_value(),
1059      CstLeafNode::StringLit(node) => node.to_serde_value(),
1060      CstLeafNode::WordLit(_)
1061      | CstLeafNode::Token(_)
1062      | CstLeafNode::Whitespace(_)
1063      | CstLeafNode::Newline(_)
1064      | CstLeafNode::Comment(_) => None,
1065    }
1066  }
1067}
1068
1069impl Display for CstLeafNode {
1070  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1071    match self {
1072      CstLeafNode::BooleanLit(node) => node.fmt(f),
1073      CstLeafNode::NullKeyword(node) => node.fmt(f),
1074      CstLeafNode::NumberLit(node) => node.fmt(f),
1075      CstLeafNode::StringLit(node) => node.fmt(f),
1076      CstLeafNode::WordLit(node) => node.fmt(f),
1077      CstLeafNode::Token(node) => node.fmt(f),
1078      CstLeafNode::Whitespace(node) => node.fmt(f),
1079      CstLeafNode::Newline(node) => node.fmt(f),
1080      CstLeafNode::Comment(node) => node.fmt(f),
1081    }
1082  }
1083}
1084
1085impl From<CstLeafNode> for CstNode {
1086  fn from(value: CstLeafNode) -> Self {
1087    CstNode::Leaf(value)
1088  }
1089}
1090
1091/// Mode to use for trailing commas.
1092#[derive(Default, Debug, Clone, Copy)]
1093pub enum TrailingCommaMode {
1094  /// Never use trailing commas.
1095  #[default]
1096  Never,
1097  /// Use trailing commas when the object is on multiple lines.
1098  IfMultiline,
1099}
1100
1101type CstRootNodeInner = RefCell<CstChildrenInner>;
1102
1103/// Root node in the file.
1104///
1105/// The root node contains one value, whitespace, and comments.
1106#[derive(Debug, Clone)]
1107pub struct CstRootNode(Rc<CstRootNodeInner>);
1108
1109impl_container_methods!(CstRootNode, Root);
1110
1111impl CstRootNode {
1112  /// Parses the text into a CST.
1113  ///
1114  /// WARNING: You MUST not drop the root node for the duration of using the CST
1115  /// or a panic could occur in certain scenarios. This is because the CST uses weak
1116  /// references for ancestors and if the root node is dropped then the weak reference
1117  /// will be lost and the CST will panic to prevent bugs when a descendant node
1118  /// attempts to access an ancestor that was dropped.
1119  ///
1120  /// ```
1121  /// use jsonc_parser::cst::CstRootNode;
1122  /// use jsonc_parser::ParseOptions;
1123  /// use jsonc_parser::json;
1124  ///
1125  /// let json_text = r#"{
1126  ///   // comment
1127  ///   "data": 123
1128  /// }"#;
1129  ///
1130  /// let root = CstRootNode::parse(json_text, &ParseOptions::default()).unwrap();
1131  /// let root_obj = root.object_value_or_set();
1132  ///
1133  /// root_obj.get("data").unwrap().set_value(json!({
1134  ///   "nested": true
1135  /// }));
1136  /// root_obj.append("new_key", json!([456, 789, false]));
1137  ///
1138  /// assert_eq!(root.to_string(), r#"{
1139  ///   // comment
1140  ///   "data": {
1141  ///     "nested": true
1142  ///   },
1143  ///   "new_key": [456, 789, false]
1144  /// }"#);
1145  /// ```
1146  pub fn parse(text: &str, parse_options: &ParseOptions) -> Result<Self, ParseError> {
1147    let parse_result = parse_to_ast(
1148      text,
1149      &crate::CollectOptions {
1150        comments: crate::CommentCollectionStrategy::AsTokens,
1151        tokens: true,
1152      },
1153      parse_options,
1154    )?;
1155
1156    Ok(
1157      CstBuilder {
1158        text,
1159        tokens: parse_result.tokens.unwrap().into_iter().collect(),
1160      }
1161      .build(parse_result.value),
1162    )
1163  }
1164
1165  /// Computes the single indentation text of the file.
1166  pub fn single_indent_text(&self) -> Option<String> {
1167    let root_value = self.value()?;
1168    let first_non_trivia_child = root_value.children_exclude_trivia_and_tokens().first()?.clone();
1169    let mut last_whitespace = None;
1170    for previous_trivia in first_non_trivia_child.previous_siblings() {
1171      match previous_trivia {
1172        CstNode::Leaf(CstLeafNode::Whitespace(whitespace)) => {
1173          last_whitespace = Some(whitespace);
1174        }
1175        CstNode::Leaf(CstLeafNode::Newline(_)) => {
1176          return last_whitespace.map(|whitespace| whitespace.0.borrow().value.clone());
1177        }
1178        _ => {
1179          last_whitespace = None;
1180        }
1181      }
1182    }
1183    None
1184  }
1185
1186  /// Newline kind used within the JSON text.
1187  pub fn newline_kind(&self) -> CstNewlineKind {
1188    let mut current_children: VecDeque<CstContainerNode> = VecDeque::from([self.clone().into()]);
1189    while let Some(child) = current_children.pop_front() {
1190      for child in child.children() {
1191        if let CstNode::Container(child) = child {
1192          current_children.push_back(child);
1193        } else if let CstNode::Leaf(CstLeafNode::Newline(node)) = child {
1194          return node.kind();
1195        }
1196      }
1197    }
1198    CstNewlineKind::LineFeed
1199  }
1200
1201  /// Gets the root value found in the file.
1202  pub fn value(&self) -> Option<CstNode> {
1203    for child in &self.0.borrow().value {
1204      if !child.is_trivia() {
1205        return Some(child.clone());
1206      }
1207    }
1208    None
1209  }
1210
1211  /// Sets potentially replacing the root value found in the JSON document.
1212  pub fn set_value(&self, root_value: CstInputValue) {
1213    let container: CstContainerNode = self.clone().into();
1214    let style_info = StyleInfo {
1215      newline_kind: self.newline_kind(),
1216      uses_trailing_commas: uses_trailing_commas(self.clone().into()),
1217    };
1218    let indents = compute_indents(&self.clone().into());
1219    let mut insert_index = if let Some(root_value) = self.value() {
1220      let index = root_value.child_index();
1221      root_value.remove_raw();
1222      index
1223    } else {
1224      let children = self.children();
1225      let mut index = match children.last() {
1226        Some(CstNode::Leaf(CstLeafNode::Newline(_))) => children.len() - 1,
1227        _ => children.len(),
1228      };
1229      let previous_node = if index == 0 { None } else { children.get(index - 1) };
1230      if let Some(CstNode::Leaf(CstLeafNode::Comment(_))) = previous_node {
1231        // insert a newline if the last node before is a comment
1232        container.raw_insert_child(Some(&mut index), CstNewline::new(style_info.newline_kind).into());
1233      }
1234      if self.child_at_index(index).is_none() {
1235        // insert a trailing newline
1236        container.raw_insert_child(Some(&mut index), CstNewline::new(style_info.newline_kind).into());
1237        index -= 1;
1238      }
1239      index
1240    };
1241    container.raw_insert_value_with_internal_indent(
1242      Some(&mut insert_index),
1243      InsertValue::Value(root_value),
1244      &style_info,
1245      &indents,
1246    );
1247  }
1248
1249  /// Gets the root value if its an object.
1250  pub fn object_value(&self) -> Option<CstObject> {
1251    self.value()?.as_object()
1252  }
1253
1254  /// Gets or creates the root value as an object, returns `Some` if successful
1255  /// or `None` if the root value already exists and is not an object.
1256  ///
1257  /// Note: Use `.object_value_or_set()` to overwrite the root value when
1258  /// it's not an object.
1259  pub fn object_value_or_create(&self) -> Option<CstObject> {
1260    match self.value() {
1261      Some(CstNode::Container(CstContainerNode::Object(node))) => Some(node),
1262      Some(_) => None,
1263      None => {
1264        self.set_value(CstInputValue::Object(Vec::new()));
1265        self.object_value()
1266      }
1267    }
1268  }
1269
1270  /// Gets the root value if it's an object or sets the root value as an object.
1271  ///
1272  /// Note: Use `.object_value_or_create()` to not overwrite the root value
1273  /// when it's not an object.
1274  pub fn object_value_or_set(&self) -> CstObject {
1275    match self.value() {
1276      Some(CstNode::Container(CstContainerNode::Object(node))) => node,
1277      _ => {
1278        self.set_value(CstInputValue::Object(Vec::new()));
1279        self.object_value().unwrap()
1280      }
1281    }
1282  }
1283
1284  /// Gets the value if its an array.
1285  pub fn array_value(&self) -> Option<CstArray> {
1286    self.value()?.as_array()
1287  }
1288
1289  /// Gets or creates the root value as an object, returns `Some` if successful
1290  /// or `None` if the root value already exists and is not an object.
1291  ///
1292  /// Note: Use `.array_value_or_set()` to overwrite the root value when
1293  /// it's not an array.
1294  pub fn array_value_or_create(&self) -> Option<CstArray> {
1295    match self.value() {
1296      Some(CstNode::Container(CstContainerNode::Array(node))) => Some(node),
1297      Some(_) => None,
1298      None => {
1299        self.set_value(CstInputValue::Array(Vec::new()));
1300        self.array_value()
1301      }
1302    }
1303  }
1304
1305  /// Gets the root value if it's an object or sets the root value as an object.
1306  ///
1307  /// Note: Use `.array_value_or_create()` to not overwrite the root value
1308  /// when it's not an object.
1309  pub fn array_value_or_set(&self) -> CstArray {
1310    match self.value() {
1311      Some(CstNode::Container(CstContainerNode::Array(node))) => node,
1312      _ => {
1313        self.set_value(CstInputValue::Array(Vec::new()));
1314        self.array_value().unwrap()
1315      }
1316    }
1317  }
1318
1319  /// Ensures this object's values use trailing commas.
1320  ///
1321  /// Note: This does not cause future values to use trailing commas.
1322  /// That will always be determined based on whether the file uses
1323  /// trailing commas or not, so it's probably best to do this last.
1324  pub fn set_trailing_commas(&self, mode: TrailingCommaMode) {
1325    let Some(value) = self.value() else {
1326      return;
1327    };
1328
1329    match value {
1330      CstNode::Container(container) => match container {
1331        CstContainerNode::Array(n) => n.set_trailing_commas(mode),
1332        CstContainerNode::Object(n) => n.set_trailing_commas(mode),
1333        _ => {}
1334      },
1335      CstNode::Leaf(_) => {}
1336    }
1337  }
1338
1339  /// Clears all the children from the root node making it empty.
1340  pub fn clear_children(&self) {
1341    let children = std::mem::take(&mut self.0.borrow_mut().value);
1342    for child in children {
1343      child.set_parent(None);
1344    }
1345  }
1346
1347  /// Converts the root CST node to a `serde_json::Value`.
1348  ///
1349  /// Returns `None` if the root has no value node.
1350  #[cfg(feature = "serde_json")]
1351  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
1352    self.value()?.to_serde_value()
1353  }
1354}
1355
1356impl Display for CstRootNode {
1357  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1358    for child in &self.0.borrow().value {
1359      write!(f, "{}", child)?;
1360    }
1361    Ok(())
1362  }
1363}
1364
1365/// Text surrounded in double quotes (ex. `"my string"`).
1366#[derive(Debug, Clone)]
1367pub struct CstStringLit(Rc<RefCell<CstValueInner<String>>>);
1368
1369impl_leaf_methods!(CstStringLit, StringLit);
1370
1371impl CstStringLit {
1372  fn new(value: String) -> Self {
1373    Self(CstValueInner::new(value))
1374  }
1375
1376  fn new_escaped(value: &str) -> Self {
1377    let mut escaped = String::with_capacity(value.len() + 2);
1378    escaped.push('"');
1379    for ch in value.chars() {
1380      match ch {
1381        '"' => escaped.push_str("\\\""),
1382        '\\' => escaped.push_str("\\\\"),
1383        '\u{08}' => escaped.push_str("\\b"),
1384        '\u{0c}' => escaped.push_str("\\f"),
1385        '\n' => escaped.push_str("\\n"),
1386        '\r' => escaped.push_str("\\r"),
1387        '\t' => escaped.push_str("\\t"),
1388        c if c.is_control() => {
1389          escaped.push_str(&format!("\\u{:04x}", c as u32));
1390        }
1391        c => escaped.push(c),
1392      }
1393    }
1394    escaped.push('"');
1395    Self::new(escaped)
1396  }
1397
1398  /// Sets the raw value of the string INCLUDING SURROUNDING QUOTES.
1399  pub fn set_raw_value(&self, value: String) {
1400    self.0.borrow_mut().value = value;
1401  }
1402
1403  /// Gets the raw unescaped value including quotes.
1404  pub fn raw_value(&self) -> String {
1405    self.0.borrow().value.clone()
1406  }
1407
1408  /// Gets the decoded string value.
1409  pub fn decoded_value(&self) -> Result<String, ParseStringErrorKind> {
1410    let inner = self.0.borrow();
1411    crate::string::parse_string(&inner.value)
1412      .map(|value| value.into_owned())
1413      .map_err(|err| err.kind)
1414  }
1415
1416  /// Replaces this node with a new value.
1417  pub fn replace_with(self, replacement: CstInputValue) -> Option<CstNode> {
1418    replace_with(self.into(), InsertValue::Value(replacement))
1419  }
1420
1421  /// Removes the node from the JSON.
1422  pub fn remove(self) {
1423    remove_comma_separated(self.into())
1424  }
1425
1426  /// Converts a CST string literal to a `serde_json::Value`.
1427  #[cfg(feature = "serde_json")]
1428  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
1429    self.decoded_value().ok().map(serde_json::Value::String)
1430  }
1431}
1432
1433impl Display for CstStringLit {
1434  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1435    write!(f, "{}", self.0.borrow().value)
1436  }
1437}
1438
1439/// Property key that is missing quotes (ex. `prop: 4`).
1440#[derive(Debug, Clone)]
1441pub struct CstWordLit(Rc<RefCell<CstValueInner<String>>>);
1442
1443impl_leaf_methods!(CstWordLit, WordLit);
1444
1445impl CstWordLit {
1446  fn new(value: String) -> Self {
1447    Self(CstValueInner::new(value))
1448  }
1449
1450  /// Sets the raw value of the word literal.
1451  pub fn set_raw_value(&self, value: String) {
1452    self.0.borrow_mut().value = value;
1453  }
1454
1455  /// Replaces this node with a new value.
1456  pub fn replace_with(self, replacement: CstInputValue) -> Option<CstNode> {
1457    replace_with(self.into(), InsertValue::Value(replacement))
1458  }
1459
1460  /// Removes the node from the JSON.
1461  pub fn remove(self) {
1462    remove_comma_separated(self.into())
1463  }
1464}
1465
1466impl Display for CstWordLit {
1467  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1468    write!(f, "{}", self.0.borrow().value)
1469  }
1470}
1471
1472#[derive(Debug, Clone)]
1473pub struct CstNumberLit(Rc<RefCell<CstValueInner<String>>>);
1474
1475impl_leaf_methods!(CstNumberLit, NumberLit);
1476
1477impl CstNumberLit {
1478  fn new(value: String) -> Self {
1479    Self(CstValueInner::new(value))
1480  }
1481
1482  /// Sets the raw string value of the number literal.
1483  pub fn set_raw_value(&self, value: String) {
1484    self.0.borrow_mut().value = value;
1485  }
1486
1487  /// Replaces this node with a new value.
1488  pub fn replace_with(self, replacement: CstInputValue) -> Option<CstNode> {
1489    replace_with(self.into(), InsertValue::Value(replacement))
1490  }
1491
1492  /// Removes the node from the JSON.
1493  pub fn remove(self) {
1494    remove_comma_separated(self.into())
1495  }
1496
1497  /// Converts a CST number literal to a `serde_json::Value`.
1498  #[cfg(feature = "serde_json")]
1499  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
1500    use std::str::FromStr;
1501    let raw = self.0.borrow().value.clone();
1502
1503    // check if this is a hexadecimal literal (0x or 0X prefix)
1504    let num_str = raw.trim_start_matches(['-', '+']);
1505    if num_str.len() > 2 && (num_str.starts_with("0x") || num_str.starts_with("0X")) {
1506      // parse hexadecimal and convert to decimal
1507      let hex_part = &num_str[2..];
1508      match i64::from_str_radix(hex_part, 16) {
1509        Ok(decimal_value) => {
1510          let final_value = if raw.starts_with('-') {
1511            -decimal_value
1512          } else {
1513            decimal_value
1514          };
1515          Some(serde_json::Value::Number(serde_json::Number::from(final_value)))
1516        }
1517        Err(_) => Some(serde_json::Value::String(raw)),
1518      }
1519    } else {
1520      // standard decimal number - strip leading + if present (serde_json doesn't accept it)
1521      let num_for_parsing = raw.trim_start_matches('+');
1522      match serde_json::Number::from_str(num_for_parsing) {
1523        Ok(number) => Some(serde_json::Value::Number(number)),
1524        // if the number is invalid, return it as a string (same behavior as AST conversion)
1525        Err(_) => Some(serde_json::Value::String(raw)),
1526      }
1527    }
1528  }
1529}
1530
1531impl Display for CstNumberLit {
1532  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1533    write!(f, "{}", self.0.borrow().value)
1534  }
1535}
1536
1537/// Boolean (`true` or `false`).
1538#[derive(Debug, Clone)]
1539pub struct CstBooleanLit(Rc<RefCell<CstValueInner<bool>>>);
1540
1541impl_leaf_methods!(CstBooleanLit, BooleanLit);
1542
1543impl CstBooleanLit {
1544  fn new(value: bool) -> Self {
1545    Self(CstValueInner::new(value))
1546  }
1547
1548  /// Gets the value of the boolean literal.
1549  pub fn value(&self) -> bool {
1550    self.0.borrow().value
1551  }
1552
1553  /// Sets the value of the boolean literal.
1554  pub fn set_value(&self, value: bool) {
1555    self.0.borrow_mut().value = value;
1556  }
1557
1558  /// Replaces this node with a new value.
1559  pub fn replace_with(self, replacement: CstInputValue) -> Option<CstNode> {
1560    replace_with(self.into(), InsertValue::Value(replacement))
1561  }
1562
1563  /// Removes the node from the JSON.
1564  pub fn remove(self) {
1565    remove_comma_separated(self.into())
1566  }
1567
1568  /// Converts a CST boolean literal to a `serde_json::Value`.
1569  #[cfg(feature = "serde_json")]
1570  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
1571    Some(serde_json::Value::Bool(self.value()))
1572  }
1573}
1574
1575impl Display for CstBooleanLit {
1576  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1577    if self.0.borrow().value {
1578      write!(f, "true")
1579    } else {
1580      write!(f, "false")
1581    }
1582  }
1583}
1584
1585/// Null keyword (`null`).
1586#[derive(Debug, Clone)]
1587pub struct CstNullKeyword(Rc<RefCell<CstValueInner<()>>>);
1588
1589impl CstNullKeyword {
1590  fn new() -> Self {
1591    Self(CstValueInner::new(()))
1592  }
1593
1594  /// Replaces this node with a new value.
1595  pub fn replace_with(self, replacement: CstInputValue) -> Option<CstNode> {
1596    replace_with(self.into(), InsertValue::Value(replacement))
1597  }
1598
1599  /// Removes the node from the JSON.
1600  pub fn remove(self) {
1601    remove_comma_separated(self.into())
1602  }
1603
1604  /// Converts a CST null keyword to a `serde_json::Value`.
1605  #[cfg(feature = "serde_json")]
1606  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
1607    Some(serde_json::Value::Null)
1608  }
1609}
1610
1611impl_leaf_methods!(CstNullKeyword, NullKeyword);
1612
1613impl Display for CstNullKeyword {
1614  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1615    write!(f, "null")
1616  }
1617}
1618
1619type CstObjectInner = RefCell<CstChildrenInner>;
1620
1621/// Object literal that may contain properties (ex. `{}`, `{ "prop": 4 }`).
1622#[derive(Debug, Clone)]
1623pub struct CstObject(Rc<CstObjectInner>);
1624
1625impl_container_methods!(CstObject, Object);
1626
1627impl CstObject {
1628  add_root_node_method!();
1629
1630  fn new_no_tokens() -> Self {
1631    Self(CstValueInner::new(Vec::new()))
1632  }
1633
1634  fn new_with_tokens() -> Self {
1635    let object = CstObject::new_no_tokens();
1636    let container: CstContainerNode = object.clone().into();
1637    container.raw_append_children(vec![CstToken::new('{').into(), CstToken::new('}').into()]);
1638    object
1639  }
1640
1641  /// Array property by name.
1642  ///
1643  /// Returns `None` if the property doesn't exist or is not an array.
1644  pub fn array_value(&self, name: &str) -> Option<CstArray> {
1645    match self.get(name)?.value()? {
1646      CstNode::Container(CstContainerNode::Array(node)) => Some(node),
1647      _ => None,
1648    }
1649  }
1650
1651  /// Ensures a property exists with an array value returning the array.
1652  ///
1653  /// Returns `None` if the property value exists, but is not an array.
1654  ///
1655  /// Note: Use `.array_value_or_set(..)` to overwrite an existing
1656  /// non-array property value.
1657  pub fn array_value_or_create(&self, name: &str) -> Option<CstArray> {
1658    match self.get(name) {
1659      Some(prop) => match prop.value()? {
1660        CstNode::Container(CstContainerNode::Array(node)) => Some(node),
1661        _ => None,
1662      },
1663      None => {
1664        self.append(name, CstInputValue::Array(Vec::new()));
1665        self.array_value(name)
1666      }
1667    }
1668  }
1669
1670  /// Ensures a property exists with an array value returning the array.
1671  ///
1672  /// Note: Use `.array_value_or_create(..)` to not overwrite an existing
1673  /// non-array property value.
1674  pub fn array_value_or_set(&self, name: &str) -> CstArray {
1675    match self.get(name) {
1676      Some(prop) => match prop.value() {
1677        Some(CstNode::Container(CstContainerNode::Array(node))) => node,
1678        Some(node) => {
1679          let mut index = node.child_index();
1680          node.remove_raw();
1681          let container: CstContainerNode = prop.clone().into();
1682          let array = CstArray::new_with_tokens();
1683          container.raw_insert_child(Some(&mut index), array.clone().into());
1684          array
1685        }
1686        _ => {
1687          let mut index = prop.children().len();
1688          let container: CstContainerNode = prop.clone().into();
1689          let array = CstArray::new_with_tokens();
1690          container.raw_insert_child(Some(&mut index), array.clone().into());
1691          array
1692        }
1693      },
1694      None => {
1695        self.append(name, CstInputValue::Array(Vec::new()));
1696        self.array_value(name).unwrap()
1697      }
1698    }
1699  }
1700
1701  /// Object property by name.
1702  ///
1703  /// Returns `None` if the property doesn't exist or is not an object.
1704  pub fn object_value(&self, name: &str) -> Option<CstObject> {
1705    match self.get(name)?.value()? {
1706      CstNode::Container(CstContainerNode::Object(node)) => Some(node),
1707      _ => None,
1708    }
1709  }
1710
1711  /// Ensures a property exists with an object value returning the object.
1712  ///
1713  /// Returns `None` if the property value exists, but is not an object.
1714  ///
1715  /// Note: Use `.object_value_or_set(..)` to overwrite an existing
1716  /// non-array property value.
1717  pub fn object_value_or_create(&self, name: &str) -> Option<CstObject> {
1718    match self.get(name) {
1719      Some(prop) => match prop.value()? {
1720        CstNode::Container(CstContainerNode::Object(node)) => Some(node),
1721        _ => None,
1722      },
1723      None => {
1724        self.append(name, CstInputValue::Object(Vec::new()));
1725        self.object_value(name)
1726      }
1727    }
1728  }
1729
1730  /// Ensures a property exists with an object value returning the object.
1731  ///
1732  /// Note: Use `.object_value_or_create(..)` to not overwrite an existing
1733  /// non-object property value.
1734  pub fn object_value_or_set(&self, name: &str) -> CstObject {
1735    match self.get(name) {
1736      Some(prop) => match prop.value() {
1737        Some(CstNode::Container(CstContainerNode::Object(node))) => node,
1738        Some(node) => {
1739          let mut index = node.child_index();
1740          node.remove_raw();
1741          let container: CstContainerNode = prop.clone().into();
1742          let object = CstObject::new_with_tokens();
1743          container.raw_insert_child(Some(&mut index), object.clone().into());
1744          object
1745        }
1746        _ => {
1747          let mut index = prop.children().len();
1748          let container: CstContainerNode = prop.clone().into();
1749          let object = CstObject::new_with_tokens();
1750          container.raw_insert_child(Some(&mut index), object.clone().into());
1751          object
1752        }
1753      },
1754      None => {
1755        self.append(name, CstInputValue::Object(Vec::new()));
1756        self.object_value(name).unwrap()
1757      }
1758    }
1759  }
1760
1761  /// Property by name.
1762  ///
1763  /// Returns `None` if the property doesn't exist.
1764  pub fn get(&self, name: &str) -> Option<CstObjectProp> {
1765    for child in &self.0.borrow().value {
1766      if let CstNode::Container(CstContainerNode::ObjectProp(prop)) = child {
1767        let Some(prop_name) = prop.name() else {
1768          continue;
1769        };
1770        let Ok(prop_name_str) = prop_name.decoded_value() else {
1771          continue;
1772        };
1773        if prop_name_str == name {
1774          return Some(prop.clone());
1775        }
1776      }
1777    }
1778    None
1779  }
1780
1781  /// Properties of the object.
1782  pub fn properties(&self) -> Vec<CstObjectProp> {
1783    self
1784      .0
1785      .borrow()
1786      .value
1787      .iter()
1788      .filter_map(|child| match child {
1789        CstNode::Container(CstContainerNode::ObjectProp(prop)) => Some(prop.clone()),
1790        _ => None,
1791      })
1792      .collect()
1793  }
1794
1795  /// Appends a property to the object.
1796  ///
1797  /// Returns the inserted object property.
1798  pub fn append(&self, prop_name: &str, value: CstInputValue) -> CstObjectProp {
1799    self.insert_or_append(None, prop_name, value)
1800  }
1801
1802  /// Inserts a property at the specified index.
1803  ///
1804  /// Returns the inserted object property.
1805  pub fn insert(&self, index: usize, prop_name: &str, value: CstInputValue) -> CstObjectProp {
1806    self.insert_or_append(Some(index), prop_name, value)
1807  }
1808
1809  fn insert_or_append(&self, index: Option<usize>, prop_name: &str, value: CstInputValue) -> CstObjectProp {
1810    self.ensure_multiline();
1811    insert_or_append_to_container(
1812      &CstContainerNode::Object(self.clone()),
1813      self.properties().into_iter().map(|c| c.into()).collect(),
1814      index,
1815      InsertValue::Property(prop_name, value),
1816    )
1817    .as_object_prop()
1818    .unwrap()
1819  }
1820
1821  /// Sorts the properties of the object.
1822  ///
1823  /// What was written with a property travels with it: the comments and blank lines above it, and
1824  /// a comment written after it on the same line. Whatever precedes the close brace, and whatever
1825  /// shares the open brace's line, belongs to no property and stays where it is. Each property
1826  /// gains or loses a comma to suit its new position, and whether the object ends with a trailing
1827  /// comma is preserved.
1828  ///
1829  /// A blank line under the open brace travels with the property it was written above, and one
1830  /// that would end up there instead is dropped, since a gap there reads as belonging to the
1831  /// object. A line comment that would otherwise comment out what now follows it gains a line
1832  /// break, which can make a single line object span several.
1833  ///
1834  /// Nothing moves until [`PropertySort::by`] or [`PropertySort::by_key`] says how to order them.
1835  ///
1836  /// # Example
1837  ///
1838  /// ```
1839  /// use jsonc_parser::ParseOptions;
1840  /// use jsonc_parser::cst::CstRootNode;
1841  ///
1842  /// let json_text = r#"{
1843  ///   "b": 2, // written about b
1844  ///   // written about a
1845  ///   "a": 1
1846  /// }"#;
1847  ///
1848  /// let root = CstRootNode::parse(json_text, &ParseOptions::default()).unwrap();
1849  /// let root_obj = root.object_value().unwrap();
1850  /// root_obj.sort_properties().by_key(|prop| prop.decoded_name());
1851  ///
1852  /// assert_eq!(root.to_string(), r#"{
1853  ///   // written about a
1854  ///   "a": 1,
1855  ///   "b": 2 // written about b
1856  /// }"#);
1857  /// ```
1858  pub fn sort_properties(&self) -> PropertySort<'_> {
1859    PropertySort {
1860      object: self,
1861      options: SortOptions::default(),
1862    }
1863  }
1864
1865  /// Replaces this node with a new value.
1866  pub fn replace_with(self, replacement: CstInputValue) -> Option<CstNode> {
1867    replace_with(self.into(), InsertValue::Value(replacement))
1868  }
1869
1870  /// Ensures this object and all its descendants use trailing commas.
1871  pub fn set_trailing_commas(&self, mode: TrailingCommaMode) {
1872    set_trailing_commas(
1873      mode,
1874      &self.clone().into(),
1875      self.properties().into_iter().map(|c| c.into()),
1876    );
1877  }
1878
1879  /// Ensures the object spans multiple lines.
1880  pub fn ensure_multiline(&self) {
1881    ensure_multiline(&self.clone().into());
1882  }
1883
1884  /// Removes the node from the JSON.
1885  pub fn remove(self) {
1886    remove_comma_separated(self.into())
1887  }
1888
1889  /// Converts a CST object to a `serde_json::Value`.
1890  #[cfg(feature = "serde_json")]
1891  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
1892    let mut map = serde_json::map::Map::new();
1893    for prop in self.properties() {
1894      if let (Some(name), Some(value)) = (prop.decoded_name(), prop.to_serde_value()) {
1895        map.insert(name, value);
1896      }
1897    }
1898    Some(serde_json::Value::Object(map))
1899  }
1900}
1901
1902impl Display for CstObject {
1903  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1904    for child in &self.0.borrow().value {
1905      write!(f, "{}", child)?;
1906    }
1907    Ok(())
1908  }
1909}
1910
1911type CstObjectPropInner = RefCell<CstChildrenInner>;
1912
1913/// Property in an object (ex. `"prop": 5`).
1914#[derive(Debug, Clone)]
1915pub struct CstObjectProp(Rc<CstObjectPropInner>);
1916
1917impl_container_methods!(CstObjectProp, ObjectProp);
1918
1919impl CstObjectProp {
1920  add_root_node_method!();
1921
1922  fn new() -> Self {
1923    Self(CstValueInner::new(Vec::new()))
1924  }
1925
1926  /// Name of the object property.
1927  ///
1928  /// Returns `None` if the name doesn't exist.
1929  pub fn name(&self) -> Option<ObjectPropName> {
1930    for child in &self.0.borrow().value {
1931      match child {
1932        CstNode::Leaf(CstLeafNode::StringLit(node)) => return Some(ObjectPropName::String(node.clone())),
1933        CstNode::Leaf(CstLeafNode::WordLit(node)) => return Some(ObjectPropName::Word(node.clone())),
1934        _ => {
1935          // someone may have manipulated this object such that this is no longer there
1936        }
1937      }
1938    }
1939    None
1940  }
1941
1942  /// Name of the object property with any escapes in it resolved.
1943  ///
1944  /// Returns `None` if the name doesn't exist or can't be decoded.
1945  pub fn decoded_name(&self) -> Option<String> {
1946    match self.name()? {
1947      ObjectPropName::String(s) => s.decoded_value().ok(),
1948      ObjectPropName::Word(w) => Some(w.0.borrow().value.clone()),
1949    }
1950  }
1951
1952  pub fn property_index(&self) -> usize {
1953    let child_index = self.child_index();
1954    let Some(parent) = self.parent().and_then(|p| p.as_object()) else {
1955      return 0;
1956    };
1957    parent
1958      .properties()
1959      .iter()
1960      .position(|p| p.child_index() == child_index)
1961      .unwrap_or(0)
1962  }
1963
1964  pub fn set_value(&self, replacement: CstInputValue) {
1965    let maybe_value = self.value();
1966    let mut value_index = maybe_value
1967      .as_ref()
1968      .map(|v| v.child_index())
1969      .unwrap_or_else(|| self.children().len());
1970    let container: CstContainerNode = self.clone().into();
1971    let indents = compute_indents(&container.clone().into());
1972    let style_info = &StyleInfo {
1973      newline_kind: container.root_node().map(|v| v.newline_kind()).unwrap_or_default(),
1974      uses_trailing_commas: uses_trailing_commas(maybe_value.unwrap_or_else(|| container.clone().into())),
1975    };
1976    self.remove_child_set_no_parent(value_index);
1977    container.raw_insert_value_with_internal_indent(
1978      Some(&mut value_index),
1979      InsertValue::Value(replacement),
1980      style_info,
1981      &indents,
1982    );
1983  }
1984
1985  /// Value of the object property.
1986  ///
1987  /// Returns `None` if the value doesn't exist.
1988  pub fn value(&self) -> Option<CstNode> {
1989    let name = self.name()?;
1990    let parent_info = name.parent_info()?;
1991    let children = &self.0.borrow().value;
1992    let mut children = children[parent_info.child_index + 1..].iter();
1993
1994    // first, skip over the colon token
1995    for child in children.by_ref() {
1996      if let CstNode::Leaf(CstLeafNode::Token(token)) = child
1997        && token.value() == ':'
1998      {
1999        break;
2000      }
2001    }
2002
2003    // now find the value
2004    for child in children {
2005      match child {
2006        CstNode::Leaf(leaf) => match leaf {
2007          CstLeafNode::BooleanLit(_)
2008          | CstLeafNode::NullKeyword(_)
2009          | CstLeafNode::NumberLit(_)
2010          | CstLeafNode::StringLit(_)
2011          | CstLeafNode::WordLit(_) => return Some(child.clone()),
2012          CstLeafNode::Token(_) | CstLeafNode::Whitespace(_) | CstLeafNode::Newline(_) | CstLeafNode::Comment(_) => {
2013            // ignore
2014          }
2015        },
2016        CstNode::Container(container) => match container {
2017          CstContainerNode::Object(_) | CstContainerNode::Array(_) => return Some(child.clone()),
2018          CstContainerNode::Root(_) | CstContainerNode::ObjectProp(_) => return None,
2019        },
2020      }
2021    }
2022
2023    None
2024  }
2025
2026  /// Gets the value if its an object.
2027  pub fn object_value(&self) -> Option<CstObject> {
2028    self.value()?.as_object()
2029  }
2030
2031  /// Gets the value if it's an object or sets the value as an object.
2032  pub fn object_value_or_set(&self) -> CstObject {
2033    match self.value() {
2034      Some(CstNode::Container(CstContainerNode::Object(node))) => node,
2035      _ => {
2036        self.set_value(CstInputValue::Object(Vec::new()));
2037        self.object_value().unwrap()
2038      }
2039    }
2040  }
2041
2042  /// Gets the value if its an array.
2043  pub fn array_value(&self) -> Option<CstArray> {
2044    self.value()?.as_array()
2045  }
2046
2047  /// Gets the value if it's an object or sets the value as an object.
2048  pub fn array_value_or_set(&self) -> CstArray {
2049    match self.value() {
2050      Some(CstNode::Container(CstContainerNode::Array(node))) => node,
2051      _ => {
2052        self.set_value(CstInputValue::Array(Vec::new()));
2053        self.array_value().unwrap()
2054      }
2055    }
2056  }
2057
2058  /// Sibling object property coming before this one.
2059  pub fn previous_property(&self) -> Option<CstObjectProp> {
2060    for sibling in self.previous_siblings() {
2061      if let CstNode::Container(CstContainerNode::ObjectProp(prop)) = sibling {
2062        return Some(prop);
2063      }
2064    }
2065    None
2066  }
2067
2068  /// Sibling object property coming after this one.
2069  pub fn next_property(&self) -> Option<CstObjectProp> {
2070    for sibling in self.next_siblings() {
2071      if let CstNode::Container(CstContainerNode::ObjectProp(prop)) = sibling {
2072        return Some(prop);
2073      }
2074    }
2075    None
2076  }
2077
2078  /// Replaces this node with a new value.
2079  pub fn replace_with(self, key: &str, replacement: CstInputValue) -> Option<CstNode> {
2080    replace_with(self.into(), InsertValue::Property(key, replacement))
2081  }
2082
2083  /// Removes the node from the JSON.
2084  pub fn remove(self) {
2085    remove_comma_separated(self.into())
2086  }
2087
2088  /// Converts a CST object property to a `serde_json::Value`.
2089  ///
2090  /// Returns the value of the property, or `None` if it has no value.
2091  #[cfg(feature = "serde_json")]
2092  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
2093    self.value()?.to_serde_value()
2094  }
2095}
2096
2097impl Display for CstObjectProp {
2098  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2099    for child in &self.0.borrow().value {
2100      write!(f, "{}", child)?;
2101    }
2102    Ok(())
2103  }
2104}
2105
2106/// An object property name that may or may not be in quotes (ex. `"prop"` in `"prop": 5`).
2107#[derive(Debug, Clone)]
2108pub enum ObjectPropName {
2109  String(CstStringLit),
2110  Word(CstWordLit),
2111}
2112
2113impl ObjectPropName {
2114  add_root_node_method!();
2115  add_parent_info_methods!();
2116
2117  /// Object property name if it's a string literal.
2118  pub fn as_string_lit(&self) -> Option<CstStringLit> {
2119    match self {
2120      ObjectPropName::String(n) => Some(n.clone()),
2121      ObjectPropName::Word(_) => None,
2122    }
2123  }
2124
2125  /// Object property name if it's a word literal (no quotes).
2126  pub fn as_word_lit(&self) -> Option<CstWordLit> {
2127    match self {
2128      ObjectPropName::String(_) => None,
2129      ObjectPropName::Word(n) => Some(n.clone()),
2130    }
2131  }
2132
2133  /// Decoded value of the string.
2134  pub fn decoded_value(&self) -> Result<String, ParseStringErrorKind> {
2135    match self {
2136      ObjectPropName::String(n) => n.decoded_value(),
2137      ObjectPropName::Word(n) => Ok(n.0.borrow().value.clone()),
2138    }
2139  }
2140
2141  fn parent_info(&self) -> Option<ParentInfo> {
2142    match self {
2143      ObjectPropName::String(n) => n.parent_info(),
2144      ObjectPropName::Word(n) => n.parent_info(),
2145    }
2146  }
2147}
2148
2149impl From<ObjectPropName> for CstNode {
2150  fn from(value: ObjectPropName) -> Self {
2151    match value {
2152      ObjectPropName::String(n) => n.into(),
2153      ObjectPropName::Word(n) => n.into(),
2154    }
2155  }
2156}
2157
2158type CstArrayInner = RefCell<CstChildrenInner>;
2159
2160/// Represents an array that may contain elements (ex. `[]`, `[1, 2, 3]`).
2161#[derive(Debug, Clone)]
2162pub struct CstArray(Rc<CstArrayInner>);
2163
2164impl_container_methods!(CstArray, Array);
2165
2166impl CstArray {
2167  add_root_node_method!();
2168
2169  fn new_no_tokens() -> Self {
2170    Self(CstValueInner::new(Vec::new()))
2171  }
2172
2173  fn new_with_tokens() -> Self {
2174    let array = CstArray::new_no_tokens();
2175    let container: CstContainerNode = array.clone().into();
2176    container.raw_append_children(vec![CstToken::new('[').into(), CstToken::new(']').into()]);
2177    array
2178  }
2179
2180  /// Elements of the array.
2181  pub fn elements(&self) -> Vec<CstNode> {
2182    self
2183      .0
2184      .borrow()
2185      .value
2186      .iter()
2187      .filter(|child| match child {
2188        CstNode::Container(_) => true,
2189        CstNode::Leaf(leaf) => match leaf {
2190          CstLeafNode::BooleanLit(_)
2191          | CstLeafNode::NullKeyword(_)
2192          | CstLeafNode::NumberLit(_)
2193          | CstLeafNode::StringLit(_)
2194          | CstLeafNode::WordLit(_) => true,
2195          CstLeafNode::Token(_) | CstLeafNode::Whitespace(_) | CstLeafNode::Newline(_) | CstLeafNode::Comment(_) => {
2196            false
2197          }
2198        },
2199      })
2200      .cloned()
2201      .collect()
2202  }
2203
2204  /// Appends an element to the end of the array.
2205  ///
2206  /// Returns the appended node.
2207  pub fn append(&self, value: CstInputValue) -> CstNode {
2208    self.insert_or_append(None, value)
2209  }
2210
2211  /// Inserts an element at the specified index.
2212  ///
2213  /// Returns the inserted node.
2214  pub fn insert(&self, index: usize, value: CstInputValue) -> CstNode {
2215    self.insert_or_append(Some(index), value)
2216  }
2217
2218  /// Sorts the elements of the array.
2219  ///
2220  /// Behaves like [`CstObject::sort_properties`], moving what was written with an element along
2221  /// with it. Nothing moves until [`ElementSort::by`] or [`ElementSort::by_key`] says how to
2222  /// order them.
2223  ///
2224  /// # Example
2225  ///
2226  /// ```
2227  /// use jsonc_parser::ParseOptions;
2228  /// use jsonc_parser::cst::CstRootNode;
2229  ///
2230  /// let json_text = r#"[
2231  ///   "b", // written about b
2232  ///   // written about a
2233  ///   "a"
2234  /// ]"#;
2235  ///
2236  /// let root = CstRootNode::parse(json_text, &ParseOptions::default()).unwrap();
2237  /// let array = root.array_value().unwrap();
2238  /// array.sort_elements().by_key(|element| element.to_string());
2239  ///
2240  /// assert_eq!(root.to_string(), r#"[
2241  ///   // written about a
2242  ///   "a",
2243  ///   "b" // written about b
2244  /// ]"#);
2245  /// ```
2246  pub fn sort_elements(&self) -> ElementSort<'_> {
2247    ElementSort {
2248      array: self,
2249      options: SortOptions::default(),
2250    }
2251  }
2252
2253  /// Ensures the array spans multiple lines.
2254  pub fn ensure_multiline(&self) {
2255    ensure_multiline(&self.clone().into());
2256  }
2257
2258  /// Ensures this array and all its descendants use trailing commas.
2259  pub fn set_trailing_commas(&self, mode: TrailingCommaMode) {
2260    set_trailing_commas(mode, &self.clone().into(), self.elements().into_iter());
2261  }
2262
2263  fn insert_or_append(&self, index: Option<usize>, value: CstInputValue) -> CstNode {
2264    insert_or_append_to_container(
2265      &CstContainerNode::Array(self.clone()),
2266      self.elements(),
2267      index,
2268      InsertValue::Value(value),
2269    )
2270  }
2271
2272  /// Replaces this node with a new value.
2273  pub fn replace_with(self, replacement: CstInputValue) -> Option<CstNode> {
2274    replace_with(self.into(), InsertValue::Value(replacement))
2275  }
2276
2277  /// Removes the node from the JSON.
2278  pub fn remove(self) {
2279    remove_comma_separated(self.into())
2280  }
2281
2282  /// Converts a CST array to a `serde_json::Value`.
2283  #[cfg(feature = "serde_json")]
2284  pub fn to_serde_value(&self) -> Option<serde_json::Value> {
2285    let elements: Vec<serde_json::Value> = self
2286      .elements()
2287      .into_iter()
2288      .filter_map(|element| element.to_serde_value())
2289      .collect();
2290    Some(serde_json::Value::Array(elements))
2291  }
2292}
2293
2294impl Display for CstArray {
2295  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2296    for child in &self.0.borrow().value {
2297      write!(f, "{}", child)?;
2298    }
2299    Ok(())
2300  }
2301}
2302
2303/// Insigificant token found in the file (ex. colon, comma, brace, etc.).
2304#[derive(Debug, Clone)]
2305pub struct CstToken(Rc<RefCell<CstValueInner<char>>>);
2306
2307impl_leaf_methods!(CstToken, Token);
2308
2309impl CstToken {
2310  fn new(value: char) -> Self {
2311    Self(CstValueInner::new(value))
2312  }
2313
2314  /// Sets the char value of the token.
2315  pub fn set_value(&self, value: char) {
2316    self.0.borrow_mut().value = value;
2317  }
2318
2319  /// Char value of the token.
2320  pub fn value(&self) -> char {
2321    self.0.borrow().value
2322  }
2323
2324  /// Removes the node from the JSON.
2325  pub fn remove(self) {
2326    Into::<CstNode>::into(self).remove_raw()
2327  }
2328}
2329
2330impl Display for CstToken {
2331  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2332    write!(f, "{}", self.0.borrow().value)
2333  }
2334}
2335
2336/// Blank space excluding newlines.
2337#[derive(Debug, Clone)]
2338pub struct CstWhitespace(Rc<RefCell<CstValueInner<String>>>);
2339
2340impl_leaf_methods!(CstWhitespace, Whitespace);
2341
2342impl CstWhitespace {
2343  fn new(value: String) -> Self {
2344    Self(CstValueInner::new(value))
2345  }
2346
2347  /// Sets the whitespace value.
2348  pub fn set_value(&self, value: String) {
2349    self.0.borrow_mut().value = value;
2350  }
2351
2352  /// Whitespace value of the node.
2353  pub fn value(&self) -> String {
2354    self.0.borrow().value.clone()
2355  }
2356
2357  /// Removes the node from the JSON.
2358  pub fn remove(self) {
2359    Into::<CstNode>::into(self).remove_raw()
2360  }
2361}
2362
2363impl Display for CstWhitespace {
2364  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2365    write!(f, "{}", self.0.borrow().value)
2366  }
2367}
2368
2369/// Kind of newline.
2370#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
2371pub enum CstNewlineKind {
2372  #[default]
2373  LineFeed,
2374  CarriageReturnLineFeed,
2375}
2376
2377/// Newline character (Lf or crlf).
2378#[derive(Debug, Clone)]
2379pub struct CstNewline(Rc<RefCell<CstValueInner<CstNewlineKind>>>);
2380
2381impl_leaf_methods!(CstNewline, Newline);
2382
2383impl CstNewline {
2384  fn new(kind: CstNewlineKind) -> Self {
2385    Self(CstValueInner::new(kind))
2386  }
2387
2388  /// Whether this is a line feed (LF) or carriage return line feed (CRLF).
2389  pub fn kind(&self) -> CstNewlineKind {
2390    self.0.borrow().value
2391  }
2392
2393  /// Sets the newline kind.
2394  pub fn set_kind(&self, kind: CstNewlineKind) {
2395    self.0.borrow_mut().value = kind;
2396  }
2397
2398  /// Removes the node from the JSON.
2399  pub fn remove(self) {
2400    Into::<CstNode>::into(self).remove_raw()
2401  }
2402}
2403
2404impl Display for CstNewline {
2405  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2406    match self.0.borrow().value {
2407      #[allow(clippy::write_with_newline)] // better to be explicit
2408      CstNewlineKind::LineFeed => write!(f, "\n"),
2409      CstNewlineKind::CarriageReturnLineFeed => write!(f, "\r\n"),
2410    }
2411  }
2412}
2413
2414#[derive(Debug, Clone)]
2415pub struct CstComment(Rc<RefCell<CstValueInner<String>>>);
2416
2417impl_leaf_methods!(CstComment, Comment);
2418
2419impl CstComment {
2420  fn new(value: String) -> Self {
2421    Self(CstValueInner::new(value))
2422  }
2423
2424  /// Whether this is a line comment.
2425  pub fn is_line_comment(&self) -> bool {
2426    self.0.borrow().value.starts_with("//")
2427  }
2428
2429  /// Sets the raw value of the comment.
2430  ///
2431  /// This SHOULD include `//` or be surrounded in `/* ... */` or
2432  /// else you'll be inserting a syntax error.
2433  pub fn set_raw_value(&self, value: String) {
2434    self.0.borrow_mut().value = value;
2435  }
2436
2437  /// Raw value of the comment including `//` or `/* ... */`.
2438  pub fn raw_value(&self) -> String {
2439    self.0.borrow().value.clone()
2440  }
2441
2442  /// Removes the node from the JSON.
2443  pub fn remove(self) {
2444    if self.is_line_comment() {
2445      for node in self.previous_siblings() {
2446        if node.is_whitespace() {
2447          node.remove_raw();
2448        } else {
2449          if node.is_newline() {
2450            node.remove_raw();
2451          }
2452          break;
2453        }
2454      }
2455    }
2456
2457    Into::<CstNode>::into(self).remove_raw()
2458  }
2459}
2460
2461impl Display for CstComment {
2462  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2463    write!(f, "{}", self.0.borrow().value)
2464  }
2465}
2466
2467struct CstBuilder<'a> {
2468  pub text: &'a str,
2469  pub tokens: VecDeque<crate::tokens::TokenAndRange<'a>>,
2470}
2471
2472impl<'a> CstBuilder<'a> {
2473  pub fn build(&mut self, ast_value: Option<crate::ast::Value<'a>>) -> CstRootNode {
2474    let root_node = CstContainerNode::Root(CstRootNode(Rc::new(RefCell::new(CstChildrenInner {
2475      parent: None,
2476      value: Vec::new(),
2477    }))));
2478
2479    if let Some(ast_value) = ast_value {
2480      let range = ast_value.range();
2481      self.scan_from_to(&root_node, 0, range.start);
2482      self.build_value(&root_node, ast_value);
2483      self.scan_from_to(&root_node, range.end, self.text.len());
2484    } else {
2485      self.scan_from_to(&root_node, 0, self.text.len());
2486    }
2487
2488    match root_node {
2489      CstContainerNode::Root(node) => node,
2490      _ => unreachable!(),
2491    }
2492  }
2493
2494  fn scan_from_to(&mut self, container: &CstContainerNode, from: usize, to: usize) {
2495    if from == to {
2496      return;
2497    }
2498
2499    let mut last_from = from;
2500    while let Some(token) = self.tokens.front() {
2501      if token.range.end <= from {
2502        self.tokens.pop_front();
2503      } else if token.range.start < to {
2504        if token.range.start > last_from {
2505          self.build_whitespace(container, &self.text[last_from..token.range.start]);
2506        }
2507        let token = self.tokens.pop_front().unwrap();
2508        match token.token {
2509          crate::tokens::Token::OpenBrace
2510          | crate::tokens::Token::CloseBrace
2511          | crate::tokens::Token::OpenBracket
2512          | crate::tokens::Token::CloseBracket
2513          | crate::tokens::Token::Comma
2514          | crate::tokens::Token::Colon => {
2515            self.build_token(container, token.token.as_str().chars().next().unwrap());
2516          }
2517          crate::tokens::Token::Null
2518          | crate::tokens::Token::String(_)
2519          | crate::tokens::Token::Word(_)
2520          | crate::tokens::Token::Boolean(_)
2521          | crate::tokens::Token::Number(_) => unreachable!(
2522            "programming error parsing cst {:?} scanning {} to {}",
2523            token.token, from, to
2524          ),
2525          crate::tokens::Token::CommentLine(_) | crate::tokens::Token::CommentBlock(_) => {
2526            container
2527              .raw_append_child(CstComment::new(self.text[token.range.start..token.range.end].to_string()).into());
2528          }
2529        }
2530        last_from = token.range.end;
2531      } else {
2532        break;
2533      }
2534    }
2535
2536    if last_from < to {
2537      self.build_whitespace(container, &self.text[last_from..to]);
2538    }
2539  }
2540
2541  fn build_value(&mut self, container: &CstContainerNode, ast_value: ast::Value<'_>) {
2542    match ast_value {
2543      ast::Value::StringLit(string_lit) => self.build_string_lit(container, string_lit),
2544      ast::Value::NumberLit(number_lit) => {
2545        container.raw_append_child(CstNumberLit::new(number_lit.value.to_string()).into())
2546      }
2547      ast::Value::BooleanLit(boolean_lit) => container.raw_append_child(CstBooleanLit::new(boolean_lit.value).into()),
2548      ast::Value::Object(object) => {
2549        let object = self.build_object(object);
2550        container.raw_append_child(object.into())
2551      }
2552      ast::Value::Array(array) => {
2553        let array = self.build_array(array);
2554        container.raw_append_child(array.into())
2555      }
2556      ast::Value::NullKeyword(_) => container.raw_append_child(CstNullKeyword::new().into()),
2557    }
2558  }
2559
2560  fn build_object(&mut self, object: ast::Object<'_>) -> CstContainerNode {
2561    let container = CstContainerNode::Object(CstObject::new_no_tokens());
2562    let mut last_range_end = object.range.start;
2563    for prop in object.properties {
2564      self.scan_from_to(&container, last_range_end, prop.range.start);
2565      last_range_end = prop.range.end;
2566      let object_prop = self.build_object_prop(prop);
2567      container.raw_append_child(CstNode::Container(object_prop));
2568    }
2569    self.scan_from_to(&container, last_range_end, object.range.end);
2570
2571    container
2572  }
2573
2574  fn build_object_prop(&mut self, prop: ast::ObjectProp<'_>) -> CstContainerNode {
2575    let container = CstContainerNode::ObjectProp(CstObjectProp::new());
2576    let name_range = prop.name.range();
2577    let value_range = prop.value.range();
2578
2579    match prop.name {
2580      ast::ObjectPropName::String(string_lit) => {
2581        self.build_string_lit(&container, string_lit);
2582      }
2583      ast::ObjectPropName::Word(word_lit) => {
2584        container.raw_append_child(CstWordLit::new(word_lit.value.to_string()).into());
2585      }
2586    }
2587
2588    self.scan_from_to(&container, name_range.end, value_range.start);
2589    self.build_value(&container, prop.value);
2590
2591    container
2592  }
2593
2594  fn build_token(&self, container: &CstContainerNode, value: char) {
2595    container.raw_append_child(CstToken::new(value).into());
2596  }
2597
2598  fn build_whitespace(&self, container: &CstContainerNode, value: &str) {
2599    if value.is_empty() {
2600      return;
2601    }
2602
2603    let mut last_found_index = 0;
2604    let mut chars = value.char_indices().peekable();
2605    let maybe_add_previous_text = |from: usize, to: usize| {
2606      let text = &value[from..to];
2607      if !text.is_empty() {
2608        container.raw_append_child(CstWhitespace::new(text.to_string()).into());
2609      }
2610    };
2611    while let Some((i, c)) = chars.next() {
2612      if c == '\r' && chars.peek().map(|(_, c)| *c) == Some('\n') {
2613        maybe_add_previous_text(last_found_index, i);
2614        container.raw_append_child(CstNewline::new(CstNewlineKind::CarriageReturnLineFeed).into());
2615        last_found_index = i + 2;
2616        chars.next(); // move past the \n
2617      } else if c == '\n' {
2618        maybe_add_previous_text(last_found_index, i);
2619        container.raw_append_child(CstNewline::new(CstNewlineKind::LineFeed).into());
2620        last_found_index = i + 1;
2621      }
2622    }
2623
2624    maybe_add_previous_text(last_found_index, value.len());
2625  }
2626
2627  fn build_string_lit(&self, container: &CstContainerNode, lit: ast::StringLit<'_>) {
2628    container.raw_append_child(CstStringLit::new(self.text[lit.range.start..lit.range.end].to_string()).into());
2629  }
2630
2631  fn build_array(&mut self, array: ast::Array<'_>) -> CstContainerNode {
2632    let container = CstContainerNode::Array(CstArray::new_no_tokens());
2633    let mut last_range_end = array.range.start;
2634    for element in array.elements {
2635      let element_range = element.range();
2636      self.scan_from_to(&container, last_range_end, element_range.start);
2637      self.build_value(&container, element);
2638      last_range_end = element_range.end;
2639    }
2640    self.scan_from_to(&container, last_range_end, array.range.end);
2641
2642    container
2643  }
2644}
2645
2646/// A sort of an object's properties, waiting to be told how to order them.
2647///
2648/// Built by [`CstObject::sort_properties`].
2649#[must_use = "nothing is sorted until `by` or `by_key` is called"]
2650pub struct PropertySort<'a> {
2651  object: &'a CstObject,
2652  options: SortOptions<'a>,
2653}
2654
2655impl<'a> PropertySort<'a> {
2656  /// Leaves a comment that heads a group of properties where it was written.
2657  ///
2658  /// A comment with a blank line above it reads as a heading for the properties beneath it rather
2659  /// than as a description of the first of them, so it stays put and the properties sort past it.
2660  /// Without this, every comment above a property travels with that property, which carries a
2661  /// heading off to wherever its first property happens to land.
2662  ///
2663  /// The blank line itself stays too, as does a blank line with no comment under it.
2664  ///
2665  /// # Example
2666  ///
2667  /// ```
2668  /// use jsonc_parser::ParseOptions;
2669  /// use jsonc_parser::cst::CstRootNode;
2670  ///
2671  /// let json_text = r#"{
2672  ///   "prop": 1,
2673  ///
2674  ///   // section
2675  ///   "prop2": 2,
2676  ///   "prop1": 1
2677  /// }"#;
2678  ///
2679  /// let root = CstRootNode::parse(json_text, &ParseOptions::default()).unwrap();
2680  /// let root_obj = root.object_value().unwrap();
2681  /// root_obj
2682  ///   .sort_properties()
2683  ///   .pin_comment_headers()
2684  ///   .by_key(|prop| prop.decoded_name());
2685  ///
2686  /// assert_eq!(root.to_string(), r#"{
2687  ///   "prop": 1,
2688  ///
2689  ///   // section
2690  ///   "prop1": 1,
2691  ///   "prop2": 2
2692  /// }"#);
2693  /// ```
2694  pub fn pin_comment_headers(mut self) -> Self {
2695    self.options.header_rule = Some(Box::new(blank_line_header_rule));
2696    self
2697  }
2698
2699  /// Decides for each property how much of what was written above it is a heading for what
2700  /// follows rather than part of the property.
2701  ///
2702  /// `rule` is handed the property and the comments written above it, in the order they appear,
2703  /// and returns how many of them, counting from the top, stay where they were written. The rest
2704  /// travel with the property, as does the blank line under whatever stayed.
2705  ///
2706  /// Returning `comments.len()` pins everything above the property and `0` pins nothing, so
2707  /// [`PropertySort::pin_comment_headers`] is `if prop.has_blank_line_before() { comments.len() }
2708  /// else { 0 }`. A count in between splits a block that is partly a heading and partly a note
2709  /// about the property itself.
2710  ///
2711  /// The rule is only consulted where a header could be written, which is a property on a line of
2712  /// its own; it is not called for an object written on one line.
2713  ///
2714  /// The rule must not change the object's children. Doing so leaves the sort with nothing safe to
2715  /// write back, so it gives up and leaves the object as the rule left it.
2716  pub fn pin_comment_headers_with(mut self, mut rule: impl FnMut(&CstObjectProp, &[CstComment]) -> usize + 'a) -> Self {
2717    self.options.header_rule = Some(Box::new(move |element, comments| match element.as_object_prop() {
2718      Some(prop) => rule(&prop, comments),
2719      None => 0,
2720    }));
2721    self
2722  }
2723
2724  /// Sorts each run of properties between blank lines on its own, so that no property crosses one.
2725  ///
2726  /// A blank line, and whatever was written under it, is the boundary between two groups, and a
2727  /// boundary stays where it is. A rule set by [`PropertySort::pin_comment_headers_with`] still
2728  /// decides what travels with the properties inside each group.
2729  ///
2730  /// # Example
2731  ///
2732  /// ```
2733  /// use jsonc_parser::ParseOptions;
2734  /// use jsonc_parser::cst::CstRootNode;
2735  ///
2736  /// let json_text = r#"{
2737  ///   "m": 1,
2738  ///
2739  ///   // section
2740  ///   "z": 2,
2741  ///   "a": 3
2742  /// }"#;
2743  ///
2744  /// let root = CstRootNode::parse(json_text, &ParseOptions::default()).unwrap();
2745  /// let root_obj = root.object_value().unwrap();
2746  /// root_obj
2747  ///   .sort_properties()
2748  ///   .within_groups()
2749  ///   .by_key(|prop| prop.decoded_name());
2750  ///
2751  /// // "m" stays above the blank line and only "z" and "a" trade places
2752  /// assert_eq!(root.to_string(), r#"{
2753  ///   "m": 1,
2754  ///
2755  ///   // section
2756  ///   "a": 3,
2757  ///   "z": 2
2758  /// }"#);
2759  /// ```
2760  pub fn within_groups(mut self) -> Self {
2761    self.options.within_groups = true;
2762    self
2763  }
2764
2765  /// Sorts the properties with the given comparator.
2766  ///
2767  /// The sort is stable, so properties that compare equal keep the order they were written in. The
2768  /// comparator must describe a total order, as the sort may panic otherwise, and it must not
2769  /// change the object's children; see [`PropertySort::pin_comment_headers_with`].
2770  pub fn by(self, mut compare: impl FnMut(&CstObjectProp, &CstObjectProp) -> Ordering) {
2771    let object = self.object.clone().into();
2772    sort_comma_separated_children(&object, self.options, |groups| {
2773      groups.sort_by(|left, right| {
2774        match (left.element.as_object_prop(), right.element.as_object_prop()) {
2775          (Some(left), Some(right)) => compare(&left, &right),
2776          // an object holds properties, so this only happens if the tree has been manipulated into
2777          // holding something else, in which case leaving the order alone is the safe answer
2778          _ => Ordering::Equal,
2779        }
2780      })
2781    });
2782  }
2783
2784  /// Sorts the properties by a key, which is worked out once per property.
2785  ///
2786  /// A child that isn't a property, which is only possible if the tree has been manipulated into
2787  /// holding something else, has no key and sorts above every property. Behaves like
2788  /// [`PropertySort::by`] in every other respect.
2789  pub fn by_key<K: Ord>(self, mut key: impl FnMut(&CstObjectProp) -> K) {
2790    let object = self.object.clone().into();
2791    sort_comma_separated_children(&object, self.options, |groups| {
2792      groups.sort_by_cached_key(|group| group.element.as_object_prop().map(|prop| key(&prop)))
2793    });
2794  }
2795}
2796
2797/// A sort of an array's elements, waiting to be told how to order them.
2798///
2799/// Built by [`CstArray::sort_elements`].
2800#[must_use = "nothing is sorted until `by` or `by_key` is called"]
2801pub struct ElementSort<'a> {
2802  array: &'a CstArray,
2803  options: SortOptions<'a>,
2804}
2805
2806impl<'a> ElementSort<'a> {
2807  /// Leaves a comment that heads a group of elements where it was written.
2808  ///
2809  /// Behaves like [`PropertySort::pin_comment_headers`].
2810  pub fn pin_comment_headers(self) -> Self {
2811    self.pin_comment_headers_with(blank_line_header_rule)
2812  }
2813
2814  /// Decides for each element how much of what was written above it is a heading for what follows
2815  /// rather than part of the element.
2816  ///
2817  /// Behaves like [`PropertySort::pin_comment_headers_with`].
2818  pub fn pin_comment_headers_with(mut self, rule: impl FnMut(&CstNode, &[CstComment]) -> usize + 'a) -> Self {
2819    self.options.header_rule = Some(Box::new(rule));
2820    self
2821  }
2822
2823  /// Sorts each run of elements between blank lines on its own, so that no element crosses one.
2824  ///
2825  /// Behaves like [`PropertySort::within_groups`].
2826  pub fn within_groups(mut self) -> Self {
2827    self.options.within_groups = true;
2828    self
2829  }
2830
2831  /// Sorts the elements with the given comparator.
2832  ///
2833  /// Behaves like [`PropertySort::by`].
2834  pub fn by(self, mut compare: impl FnMut(&CstNode, &CstNode) -> Ordering) {
2835    let array = self.array.clone().into();
2836    sort_comma_separated_children(&array, self.options, |groups| {
2837      groups.sort_by(|left, right| compare(&left.element, &right.element))
2838    });
2839  }
2840
2841  /// Sorts the elements by a key, which is worked out once per element.
2842  ///
2843  /// Behaves like [`PropertySort::by_key`].
2844  pub fn by_key<K: Ord>(self, mut key: impl FnMut(&CstNode) -> K) {
2845    let array = self.array.clone().into();
2846    sort_comma_separated_children(&array, self.options, |groups| {
2847      groups.sort_by_cached_key(|group| key(&group.element))
2848    });
2849  }
2850}
2851
2852/// Decides how many of the comments written above an element stay where they are when it moves.
2853type HeaderRule<'a> = Box<dyn FnMut(&CstNode, &[CstComment]) -> usize + 'a>;
2854
2855/// What a sort does with the trivia it moves past, set through [`PropertySort`] and [`ElementSort`].
2856#[derive(Default)]
2857struct SortOptions<'a> {
2858  header_rule: Option<HeaderRule<'a>>,
2859  within_groups: bool,
2860}
2861
2862impl SortOptions<'_> {
2863  /// How many of `comments` stay where they were written rather than travelling with `element`.
2864  fn pinned_comment_count(&mut self, element: &CstNode, comments: &[CstComment]) -> usize {
2865    match &mut self.header_rule {
2866      Some(rule) => rule(element, comments),
2867      None => 0,
2868    }
2869  }
2870}
2871
2872/// The rule [`PropertySort::pin_comment_headers`] and [`ElementSort::pin_comment_headers`] use: a
2873/// comment with a blank line above it heads what follows rather than describing the first of them.
2874fn blank_line_header_rule(element: &CstNode, comments: &[CstComment]) -> usize {
2875  if element.has_blank_line_before() {
2876    comments.len()
2877  } else {
2878    0
2879  }
2880}
2881
2882/// What sits between two elements and stays where it is, because it positions whatever comes next
2883/// rather than belonging to either element.
2884///
2885/// Both parts are stretches of the container's own children, which moving elements around only
2886/// ever copies, so they're held as ranges rather than as lists of their own.
2887struct Separator {
2888  /// The line break that ended the previous element line, whatever of the trivia under it was
2889  /// written as a header for what follows, and on a single line the space between two elements.
2890  before: Range<usize>,
2891  /// The indentation directly in front of the element.
2892  indent: Range<usize>,
2893}
2894
2895/// An element of a comma separated container along with the trivia that travels with it.
2896///
2897/// Held as ranges for the same reason as [`Separator`].
2898struct SortableGroup {
2899  /// Where the element was written, so that a sort changing nothing can leave the tree alone.
2900  index: usize,
2901  /// Whether a blank line separates this element from the one before it, which is what divides a
2902  /// container into groups.
2903  starts_group: bool,
2904  /// What was written before the element and belongs to it: its own comments and indentation.
2905  leading: Range<usize>,
2906  element: CstNode,
2907  /// Whatever separates the element from its comma, the comma, and any comment written after that
2908  /// on the same line.
2909  trailing: Range<usize>,
2910  /// Where the element's comma sits, if it was written with one.
2911  comma: Option<usize>,
2912}
2913
2914/// Reorders the elements of an object or array, moving what was written with each element along
2915/// with it and leaving the separators between them where they are.
2916///
2917/// `sort` is handed the elements of one group at a time, in the order they were written, and is
2918/// expected to sort them stably. Without [`SortOptions::within_groups`] there is a single group
2919/// holding everything.
2920fn sort_comma_separated_children(
2921  container: &CstContainerNode,
2922  mut options: SortOptions<'_>,
2923  mut sort: impl FnMut(&mut [SortableGroup]),
2924) {
2925  let children = container.children();
2926  // the surrounding tokens are what the elements sit between, so there's nothing to sort without them
2927  if children.len() < 2 || !children[0].is_token() || !children[children.len() - 1].is_token() {
2928    return;
2929  }
2930  let region = &children[1..children.len() - 1];
2931
2932  // Split the region into the groups that move and the separators that stay put. Each group is
2933  // preceded by exactly one separator, so the two line up.
2934  let mut separators: Vec<Separator> = Vec::new();
2935  let mut groups: Vec<SortableGroup> = Vec::new();
2936  let mut index = 0;
2937  let tail = loop {
2938    let run_start = index;
2939    while index < region.len() && !is_sortable_element(&region[index]) {
2940      index += 1;
2941    }
2942    if index == region.len() {
2943      // what follows the last element belongs to no element and stays where it is
2944      break run_start..region.len();
2945    }
2946    let run = run_start..index;
2947    let starts_group = has_blank_line(region[run.clone()].iter().cloned());
2948    let (separator, leading) = split_separator(region, run, &region[index], starts_group, &mut options);
2949    separators.push(separator);
2950    let trailing = index + 1..trailing_run_end(region, index + 1);
2951    groups.push(SortableGroup {
2952      index: groups.len(),
2953      starts_group,
2954      leading,
2955      element: region[index].clone(),
2956      comma: region[trailing.clone()]
2957        .iter()
2958        .position(|n| n.is_comma())
2959        .map(|at| trailing.start + at),
2960      trailing: trailing.clone(),
2961    });
2962    index = trailing.end;
2963  };
2964
2965  if groups.len() < 2 {
2966    return;
2967  }
2968
2969  // whether the author ended the container with a comma, which the new last element takes over
2970  let ends_with_comma = groups[groups.len() - 1].comma.is_some();
2971  if options.within_groups {
2972    // a blank line divides the container, and a divider is not something an element sorts past
2973    for group in groups.chunk_by_mut(|_, next| !next.starts_group) {
2974      sort(group);
2975    }
2976  } else {
2977    sort(&mut groups);
2978  }
2979  // Changing the container while the sort runs would leave the ranges worked out above pointing
2980  // at children that have moved, so writing them back would undo the change and detach whatever
2981  // the caller is holding. A child that was removed or replaced no longer answers to its slot.
2982  let unchanged = container.children().len() == children.len()
2983    && children
2984      .iter()
2985      .enumerate()
2986      .all(|(index, child)| child.parent_info().map(|info| info.child_index) == Some(index));
2987  if !unchanged {
2988    return;
2989  }
2990  if groups
2991    .iter()
2992    .enumerate()
2993    .all(|(position, group)| position == group.index)
2994  {
2995    return;
2996  }
2997
2998  // a blank line here reads as a gap under the open token rather than as something written with
2999  // the element that follows, so it doesn't travel with whatever sorted to the top
3000  let first_leading = &mut groups[0].leading;
3001  first_leading.start += leading_blank_line_len(&region[first_leading.clone()]);
3002
3003  let last_index = groups.len() - 1;
3004  let mut new_children = Vec::with_capacity(children.len());
3005  new_children.push(children[0].clone());
3006  for (position, (separator, group)) in separators.into_iter().zip(groups).enumerate() {
3007    new_children.extend_from_slice(&region[separator.before]);
3008    new_children.extend_from_slice(&region[group.leading]);
3009    new_children.extend_from_slice(&region[separator.indent]);
3010    new_children.push(group.element);
3011    let wants_comma = position < last_index || ends_with_comma;
3012    push_trailing(&mut new_children, region, group.trailing, group.comma, wants_comma);
3013  }
3014  new_children.extend_from_slice(&region[tail]);
3015  new_children.push(children[children.len() - 1].clone());
3016  let newline_kind = container
3017    .root_node()
3018    .map(|root| root.newline_kind())
3019    .unwrap_or(CstNewlineKind::LineFeed);
3020  restore_line_comment_line_ends(&mut new_children, newline_kind);
3021  container.raw_set_children(new_children);
3022}
3023
3024/// Whether the node is something an object or array holds rather than the punctuation and trivia
3025/// written around it.
3026fn is_sortable_element(node: &CstNode) -> bool {
3027  !node.is_trivia() && !node.is_token()
3028}
3029
3030/// How much of the start of a run is blank lines, counting a line of nothing but whitespace as one.
3031///
3032/// Stops at the first line holding anything, so the indentation in front of a comment is left for
3033/// the comment rather than counted as a blank line of its own.
3034fn leading_blank_line_len(run: &[CstNode]) -> usize {
3035  let mut len = 0;
3036  let mut index = 0;
3037  while index < run.len() {
3038    let mut end = index;
3039    while end < run.len() && run[end].is_whitespace() {
3040      end += 1;
3041    }
3042    if end < run.len() && run[end].is_newline() {
3043      index = end + 1;
3044      len = index;
3045    } else {
3046      break;
3047    }
3048  }
3049  len
3050}
3051
3052/// Whether a run of trivia leaves a line empty, which is what marks a group boundary and what
3053/// tells a comment heading a group from one describing the element beneath it.
3054///
3055/// Reads the same either way round, so the run may be walked forwards or backwards.
3056fn has_blank_line(run: impl IntoIterator<Item = CstNode>) -> bool {
3057  let mut ended_a_line = false;
3058  for node in run {
3059    if node.is_newline() {
3060      if ended_a_line {
3061        return true;
3062      }
3063      ended_a_line = true;
3064    } else if !node.is_whitespace() {
3065      ended_a_line = false;
3066    }
3067  }
3068  false
3069}
3070
3071/// Splits what was written between two elements into the separator, which stays where it is, and
3072/// the trivia belonging to the element that follows.
3073///
3074/// The separator is the line break that ended the previous element's line together with the
3075/// indentation under it, or on a single line the whitespace between the two elements. Both
3076/// position whatever comes next, so they belong to the slot rather than to either element. What
3077/// sits between them came with the element that follows and travels with it, except for however
3078/// much of it the sort's header rule says was written as a header for what comes next.
3079fn split_separator(
3080  region: &[CstNode],
3081  run: Range<usize>,
3082  element: &CstNode,
3083  starts_group: bool,
3084  options: &mut SortOptions<'_>,
3085) -> (Separator, Range<usize>) {
3086  let nodes = &region[run.clone()];
3087  let Some(newline) = nodes.iter().position(|n| n.is_newline()) else {
3088    // nothing indents anything on a single line, so all that is here is the space between the two
3089    let before = nodes.iter().take_while(|n| n.is_whitespace()).count();
3090    return (
3091      Separator {
3092        before: run.start..run.start + before,
3093        indent: run.end..run.end,
3094      },
3095      run.start + before..run.end,
3096    );
3097  };
3098  let indent_len = nodes[newline + 1..]
3099    .iter()
3100    .rev()
3101    .take_while(|n| n.is_whitespace())
3102    .count();
3103  let indent_start = nodes.len() - indent_len;
3104  let leading = &nodes[newline + 1..indent_start];
3105  let header_len = if starts_group && options.within_groups {
3106    // the blank line and whatever was written under it are the boundary between two groups, and a
3107    // boundary is not something an element sorts past, so none of it travels
3108    leading.len()
3109  } else {
3110    header_len(leading, element, options)
3111  };
3112  let leading_start = newline + 1 + header_len;
3113  (
3114    Separator {
3115      before: run.start..run.start + leading_start,
3116      indent: run.start + indent_start..run.end,
3117    },
3118    run.start + leading_start..run.start + indent_start,
3119  )
3120}
3121
3122/// How much of what was written above an element was written as a header for it rather than as
3123/// part of it, and so stays where it is when the element moves.
3124///
3125/// The header runs up to the line the first comment that isn't part of it begins on, so that the
3126/// blank line under a header stays with the header where it reads.
3127fn header_len(leading: &[CstNode], element: &CstNode, options: &mut SortOptions<'_>) -> usize {
3128  // the common sort sets no rule at all, and then nothing above an element ever stays
3129  if options.header_rule.is_none() {
3130    return 0;
3131  }
3132  let comments = leading
3133    .iter()
3134    .filter_map(|node| match node {
3135      CstNode::Leaf(CstLeafNode::Comment(comment)) => Some(comment.clone()),
3136      _ => None,
3137    })
3138    .collect::<Vec<_>>();
3139  let pinned = options.pinned_comment_count(element, &comments);
3140  if pinned >= comments.len() {
3141    return leading.len();
3142  }
3143  // A blank line is how the container was laid out rather than something written with the element,
3144  // so it stays put whenever the caller is deciding what travels, even when no comment does.
3145  let blank_lines = leading_blank_line_len(leading);
3146  if pinned == 0 {
3147    return blank_lines;
3148  }
3149  let first_travelling = leading
3150    .iter()
3151    .enumerate()
3152    .filter(|(_, node)| node.is_comment())
3153    .map(|(index, _)| index)
3154    .nth(pinned)
3155    .expect("a comment past the pinned ones, since fewer were pinned than there are");
3156  // back up to the start of that comment's line, so that a header never ends part way along one
3157  // and leaves what follows glued to it
3158  let mut split = first_travelling;
3159  while split > 0 && !leading[split - 1].is_newline() {
3160    split -= 1;
3161  }
3162  split.max(blank_lines)
3163}
3164
3165/// The end of the run after an element that was written with it: whatever separates the element
3166/// from its comma, the comma itself, and any comment written after that on the same line.
3167///
3168/// The comma comes along wherever the author put it, including on a later line, so that it can
3169/// never be mistaken for something belonging to the element that follows.
3170fn trailing_run_end(region: &[CstNode], start: usize) -> usize {
3171  let mut end = start;
3172  for (index, node) in region.iter().enumerate().skip(start) {
3173    if is_sortable_element(node) {
3174      break;
3175    } else if node.is_comma() {
3176      end = index + 1;
3177      break;
3178    }
3179  }
3180  // a comment after that was written with the element too, but only when nothing else shares its line
3181  if rest_of_line_is_trivia(region, end) {
3182    for (index, node) in region.iter().enumerate().skip(end) {
3183      if node.is_newline() {
3184        break;
3185      } else if node.is_comment() {
3186        end = index + 1;
3187      }
3188    }
3189  }
3190  end
3191}
3192
3193/// Whether the rest of the line holds nothing but whitespace and comments, which is what decides
3194/// whether a comment there was written with what precedes it or with what follows.
3195fn rest_of_line_is_trivia(region: &[CstNode], start: usize) -> bool {
3196  region
3197    .iter()
3198    .skip(start)
3199    .take_while(|n| !n.is_newline())
3200    .all(|n| n.is_whitespace() || n.is_comment())
3201}
3202
3203/// Writes out what followed the element, with its comma added or dropped to suit its new position.
3204fn push_trailing(
3205  out: &mut Vec<CstNode>,
3206  region: &[CstNode],
3207  trailing: Range<usize>,
3208  comma: Option<usize>,
3209  wants_comma: bool,
3210) {
3211  match comma {
3212    Some(comma) if !wants_comma => {
3213      // the space that offset the comma has nothing left to offset
3214      let end = if comma > trailing.start && region[comma - 1].is_whitespace() {
3215        comma - 1
3216      } else {
3217        comma
3218      };
3219      out.extend_from_slice(&region[trailing.start..end]);
3220      out.extend_from_slice(&region[comma + 1..trailing.end]);
3221    }
3222    None if wants_comma => {
3223      out.push(CstToken::new(',').into());
3224      out.extend_from_slice(&region[trailing]);
3225    }
3226    _ => out.extend_from_slice(&region[trailing]),
3227  }
3228}
3229
3230/// Puts back the line break a line comment needs in order to end where it did.
3231///
3232/// A line comment runs to the end of its line, so moving one can leave it in front of what used to
3233/// come earlier, commenting out the next element or the closing token.
3234fn restore_line_comment_line_ends(children: &mut Vec<CstNode>, newline_kind: CstNewlineKind) {
3235  let mut index = 0;
3236  while index < children.len() {
3237    if is_line_comment(&children[index])
3238      && let Some(next) = children[index + 1..].iter().position(|n| !n.is_whitespace())
3239      && !children[index + 1 + next].is_newline()
3240    {
3241      children.insert(index + 1, CstNewline::new(newline_kind).into());
3242    }
3243    index += 1;
3244  }
3245}
3246
3247fn is_line_comment(node: &CstNode) -> bool {
3248  matches!(node, CstNode::Leaf(CstLeafNode::Comment(comment)) if comment.is_line_comment())
3249}
3250
3251fn remove_comma_separated(node: CstNode) {
3252  fn check_next_node_same_line(trailing_comma: &CstToken) -> bool {
3253    for sibling in trailing_comma.next_siblings() {
3254      match sibling {
3255        CstNode::Container(_) => return true,
3256        CstNode::Leaf(n) => match n {
3257          CstLeafNode::BooleanLit(_)
3258          | CstLeafNode::NullKeyword(_)
3259          | CstLeafNode::NumberLit(_)
3260          | CstLeafNode::StringLit(_)
3261          | CstLeafNode::WordLit(_)
3262          | CstLeafNode::Token(_) => return true,
3263          CstLeafNode::Whitespace(_) | CstLeafNode::Comment(_) => {
3264            // keep going
3265          }
3266          CstLeafNode::Newline(_) => return false,
3267        },
3268      }
3269    }
3270
3271    true
3272  }
3273
3274  let parent = node.parent();
3275  let trailing_comma = node.trailing_comma();
3276  let is_in_array_or_obj = parent
3277    .as_ref()
3278    .map(|p| matches!(p, CstContainerNode::Array(_) | CstContainerNode::Object(_)))
3279    .unwrap_or(false);
3280  let remove_up_to_next_line = trailing_comma
3281    .as_ref()
3282    .map(|c| !check_next_node_same_line(c))
3283    .unwrap_or(true);
3284
3285  for previous in node.previous_siblings() {
3286    if previous.is_trivia() && !previous.is_newline() {
3287      previous.remove_raw();
3288    } else {
3289      break;
3290    }
3291  }
3292
3293  let mut found_newline = false;
3294
3295  // remove up to the trailing comma
3296  if trailing_comma.is_some() {
3297    let mut next_siblings = node.next_siblings();
3298    for next in next_siblings.by_ref() {
3299      let is_comma = next.is_comma();
3300      if next.is_newline() {
3301        found_newline = true;
3302      }
3303      next.remove_raw();
3304      if is_comma {
3305        break;
3306      }
3307    }
3308  } else if is_in_array_or_obj && let Some(previous_comma) = node.previous_siblings().find(|n| n.is_comma()) {
3309    previous_comma.remove();
3310  }
3311
3312  // remove up to the newline
3313  if remove_up_to_next_line && !found_newline {
3314    let mut next_siblings = node.next_siblings().peekable();
3315    while let Some(sibling) = next_siblings.next() {
3316      if sibling.is_trivia() {
3317        if sibling.is_newline() {
3318          sibling.remove_raw();
3319          break;
3320        } else if sibling.is_whitespace()
3321          && next_siblings
3322            .peek()
3323            .map(|n| !n.is_whitespace() && !n.is_newline() && !n.is_comment())
3324            .unwrap_or(false)
3325        {
3326          break;
3327        }
3328        sibling.remove_raw();
3329      } else {
3330        break;
3331      }
3332    }
3333  }
3334
3335  node.remove_raw();
3336
3337  if let Some(parent) = parent {
3338    match parent {
3339      CstContainerNode::Root(n) => {
3340        if n.children().iter().all(|c| c.is_whitespace() || c.is_newline()) {
3341          n.clear_children();
3342        }
3343      }
3344      CstContainerNode::Object(_) | CstContainerNode::Array(_) => {
3345        let children = parent.children();
3346        if children
3347          .iter()
3348          .skip(1)
3349          .take(children.len() - 2)
3350          .all(|c| c.is_whitespace() || c.is_newline())
3351        {
3352          for c in children {
3353            if c.is_whitespace() || c.is_newline() {
3354              c.remove();
3355            }
3356          }
3357        }
3358      }
3359      CstContainerNode::ObjectProp(_) => {}
3360    }
3361  }
3362}
3363
3364fn indent_text(node: &CstNode) -> Option<String> {
3365  let mut last_whitespace: Option<String> = None;
3366  for previous_sibling in node.previous_siblings() {
3367    match previous_sibling {
3368      CstNode::Container(_) => return None,
3369      CstNode::Leaf(leaf) => match leaf {
3370        CstLeafNode::Newline(_) => {
3371          return last_whitespace;
3372        }
3373        CstLeafNode::Whitespace(whitespace) => {
3374          last_whitespace = match last_whitespace {
3375            Some(last_whitespace) => Some(format!("{}{}", whitespace.value(), last_whitespace)),
3376            None => Some(whitespace.value()),
3377          };
3378        }
3379        CstLeafNode::Comment(_) => {
3380          last_whitespace = None;
3381        }
3382        _ => return None,
3383      },
3384    }
3385  }
3386  last_whitespace
3387}
3388
3389fn uses_trailing_commas(node: CstNode) -> bool {
3390  let node = match node {
3391    CstNode::Container(node) => node,
3392    CstNode::Leaf(_) => return false,
3393  };
3394  let mut pending_nodes: VecDeque<CstContainerNode> = VecDeque::from([node.clone()]);
3395  while let Some(node) = pending_nodes.pop_front() {
3396    let children = node.children();
3397    if !node.is_root() {
3398      if let Some(object) = node.as_object() {
3399        if children.iter().any(|c| c.is_whitespace()) {
3400          let properties = object.properties();
3401          if let Some(last_property) = properties.last() {
3402            return last_property.trailing_comma().is_some();
3403          }
3404        }
3405      } else if let Some(object) = node.as_array()
3406        && children.iter().any(|c| c.is_whitespace())
3407      {
3408        let elements = object.elements();
3409        if let Some(last_property) = elements.last() {
3410          return last_property.trailing_comma().is_some();
3411        }
3412      }
3413    }
3414
3415    for child in children {
3416      if let CstNode::Container(child) = child {
3417        pending_nodes.push_back(child);
3418      }
3419    }
3420  }
3421
3422  false // default to false
3423}
3424
3425fn replace_with(node: CstNode, replacement: InsertValue) -> Option<CstNode> {
3426  let mut child_index = node.child_index();
3427  let parent = node.parent()?;
3428  let indents = compute_indents(&parent.clone().into());
3429  let style_info = StyleInfo {
3430    newline_kind: parent.root_node().map(|r| r.newline_kind()).unwrap_or_default(),
3431    uses_trailing_commas: uses_trailing_commas(parent.clone().into()),
3432  };
3433  parent.remove_child_set_no_parent(child_index);
3434  parent.raw_insert_value_with_internal_indent(Some(&mut child_index), replacement, &style_info, &indents);
3435  parent.child_at_index(child_index - 1)
3436}
3437
3438enum InsertValue<'a> {
3439  Value(CstInputValue),
3440  Property(&'a str, CstInputValue),
3441}
3442
3443fn insert_or_append_to_container(
3444  container: &CstContainerNode,
3445  elements: Vec<CstNode>,
3446  index: Option<usize>,
3447  value: InsertValue,
3448) -> CstNode {
3449  fn has_separating_newline(siblings: impl Iterator<Item = CstNode>) -> bool {
3450    for sibling in siblings {
3451      if sibling.is_newline() {
3452        return true;
3453      } else if sibling.is_trivia() {
3454        continue;
3455      } else {
3456        break;
3457      }
3458    }
3459    false
3460  }
3461
3462  trim_inner_start_and_end_blanklines(container);
3463
3464  let children = container.children();
3465  let index = index.unwrap_or(elements.len());
3466  let index = std::cmp::min(index, elements.len());
3467  let next_node = elements.get(index);
3468  let previous_node = if index == 0 { None } else { elements.get(index - 1) };
3469  let style_info = StyleInfo {
3470    newline_kind: container.root_node().map(|r| r.newline_kind()).unwrap_or_default(),
3471    uses_trailing_commas: uses_trailing_commas(container.clone().into()),
3472  };
3473  let indents = compute_indents(&container.clone().into());
3474  let child_indents = elements
3475    .first()
3476    .map(compute_indents)
3477    .unwrap_or_else(|| indents.indent());
3478  let has_newline = children.iter().any(|child| child.is_newline());
3479  let force_multiline = has_newline
3480    || match &value {
3481      InsertValue::Value(v) => v.force_multiline(),
3482      InsertValue::Property(..) => true,
3483    };
3484  let mut insert_index: usize;
3485  let inserted_node: CstNode;
3486  if let Some(previous_node) = previous_node {
3487    if previous_node.trailing_comma().is_none() {
3488      let mut index = previous_node.child_index() + 1;
3489      container.raw_insert_child(Some(&mut index), CstToken::new(',').into());
3490    }
3491
3492    let trailing_comma: CstNode = previous_node.trailing_comma().unwrap().into();
3493    insert_index = trailing_comma
3494      .trailing_comments_same_line()
3495      .last()
3496      .map(|t| t.child_index())
3497      .unwrap_or_else(|| trailing_comma.child_index())
3498      + 1;
3499    if force_multiline {
3500      container.raw_insert_children(
3501        Some(&mut insert_index),
3502        vec![
3503          CstNewline::new(style_info.newline_kind).into(),
3504          CstWhitespace::new(child_indents.current_indent.clone()).into(),
3505        ],
3506      );
3507      container.raw_insert_value_with_internal_indent(Some(&mut insert_index), value, &style_info, &child_indents);
3508      inserted_node = container.child_at_index(insert_index - 1).unwrap();
3509    } else {
3510      container.raw_insert_child(Some(&mut insert_index), CstWhitespace::new(" ".to_string()).into());
3511      container.raw_insert_value_with_internal_indent(Some(&mut insert_index), value, &style_info, &child_indents);
3512      inserted_node = container.child_at_index(insert_index - 1).unwrap();
3513    }
3514  } else {
3515    insert_index = if elements.is_empty() {
3516      children
3517        .iter()
3518        .rev()
3519        .skip(1)
3520        .take_while(|t| t.is_whitespace() || t.is_newline())
3521        .last()
3522        .unwrap_or_else(|| children.last().unwrap())
3523        .child_index()
3524    } else {
3525      children.first().unwrap().child_index() + 1
3526    };
3527    if force_multiline {
3528      container.raw_insert_children(
3529        Some(&mut insert_index),
3530        vec![
3531          CstNewline::new(style_info.newline_kind).into(),
3532          CstWhitespace::new(child_indents.current_indent.clone()).into(),
3533        ],
3534      );
3535      container.raw_insert_value_with_internal_indent(Some(&mut insert_index), value, &style_info, &child_indents);
3536      inserted_node = container.child_at_index(insert_index - 1).unwrap();
3537      if next_node.is_none()
3538        && !has_separating_newline(container.child_at_index(insert_index - 1).unwrap().next_siblings())
3539      {
3540        container.raw_insert_children(
3541          Some(&mut insert_index),
3542          vec![
3543            CstNewline::new(style_info.newline_kind).into(),
3544            CstWhitespace::new(indents.current_indent.clone()).into(),
3545          ],
3546        );
3547      }
3548    } else {
3549      container.raw_insert_value_with_internal_indent(Some(&mut insert_index), value, &style_info, &child_indents);
3550      inserted_node = container.child_at_index(insert_index - 1).unwrap();
3551    }
3552  }
3553
3554  if next_node.is_some() {
3555    container.raw_insert_children(Some(&mut insert_index), vec![CstToken::new(',').into()]);
3556
3557    if force_multiline {
3558      let comma_token = container.child_at_index(insert_index - 1).unwrap();
3559      if !has_separating_newline(comma_token.next_siblings()) {
3560        container.raw_insert_children(
3561          Some(&mut insert_index),
3562          vec![
3563            CstNewline::new(style_info.newline_kind).into(),
3564            CstWhitespace::new(indents.current_indent.clone()).into(),
3565          ],
3566        );
3567      }
3568    } else {
3569      container.raw_insert_child(Some(&mut insert_index), CstWhitespace::new(" ".to_string()).into());
3570    }
3571  } else if style_info.uses_trailing_commas && force_multiline {
3572    container.raw_insert_children(Some(&mut insert_index), vec![CstToken::new(',').into()]);
3573  }
3574
3575  inserted_node
3576}
3577
3578fn set_trailing_commas(
3579  mode: TrailingCommaMode,
3580  parent: &CstContainerNode,
3581  elems_or_props: impl Iterator<Item = CstNode>,
3582) {
3583  let mut elems_or_props = elems_or_props.peekable();
3584  let use_trailing_commas = match mode {
3585    TrailingCommaMode::Never => false,
3586    TrailingCommaMode::IfMultiline => true,
3587  };
3588  while let Some(element) = elems_or_props.next() {
3589    // handle last element
3590    if elems_or_props.peek().is_none() {
3591      if use_trailing_commas {
3592        if element.trailing_comma().is_none() && parent.children().iter().any(|c| c.is_newline()) {
3593          let mut insert_index = element.child_index() + 1;
3594          parent.raw_insert_child(Some(&mut insert_index), CstToken::new(',').into());
3595        }
3596      } else if let Some(trailing_comma) = element.trailing_comma() {
3597        trailing_comma.remove();
3598      }
3599    }
3600
3601    // handle children
3602    let maybe_prop_value = element.as_object_prop().and_then(|p| p.value());
3603    match maybe_prop_value.unwrap_or(element) {
3604      CstNode::Container(CstContainerNode::Array(array)) => {
3605        array.set_trailing_commas(mode);
3606      }
3607      CstNode::Container(CstContainerNode::Object(object)) => {
3608        object.set_trailing_commas(mode);
3609      }
3610      _ => {}
3611    }
3612  }
3613}
3614
3615fn trim_inner_start_and_end_blanklines(node: &CstContainerNode) {
3616  fn remove_blank_lines_after_first(children: &mut Peekable<impl Iterator<Item = CstNode>>) {
3617    // try to find the first newline
3618    for child in children.by_ref() {
3619      if child.is_whitespace() {
3620        // keep searching
3621      } else if child.is_newline() {
3622        break; // found
3623      } else {
3624        return; // stop, no leading blank lines
3625      }
3626    }
3627
3628    let mut pending = Vec::new();
3629    for child in children.by_ref() {
3630      if child.is_whitespace() {
3631        pending.push(child);
3632      } else if child.is_newline() {
3633        child.remove();
3634        for child in pending.drain(..) {
3635          child.remove();
3636        }
3637      } else {
3638        break;
3639      }
3640    }
3641  }
3642
3643  let children = node.children();
3644  let len = children.len();
3645
3646  if len < 2 {
3647    return; // should never happen because this should only be called for array and object
3648  }
3649
3650  // remove blank lines from the front and back
3651  let mut children = children.into_iter().skip(1).take(len - 2).peekable();
3652  remove_blank_lines_after_first(&mut children);
3653  let mut children = children.rev().peekable();
3654  remove_blank_lines_after_first(&mut children);
3655}
3656
3657fn ensure_multiline(container: &CstContainerNode) {
3658  let children = container.children();
3659  if children.iter().any(|c| c.is_newline()) {
3660    return;
3661  }
3662
3663  let indents = compute_indents(&container.clone().into());
3664  let child_indents = indents.indent();
3665  let newline_kind = container
3666    .root_node()
3667    .map(|r| r.newline_kind())
3668    .unwrap_or(CstNewlineKind::LineFeed);
3669
3670  // insert a newline at the start of every part
3671  let children_len = children.len();
3672  let mut children = children.into_iter().skip(1).peekable().take(children_len - 2);
3673  let mut index = 1;
3674  while let Some(child) = children.next() {
3675    if child.is_whitespace() {
3676      child.remove();
3677      continue;
3678    } else {
3679      // insert a newline
3680      container.raw_insert_child(Some(&mut index), CstNewline::new(newline_kind).into());
3681      container.raw_insert_child(
3682        Some(&mut index),
3683        CstWhitespace::new(child_indents.current_indent.clone()).into(),
3684      );
3685
3686      // current node
3687      index += 1;
3688
3689      // consume the next tokens until the next comma
3690      let mut trailing_whitespace = Vec::new();
3691      for next_child in children.by_ref() {
3692        if next_child.is_whitespace() {
3693          trailing_whitespace.push(next_child);
3694        } else {
3695          index += 1 + trailing_whitespace.len();
3696          trailing_whitespace.clear();
3697          if next_child.token_char() == Some(',') {
3698            break;
3699          }
3700        }
3701      }
3702
3703      for trailing_whitespace in trailing_whitespace {
3704        trailing_whitespace.remove();
3705      }
3706    }
3707  }
3708
3709  // insert the last newline
3710  container.raw_insert_child(Some(&mut index), CstNewline::new(newline_kind).into());
3711  if !indents.current_indent.is_empty() {
3712    container.raw_insert_child(Some(&mut index), CstWhitespace::new(indents.current_indent).into());
3713  }
3714}
3715
3716#[derive(Debug)]
3717struct Indents {
3718  current_indent: String,
3719  single_indent: String,
3720}
3721
3722impl Indents {
3723  pub fn indent(&self) -> Indents {
3724    Indents {
3725      current_indent: format!("{}{}", self.current_indent, self.single_indent),
3726      single_indent: self.single_indent.clone(),
3727    }
3728  }
3729}
3730
3731fn compute_indents(node: &CstNode) -> Indents {
3732  let mut indent_level = 0;
3733  let mut stored_last_indent = node.indent_text();
3734  let mut ancestors = node.ancestors().peekable();
3735
3736  while ancestors.peek().and_then(|p| p.as_object_prop()).is_some() {
3737    ancestors.next();
3738  }
3739
3740  while let Some(ancestor) = ancestors.next() {
3741    if ancestor.is_root() {
3742      break;
3743    }
3744
3745    if ancestors.peek().and_then(|p| p.as_object_prop()).is_some() {
3746      continue;
3747    }
3748
3749    indent_level += 1;
3750
3751    if let Some(indent_text) = ancestor.indent_text() {
3752      match stored_last_indent {
3753        Some(last_indent) => {
3754          if let Some(single_indent_text) = last_indent.strip_prefix(&indent_text) {
3755            return Indents {
3756              current_indent: format!("{}{}", last_indent, single_indent_text.repeat(indent_level - 1)),
3757              single_indent: single_indent_text.to_string(),
3758            };
3759          }
3760          stored_last_indent = None;
3761        }
3762        None => {
3763          stored_last_indent = Some(indent_text);
3764        }
3765      }
3766    } else {
3767      stored_last_indent = None;
3768    }
3769  }
3770
3771  if indent_level == 1
3772    && let Some(indent_text) = node.indent_text()
3773  {
3774    return Indents {
3775      current_indent: indent_text.clone(),
3776      single_indent: indent_text,
3777    };
3778  }
3779
3780  // try to discover the single indent level by looking at the root node's children
3781  if let Some(root_value) = node.root_node().and_then(|r| r.value()) {
3782    for child in root_value.children() {
3783      if let Some(single_indent) = child.indent_text() {
3784        return Indents {
3785          current_indent: single_indent.repeat(indent_level),
3786          single_indent,
3787        };
3788      }
3789    }
3790  }
3791
3792  // assume two space indentation
3793  let single_indent = "  ";
3794  Indents {
3795    current_indent: single_indent.repeat(indent_level),
3796    single_indent: single_indent.to_string(),
3797  }
3798}
3799
3800struct AncestorIterator {
3801  // pre-emptively store the next ancestor in case
3802  // the currently returned sibling is removed
3803  next: Option<CstContainerNode>,
3804}
3805
3806impl AncestorIterator {
3807  pub fn new(node: CstNode) -> Self {
3808    Self {
3809      next: node.parent_info().map(|i| i.parent.as_container_node()),
3810    }
3811  }
3812}
3813
3814impl Iterator for AncestorIterator {
3815  type Item = CstContainerNode;
3816
3817  fn next(&mut self) -> Option<Self::Item> {
3818    let next = self.next.take()?;
3819    self.next = next.parent_info().map(|i| i.parent.as_container_node());
3820    Some(next)
3821  }
3822}
3823
3824struct NextSiblingIterator {
3825  // pre-emptively store the next sibling in case
3826  // the currently returned sibling is removed
3827  next: Option<CstNode>,
3828}
3829
3830impl NextSiblingIterator {
3831  pub fn new(node: CstNode) -> Self {
3832    Self {
3833      next: node.next_sibling(),
3834    }
3835  }
3836}
3837
3838impl Iterator for NextSiblingIterator {
3839  type Item = CstNode;
3840
3841  fn next(&mut self) -> Option<Self::Item> {
3842    let next_sibling = self.next.take()?;
3843    self.next = next_sibling.next_sibling();
3844    Some(next_sibling)
3845  }
3846}
3847
3848struct PreviousSiblingIterator {
3849  // pre-emptively store the previous sibling in case
3850  // the currently returned sibling is removed
3851  previous: Option<CstNode>,
3852}
3853
3854impl PreviousSiblingIterator {
3855  pub fn new(node: CstNode) -> Self {
3856    Self {
3857      previous: node.previous_sibling(),
3858    }
3859  }
3860}
3861
3862impl Iterator for PreviousSiblingIterator {
3863  type Item = CstNode;
3864
3865  fn next(&mut self) -> Option<Self::Item> {
3866    let previous_sibling = self.previous.take()?;
3867    self.previous = previous_sibling.previous_sibling();
3868    Some(previous_sibling)
3869  }
3870}
3871
3872#[cfg(test)]
3873mod test {
3874  use pretty_assertions::assert_eq;
3875
3876  use crate::cst::CstInputValue;
3877  use crate::cst::TrailingCommaMode;
3878  use crate::json;
3879
3880  use super::CstRootNode;
3881
3882  #[test]
3883  fn single_indent_text() {
3884    let cases = [
3885      (
3886        "  ",
3887        r#"
3888{
3889  "prop": {
3890    "nested": 4
3891  }
3892}
3893    "#,
3894      ),
3895      (
3896        "  ",
3897        r#"
3898{
3899  /* test */ "prop": {}
3900}
3901    "#,
3902      ),
3903      (
3904        "    ",
3905        r#"
3906{
3907    /* test */  "prop": {}
3908}
3909    "#,
3910      ),
3911      (
3912        "\t",
3913        "
3914{
3915\t/* test */  \"prop\": {}
3916}
3917    ",
3918      ),
3919    ];
3920    for (expected, text) in cases {
3921      let root = build_cst(text);
3922      assert_eq!(root.single_indent_text(), Some(expected.to_string()), "Text: {}", text);
3923    }
3924  }
3925
3926  #[test]
3927  fn modify_values() {
3928    let cst = build_cst(
3929      r#"{
3930    "value": 5,
3931    // comment
3932    "value2": "hello",
3933    value3: true
3934}"#,
3935    );
3936
3937    let root_value = cst.value().unwrap();
3938    let root_obj = root_value.as_object().unwrap();
3939    {
3940      let prop = root_obj.get("value").unwrap();
3941      prop
3942        .value()
3943        .unwrap()
3944        .as_number_lit()
3945        .unwrap()
3946        .set_raw_value("10".to_string());
3947      assert!(prop.trailing_comma().is_some());
3948      assert!(prop.previous_property().is_none());
3949      assert_eq!(
3950        prop.next_property().unwrap().name().unwrap().decoded_value().unwrap(),
3951        "value2"
3952      );
3953      assert_eq!(prop.indent_text().unwrap(), "    ");
3954    }
3955    {
3956      let prop = root_obj.get("value2").unwrap();
3957      prop
3958        .value()
3959        .unwrap()
3960        .as_string_lit()
3961        .unwrap()
3962        .set_raw_value("\"5\"".to_string());
3963      assert!(prop.trailing_comma().is_some());
3964      assert_eq!(
3965        prop
3966          .previous_property()
3967          .unwrap()
3968          .name()
3969          .unwrap()
3970          .decoded_value()
3971          .unwrap(),
3972        "value"
3973      );
3974      assert_eq!(
3975        prop.next_property().unwrap().name().unwrap().decoded_value().unwrap(),
3976        "value3"
3977      );
3978    }
3979    {
3980      let prop = root_obj.get("value3").unwrap();
3981      prop.value().unwrap().as_boolean_lit().unwrap().set_value(false);
3982      prop
3983        .name()
3984        .unwrap()
3985        .as_word_lit()
3986        .unwrap()
3987        .set_raw_value("value4".to_string());
3988      assert!(prop.trailing_comma().is_none());
3989      assert_eq!(
3990        prop
3991          .previous_property()
3992          .unwrap()
3993          .name()
3994          .unwrap()
3995          .decoded_value()
3996          .unwrap(),
3997        "value2"
3998      );
3999      assert!(prop.next_property().is_none());
4000    }
4001
4002    assert_eq!(
4003      cst.to_string(),
4004      r#"{
4005    "value": 10,
4006    // comment
4007    "value2": "5",
4008    value4: false
4009}"#
4010    );
4011  }
4012
4013  #[test]
4014  fn remove_properties() {
4015    fn run_test(prop_name: &str, json: &str, expected: &str) {
4016      let cst = build_cst(json);
4017      let root_value = cst.value().unwrap();
4018      let root_obj = root_value.as_object().unwrap();
4019      let prop = root_obj.get(prop_name).unwrap();
4020      prop.remove();
4021      assert_eq!(cst.to_string(), expected);
4022    }
4023
4024    run_test(
4025      "value2",
4026      r#"{
4027    "value": 5,
4028    // comment
4029    "value2": "hello",
4030    value3: true
4031}"#,
4032      r#"{
4033    "value": 5,
4034    // comment
4035    value3: true
4036}"#,
4037    );
4038
4039    run_test(
4040      "value2",
4041      r#"{
4042    "value": 5,
4043    // comment
4044    "value2": "hello"
4045    ,value3: true
4046}"#,
4047      // this is fine... people doing stupid things
4048      r#"{
4049    "value": 5,
4050    // comment
4051value3: true
4052}"#,
4053    );
4054
4055    run_test("value", r#"{ "value": 5 }"#, r#"{}"#);
4056    run_test("value", r#"{ "value": 5, "value2": 6 }"#, r#"{ "value2": 6 }"#);
4057    run_test("value2", r#"{ "value": 5, "value2": 6 }"#, r#"{ "value": 5 }"#);
4058  }
4059
4060  #[test]
4061  fn insert_properties() {
4062    fn run_test(index: usize, prop_name: &str, value: CstInputValue, json: &str, expected: &str) {
4063      let cst = build_cst(json);
4064      let root_value = cst.value().unwrap();
4065      let root_obj = root_value.as_object().unwrap();
4066      root_obj.insert(index, prop_name, value);
4067      assert_eq!(cst.to_string(), expected, "Initial text: {}", json);
4068    }
4069
4070    run_test(
4071      0,
4072      "propName",
4073      json!([1]),
4074      r#"{}"#,
4075      r#"{
4076  "propName": [1]
4077}"#,
4078    );
4079
4080    // inserting before first prop
4081    run_test(
4082      0,
4083      "value0",
4084      json!([1]),
4085      r#"{
4086    "value1": 5
4087}"#,
4088      r#"{
4089    "value0": [1],
4090    "value1": 5
4091}"#,
4092    );
4093
4094    // inserting before first prop with leading comment
4095    run_test(
4096      0,
4097      "value0",
4098      json!([1]),
4099      r#"{
4100    // some comment
4101    "value1": 5
4102}"#,
4103      r#"{
4104    "value0": [1],
4105    // some comment
4106    "value1": 5
4107}"#,
4108    );
4109
4110    // inserting after last prop with trailing comment
4111    run_test(
4112      1,
4113      "value1",
4114      json!({
4115        "value": 1
4116      }),
4117      r#"{
4118    "value0": 5 // comment
4119}"#,
4120      r#"{
4121    "value0": 5, // comment
4122    "value1": {
4123        "value": 1
4124    }
4125}"#,
4126    );
4127
4128    // maintain trailing comma
4129    run_test(
4130      1,
4131      "propName",
4132      json!(true),
4133      r#"{
4134  "value": 4,
4135}"#,
4136      r#"{
4137  "value": 4,
4138  "propName": true,
4139}"#,
4140    );
4141
4142    // insert when is on a single line
4143    run_test(
4144      1,
4145      "propName",
4146      json!(true),
4147      r#"{ "value": 4 }"#,
4148      r#"{
4149  "value": 4,
4150  "propName": true
4151}"#,
4152    );
4153
4154    // insert when is on a single line with trailing comma
4155    run_test(
4156      1,
4157      "propName",
4158      json!(true),
4159      r#"{ "value": 4, }"#,
4160      r#"{
4161  "value": 4,
4162  "propName": true,
4163}"#,
4164    );
4165  }
4166
4167  #[test]
4168  fn remove_array_elements() {
4169    fn run_test(index: usize, json: &str, expected: &str) {
4170      let cst = build_cst(json);
4171      let root_value = cst.value().unwrap();
4172      let root_array = root_value.as_array().unwrap();
4173      let element = root_array.elements().get(index).unwrap().clone();
4174      element.remove();
4175      assert_eq!(cst.to_string(), expected);
4176    }
4177
4178    run_test(
4179      0,
4180      r#"[
4181      1,
4182]"#,
4183      r#"[]"#,
4184    );
4185    run_test(
4186      0,
4187      r#"[
4188      1,
4189      2,
4190]"#,
4191      r#"[
4192      2,
4193]"#,
4194    );
4195    run_test(
4196      0,
4197      r#"[
4198      1,
4199      2,
4200]"#,
4201      r#"[
4202      2,
4203]"#,
4204    );
4205
4206    run_test(
4207      1,
4208      r#"[
4209      1, // other comment
4210      2, // trailing comment
4211]"#,
4212      r#"[
4213      1, // other comment
4214]"#,
4215    );
4216
4217    run_test(
4218      1,
4219      r#"[
4220      1, // comment
4221      2
4222]"#,
4223      r#"[
4224      1 // comment
4225]"#,
4226    );
4227
4228    run_test(1, r#"[1, 2]"#, r#"[1]"#);
4229    run_test(1, r#"[ 1, 2 /* test */ ]"#, r#"[ 1 ]"#);
4230    run_test(1, r#"[1, /* test */ 2]"#, r#"[1]"#);
4231    run_test(
4232      1,
4233      r#"[1 /* a */, /* b */ 2 /* c */, /* d */ true]"#,
4234      r#"[1 /* a */, /* d */ true]"#,
4235    );
4236  }
4237
4238  #[test]
4239  fn insert_array_element() {
4240    #[track_caller]
4241    fn run_test(index: usize, value: CstInputValue, json: &str, expected: &str) {
4242      let cst = build_cst(json);
4243      let root_value = cst.value().unwrap();
4244      let root_array = root_value.as_array().unwrap();
4245      root_array.insert(index, value);
4246      assert_eq!(cst.to_string(), expected, "Initial text: {}", json);
4247    }
4248
4249    run_test(0, json!([1]), r#"[]"#, r#"[[1]]"#);
4250    run_test(0, json!([1, true, false, {}]), r#"[]"#, r#"[[1, true, false, {}]]"#);
4251    run_test(0, json!(10), r#"[]"#, r#"[10]"#);
4252    run_test(0, json!(10), r#"[1]"#, r#"[10, 1]"#);
4253    run_test(1, json!(10), r#"[1]"#, r#"[1, 10]"#);
4254    run_test(
4255      0,
4256      json!(10),
4257      r#"[
4258    1
4259]"#,
4260      r#"[
4261    10,
4262    1
4263]"#,
4264    );
4265    run_test(
4266      0,
4267      json!(10),
4268      r#"[
4269    /* test */ 1
4270]"#,
4271      r#"[
4272    10,
4273    /* test */ 1
4274]"#,
4275    );
4276
4277    run_test(
4278      0,
4279      json!({
4280        "value": 1,
4281      }),
4282      r#"[]"#,
4283      r#"[
4284  {
4285    "value": 1
4286  }
4287]"#,
4288    );
4289
4290    // only comment
4291    run_test(
4292      0,
4293      json!({
4294        "value": 1,
4295      }),
4296      r#"[
4297    // comment
4298]"#,
4299      r#"[
4300    // comment
4301    {
4302        "value": 1
4303    }
4304]"#,
4305    );
4306
4307    // blank line
4308    run_test(
4309      0,
4310      json!({
4311        "value": 1,
4312      }),
4313      r#"[
4314
4315]"#,
4316      r#"[
4317  {
4318    "value": 1
4319  }
4320]"#,
4321    );
4322  }
4323
4324  #[test]
4325  fn append_to_multiline_array_does_not_expose_phantom_string_lit() {
4326    // regression test for https://github.com/dprint/jsonc-parser/issues/78
4327    let cst = build_cst(
4328      r#"{
4329  "servers": [
4330    {"name": "linear"},
4331    {"name": "supabase"}
4332  ]
4333}"#,
4334    );
4335    let arr = cst.object_value_or_create().unwrap().array_value("servers").unwrap();
4336    arr.append(CstInputValue::Object(vec![(
4337      "name".to_string(),
4338      CstInputValue::String("github".to_string()),
4339    )]));
4340
4341    let elements = arr.elements();
4342    assert_eq!(elements.len(), 3);
4343    for el in &elements {
4344      assert!(
4345        el.as_string_lit().is_none(),
4346        "element should not be a string lit: {:?}",
4347        el
4348      );
4349      assert!(el.as_object().is_some(), "element should be an object: {:?}", el);
4350    }
4351  }
4352
4353  #[test]
4354  fn insert_array_element_trailing_commas() {
4355    let cst = build_cst(
4356      r#"{
4357    "prop": [
4358      1,
4359      2,
4360    ]
4361}"#,
4362    );
4363    cst
4364      .object_value_or_create()
4365      .unwrap()
4366      .array_value("prop")
4367      .unwrap()
4368      .append(json!(3));
4369    assert_eq!(
4370      cst.to_string(),
4371      r#"{
4372    "prop": [
4373      1,
4374      2,
4375      3,
4376    ]
4377}"#
4378    );
4379  }
4380
4381  #[test]
4382  fn remove_comment() {
4383    #[track_caller]
4384    fn run_test(json: &str, expected: &str) {
4385      let cst = build_cst(json);
4386      let root_value = cst.value().unwrap();
4387      let root_obj = root_value.as_object().unwrap();
4388      root_obj
4389        .children()
4390        .into_iter()
4391        .filter_map(|c| c.as_comment())
4392        .next()
4393        .unwrap()
4394        .remove();
4395      assert_eq!(cst.to_string(), expected);
4396    }
4397
4398    run_test(
4399      r#"{
4400    "value": 5,
4401    // comment
4402    "value2": "hello",
4403    value3: true
4404}"#,
4405      r#"{
4406    "value": 5,
4407    "value2": "hello",
4408    value3: true
4409}"#,
4410    );
4411
4412    run_test(
4413      r#"{
4414    "value": 5,  // comment
4415    "value2": "hello",
4416    value3: true
4417}"#,
4418      r#"{
4419    "value": 5,
4420    "value2": "hello",
4421    value3: true
4422}"#,
4423    );
4424  }
4425
4426  #[test]
4427  fn object_value_or_create() {
4428    // existing
4429    {
4430      let cst = build_cst(r#"{ "value": 1 }"#);
4431      let obj = cst.object_value_or_create().unwrap();
4432      assert!(obj.get("value").is_some());
4433    }
4434    // empty file
4435    {
4436      let cst = build_cst(r#""#);
4437      cst.object_value_or_create().unwrap();
4438      assert_eq!(cst.to_string(), "{}\n");
4439    }
4440    // comment
4441    {
4442      let cst = build_cst("// Copyright something");
4443      cst.object_value_or_create().unwrap();
4444      assert_eq!(cst.to_string(), "// Copyright something\n{}\n");
4445    }
4446    // comment and newline
4447    {
4448      let cst = build_cst("// Copyright something\n");
4449      cst.object_value_or_create().unwrap();
4450      assert_eq!(cst.to_string(), "// Copyright something\n{}\n");
4451    }
4452  }
4453
4454  #[test]
4455  fn array_ensure_multiline() {
4456    // empty
4457    {
4458      let cst = build_cst(r#"[]"#);
4459      cst.value().unwrap().as_array().unwrap().ensure_multiline();
4460      assert_eq!(cst.to_string(), "[\n]");
4461    }
4462    // whitespace only
4463    {
4464      let cst = build_cst(r#"[   ]"#);
4465      cst.value().unwrap().as_array().unwrap().ensure_multiline();
4466      assert_eq!(cst.to_string(), "[\n]");
4467    }
4468    // comments only
4469    {
4470      let cst = build_cst(r#"[  /* test */  ]"#);
4471      cst.value().unwrap().as_array().unwrap().ensure_multiline();
4472      assert_eq!(cst.to_string(), "[\n  /* test */\n]");
4473    }
4474    // elements
4475    {
4476      let cst = build_cst(r#"[  1,   2, /* test */ 3  ]"#);
4477      cst.value().unwrap().as_array().unwrap().ensure_multiline();
4478      assert_eq!(
4479        cst.to_string(),
4480        r#"[
4481  1,
4482  2,
4483  /* test */ 3
4484]"#
4485      );
4486    }
4487    // elements deep
4488    {
4489      let cst = build_cst(
4490        r#"{
4491  "prop": {
4492    "value": [  1,   2, /* test */ 3  ]
4493  }
4494}"#,
4495      );
4496      cst
4497        .value()
4498        .unwrap()
4499        .as_object()
4500        .unwrap()
4501        .get("prop")
4502        .unwrap()
4503        .value()
4504        .unwrap()
4505        .as_object()
4506        .unwrap()
4507        .get("value")
4508        .unwrap()
4509        .value()
4510        .unwrap()
4511        .as_array()
4512        .unwrap()
4513        .ensure_multiline();
4514      assert_eq!(
4515        cst.to_string(),
4516        r#"{
4517  "prop": {
4518    "value": [
4519      1,
4520      2,
4521      /* test */ 3
4522    ]
4523  }
4524}"#
4525      );
4526    }
4527    // \r\n newlines
4528    {
4529      let cst = build_cst("[  1,   2, /* test */ 3  ]\r\n");
4530      cst.value().unwrap().as_array().unwrap().ensure_multiline();
4531      assert_eq!(cst.to_string(), "[\r\n  1,\r\n  2,\r\n  /* test */ 3\r\n]\r\n");
4532    }
4533  }
4534
4535  #[test]
4536  fn object_ensure_multiline() {
4537    // empty
4538    {
4539      let cst = build_cst(r#"{}"#);
4540      cst.value().unwrap().as_object().unwrap().ensure_multiline();
4541      assert_eq!(cst.to_string(), "{\n}");
4542    }
4543    // whitespace only
4544    {
4545      let cst = build_cst(r#"{   }"#);
4546      cst.value().unwrap().as_object().unwrap().ensure_multiline();
4547      assert_eq!(cst.to_string(), "{\n}");
4548    }
4549    // comments only
4550    {
4551      let cst = build_cst(r#"{  /* test */  }"#);
4552      cst.value().unwrap().as_object().unwrap().ensure_multiline();
4553      assert_eq!(cst.to_string(), "{\n  /* test */\n}");
4554    }
4555    // elements
4556    {
4557      let cst = build_cst(r#"{  prop: 1,   prop2: 2, /* test */ prop3: 3  }"#);
4558      cst.value().unwrap().as_object().unwrap().ensure_multiline();
4559      assert_eq!(
4560        cst.to_string(),
4561        r#"{
4562  prop: 1,
4563  prop2: 2,
4564  /* test */ prop3: 3
4565}"#
4566      );
4567    }
4568    // elements deep
4569    {
4570      let cst = build_cst(
4571        r#"{
4572  "prop": {
4573    "value": {  prop: 1,   prop2: 2, /* test */ prop3: 3  }
4574  }
4575}"#,
4576      );
4577      cst
4578        .value()
4579        .unwrap()
4580        .as_object()
4581        .unwrap()
4582        .get("prop")
4583        .unwrap()
4584        .value()
4585        .unwrap()
4586        .as_object()
4587        .unwrap()
4588        .get("value")
4589        .unwrap()
4590        .value()
4591        .unwrap()
4592        .as_object()
4593        .unwrap()
4594        .ensure_multiline();
4595      assert_eq!(
4596        cst.to_string(),
4597        r#"{
4598  "prop": {
4599    "value": {
4600      prop: 1,
4601      prop2: 2,
4602      /* test */ prop3: 3
4603    }
4604  }
4605}"#
4606      );
4607    }
4608  }
4609
4610  #[test]
4611  fn sets_trailing_commas() {
4612    fn run_test(input: &str, mode: crate::cst::TrailingCommaMode, expected: &str) {
4613      let cst = build_cst(input);
4614      let root_value = cst.value().unwrap();
4615      let root_obj = root_value.as_object().unwrap();
4616      root_obj.set_trailing_commas(mode);
4617      assert_eq!(cst.to_string(), expected);
4618    }
4619
4620    // empty object
4621    run_test(
4622      r#"{
4623}"#,
4624      TrailingCommaMode::Never,
4625      r#"{
4626}"#,
4627    );
4628    run_test(
4629      r#"{
4630    // test
4631}"#,
4632      TrailingCommaMode::IfMultiline,
4633      r#"{
4634    // test
4635}"#,
4636    );
4637
4638    // single-line object
4639    run_test(r#"{"a": 1}"#, TrailingCommaMode::Never, r#"{"a": 1}"#);
4640    run_test(r#"{"a": 1}"#, TrailingCommaMode::IfMultiline, r#"{"a": 1}"#);
4641    // multiline object
4642    run_test(
4643      r#"{
4644  "a": 1,
4645  "b": 2,
4646  "c": [1, 2, 3],
4647  "d": [
4648      1
4649  ]
4650}"#,
4651      TrailingCommaMode::IfMultiline,
4652      r#"{
4653  "a": 1,
4654  "b": 2,
4655  "c": [1, 2, 3],
4656  "d": [
4657      1,
4658  ],
4659}"#,
4660    );
4661    run_test(
4662      r#"{
4663"a": 1,
4664"b": 2,
4665}"#,
4666      TrailingCommaMode::Never,
4667      r#"{
4668"a": 1,
4669"b": 2
4670}"#,
4671    );
4672  }
4673
4674  #[test]
4675  fn or_create_methods() {
4676    let cst = build_cst("");
4677    let obj = cst.object_value_or_create().unwrap();
4678    assert_eq!(cst.to_string(), "{}\n");
4679    assert!(cst.array_value_or_create().is_none());
4680    assert_eq!(obj.object_value_or_create("prop").unwrap().to_string(), "{}");
4681    assert!(obj.array_value_or_create("prop").is_none());
4682    assert_eq!(obj.array_value_or_create("prop2").unwrap().to_string(), "[]");
4683    assert_eq!(
4684      cst.to_string(),
4685      r#"{
4686  "prop": {},
4687  "prop2": []
4688}
4689"#
4690    );
4691  }
4692
4693  #[test]
4694  fn or_set_methods() {
4695    let cst = build_cst("");
4696    let array = cst.array_value_or_set();
4697    assert_eq!(array.to_string(), "[]");
4698    assert_eq!(cst.to_string(), "[]\n");
4699    let object = cst.object_value_or_set();
4700    assert_eq!(object.to_string(), "{}");
4701    assert_eq!(cst.to_string(), "{}\n");
4702    let value = object.array_value_or_set("test");
4703    assert_eq!(value.to_string(), "[]");
4704    assert_eq!(cst.to_string(), "{\n  \"test\": []\n}\n");
4705    let value = object.object_value_or_set("test");
4706    assert_eq!(value.to_string(), "{}");
4707    assert_eq!(cst.to_string(), "{\n  \"test\": {}\n}\n");
4708    let value = object.array_value_or_set("test");
4709    assert_eq!(value.to_string(), "[]");
4710    assert_eq!(cst.to_string(), "{\n  \"test\": []\n}\n");
4711    value.append(json!(1));
4712    assert_eq!(cst.to_string(), "{\n  \"test\": [1]\n}\n");
4713    let value = object.object_value_or_set("test");
4714    assert_eq!(value.to_string(), "{}");
4715    assert_eq!(cst.to_string(), "{\n  \"test\": {}\n}\n");
4716    let test_prop = object.get("test").unwrap();
4717    assert!(test_prop.object_value().is_some());
4718    assert!(test_prop.array_value().is_none());
4719    test_prop.array_value_or_set();
4720    assert_eq!(cst.to_string(), "{\n  \"test\": []\n}\n");
4721    assert!(test_prop.object_value().is_none());
4722    assert!(test_prop.array_value().is_some());
4723    test_prop.object_value_or_set();
4724    assert_eq!(cst.to_string(), "{\n  \"test\": {}\n}\n");
4725  }
4726
4727  #[test]
4728  fn expression_properties_and_values() {
4729    #[track_caller]
4730    fn run_test(value: CstInputValue, expected: &str) {
4731      let cst = build_cst("");
4732      cst.set_value(value);
4733      assert_eq!(cst.to_string(), format!("{}\n", expected));
4734    }
4735
4736    run_test(json!(1), "1");
4737    run_test(json!("test"), "\"test\"");
4738    {
4739      let text = "test";
4740      run_test(json!(text), "\"test\"");
4741    }
4742    {
4743      let num = 1;
4744      run_test(json!(num), "1");
4745    }
4746    {
4747      let vec = vec![1, 2, 3];
4748      run_test(json!(vec), "[1, 2, 3]");
4749    }
4750    {
4751      let vec = vec![1, 2, 3];
4752      run_test(
4753        json!({
4754          "value": vec,
4755        }),
4756        r#"{
4757  "value": [1, 2, 3]
4758}"#,
4759      );
4760    }
4761    run_test(
4762      json!({
4763        notQuoted: 1,
4764        "quoted": 2,
4765      }),
4766      r#"{
4767  "notQuoted": 1,
4768  "quoted": 2
4769}"#,
4770    )
4771  }
4772
4773  #[test]
4774  fn property_index() {
4775    let cst = build_cst("{ \"prop\": 1, \"prop2\": 2, \"prop3\": 3 }");
4776    let object = cst.object_value().unwrap();
4777    for (i, prop) in object.properties().into_iter().enumerate() {
4778      assert_eq!(prop.property_index(), i);
4779    }
4780  }
4781
4782  #[test]
4783  fn element_index() {
4784    let cst = build_cst("[1, 2, true ,false]");
4785    let array = cst.array_value().unwrap();
4786    for (i, prop) in array.elements().into_iter().enumerate() {
4787      assert_eq!(prop.element_index().unwrap(), i);
4788    }
4789  }
4790
4791  #[test]
4792  fn missing_comma_between_array_elements() {
4793    build_cst("[1 2]");
4794
4795    // but is strict when strict
4796    let options = crate::ParseOptions {
4797      allow_missing_commas: false,
4798      ..Default::default()
4799    };
4800    assert_eq!(
4801      CstRootNode::parse("[1 2]", &options).err().unwrap().to_string(),
4802      "Expected comma on line 1 column 3"
4803    );
4804    CstRootNode::parse("[1, 2]", &options).unwrap();
4805  }
4806
4807  #[test]
4808  fn sort_properties() {
4809    #[track_caller]
4810    fn run_test(json: &str, expected: &str) {
4811      let cst = build_cst(json);
4812      let root_obj = cst.object_value().unwrap();
4813      root_obj.sort_properties().by_key(|prop| prop.decoded_name());
4814      assert_eq!(cst.to_string(), expected);
4815      // the result is still the same json, and sorting it again changes nothing
4816      build_cst(&cst.to_string());
4817      let sorted = cst.to_string();
4818      root_obj.sort_properties().by_key(|prop| prop.decoded_name());
4819      assert_eq!(cst.to_string(), sorted);
4820    }
4821
4822    run_test("{\n  \"b\": 2,\n  \"a\": 1\n}", "{\n  \"a\": 1,\n  \"b\": 2\n}");
4823    // a single line object keeps the spacing that separates its properties
4824    run_test("{ \"b\": 2, \"a\": 1 }", "{ \"a\": 1, \"b\": 2 }");
4825    run_test("{\"b\":2,\"a\":1}", "{\"a\":1,\"b\":2}");
4826    // the trailing comma the object was written with belongs to whatever ends up last
4827    run_test("{\n  \"b\": 2,\n  \"a\": 1,\n}", "{\n  \"a\": 1,\n  \"b\": 2,\n}");
4828    // nothing to do
4829    run_test("{}", "{}");
4830    run_test("{ \"a\": 1 }", "{ \"a\": 1 }");
4831    run_test("{\n  \"a\": 1,\n  \"b\": 2\n}", "{\n  \"a\": 1,\n  \"b\": 2\n}");
4832    // values are moved as they were written, not reformatted
4833    run_test(
4834      "{\n  \"b\": { \"z\": 1 },\n  \"a\": [3,   1]\n}",
4835      "{\n  \"a\": [3,   1],\n  \"b\": { \"z\": 1 }\n}",
4836    );
4837    // word (unquoted) names sort by the same name the parser reads
4838    run_test("{\n  b: 2,\n  a: 1\n}", "{\n  a: 1,\n  b: 2\n}");
4839    // an escape is decoded to find the name, and left as written when the property moves
4840    run_test(
4841      "{\n  \"b\": 2,\n  \"\\u0061\": 1\n}",
4842      "{\n  \"\\u0061\": 1,\n  \"b\": 2\n}",
4843    );
4844    // properties sharing a name keep the order they were written in
4845    run_test(
4846      "{\n  \"b\": 2,\n  \"a\": \"first\",\n  \"a\": \"second\"\n}",
4847      "{\n  \"a\": \"first\",\n  \"a\": \"second\",\n  \"b\": 2\n}",
4848    );
4849    // a comma is added where the new order needs one, even if the author left it out
4850    run_test("{\n  \"b\": 2\n  \"a\": 1\n}", "{\n  \"a\": 1,\n  \"b\": 2\n}");
4851    // a comma written at the start of a line belongs to the property above it
4852    run_test("{\n  \"b\": 2\n  , \"a\": 1\n}", "{\n  \"a\": 1, \"b\": 2\n\n}");
4853    // the space that offset a removed comma goes with it
4854    run_test("{ \"b\": 2 , \"a\": 1 }", "{ \"a\": 1, \"b\": 2 }");
4855    // properties keep their indentation when they change lines
4856    run_test("{\n  \"b\": 2, \"a\": 1\n}", "{\n  \"a\": 1, \"b\": 2\n}");
4857    // carriage returns survive the move
4858    run_test(
4859      "{\r\n  \"b\": 2,\r\n  \"a\": 1\r\n}",
4860      "{\r\n  \"a\": 1,\r\n  \"b\": 2\r\n}",
4861    );
4862  }
4863
4864  #[test]
4865  fn sort_properties_moves_comments_and_blank_lines() {
4866    #[track_caller]
4867    fn run_test(json: &str, expected: &str) {
4868      let cst = build_cst(json);
4869      let root_obj = cst.object_value().unwrap();
4870      root_obj.sort_properties().by_key(|prop| prop.decoded_name());
4871      assert_eq!(cst.to_string(), expected);
4872      build_cst(&cst.to_string());
4873      let sorted = cst.to_string();
4874      root_obj.sort_properties().by_key(|prop| prop.decoded_name());
4875      assert_eq!(cst.to_string(), sorted);
4876    }
4877
4878    // a comment above a property was written with it and travels with it
4879    run_test(
4880      "{\n  // about b\n  \"b\": 2,\n  \"a\": 1\n}",
4881      "{\n  \"a\": 1,\n  // about b\n  \"b\": 2\n}",
4882    );
4883    // so does a comment written after it on the same line, which loses the comma it sat behind
4884    run_test(
4885      "{\n  \"b\": 2, // about b\n  \"a\": 1\n}",
4886      "{\n  \"a\": 1,\n  \"b\": 2 // about b\n}",
4887    );
4888    // and gains one when it moves off the end
4889    run_test(
4890      "{\n  \"b\": 2,\n  \"a\": 1 // about a\n}",
4891      "{\n  \"a\": 1, // about a\n  \"b\": 2\n}",
4892    );
4893    // a comment on the open brace line belongs to no property and stays where it is
4894    run_test(
4895      "{ // about the object\n  \"b\": 2,\n  \"a\": 1\n}",
4896      "{ // about the object\n  \"a\": 1,\n  \"b\": 2\n}",
4897    );
4898    // as does one written under the last property
4899    run_test(
4900      "{\n  \"b\": 2,\n  \"a\": 1\n  // dangling\n}",
4901      "{\n  \"a\": 1,\n  \"b\": 2\n  // dangling\n}",
4902    );
4903    // a comment between two properties on one line was written above the second of them
4904    run_test(
4905      "{ \"b\": 2, /* between */ \"a\": 1 }",
4906      "{ /* between */ \"a\": 1, \"b\": 2 }",
4907    );
4908    // a block comment above a property travels like a line comment does
4909    run_test(
4910      "{\n  /* about b */\n  \"b\": 2,\n  \"a\": 1\n}",
4911      "{\n  \"a\": 1,\n  /* about b */\n  \"b\": 2\n}",
4912    );
4913    // a blank line above a property travels with it
4914    run_test(
4915      "{\n  \"c\": 3,\n  \"a\": 1,\n\n  \"b\": 2\n}",
4916      "{\n  \"a\": 1,\n\n  \"b\": 2,\n  \"c\": 3\n}",
4917    );
4918    // but one that ends up under the open brace reads as a gap rather than as part of a property
4919    run_test("{\n  \"b\": 2,\n\n  \"a\": 1\n}", "{\n  \"a\": 1,\n  \"b\": 2\n}");
4920  }
4921
4922  #[test]
4923  fn sort_properties_pinning_comment_headers() {
4924    #[track_caller]
4925    fn run_test(json: &str, expected: &str) {
4926      let cst = build_cst(json);
4927      let root_obj = cst.object_value().unwrap();
4928      root_obj
4929        .sort_properties()
4930        .pin_comment_headers()
4931        .by_key(|prop| prop.decoded_name());
4932      assert_eq!(cst.to_string(), expected);
4933      build_cst(&cst.to_string());
4934    }
4935
4936    // a comment under a blank line heads what follows, so the properties sort past it
4937    run_test(
4938      "{\n  \"prop\": 1,\n\n  // section\n  \"prop2\": 2,\n  \"prop1\": 1\n}",
4939      "{\n  \"prop\": 1,\n\n  // section\n  \"prop1\": 1,\n  \"prop2\": 2\n}",
4940    );
4941    // but a comment written flush against its property still describes it and travels with it
4942    run_test(
4943      "{\n  // about b\n  \"b\": 2,\n  \"a\": 1\n}",
4944      "{\n  \"a\": 1,\n  // about b\n  \"b\": 2\n}",
4945    );
4946    // the two can sit in the same object
4947    run_test(
4948      "{\n  \"c\": 3,\n\n  // section\n  // about b\n  \"b\": 2,\n  \"a\": 1\n}",
4949      "{\n  \"a\": 1,\n\n  // section\n  // about b\n  \"b\": 2,\n  \"c\": 3\n}",
4950    );
4951    // a blank line with no comment under it stays where it is as well
4952    run_test(
4953      "{\n  \"c\": 3,\n\n  \"b\": 2,\n  \"a\": 1\n}",
4954      "{\n  \"a\": 1,\n\n  \"b\": 2,\n  \"c\": 3\n}",
4955    );
4956    // every heading stays over its own group
4957    run_test(
4958      "{\n\n  // first\n  \"d\": 4,\n  \"c\": 3,\n\n  // second\n  \"b\": 2,\n  \"a\": 1\n}",
4959      "{\n\n  // first\n  \"a\": 1,\n  \"b\": 2,\n\n  // second\n  \"c\": 3,\n  \"d\": 4\n}",
4960    );
4961    // a block comment heads a group the same way
4962    run_test(
4963      "{\n  \"c\": 3,\n\n  /* section */\n  \"b\": 2,\n  \"a\": 1\n}",
4964      "{\n  \"a\": 1,\n\n  /* section */\n  \"b\": 2,\n  \"c\": 3\n}",
4965    );
4966    // an object with no blank lines sorts exactly as it does without the option
4967    run_test("{\n  \"b\": 2,\n  \"a\": 1\n}", "{\n  \"a\": 1,\n  \"b\": 2\n}");
4968  }
4969
4970  #[test]
4971  fn sort_properties_within_groups() {
4972    #[track_caller]
4973    fn run_test(json: &str, expected: &str) {
4974      let cst = build_cst(json);
4975      let root_obj = cst.object_value().unwrap();
4976      root_obj
4977        .sort_properties()
4978        .within_groups()
4979        .by_key(|prop| prop.decoded_name());
4980      assert_eq!(cst.to_string(), expected);
4981      build_cst(&cst.to_string());
4982    }
4983
4984    // a blank line divides the object and nothing sorts across it
4985    run_test(
4986      "{\n  \"m\": 1,\n\n  // section\n  \"z\": 2,\n  \"a\": 3\n}",
4987      "{\n  \"m\": 1,\n\n  // section\n  \"a\": 3,\n  \"z\": 2\n}",
4988    );
4989    // every group sorts on its own
4990    run_test(
4991      "{\n  \"d\": 4,\n  \"c\": 3,\n\n  \"b\": 2,\n  \"a\": 1\n}",
4992      "{\n  \"c\": 3,\n  \"d\": 4,\n\n  \"a\": 1,\n  \"b\": 2\n}",
4993    );
4994    // the trailing comma still belongs to whatever ends the object
4995    run_test(
4996      "{\n  \"b\": 2,\n\n  \"d\": 4,\n  \"c\": 3,\n}",
4997      "{\n  \"b\": 2,\n\n  \"c\": 3,\n  \"d\": 4,\n}",
4998    );
4999    // a group of one has nothing to sort
5000    run_test("{\n  \"b\": 2,\n\n  \"a\": 1\n}", "{\n  \"b\": 2,\n\n  \"a\": 1\n}");
5001    // an object with no blank line is one group
5002    run_test("{\n  \"b\": 2,\n  \"a\": 1\n}", "{\n  \"a\": 1,\n  \"b\": 2\n}");
5003    // a comment directly under the blank line is part of the boundary and stays with it
5004    run_test(
5005      "{\n  \"z\": 1,\n\n  // section\n  \"b\": 2,\n  \"a\": 3\n}",
5006      "{\n  \"z\": 1,\n\n  // section\n  \"a\": 3,\n  \"b\": 2\n}",
5007    );
5008    // but one written further down the group belongs to its property and travels with it
5009    run_test(
5010      "{\n  \"z\": 1,\n\n  \"c\": 3,\n  // about b\n  \"b\": 2,\n  \"a\": 0\n}",
5011      "{\n  \"z\": 1,\n\n  \"a\": 0,\n  // about b\n  \"b\": 2,\n  \"c\": 3\n}",
5012    );
5013  }
5014
5015  #[test]
5016  fn sort_properties_pinning_some_of_the_comments() {
5017    #[track_caller]
5018    fn run_test(json: &str, expected: &str) {
5019      let cst = build_cst(json);
5020      let root_obj = cst.object_value().unwrap();
5021      // only the first comment above a property heads its group; the rest are its own
5022      root_obj
5023        .sort_properties()
5024        .pin_comment_headers_with(|prop, _| if prop.has_blank_line_before() { 1 } else { 0 })
5025        .by_key(|prop| prop.decoded_name());
5026      assert_eq!(cst.to_string(), expected);
5027      build_cst(&cst.to_string());
5028    }
5029
5030    // the first comment heads the group and the second describes the property under it
5031    run_test(
5032      "{\n  \"c\": 3,\n\n  // section\n  // about b\n  \"b\": 2,\n  \"a\": 1\n}",
5033      "{\n  \"a\": 1,\n\n  // section\n  // about b\n  \"b\": 2,\n  \"c\": 3\n}",
5034    );
5035    // a blank line between the header and the note keeps the blank with the header
5036    run_test(
5037      "{\n  \"c\": 3,\n\n  // section\n\n  // about b\n  \"b\": 2,\n  \"a\": 1\n}",
5038      "{\n  \"a\": 1,\n\n  // section\n\n  // about b\n  \"b\": 2,\n  \"c\": 3\n}",
5039    );
5040  }
5041
5042  #[test]
5043  fn sort_gives_up_when_the_comparator_changes_the_object() {
5044    let text = "{
5045  \"b\": 2,
5046  \"a\": 1
5047}";
5048    let cst = build_cst(text);
5049    let root_obj = cst.object_value().unwrap();
5050    root_obj.sort_properties().by_key(|prop| {
5051      // removing a property leaves the sort with nothing safe to write back
5052      if prop.decoded_name().as_deref() == Some("b") {
5053        prop.clone().remove();
5054      }
5055      prop.decoded_name()
5056    });
5057
5058    // the removal stands, but nothing was reordered on top of it
5059    assert_eq!(
5060      cst.to_string(),
5061      "{
5062  \"a\": 1
5063}"
5064    );
5065  }
5066
5067  #[test]
5068  fn sort_gives_up_when_the_comparator_replaces_a_member() {
5069    let cst = build_cst("{\n  \"b\": 2,\n  \"a\": 1\n}");
5070    let root_obj = cst.object_value().unwrap();
5071    root_obj.sort_properties().by_key(|prop| {
5072      let name = prop.decoded_name();
5073      // a replacement leaves the child count alone, so only checking that would miss it
5074      if name.as_deref() == Some("b") {
5075        prop.clone().replace_with("zzz", json!(9));
5076      }
5077      name
5078    });
5079
5080    // the replacement stands and nothing was reordered on top of it
5081    assert_eq!(cst.to_string(), "{\n  \"zzz\": 9,\n  \"a\": 1\n}");
5082  }
5083
5084  #[test]
5085  fn sort_keeps_line_comments_ending_their_line() {
5086    #[track_caller]
5087    fn run_test(json: &str, expected: &str) {
5088      let cst = build_cst(json);
5089      let root_obj = cst.object_value().unwrap();
5090      root_obj.sort_properties().by_key(|prop| prop.decoded_name());
5091      assert_eq!(cst.to_string(), expected);
5092      // without the line break the comment would swallow whatever follows it
5093      build_cst(&cst.to_string());
5094      let sorted = cst.to_string();
5095      root_obj.sort_properties().by_key(|prop| prop.decoded_name());
5096      assert_eq!(cst.to_string(), sorted);
5097    }
5098
5099    // a line comment that would swallow the property after it gains a line break
5100    run_test(
5101      "{\"b\": 2, \"a\": 1 // about a\n}",
5102      "{\"a\": 1, // about a\n \"b\": 2\n}",
5103    );
5104    // and one that would swallow the close brace gains one too
5105    run_test(
5106      "{ \"b\": 2, // about b\n  \"a\": 1 }",
5107      "{ \"a\": 1,\n  \"b\": 2 // about b\n }",
5108    );
5109    // a block comment needs no such help
5110    run_test(
5111      "{\"b\": 2, \"a\": 1 /* about a */}",
5112      "{\"a\": 1, /* about a */ \"b\": 2}",
5113    );
5114  }
5115
5116  #[test]
5117  fn sort_properties_keeps_the_tree_usable() {
5118    let cst = build_cst("{\n  \"b\": 2,\n  \"a\": 1\n}");
5119    let root_obj = cst.object_value().unwrap();
5120    let b = root_obj.get("b").unwrap();
5121    root_obj.sort_properties().by_key(|prop| prop.decoded_name());
5122
5123    // the handle taken before the sort still points at the same property in its new place
5124    assert_eq!(b.decoded_name().unwrap(), "b");
5125    assert_eq!(b.property_index(), 1);
5126    assert_eq!(
5127      root_obj
5128        .properties()
5129        .iter()
5130        .map(|p| p.decoded_name().unwrap())
5131        .collect::<Vec<_>>(),
5132      ["a", "b"]
5133    );
5134    // and the property that moved can still be edited afterwards
5135    b.set_value(json!(3));
5136    assert_eq!(cst.to_string(), "{\n  \"a\": 1,\n  \"b\": 3\n}");
5137  }
5138
5139  #[test]
5140  fn sort_elements() {
5141    #[track_caller]
5142    fn run_test(json: &str, expected: &str) {
5143      let cst = build_cst(json);
5144      let array = cst.array_value().unwrap();
5145      array.sort_elements().by_key(|element| element.to_string());
5146      assert_eq!(cst.to_string(), expected);
5147      build_cst(&cst.to_string());
5148      let sorted = cst.to_string();
5149      array.sort_elements().by_key(|element| element.to_string());
5150      assert_eq!(cst.to_string(), sorted);
5151    }
5152
5153    run_test("[3, 1, 2]", "[1, 2, 3]");
5154    run_test("[\n  3,\n  1\n]", "[\n  1,\n  3\n]");
5155    // the trailing comma the array was written with belongs to whatever ends up last
5156    run_test("[\n  3,\n  1,\n]", "[\n  1,\n  3,\n]");
5157    // a comment above an element travels with it
5158    run_test("[\n  // about 3\n  3,\n  1\n]", "[\n  1,\n  // about 3\n  3\n]");
5159    // as does one written after it on the same line
5160    run_test("[\n  3, // about 3\n  1\n]", "[\n  1,\n  3 // about 3\n]");
5161    // a line comment that would swallow the close bracket gains a line break
5162    run_test("[2, // about 2\n1]", "[1,\n2 // about 2\n]");
5163    // a blank line above an element travels with it
5164    run_test("[\n  3,\n\n  1\n]", "[\n  1,\n  3\n]");
5165    // nothing to do
5166    run_test("[]", "[]");
5167    run_test("[1]", "[1]");
5168    // an array sorts before an object by text, so these are already in order
5169    run_test("[\n  [3, 2],\n  {\"a\": 1}\n]", "[\n  [3, 2],\n  {\"a\": 1}\n]");
5170  }
5171
5172  #[track_caller]
5173  fn build_cst(text: &str) -> CstRootNode {
5174    CstRootNode::parse(text, &crate::ParseOptions::default()).unwrap()
5175  }
5176
5177  #[cfg(feature = "serde_json")]
5178  mod serde_tests {
5179    use super::build_cst;
5180    use serde_json::Value as SerdeValue;
5181    use std::str::FromStr;
5182
5183    #[test]
5184    fn test_cst_to_serde_value_primitives() {
5185      let root = build_cst(r#"42"#);
5186      let value = root.to_serde_value().unwrap();
5187      assert_eq!(value, SerdeValue::Number(serde_json::Number::from_str("42").unwrap()));
5188
5189      let root = build_cst(r#""hello""#);
5190      let value = root.to_serde_value().unwrap();
5191      assert_eq!(value, SerdeValue::String("hello".to_string()));
5192
5193      let root = build_cst(r#"true"#);
5194      let value = root.to_serde_value().unwrap();
5195      assert_eq!(value, SerdeValue::Bool(true));
5196
5197      let root = build_cst(r#"false"#);
5198      let value = root.to_serde_value().unwrap();
5199      assert_eq!(value, SerdeValue::Bool(false));
5200
5201      let root = build_cst(r#"null"#);
5202      let value = root.to_serde_value().unwrap();
5203      assert_eq!(value, SerdeValue::Null);
5204    }
5205
5206    #[test]
5207    fn test_cst_to_serde_value_array() {
5208      let root = build_cst(r#"[1, 2, 3]"#);
5209      let value = root.to_serde_value().unwrap();
5210      let expected = SerdeValue::Array(vec![
5211        SerdeValue::Number(serde_json::Number::from_str("1").unwrap()),
5212        SerdeValue::Number(serde_json::Number::from_str("2").unwrap()),
5213        SerdeValue::Number(serde_json::Number::from_str("3").unwrap()),
5214      ]);
5215      assert_eq!(value, expected);
5216    }
5217
5218    #[test]
5219    fn test_cst_to_serde_value_array_with_comments() {
5220      let root = build_cst(
5221        r#"[
5222        // comment 1
5223        1,
5224        2, // comment 2
5225        3
5226      ]"#,
5227      );
5228      let value = root.to_serde_value().unwrap();
5229      let expected = SerdeValue::Array(vec![
5230        SerdeValue::Number(serde_json::Number::from_str("1").unwrap()),
5231        SerdeValue::Number(serde_json::Number::from_str("2").unwrap()),
5232        SerdeValue::Number(serde_json::Number::from_str("3").unwrap()),
5233      ]);
5234      assert_eq!(value, expected);
5235    }
5236
5237    #[test]
5238    fn test_cst_to_serde_value_object() {
5239      let root = build_cst(
5240        r#"{
5241        "name": "Alice",
5242        "age": 30,
5243        "active": true
5244      }"#,
5245      );
5246      let value = root.to_serde_value().unwrap();
5247
5248      let mut expected_map = serde_json::map::Map::new();
5249      expected_map.insert("name".to_string(), SerdeValue::String("Alice".to_string()));
5250      expected_map.insert(
5251        "age".to_string(),
5252        SerdeValue::Number(serde_json::Number::from_str("30").unwrap()),
5253      );
5254      expected_map.insert("active".to_string(), SerdeValue::Bool(true));
5255
5256      assert_eq!(value, SerdeValue::Object(expected_map));
5257    }
5258
5259    #[test]
5260    fn test_cst_to_serde_value_object_with_comments() {
5261      let root = build_cst(
5262        r#"{
5263        // This is a name
5264        "name": "Bob",
5265        /* age field */
5266        "age": 25
5267      }"#,
5268      );
5269      let value = root.to_serde_value().unwrap();
5270
5271      let mut expected_map = serde_json::map::Map::new();
5272      expected_map.insert("name".to_string(), SerdeValue::String("Bob".to_string()));
5273      expected_map.insert(
5274        "age".to_string(),
5275        SerdeValue::Number(serde_json::Number::from_str("25").unwrap()),
5276      );
5277
5278      assert_eq!(value, SerdeValue::Object(expected_map));
5279    }
5280
5281    #[test]
5282    fn test_cst_to_serde_value_nested() {
5283      let root = build_cst(
5284        r#"{
5285        "person": {
5286          "name": "Charlie",
5287          "hobbies": ["reading", "gaming"]
5288        },
5289        "count": 42
5290      }"#,
5291      );
5292      let value = root.to_serde_value().unwrap();
5293
5294      let mut hobbies = Vec::new();
5295      hobbies.push(SerdeValue::String("reading".to_string()));
5296      hobbies.push(SerdeValue::String("gaming".to_string()));
5297
5298      let mut person_map = serde_json::map::Map::new();
5299      person_map.insert("name".to_string(), SerdeValue::String("Charlie".to_string()));
5300      person_map.insert("hobbies".to_string(), SerdeValue::Array(hobbies));
5301
5302      let mut expected_map = serde_json::map::Map::new();
5303      expected_map.insert("person".to_string(), SerdeValue::Object(person_map));
5304      expected_map.insert(
5305        "count".to_string(),
5306        SerdeValue::Number(serde_json::Number::from_str("42").unwrap()),
5307      );
5308
5309      assert_eq!(value, SerdeValue::Object(expected_map));
5310    }
5311
5312    #[test]
5313    fn test_cst_to_serde_value_with_trailing_comma() {
5314      let root = build_cst(
5315        r#"{
5316        "a": 1,
5317        "b": 2,
5318      }"#,
5319      );
5320      let value = root.to_serde_value().unwrap();
5321
5322      let mut expected_map = serde_json::map::Map::new();
5323      expected_map.insert(
5324        "a".to_string(),
5325        SerdeValue::Number(serde_json::Number::from_str("1").unwrap()),
5326      );
5327      expected_map.insert(
5328        "b".to_string(),
5329        SerdeValue::Number(serde_json::Number::from_str("2").unwrap()),
5330      );
5331
5332      assert_eq!(value, SerdeValue::Object(expected_map));
5333    }
5334
5335    #[test]
5336    fn test_cst_to_serde_value_empty_structures() {
5337      let root = build_cst(r#"{}"#);
5338      let value = root.to_serde_value().unwrap();
5339      assert_eq!(value, SerdeValue::Object(serde_json::map::Map::new()));
5340
5341      let root = build_cst(r#"[]"#);
5342      let value = root.to_serde_value().unwrap();
5343      assert_eq!(value, SerdeValue::Array(Vec::new()));
5344    }
5345
5346    #[test]
5347    fn test_cst_to_serde_value_scientific_notation() {
5348      let root = build_cst(r#"0.3e+025"#);
5349      let value = root.to_serde_value().unwrap();
5350      assert_eq!(
5351        value,
5352        SerdeValue::Number(serde_json::Number::from_str("0.3e+025").unwrap())
5353      );
5354    }
5355
5356    #[test]
5357    fn test_cst_node_to_serde_value() {
5358      let root = build_cst(r#"{ "test": 123 }"#);
5359      let value_node = root.value().unwrap();
5360      let json_value = value_node.to_serde_value().unwrap();
5361
5362      let mut expected_map = serde_json::map::Map::new();
5363      expected_map.insert(
5364        "test".to_string(),
5365        SerdeValue::Number(serde_json::Number::from_str("123").unwrap()),
5366      );
5367
5368      assert_eq!(json_value, SerdeValue::Object(expected_map));
5369    }
5370
5371    #[test]
5372    fn test_cst_object_prop_to_serde_value() {
5373      let root = build_cst(r#"{ "key": [1, 2, 3] }"#);
5374      let obj = root.value().unwrap().as_object().unwrap();
5375      let prop = obj.get("key").unwrap();
5376      let prop_value = prop.to_serde_value().unwrap();
5377
5378      let expected = SerdeValue::Array(vec![
5379        SerdeValue::Number(serde_json::Number::from_str("1").unwrap()),
5380        SerdeValue::Number(serde_json::Number::from_str("2").unwrap()),
5381        SerdeValue::Number(serde_json::Number::from_str("3").unwrap()),
5382      ]);
5383
5384      assert_eq!(prop_value, expected);
5385    }
5386  }
5387
5388  #[test]
5389  fn new_escaped_handles_backslashes() {
5390    let cst = build_cst(r#"{"key": "old"}"#);
5391    let root_obj = cst.object_value().unwrap();
5392    let prop = root_obj.get("key").unwrap();
5393    // String containing a backslash: /.github/workflows/lint\.yaml$/
5394    prop.set_value(json!("/.github/workflows/lint\\.yaml$/"));
5395    assert_eq!(cst.to_string(), r#"{"key": "/.github/workflows/lint\\.yaml$/"}"#,);
5396    // Verify decoded value roundtrips correctly
5397    let decoded = root_obj
5398      .get("key")
5399      .unwrap()
5400      .value()
5401      .unwrap()
5402      .as_string_lit()
5403      .unwrap()
5404      .decoded_value()
5405      .unwrap();
5406    assert_eq!(decoded, "/.github/workflows/lint\\.yaml$/");
5407  }
5408
5409  #[test]
5410  fn new_escaped_handles_control_characters() {
5411    let cst = build_cst(r#"{}"#);
5412    let root_obj = cst.object_value_or_create().unwrap();
5413
5414    root_obj.append("tab", json!("hello\tworld"));
5415    root_obj.append("newline", json!("hello\nworld"));
5416    root_obj.append("cr", json!("hello\rworld"));
5417    root_obj.append("backspace", json!("hello\u{08}world"));
5418    root_obj.append("formfeed", json!("hello\u{0c}world"));
5419
5420    let text = cst.to_string();
5421    assert!(text.contains(r#""hello\tworld""#), "tab not escaped: {}", text);
5422    assert!(text.contains(r#""hello\nworld""#), "newline not escaped: {}", text);
5423    assert!(text.contains(r#""hello\rworld""#), "cr not escaped: {}", text);
5424    assert!(text.contains(r#""hello\bworld""#), "backspace not escaped: {}", text);
5425    assert!(text.contains(r#""hello\fworld""#), "formfeed not escaped: {}", text);
5426
5427    // Verify decoded values roundtrip correctly
5428    for (key, expected) in [
5429      ("tab", "hello\tworld"),
5430      ("newline", "hello\nworld"),
5431      ("cr", "hello\rworld"),
5432      ("backspace", "hello\u{08}world"),
5433      ("formfeed", "hello\u{0c}world"),
5434    ] {
5435      let decoded = root_obj
5436        .get(key)
5437        .unwrap()
5438        .value()
5439        .unwrap()
5440        .as_string_lit()
5441        .unwrap()
5442        .decoded_value()
5443        .unwrap();
5444      assert_eq!(decoded, expected, "roundtrip failed for key: {}", key);
5445    }
5446  }
5447
5448  #[test]
5449  fn new_escaped_handles_quotes_and_backslashes_together() {
5450    let cst = build_cst(r#"{}"#);
5451    let root_obj = cst.object_value_or_create().unwrap();
5452
5453    root_obj.append("mixed", json!("say \"hello\\world\""));
5454
5455    let text = cst.to_string();
5456    assert!(
5457      text.contains(r#""say \"hello\\world\"""#),
5458      "mixed escaping failed: {}",
5459      text
5460    );
5461
5462    let decoded = root_obj
5463      .get("mixed")
5464      .unwrap()
5465      .value()
5466      .unwrap()
5467      .as_string_lit()
5468      .unwrap()
5469      .decoded_value()
5470      .unwrap();
5471    assert_eq!(decoded, "say \"hello\\world\"");
5472  }
5473
5474  #[test]
5475  fn new_escaped_in_array_values() {
5476    let cst = build_cst(r#"{"items": []}"#);
5477    let root_obj = cst.object_value().unwrap();
5478    let arr = root_obj.array_value("items").unwrap();
5479
5480    arr.append(json!("path\\to\\file"));
5481    arr.append(json!("line1\nline2"));
5482
5483    let text = cst.to_string();
5484    assert!(
5485      text.contains(r#""path\\to\\file""#),
5486      "backslash in array element: {}",
5487      text
5488    );
5489    assert!(text.contains(r#""line1\nline2""#), "newline in array element: {}", text);
5490  }
5491
5492  #[test]
5493  fn new_escaped_property_name_with_special_chars() {
5494    let cst = build_cst(r#"{}"#);
5495    let root_obj = cst.object_value_or_create().unwrap();
5496
5497    root_obj.append("key\\with\\backslash", json!("value"));
5498
5499    let text = cst.to_string();
5500    assert!(
5501      text.contains(r#""key\\with\\backslash""#),
5502      "property name escaping failed: {}",
5503      text
5504    );
5505
5506    let decoded = root_obj
5507      .properties()
5508      .first()
5509      .unwrap()
5510      .name()
5511      .unwrap()
5512      .decoded_value()
5513      .unwrap();
5514    assert_eq!(decoded, "key\\with\\backslash");
5515  }
5516}