Skip to main content

jjpwrgem_parse/
format.rs

1mod prettify;
2#[cfg(feature = "serde")]
3pub mod serde;
4pub(crate) mod uglify;
5
6pub use prettify::{prettify_document, prettify_document_into, prettify_str};
7pub use uglify::{uglify_document, uglify_document_into, uglify_str, uglify_str_into};
8
9use crate::ast::Document;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum FormatRequest {
13    Json(JsonMode),
14    Jsonlines(JsonlinesOptions),
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum JsonMode {
19    Prettify(PrettifyOptions),
20    Uglify,
21}
22
23impl Default for FormatRequest {
24    fn default() -> Self {
25        Self::Json(JsonMode::default())
26    }
27}
28
29impl Default for JsonMode {
30    fn default() -> Self {
31        Self::Prettify(PrettifyOptions::default())
32    }
33}
34
35#[derive(bon::Builder, Debug, Clone, PartialEq, Eq)]
36pub struct PrettifyOptions {
37    #[builder(default = 80)]
38    pub preferred_width: usize,
39    #[builder(default)]
40    pub line_ending: LineEnding,
41}
42
43impl Default for PrettifyOptions {
44    fn default() -> Self {
45        Self::builder().build()
46    }
47}
48
49#[derive(bon::Builder, Default, Debug, Clone, PartialEq, Eq)]
50pub struct JsonlinesOptions {
51    #[builder(default)]
52    pub line_ending: LineEnding,
53}
54
55pub fn format_document<S: AsRef<str>>(doc: &Document<S>, mode: &JsonMode) -> String {
56    match mode {
57        JsonMode::Prettify(opts) => prettify_document(doc, opts.preferred_width, opts.line_ending),
58        JsonMode::Uglify => uglify_document(doc),
59    }
60}
61
62use crate::tokens::{FALSE, NULL, TRUE};
63
64pub(crate) fn join_into<T, B>(
65    buf: &mut B,
66    items: impl IntoIterator<Item = T>,
67    mut item_fmt: impl FnMut(&mut B, &T),
68    mut delim_fmt: impl FnMut(&mut B, &T),
69) {
70    let mut iter = items.into_iter();
71    if let Some(first) = iter.next() {
72        item_fmt(buf, &first);
73        for item in iter {
74            delim_fmt(buf, &item);
75            item_fmt(buf, &item);
76        }
77    }
78}
79
80#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
81pub enum LineEnding {
82    #[default]
83    Lf,
84    CrLf,
85}
86
87impl LineEnding {
88    pub const fn as_str(self) -> &'static str {
89        match self {
90            Self::Lf => "\n",
91            Self::CrLf => "\r\n",
92        }
93    }
94}
95
96pub(crate) trait Emitter {
97    fn push(&mut self, c: char);
98    fn push_str(&mut self, s: &str);
99
100    // defaults
101    fn push_quoted(&mut self, s: &str) {
102        self.push('"');
103        self.push_str(s);
104        self.push('"');
105    }
106
107    fn emit_null(&mut self) {
108        self.push_str(NULL);
109    }
110    fn emit_string(&mut self, s: &str) {
111        self.push_quoted(s);
112    }
113    fn emit_mantissa(&mut self, n: &str) {
114        self.push_str(n);
115    }
116    fn emit_exponent(&mut self, e: &str) {
117        self.push('e');
118        self.push_str(e);
119    }
120    fn emit_boolean(&mut self, b: bool) {
121        self.push_str(if b { TRUE } else { FALSE });
122    }
123    fn emit_item_delim(&mut self) {
124        self.push(',');
125    }
126    fn emit_array_open(&mut self) {
127        self.push('[');
128    }
129    fn emit_array_close(&mut self) {
130        self.push(']');
131    }
132    fn emit_object_open(&mut self) {
133        self.push('{');
134    }
135    fn emit_object_close(&mut self) {
136        self.push('}');
137    }
138    fn emit_key_val_delim(&mut self) {
139        self.push(':');
140    }
141}