sqlparser/display_utils.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Utilities for formatting SQL AST nodes with pretty printing support.
19//!
20//! The module provides formatters that implement the `Display` trait with support
21//! for both regular (`{}`) and pretty (`{:#}`) formatting modes. Pretty printing
22//! adds proper indentation and line breaks to make SQL statements more readable.
23
24use core::fmt::{self, Display, Write};
25
26/// A wrapper around a value that adds an indent to the value when displayed with {:#}.
27pub(crate) struct Indent<T>(pub T);
28
29const INDENT: &str = " ";
30
31impl<T> Display for Indent<T>
32where
33 T: Display,
34{
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 if f.alternate() {
37 f.write_str(INDENT)?;
38 write!(Indent(f), "{:#}", self.0)
39 } else {
40 self.0.fmt(f)
41 }
42 }
43}
44
45/// Adds an indent to the inner writer
46impl<T> Write for Indent<T>
47where
48 T: Write,
49{
50 fn write_str(&mut self, s: &str) -> fmt::Result {
51 self.0.write_str(s)?;
52 // Our NewLine and SpaceOrNewline utils always print individual newlines as a single-character string.
53 if s == "\n" {
54 self.0.write_str(INDENT)?;
55 }
56 Ok(())
57 }
58}
59
60/// A value that inserts a newline when displayed with {:#}, but not when displayed with {}.
61pub(crate) struct NewLine;
62
63impl Display for NewLine {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 if f.alternate() {
66 f.write_char('\n')
67 } else {
68 Ok(())
69 }
70 }
71}
72
73/// A value that inserts a space when displayed with {}, but a newline when displayed with {:#}.
74pub(crate) struct SpaceOrNewline;
75
76impl Display for SpaceOrNewline {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 if f.alternate() {
79 f.write_char('\n')
80 } else {
81 f.write_char(' ')
82 }
83 }
84}
85
86/// A value that displays a comma-separated list of values.
87/// When pretty-printed (using {:#}), it displays each value on a new line.
88pub(crate) struct DisplayCommaSeparated<'a, T: fmt::Display>(pub(crate) &'a [T]);
89
90impl<T: fmt::Display> fmt::Display for DisplayCommaSeparated<'_, T> {
91 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92 let mut first = true;
93 for t in self.0 {
94 if !first {
95 f.write_char(',')?;
96 SpaceOrNewline.fmt(f)?;
97 }
98 first = false;
99 t.fmt(f)?;
100 }
101 Ok(())
102 }
103}
104
105/// Displays a whitespace, followed by a comma-separated list that is indented when pretty-printed.
106pub(crate) fn indented_list<T: fmt::Display>(f: &mut fmt::Formatter, items: &[T]) -> fmt::Result {
107 SpaceOrNewline.fmt(f)?;
108 Indent(DisplayCommaSeparated(items)).fmt(f)
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_indent() {
117 struct TwoLines;
118
119 impl Display for TwoLines {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 f.write_str("line 1")?;
122 SpaceOrNewline.fmt(f)?;
123 f.write_str("line 2")
124 }
125 }
126
127 let indent = Indent(TwoLines);
128 assert_eq!(
129 indent.to_string(),
130 TwoLines.to_string(),
131 "Only the alternate form should be indented"
132 );
133 assert_eq!(format!("{:#}", indent), " line 1\n line 2");
134 }
135}