1#![cfg_attr(not(feature = "std"), no_std)]
93#![doc(html_root_url = "https://docs.rs/indenter/0.3.4")]
94#![warn(
95 missing_debug_implementations,
96 missing_docs,
97 missing_doc_code_examples,
98 rust_2018_idioms,
99 unreachable_pub,
100 bad_style,
101 dead_code,
102 improper_ctypes,
103 non_shorthand_field_patterns,
104 no_mangle_generic_items,
105 overflowing_literals,
106 path_statements,
107 patterns_in_fns_without_body,
108 unconditional_recursion,
109 unused,
110 unused_allocation,
111 unused_comparisons,
112 unused_parens,
113 while_true
114)]
115
116use core::fmt;
117
118#[allow(missing_debug_implementations)]
120pub enum Format<'a> {
121 Uniform {
125 indentation: &'static str,
127 },
128 Numbered {
133 ind: usize,
135 },
136 Custom {
140 inserter: &'a mut Inserter,
142 },
143}
144
145#[allow(missing_debug_implementations)]
155pub struct Indented<'a, D: ?Sized> {
156 inner: &'a mut D,
157 line_number: usize,
158 needs_indent: bool,
159 format: Format<'a>,
160}
161
162pub type Inserter = dyn FnMut(usize, &mut dyn fmt::Write) -> fmt::Result;
166
167impl Format<'_> {
168 fn insert_indentation(&mut self, line: usize, f: &mut dyn fmt::Write) -> fmt::Result {
169 match self {
170 Format::Uniform { indentation } => write!(f, "{}", indentation),
171 Format::Numbered { ind } => {
172 if line == 0 {
173 write!(f, "{: >4}: ", ind)
174 } else {
175 write!(f, " ")
176 }
177 }
178 Format::Custom { inserter } => inserter(line, f),
179 }
180 }
181}
182
183impl<'a, D> Indented<'a, D> {
184 pub fn ind(self, ind: usize) -> Self {
186 self.with_format(Format::Numbered { ind })
187 }
188
189 pub fn with_str(self, indentation: &'static str) -> Self {
191 self.with_format(Format::Uniform { indentation })
192 }
193
194 pub fn with_format(mut self, format: Format<'a>) -> Self {
196 self.format = format;
197 self
198 }
199}
200
201impl<T> fmt::Write for Indented<'_, T>
202where
203 T: fmt::Write + ?Sized,
204{
205 fn write_str(&mut self, s: &str) -> fmt::Result {
206 for (i, line) in s.split('\n').enumerate() {
207 if i > 0 {
208 self.inner.write_char('\n')?;
209 self.needs_indent = true;
210 }
211
212 if self.needs_indent {
213 if line.is_empty() {
215 continue;
216 }
217
218 self.format
219 .insert_indentation(self.line_number, &mut self.inner)?;
220 self.line_number += 1;
221 self.needs_indent = false;
222 }
223
224 self.inner.write_str(line)?;
225 }
226
227 Ok(())
228 }
229}
230
231pub fn indented<D: ?Sized>(f: &mut D) -> Indented<'_, D> {
233 Indented {
234 inner: f,
235 line_number: 0,
236 needs_indent: true,
237 format: Format::Uniform {
238 indentation: " ",
239 },
240 }
241}
242
243#[cfg(feature = "std")]
250#[allow(missing_debug_implementations)]
251pub struct CodeFormatter<'a, T> {
252 f: &'a mut T,
253 level: u32,
254 indentation: String,
255}
256
257#[cfg(feature = "std")]
258impl<'a, T: fmt::Write> fmt::Write for CodeFormatter<'a, T> {
259 fn write_str(&mut self, input: &str) -> fmt::Result {
260 let input = match input.chars().next() {
261 Some('\n') => &input[1..],
262 _ => return self.f.write_str(input),
263 };
264
265 let min = input
266 .split('\n')
267 .map(|line| line.chars().take_while(char::is_ascii_whitespace).count())
268 .filter(|count| *count > 0)
269 .min()
270 .unwrap_or_default();
271
272 let input = input.trim_end_matches(|c| char::is_ascii_whitespace(&c));
273
274 for line in input.split('\n') {
275 if line.len().saturating_sub(min) > 0 {
276 for _ in 0..self.level {
277 self.f.write_str(&self.indentation)?;
278 }
279 }
280
281 if line.len() >= min {
282 self.f.write_str(&line[min..])?;
283 } else {
284 self.f.write_str(&line)?;
285 }
286 self.f.write_char('\n')?;
287 }
288
289 Ok(())
290 }
291
292 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
293 self.write_str(&args.to_string())
294 }
295}
296
297#[cfg(feature = "std")]
298impl<'a, T: fmt::Write> CodeFormatter<'a, T> {
299 pub fn new<S: Into<String>>(f: &'a mut T, indentation: S) -> Self {
302 Self {
303 f,
304 level: 0,
305 indentation: indentation.into(),
306 }
307 }
308
309 pub fn set_level(&mut self, level: u32) {
311 self.level = level;
312 }
313
314 pub fn indent(&mut self, inc: u32) {
316 self.level = self.level.saturating_add(inc);
317 }
318
319 pub fn dedent(&mut self, inc: u32) {
321 self.level = self.level.saturating_sub(inc);
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 extern crate alloc;
328
329 use super::*;
330 use alloc::string::String;
331 use core::fmt::Write as _;
332
333 #[test]
334 fn one_digit() {
335 let input = "verify\nthis";
336 let expected = " 2: verify\n this";
337 let mut output = String::new();
338
339 indented(&mut output).ind(2).write_str(input).unwrap();
340
341 assert_eq!(expected, output);
342 }
343
344 #[test]
345 fn two_digits() {
346 let input = "verify\nthis";
347 let expected = " 12: verify\n this";
348 let mut output = String::new();
349
350 indented(&mut output).ind(12).write_str(input).unwrap();
351
352 assert_eq!(expected, output);
353 }
354
355 #[test]
356 fn no_digits() {
357 let input = "verify\nthis";
358 let expected = " verify\n this";
359 let mut output = String::new();
360
361 indented(&mut output).write_str(input).unwrap();
362
363 assert_eq!(expected, output);
364 }
365
366 #[test]
367 fn with_str() {
368 let input = "verify\nthis";
369 let expected = "...verify\n...this";
370 let mut output = String::new();
371
372 indented(&mut output)
373 .with_str("...")
374 .write_str(input)
375 .unwrap();
376
377 assert_eq!(expected, output);
378 }
379
380 #[test]
381 fn dyn_write() {
382 let input = "verify\nthis";
383 let expected = " verify\n this";
384 let mut output = String::new();
385 let writer: &mut dyn core::fmt::Write = &mut output;
386
387 indented(writer).write_str(input).unwrap();
388
389 assert_eq!(expected, output);
390 }
391
392 #[test]
393 fn nice_api() {
394 let input = "verify\nthis";
395 let expected = " 1: verify\n this";
396 let output = &mut String::new();
397 let n = 1;
398
399 write!(
400 indented(output).with_format(Format::Custom {
401 inserter: &mut move |line_no, f| {
402 if line_no == 0 {
403 write!(f, "{: >4}: ", n)
404 } else {
405 write!(f, " ")
406 }
407 }
408 }),
409 "{}",
410 input
411 )
412 .unwrap();
413
414 assert_eq!(expected, output);
415 }
416
417 #[test]
418 fn nice_api_2() {
419 let input = "verify\nthis";
420 let expected = " verify\n this";
421 let output = &mut String::new();
422
423 write!(
424 indented(output).with_format(Format::Uniform { indentation: " " }),
425 "{}",
426 input
427 )
428 .unwrap();
429
430 assert_eq!(expected, output);
431 }
432
433 #[test]
434 fn empty_lines() {
435 let input = "\n\n\nverify\n\nthis";
436 let expected = "\n\n\n verify\n\n this";
437 let output = &mut String::new();
438
439 write!(indented(output).with_str(" "), "{}", input).unwrap();
440
441 assert_eq!(expected, output);
442 }
443
444 #[test]
445 fn trailing_newlines() {
446 let input = "verify\nthis\n";
447 let expected = " verify\n this\n";
448 let output = &mut String::new();
449
450 write!(indented(output).with_str(" "), "{}", input).unwrap();
451
452 assert_eq!(expected, output);
453 }
454
455 #[test]
456 fn several_interpolations() {
457 let input = "verify\nthis\n";
458 let expected = " verify\n this\n and verify\n this\n";
459 let output = &mut String::new();
460
461 write!(indented(output).with_str(" "), "{} and {}", input, input).unwrap();
462
463 assert_eq!(expected, output);
464 }
465
466 #[test]
467 fn several_interpolations_keep_monotonic_line_numbers() {
468 let input = '\n';
469 let expected = "verify\n this\n and this";
470 let output = &mut String::new();
471
472 write!(
473 indented(output).with_format(Format::Custom {
474 inserter: &mut move |line_no, f| { write!(f, "{:spaces$}", "", spaces = line_no) }
475 }),
476 "verify{}this{0}and this",
477 input
478 )
479 .unwrap();
480
481 assert_eq!(expected, output);
482 }
483}
484
485#[cfg(all(test, feature = "std"))]
486mod tests_std {
487 use super::*;
488 use core::fmt::Write as _;
489
490 #[test]
491 fn dedent() {
492 let mut s = String::new();
493 let mut f = CodeFormatter::new(&mut s, " ");
494 write!(
495 f,
496 r#"
497 struct Foo;
498
499 impl Foo {{
500 fn foo() {{
501 todo!()
502 }}
503 }}
504 "#,
505 )
506 .unwrap();
507 assert_eq!(
508 s,
509 "struct Foo;\n\nimpl Foo {\n fn foo() {\n todo!()\n }\n}\n"
510 );
511
512 let mut s = String::new();
513 let mut f = CodeFormatter::new(&mut s, " ");
514 write!(
515 f,
516 r#"
517 struct Foo;
518
519 impl Foo {{
520 fn foo() {{
521 todo!()
522 }}
523 }}"#,
524 )
525 .unwrap();
526 assert_eq!(
527 s,
528 "struct Foo;\n\nimpl Foo {\n fn foo() {\n todo!()\n }\n}\n"
529 );
530 }
531
532 #[test]
533 fn indent() {
534 let mut s = String::new();
535 let mut f = CodeFormatter::new(&mut s, " ");
536 f.indent(1);
537 write!(
538 f,
539 r#"
540 struct Foo;
541
542 impl Foo {{
543 fn foo() {{
544 todo!()
545 }}
546 }}
547 "#,
548 )
549 .unwrap();
550 assert_eq!(s, " struct Foo;\n\n impl Foo {\n fn foo() {\n todo!()\n }\n }\n");
551 }
552
553 #[test]
554 fn inline() {
555 let mut s = String::new();
556 let mut f = CodeFormatter::new(&mut s, " ");
557 write!(
558 f,
559 r#"struct Foo;
560 fn foo() {{
561 }}"#,
562 )
563 .unwrap();
564 assert_eq!(s, "struct Foo;\n fn foo() {\n }");
565 }
566
567 #[test]
568 fn split_prefix() {
569 let mut s = String::new();
570 let mut f = CodeFormatter::new(&mut s, " ");
571 writeln!(f).unwrap();
572 assert_eq!(s, "\n");
573 }
574}