Skip to main content

json_syntax/print/
mod.rs

1use core::fmt;
2use printer::JsonPrinter;
3use sizes::JsonSizesVisitor;
4
5use crate::visitor::JsonVisit;
6
7mod printer;
8pub(crate) mod sizes;
9
10#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
11pub enum Indent {
12	Spaces(u8),
13	Tabs(u8),
14}
15
16impl Indent {
17	pub fn by(self, n: usize) -> IndentBy {
18		IndentBy(self, n)
19	}
20}
21
22impl fmt::Display for Indent {
23	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24		match self {
25			Self::Spaces(n) => {
26				for _ in 0..*n {
27					f.write_str(" ")?
28				}
29			}
30			Self::Tabs(n) => {
31				for _ in 0..*n {
32					f.write_str("\t")?
33				}
34			}
35		}
36
37		Ok(())
38	}
39}
40
41pub struct IndentBy(Indent, usize);
42
43impl fmt::Display for IndentBy {
44	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45		for _ in 0..self.1 {
46			self.0.fmt(f)?
47		}
48
49		Ok(())
50	}
51}
52
53pub struct Spaces(pub usize);
54
55impl fmt::Display for Spaces {
56	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57		for _ in 0..self.0 {
58			f.write_str(" ")?
59		}
60
61		Ok(())
62	}
63}
64
65#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
66pub enum Padding {
67	Spaces(u8),
68	NewLine,
69}
70
71impl fmt::Display for Padding {
72	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73		match self {
74			Self::Spaces(n) => {
75				for _ in 0..*n {
76					f.write_str(" ")?
77				}
78			}
79			Self::NewLine => f.write_str("\n")?,
80		}
81
82		Ok(())
83	}
84}
85
86#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
87pub enum Limit {
88	/// Always expanded, even if empty.
89	Always,
90
91	/// Expanded if the array/object has more than the given number of items.
92	Item(usize),
93
94	/// Expanded if the representation of the array/object is more than the
95	/// given number of characters long.
96	Width(usize),
97
98	/// Expanded if the array/object has more than the given number of items
99	/// (first argument), or if its the representation is more than the
100	/// given number of characters long (second argument).
101	ItemOrWidth(usize, usize),
102}
103
104/// Print options.
105#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
106#[non_exhaustive]
107pub struct JsonPrintOptions {
108	/// Indentation string.
109	pub indent: Indent,
110
111	/// String added after `[`.
112	pub array_begin: usize,
113
114	/// String added before `]`.
115	pub array_end: usize,
116
117	/// Number of spaces inside an inlined empty array.
118	pub array_empty: usize,
119
120	/// Number of spaces before a comma in an array.
121	pub array_before_comma: usize,
122
123	/// Number of spaces after a comma in an array.
124	pub array_after_comma: usize,
125
126	/// Limit after which an array is expanded.
127	pub array_limit: Option<Limit>,
128
129	/// String added after `{`.
130	pub object_begin: usize,
131
132	/// String added before `}`.
133	pub object_end: usize,
134
135	/// Number of spaces inside an inlined empty object.
136	pub object_empty: usize,
137
138	/// Number of spaces before a comma in an object.
139	pub object_before_comma: usize,
140
141	/// Number of spaces after a comma in an object.
142	pub object_after_comma: usize,
143
144	/// Number of spaces before a colon in an object.
145	pub object_before_colon: usize,
146
147	/// Number of spaces after a colon in an object.
148	pub object_after_colon: usize,
149
150	/// Limit after which an array is expanded.
151	pub object_limit: Option<Limit>,
152}
153
154impl JsonPrintOptions {
155	/// Pretty print options.
156	pub const PRETTY: Self = Self {
157		indent: Indent::Spaces(2),
158		array_begin: 1,
159		array_end: 1,
160		array_empty: 0,
161		array_before_comma: 0,
162		array_after_comma: 1,
163		array_limit: Some(Limit::ItemOrWidth(1, 16)),
164		object_begin: 1,
165		object_end: 1,
166		object_empty: 0,
167		object_before_comma: 0,
168		object_after_comma: 1,
169		object_before_colon: 0,
170		object_after_colon: 1,
171		object_limit: Some(Limit::ItemOrWidth(1, 16)),
172	};
173
174	/// Compact print options.
175	///
176	/// Values will be formatted on a single line without spaces.
177	pub const COMPACT: Self = Self {
178		indent: Indent::Spaces(0),
179		array_begin: 0,
180		array_end: 0,
181		array_empty: 0,
182		array_before_comma: 0,
183		array_after_comma: 0,
184		array_limit: None,
185		object_begin: 0,
186		object_end: 0,
187		object_empty: 0,
188		object_before_comma: 0,
189		object_after_comma: 0,
190		object_before_colon: 0,
191		object_after_colon: 0,
192		object_limit: None,
193	};
194
195	/// Inline print options.
196	///
197	/// Values will be formatted on a single line with some spaces.
198	pub const INLINE: Self = Self {
199		indent: Indent::Spaces(0),
200		array_begin: 1,
201		array_end: 1,
202		array_empty: 0,
203		array_before_comma: 0,
204		array_after_comma: 1,
205		array_limit: None,
206		object_begin: 1,
207		object_end: 1,
208		object_empty: 0,
209		object_before_comma: 0,
210		object_after_comma: 1,
211		object_before_colon: 0,
212		object_after_colon: 1,
213		object_limit: None,
214	};
215}
216
217/// Print methods.
218pub trait JsonPrint {
219	/// Print the value with `Options::pretty` options.
220	#[inline(always)]
221	fn pretty_print(&self) -> Printed<'_, 'static, Self> {
222		self.print_with(&JsonPrintOptions::PRETTY)
223	}
224
225	/// Print the value with `Options::compact` options.
226	#[inline(always)]
227	fn compact_print(&self) -> Printed<'_, 'static, Self> {
228		self.print_with(&JsonPrintOptions::COMPACT)
229	}
230
231	/// Print the value with `Options::inline` options.
232	#[inline(always)]
233	fn inline_print(&self) -> Printed<'_, 'static, Self> {
234		self.print_with(&JsonPrintOptions::INLINE)
235	}
236
237	/// Print the value with the given options.
238	#[inline(always)]
239	fn print_with<'o>(&self, options: &'o JsonPrintOptions) -> Printed<'_, 'o, Self> {
240		Printed(self, options, 0)
241	}
242
243	fn fmt_with(
244		&self,
245		f: &mut fmt::Formatter,
246		options: &JsonPrintOptions,
247		indent: usize,
248	) -> fmt::Result;
249}
250
251impl<T: JsonVisit> JsonPrint for T {
252	fn fmt_with(
253		&self,
254		f: &mut fmt::Formatter,
255		options: &JsonPrintOptions,
256		indent: usize,
257	) -> fmt::Result {
258		let mut sizes = Vec::new();
259		self.visit(JsonSizesVisitor::new(options, &mut sizes));
260		let mut offset = 0;
261		self.visit(JsonPrinter::new(options, &sizes, &mut offset, indent, f))
262	}
263}
264
265/// Printed value.
266pub struct Printed<'t, 'o, T: ?Sized>(&'t T, &'o JsonPrintOptions, usize);
267
268impl<T: JsonPrint> fmt::Display for Printed<'_, '_, T> {
269	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270		self.0.fmt_with(f, self.1, self.2)
271	}
272}