json5format/
formatter.rs

1// Copyright (c) 2020 Google LLC All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5#![deny(missing_docs)]
6use {
7    crate::{content::*, error::*, options::*},
8    std::cell::RefCell,
9    std::collections::HashMap,
10    std::collections::HashSet,
11    std::rc::Rc,
12};
13
14pub(crate) struct SubpathOptions {
15    /// Options for the matching property name, including subpath-options for nested containers.
16    /// If matched, these options apply exclusively; the `options_for_next_level` will not apply.
17    subpath_options_by_name: HashMap<String, Rc<RefCell<SubpathOptions>>>,
18
19    /// Options for nested containers under any array item, or any property not matching a property
20    /// name in `subpath_options_by_name`.
21    unnamed_subpath_options: Option<Rc<RefCell<SubpathOptions>>>,
22
23    /// The options that override the default FormatOptions (those passed to `with_options()`) for
24    /// the matched path.
25    pub options: FormatOptions,
26
27    /// A map of property names to priority values, for sorting properties at the matched path.
28    property_name_priorities: HashMap<&'static str, usize>,
29}
30
31impl SubpathOptions {
32    /// Properties without an explicit priority will be sorted after prioritized properties and
33    /// retain their original order with respect to any other unpriorized properties.
34    const NO_PRIORITY: usize = std::usize::MAX;
35
36    pub fn new(default_options: &FormatOptions) -> Self {
37        Self {
38            subpath_options_by_name: HashMap::new(),
39            unnamed_subpath_options: None,
40            options: default_options.clone(),
41            property_name_priorities: HashMap::new(),
42        }
43    }
44
45    pub fn override_default_options(&mut self, path_options: &HashSet<PathOption>) {
46        for path_option in path_options.iter() {
47            use PathOption::*;
48            match path_option {
49                TrailingCommas(path_value) => self.options.trailing_commas = *path_value,
50                CollapseContainersOfOne(path_value) => {
51                    self.options.collapse_containers_of_one = *path_value
52                }
53                SortArrayItems(path_value) => self.options.sort_array_items = *path_value,
54                PropertyNameOrder(property_names) => {
55                    for (index, property_name) in property_names.iter().enumerate() {
56                        self.property_name_priorities.insert(property_name, index);
57                    }
58                }
59            }
60        }
61    }
62
63    pub fn get_or_create_subpath_options(
64        &mut self,
65        path: &[&str],
66        default_options: &FormatOptions,
67    ) -> Rc<RefCell<SubpathOptions>> {
68        let name_or_star = path[0];
69        let remaining_path = &path[1..];
70        let subpath_options_ref = if name_or_star == "*" {
71            self.unnamed_subpath_options.as_ref()
72        } else {
73            self.subpath_options_by_name.get(name_or_star)
74        };
75        let subpath_options = match subpath_options_ref {
76            Some(existing_options) => existing_options.clone(),
77            None => {
78                let new_options = Rc::new(RefCell::new(SubpathOptions::new(default_options)));
79                if name_or_star == "*" {
80                    self.unnamed_subpath_options = Some(new_options.clone());
81                } else {
82                    self.subpath_options_by_name
83                        .insert(name_or_star.to_string(), new_options.clone());
84                }
85                new_options
86            }
87        };
88        if remaining_path.len() == 0 {
89            subpath_options
90        } else {
91            (*subpath_options.borrow_mut())
92                .get_or_create_subpath_options(remaining_path, default_options)
93        }
94    }
95
96    fn get_subpath_options(&self, path: &[&str]) -> Option<Rc<RefCell<SubpathOptions>>> {
97        let name_or_star = path[0];
98        let remaining_path = &path[1..];
99        let subpath_options_ref = if name_or_star == "*" {
100            self.unnamed_subpath_options.as_ref()
101        } else {
102            self.subpath_options_by_name.get(name_or_star)
103        };
104        if let Some(subpath_options) = subpath_options_ref {
105            if remaining_path.len() == 0 {
106                Some(subpath_options.clone())
107            } else {
108                (*subpath_options.borrow()).get_subpath_options(remaining_path)
109            }
110        } else {
111            None
112        }
113    }
114
115    fn get_options_for(&self, name_or_star: &str) -> Option<Rc<RefCell<SubpathOptions>>> {
116        self.get_subpath_options(&[name_or_star])
117    }
118
119    pub fn get_property_priority(&self, property_name: &str) -> usize {
120        match self.property_name_priorities.get(property_name) {
121            Some(priority) => *priority,
122            None => SubpathOptions::NO_PRIORITY,
123        }
124    }
125
126    fn debug_format(
127        &self,
128        formatter: &mut std::fmt::Formatter<'_>,
129        indent: &str,
130    ) -> std::fmt::Result {
131        writeln!(formatter, "{{")?;
132        let next_indent = indent.to_owned() + "    ";
133        writeln!(formatter, "{}options = {:?}", &next_indent, self.options)?;
134        writeln!(
135            formatter,
136            "{}property_name_priorities = {:?}",
137            &next_indent, self.property_name_priorities
138        )?;
139        if let Some(unnamed_subpath_options) = &self.unnamed_subpath_options {
140            write!(formatter, "{}* = ", &next_indent)?;
141            (*unnamed_subpath_options.borrow()).debug_format(formatter, &next_indent)?;
142            writeln!(formatter)?;
143        }
144        for (property_name, subpath_options) in self.subpath_options_by_name.iter() {
145            write!(formatter, "{}{} = ", &next_indent, property_name)?;
146            (*subpath_options.borrow()).debug_format(formatter, &next_indent)?;
147            writeln!(formatter)?;
148        }
149        writeln!(formatter, "{}}}", &indent)
150    }
151}
152
153impl std::fmt::Debug for SubpathOptions {
154    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        self.debug_format(formatter, "")
156    }
157}
158
159/// A JSON5 formatter that produces formatted JSON5 document content from a JSON5 `ParsedDocument`.
160pub(crate) struct Formatter {
161    /// The current depth of the partially-generated document while formatting. Each nexted array or
162    /// object increases the depth by 1. After the formatted array or object has been generated, the
163    /// depth decreases by 1.
164    depth: usize,
165
166    /// The next value to be written should be indented.
167    pending_indent: bool,
168
169    /// The UTF-8 bytes of the output document as it is being generated.
170    bytes: Vec<u8>,
171
172    /// The 1-based column number of the next character to be appended.
173    column: usize,
174
175    /// While generating the formatted document, these are the options to be applied at each nesting
176    /// depth and path, from the document root to the object or array currently being generated. If
177    /// the current path has no explicit options, the value at the top of the stack is None.
178    subpath_options_stack: Vec<Option<Rc<RefCell<SubpathOptions>>>>,
179
180    /// Options that alter how the formatter generates the formatted output. This instance of
181    /// FormatOptions is a subset of the FormatOptions passed to the `with_options` constructor.
182    /// The `options_by_path` are first removed, and then used to initialize the SubpathOptions
183    /// hierarchy rooted at the `document_root_options_ref`.
184    default_options: FormatOptions,
185}
186
187impl Formatter {
188    /// Create and return a Formatter, with the given options to be applied to the
189    /// [Json5Format::to_utf8()](struct.Json5Format.html#method.to_utf8) operation.
190    pub fn new(
191        default_options: FormatOptions,
192        document_root_options_ref: Rc<RefCell<SubpathOptions>>,
193    ) -> Self {
194        Formatter {
195            depth: 0,
196            pending_indent: false,
197            bytes: vec![],
198            column: 1,
199            subpath_options_stack: vec![Some(document_root_options_ref)],
200            default_options,
201        }
202    }
203
204    pub fn increase_indent(&mut self) -> Result<&mut Formatter, Error> {
205        self.depth += 1;
206        Ok(self)
207    }
208
209    pub fn decrease_indent(&mut self) -> Result<&mut Formatter, Error> {
210        self.depth -= 1;
211        Ok(self)
212    }
213
214    /// Appends the given string, indenting if required.
215    pub fn append(&mut self, content: &str) -> Result<&mut Formatter, Error> {
216        if self.pending_indent && !content.starts_with("\n") {
217            let spaces = self.depth * self.default_options.indent_by;
218            self.bytes.extend_from_slice(" ".repeat(spaces).as_bytes());
219            self.column = spaces + 1;
220            self.pending_indent = false;
221        }
222        if content.ends_with("\n") {
223            self.column = 1;
224            self.bytes.extend_from_slice(content.as_bytes());
225        } else {
226            let mut first = true;
227            for line in content.lines() {
228                if !first {
229                    self.bytes.extend_from_slice("\n".as_bytes());
230                    self.column = 1;
231                }
232                self.bytes.extend_from_slice(line.as_bytes());
233                self.column += line.len();
234                first = false;
235            }
236        }
237        Ok(self)
238    }
239
240    pub fn append_newline(&mut self) -> Result<&mut Formatter, Error> {
241        self.append("\n")
242    }
243
244    /// Outputs a newline (unless this is the first line), and sets the `pending_indent` flag to
245    /// indicate the next non-blank line should be indented.
246    pub fn start_next_line(&mut self) -> Result<&mut Formatter, Error> {
247        if self.bytes.len() > 0 {
248            self.append_newline()?;
249        }
250        self.pending_indent = true;
251        Ok(self)
252    }
253
254    fn format_content<F>(&mut self, content_fn: F) -> Result<&mut Formatter, Error>
255    where
256        F: FnOnce(&mut Formatter) -> Result<&mut Formatter, Error>,
257    {
258        content_fn(self)
259    }
260
261    pub fn format_container<F>(
262        &mut self,
263        left_brace: &str,
264        right_brace: &str,
265        content_fn: F,
266    ) -> Result<&mut Formatter, Error>
267    where
268        F: FnOnce(&mut Formatter) -> Result<&mut Formatter, Error>,
269    {
270        self.append(left_brace)?
271            .increase_indent()?
272            .format_content(content_fn)?
273            .decrease_indent()?
274            .append(right_brace)
275    }
276
277    fn format_comments_internal(
278        &mut self,
279        comments: &Vec<Comment>,
280        leading_blank_line: bool,
281    ) -> Result<&mut Formatter, Error> {
282        let mut previous: Option<&Comment> = None;
283        for comment in comments.iter() {
284            match previous {
285                Some(previous) => {
286                    if comment.is_block() || previous.is_block() {
287                        // Separate block comments and contiguous line comments.
288                        // Use append_newline() instead of start_next_line() because block comment
289                        // lines after the first line append their own indentation spaces.
290                        self.append_newline()?;
291                    }
292                }
293                None => {
294                    if leading_blank_line {
295                        self.start_next_line()?;
296                    }
297                }
298            }
299            comment.format(self)?;
300            previous = Some(comment)
301        }
302        Ok(self)
303    }
304
305    pub fn format_comments(
306        &mut self,
307        comments: &Vec<Comment>,
308        is_first: bool,
309    ) -> Result<&mut Formatter, Error> {
310        self.format_comments_internal(comments, !is_first)
311    }
312
313    pub fn format_trailing_comments(
314        &mut self,
315        comments: &Vec<Comment>,
316    ) -> Result<&mut Formatter, Error> {
317        self.format_comments_internal(comments, true)
318    }
319
320    pub fn get_current_subpath_options(&self) -> Option<&Rc<RefCell<SubpathOptions>>> {
321        self.subpath_options_stack.last().unwrap().as_ref()
322    }
323
324    fn enter_scope(&mut self, name_or_star: &str) {
325        let mut subpath_options_to_push = None;
326        if let Some(current_subpath_options_ref) = self.get_current_subpath_options() {
327            let current_subpath_options = &*current_subpath_options_ref.borrow();
328            if let Some(next_subpath_options_ref) =
329                current_subpath_options.get_options_for(name_or_star)
330            {
331                // SubpathOptions were explicitly provided for:
332                //   * the given property name in the current object; or
333                //   * all array items within the current array (as indicated by "*")
334                subpath_options_to_push = Some(next_subpath_options_ref.clone());
335            } else if name_or_star != "*" {
336                if let Some(next_subpath_options_ref) = current_subpath_options.get_options_for("*")
337                {
338                    // `name_or_star` was a property name, and SubpathOptions for this path were
339                    // _not_ explicitly defined for this name. In this case, a Subpath defined with
340                    // "*" at this Subpath location, if provided, matches any property name in the
341                    // current object (like a wildcard).
342                    subpath_options_to_push = Some(next_subpath_options_ref.clone());
343                }
344            }
345        }
346        self.subpath_options_stack.push(subpath_options_to_push);
347    }
348
349    fn exit_scope(&mut self) {
350        self.subpath_options_stack.pop();
351    }
352
353    fn format_scoped_value(
354        &mut self,
355        name: Option<&str>,
356        value: &mut Value,
357        is_first: bool,
358        is_last: bool,
359        container_has_pending_comments: bool,
360    ) -> Result<&mut Formatter, Error> {
361        let collapsed = is_first
362            && is_last
363            && value.is_primitive()
364            && !value.has_comments()
365            && !container_has_pending_comments
366            && self.options_in_scope().collapse_containers_of_one;
367        match name {
368            // Above the enter_scope(...), the container's SubpathOptions affect formatting
369            // and below, formatting is affected by named property or item SubpathOptions.
370            //                 vvvvvvvvvvv
371            Some(name) => self.enter_scope(name),
372            None => self.enter_scope("*"),
373        }
374        if collapsed {
375            self.append(" ")?;
376        } else {
377            if is_first {
378                self.start_next_line()?;
379            }
380            self.format_comments(&value.comments().before_value(), is_first)?;
381        }
382        if let Some(name) = name {
383            self.append(&format!("{}: ", name))?;
384        }
385        value.format(self)?;
386        self.exit_scope();
387        //   ^^^^^^^^^^
388        // Named property or item SubpathOptions affect Formatting above exit_scope(...)
389        // and below, formatting is affected by the container's SubpathOptions.
390        if collapsed {
391            self.append(" ")?;
392        } else {
393            self.append_comma(is_last)?
394                .append_end_of_line_comment(value.comments().end_of_line())?
395                .start_next_line()?;
396        }
397        Ok(self)
398    }
399
400    pub fn format_item(
401        &mut self,
402        item: &Rc<RefCell<Value>>,
403        is_first: bool,
404        is_last: bool,
405        container_has_pending_comments: bool,
406    ) -> Result<&mut Formatter, Error> {
407        self.format_scoped_value(
408            None,
409            &mut *item.borrow_mut(),
410            is_first,
411            is_last,
412            container_has_pending_comments,
413        )
414    }
415
416    pub fn format_property(
417        &mut self,
418        property: &Property,
419        is_first: bool,
420        is_last: bool,
421        container_has_pending_comments: bool,
422    ) -> Result<&mut Formatter, Error> {
423        self.format_scoped_value(
424            Some(&property.name),
425            &mut *property.value.borrow_mut(),
426            is_first,
427            is_last,
428            container_has_pending_comments,
429        )
430    }
431
432    pub fn options_in_scope(&self) -> FormatOptions {
433        match self.get_current_subpath_options() {
434            Some(subpath_options) => (*subpath_options.borrow()).options.clone(),
435            None => self.default_options.clone(),
436        }
437    }
438
439    fn append_comma(&mut self, is_last: bool) -> Result<&mut Formatter, Error> {
440        if !is_last || self.options_in_scope().trailing_commas {
441            self.append(",")?;
442        }
443        Ok(self)
444    }
445
446    /// Outputs the value's end-of-line comment. If the comment has multiple lines, the first line
447    /// is written from the current position and all subsequent lines are written on their own line,
448    /// left-aligned directly under the first comment.
449    fn append_end_of_line_comment(
450        &mut self,
451        comment: &Option<String>,
452    ) -> Result<&mut Formatter, Error> {
453        if let Some(comment) = comment {
454            let start_column = self.column;
455            let mut first = true;
456            for line in comment.lines() {
457                if !first {
458                    self.append_newline()?;
459                    self.append(&" ".repeat(start_column - 1))?;
460                }
461                self.append(&format!(" //{}", line))?;
462                first = false;
463            }
464        }
465        Ok(self)
466    }
467
468    /// Formats the given document into the returned UTF8 byte buffer, consuming self.
469    pub fn format(mut self, parsed_document: &ParsedDocument) -> Result<Vec<u8>, Error> {
470        parsed_document.content.format_content(&mut self)?;
471        Ok(self.bytes)
472    }
473}