// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. //! Utilities for formatting SQL AST nodes with pretty printing support. //! //! The module provides formatters that implement the `Display` trait with support //! for both regular (`{}`) and pretty (`{:#}`) formatting modes. Pretty printing //! adds proper indentation and line breaks to make SQL statements more readable. use core::fmt::{self, Display, Write}; /// A wrapper around a value that adds an indent to the value when displayed with {:#}. pub(crate) struct Indent(pub T); const INDENT: &str = " "; impl Display for Indent where T: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { f.write_str(INDENT)?; write!(Indent(f), "{:#}", self.0) } else { self.0.fmt(f) } } } /// Adds an indent to the inner writer impl Write for Indent where T: Write, { fn write_str(&mut self, s: &str) -> fmt::Result { self.0.write_str(s)?; // Our NewLine and SpaceOrNewline utils always print individual newlines as a single-character string. if s == "\n" { self.0.write_str(INDENT)?; } Ok(()) } } /// A value that inserts a newline when displayed with {:#}, but not when displayed with {}. pub(crate) struct NewLine; impl Display for NewLine { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { f.write_char('\n') } else { Ok(()) } } } /// A value that inserts a space when displayed with {}, but a newline when displayed with {:#}. pub(crate) struct SpaceOrNewline; impl Display for SpaceOrNewline { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { f.write_char('\n') } else { f.write_char(' ') } } } /// A value that displays a comma-separated list of values. /// When pretty-printed (using {:#}), it displays each value on a new line. pub(crate) struct DisplayCommaSeparated<'a, T: fmt::Display>(pub(crate) &'a [T]); impl fmt::Display for DisplayCommaSeparated<'_, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut first = true; for t in self.0 { if !first { f.write_char(',')?; SpaceOrNewline.fmt(f)?; } first = false; t.fmt(f)?; } Ok(()) } } /// Displays a whitespace, followed by a comma-separated list that is indented when pretty-printed. pub(crate) fn indented_list(f: &mut fmt::Formatter, items: &[T]) -> fmt::Result { SpaceOrNewline.fmt(f)?; Indent(DisplayCommaSeparated(items)).fmt(f) } #[cfg(test)] mod tests { use super::*; #[test] fn test_indent() { struct TwoLines; impl Display for TwoLines { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("line 1")?; SpaceOrNewline.fmt(f)?; f.write_str("line 2") } } let indent = Indent(TwoLines); assert_eq!( indent.to_string(), TwoLines.to_string(), "Only the alternate form should be indented" ); assert_eq!(format!("{:#}", indent), " line 1\n line 2"); } }