jasn/formatter/
options.rs

1/// Formatting options for JASN output.
2#[derive(Debug, Clone)]
3pub struct Options {
4    /// Indentation string (e.g., "  " or "\t"). Empty string means compact output.
5    pub indent: String,
6
7    /// Add trailing commas to lists and maps.
8    pub trailing_commas: bool,
9
10    /// Quote style for strings.
11    pub quote_style: QuoteStyle,
12
13    /// Binary data encoding preference.
14    pub binary_encoding: BinaryEncoding,
15
16    /// Use unquoted keys in maps when possible.
17    pub unquoted_keys: bool,
18
19    /// Add leading plus sign to positive numbers (+42, +3.14, +inf).
20    pub leading_plus: bool,
21
22    /// Sort map keys alphabetically for consistent output.
23    pub sort_keys: bool,
24
25    /// Escape all non-ASCII characters as \uXXXX sequences.
26    pub escape_unicode: bool,
27
28    /// Use 'Z' for UTC timestamps instead of '+00:00'.
29    pub use_zulu: bool,
30
31    /// Precision for timestamp fractional seconds.
32    pub timestamp_precision: TimestampPrecision,
33}
34
35impl Default for Options {
36    fn default() -> Self {
37        Self::pretty()
38    }
39}
40
41impl Options {
42    /// Creates options for compact output.
43    pub fn compact() -> Self {
44        Self {
45            indent: String::new(),
46            trailing_commas: false,
47            quote_style: QuoteStyle::Double,
48            binary_encoding: BinaryEncoding::Base64,
49            unquoted_keys: true,
50            leading_plus: false,
51            sort_keys: false,
52            escape_unicode: true,
53            use_zulu: true,
54            timestamp_precision: TimestampPrecision::Auto,
55        }
56    }
57
58    /// Creates options for pretty-printed output.
59    pub fn pretty() -> Self {
60        Self {
61            indent: "  ".to_string(),
62            trailing_commas: true,
63            quote_style: QuoteStyle::Double,
64            binary_encoding: BinaryEncoding::Base64,
65            unquoted_keys: true,
66            leading_plus: false,
67            sort_keys: true,
68            escape_unicode: false,
69            use_zulu: true,
70            timestamp_precision: TimestampPrecision::Auto,
71        }
72    }
73
74    /// Sets the indentation string.
75    pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
76        self.indent = indent.into();
77        self
78    }
79
80    /// Sets whether to use trailing commas.
81    pub fn with_trailing_commas(mut self, enable: bool) -> Self {
82        self.trailing_commas = enable;
83        self
84    }
85
86    /// Sets the quote style.
87    pub fn with_quote_style(mut self, style: QuoteStyle) -> Self {
88        self.quote_style = style;
89        self
90    }
91
92    /// Sets the binary encoding preference.
93    pub fn with_binary_encoding(mut self, encoding: BinaryEncoding) -> Self {
94        self.binary_encoding = encoding;
95        self
96    }
97
98    /// Sets whether to use unquoted keys.
99    pub fn with_unquoted_keys(mut self, enable: bool) -> Self {
100        self.unquoted_keys = enable;
101        self
102    }
103
104    /// Sets whether to add leading plus sign to positive numbers.
105    pub fn with_leading_plus(mut self, enable: bool) -> Self {
106        self.leading_plus = enable;
107        self
108    }
109
110    /// Sets whether to sort map keys alphabetically.
111    pub fn with_sort_keys(mut self, enable: bool) -> Self {
112        self.sort_keys = enable;
113        self
114    }
115
116    /// Sets whether to escape non-ASCII characters as \uXXXX.
117    pub fn with_escape_unicode(mut self, enable: bool) -> Self {
118        self.escape_unicode = enable;
119        self
120    }
121
122    /// Sets whether to use 'Z' for UTC timestamps instead of '+00:00'.
123    pub fn with_use_zulu(mut self, enable: bool) -> Self {
124        self.use_zulu = enable;
125        self
126    }
127
128    /// Sets the precision for timestamp fractional seconds.
129    pub fn with_timestamp_precision(mut self, precision: TimestampPrecision) -> Self {
130        self.timestamp_precision = precision;
131        self
132    }
133}
134
135/// Quote style for strings and map keys.
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137pub enum QuoteStyle {
138    /// Always use double quotes: "string"
139    Double,
140
141    /// Always use single quotes: 'string'
142    Single,
143
144    /// Prefer double quotes, but use single if string contains "
145    PreferDouble,
146}
147
148/// Binary data encoding preference.
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub enum BinaryEncoding {
151    /// Always use base64: b64"..."
152    Base64,
153
154    /// Always use hex: hex"..."
155    Hex,
156}
157
158/// Precision for timestamp fractional seconds.
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub enum TimestampPrecision {
161    /// Automatically use minimum necessary digits (default).
162    Auto,
163
164    /// No fractional seconds (whole seconds only).
165    Seconds,
166
167    /// Milliseconds (3 decimal places).
168    Milliseconds,
169
170    /// Microseconds (6 decimal places).
171    Microseconds,
172
173    /// Nanoseconds (9 decimal places).
174    Nanoseconds,
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn test_compact_options() {
183        let opts = Options::compact();
184        assert!(opts.indent.is_empty());
185        assert!(!opts.trailing_commas);
186        assert!(opts.unquoted_keys);
187    }
188
189    #[test]
190    fn test_pretty_options() {
191        let opts = Options::pretty();
192        assert_eq!(opts.indent, "  ");
193        assert!(opts.trailing_commas);
194        assert!(opts.unquoted_keys);
195    }
196
197    #[test]
198    fn test_builder_pattern() {
199        let opts = Options::compact()
200            .with_indent("\t")
201            .with_trailing_commas(true)
202            .with_quote_style(QuoteStyle::Single);
203
204        assert_eq!(opts.indent, "\t");
205        assert!(opts.trailing_commas);
206        assert_eq!(opts.quote_style, QuoteStyle::Single);
207    }
208}