main
 1use anyhow::{Context, Result};
 2use sqlparser::dialect::GenericDialect;
 3use sqlparser::parser::Parser;
 4use std::io::{self, Read};
 5
 6mod formatter;
 7
 8use formatter::format_statement;
 9
10fn main() {
11    match run() {
12        Ok(()) => std::process::exit(0),
13        Err(_) => std::process::exit(1),
14    }
15}
16
17fn run() -> Result<()> {
18    let mut input = String::new();
19    io::stdin()
20        .read_to_string(&mut input)
21        .context("Failed to read from stdin")?;
22
23    let input = input.trim();
24    if input.is_empty() {
25        return Ok(());
26    }
27
28    let has_semicolon = input.ends_with(';');
29
30    let dialect = GenericDialect {};
31    match Parser::parse_sql(&dialect, input) {
32        Ok(statements) => {
33            let mut output = Vec::new();
34            for (i, statement) in statements.iter().enumerate() {
35                if i > 0 {
36                    output.push("\n".to_string());
37                }
38                output.push(format_statement(statement, 0));
39            }
40            let mut formatted = output.join("");
41            formatted = cleanup_whitespace(&formatted);
42            if has_semicolon {
43                formatted.push(';');
44            }
45            println!("{}", formatted);
46            Ok(())
47        }
48        Err(_) => {
49            print!("{}", input);
50            if !input.ends_with('\n') {
51                println!();
52            }
53            Err(anyhow::anyhow!("Parse error"))
54        }
55    }
56}
57
58fn cleanup_whitespace(sql: &str) -> String {
59    sql.trim_end()
60        .lines()
61        .map(|line| line.trim_end())
62        .collect::<Vec<_>>()
63        .join("\n")
64}