Skip to main content

lutra_sql/
display_utils.rs

1//! Utilities for formatting SQL AST nodes with pretty printing support.
2//!
3//! The module provides formatters that implement the `Display` trait with support
4//! for both regular (`{}`) and pretty (`{:#}`) formatting modes. Pretty printing
5//! adds proper indentation and line breaks to make SQL statements more readable.
6
7use core::fmt::{self, Display, Write};
8
9/// A wrapper around a value that adds an indent to the value when displayed with {:#}.
10pub struct Indent<T>(pub T);
11
12const INDENT: &str = "  ";
13
14impl<T> Display for Indent<T>
15where
16    T: Display,
17{
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        if f.alternate() {
20            f.write_str(INDENT)?;
21            write!(Indent(f), "{:#}", self.0)
22        } else {
23            self.0.fmt(f)
24        }
25    }
26}
27
28/// Adds an indent to the inner writer
29impl<T> Write for Indent<T>
30where
31    T: Write,
32{
33    fn write_str(&mut self, s: &str) -> fmt::Result {
34        self.0.write_str(s)?;
35        // Our NewLine and SpaceOrNewline utils always print individual newlines as a single-character string.
36        if s == "\n" {
37            self.0.write_str(INDENT)?;
38        }
39        Ok(())
40    }
41}
42
43/// A value that inserts a newline when displayed with {:#}, but not when displayed with {}.
44pub(crate) struct NewLine;
45
46impl Display for NewLine {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        if f.alternate() {
49            f.write_char('\n')
50        } else {
51            Ok(())
52        }
53    }
54}
55
56/// A value that inserts a space when displayed with {}, but a newline when displayed with {:#}.
57pub(crate) struct SpaceOrNewline;
58
59impl Display for SpaceOrNewline {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        if f.alternate() {
62            f.write_char('\n')
63        } else {
64            f.write_char(' ')
65        }
66    }
67}
68
69/// A value that displays a comma-separated list of values.
70/// When pretty-printed (using {:#}), it displays each value on a new line.
71pub struct DisplayCommaSeparated<'a, T: fmt::Display>(pub &'a [T]);
72
73impl<T: fmt::Display> fmt::Display for DisplayCommaSeparated<'_, T> {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        let mut first = true;
76        for t in self.0 {
77            if !first {
78                f.write_char(',')?;
79                SpaceOrNewline.fmt(f)?;
80            }
81            first = false;
82            t.fmt(f)?;
83        }
84        Ok(())
85    }
86}
87
88/// Displays a whitespace, followed by a comma-separated list that is indented when pretty-printed.
89pub(crate) fn indented_list<T: fmt::Display>(f: &mut fmt::Formatter, items: &[T]) -> fmt::Result {
90    SpaceOrNewline.fmt(f)?;
91    Indent(DisplayCommaSeparated(items)).fmt(f)
92}