diff --git a/src/formatter.rs b/src/formatter.rs index 9f69329..83bccc5 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -80,6 +80,9 @@ pub(crate) fn format( TokenKind::Placeholder => { formatter.format_placeholder(token, &mut formatted_query); } + TokenKind::DoubleColon => { + formatter.format_double_colon(token, &mut formatted_query); + } _ => match token.value { "," => { formatter.format_comma(token, &mut formatted_query); @@ -150,6 +153,10 @@ impl<'a> Formatter<'a> { self.add_new_line(query); } + fn format_double_colon(&self, _token: &Token<'_>, query: &mut String) { + self.trim_all_spaces_end(query); + query.push_str("::"); + } fn format_block_comment(&self, token: &Token<'_>, query: &mut String) { self.add_new_line(query); query.push_str(&self.indent_comment(token.value)); diff --git a/src/lib.rs b/src/lib.rs index c6388c7..5d7d2da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -669,7 +669,7 @@ mod tests { #[test] fn it_formats_postgres_specific_operators() { let strings = [ - ("column::int", "column :: int"), + ("column::int", "column::int"), ("v->2", "v -> 2"), ("v->>2", "v ->> 2"), ("foo ~~ 'hello'", "foo ~~ 'hello'"), @@ -1840,6 +1840,26 @@ SELECT left ~= right" ); + assert_eq!(format(input, &QueryParams::None, &options), expected); + } + #[test] + fn it_formats_double_colons() { + let input = "select text :: text, num::integer, data::json, (x - y)::integer frOM foo"; + let options = FormatOptions { + uppercase: Some(false), + ..FormatOptions::default() + }; + let expected = indoc!( + " +select + text::text, + num::integer, + data::json, + (x - y)::integer +from + foo" + ); + assert_eq!(format(input, &QueryParams::None, &options), expected); } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 5a5ed1b..0d2ef78 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -46,6 +46,7 @@ pub(crate) struct Token<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum TokenKind { + DoubleColon, Whitespace, String, Reserved, @@ -107,12 +108,24 @@ fn get_next_token<'a>( last_reserved_top_level_token, ) }) + .or_else(|_| get_double_colon_token(input)) .or_else(|_| get_operator_token(input)) .or_else(|_| get_placeholder_token(input, named_placeholders)) .or_else(|_| get_word_token(input)) .or_else(|_| get_any_other_char(input)) } - +fn get_double_colon_token(input: &str) -> IResult<&str, Token<'_>> { + tag("::")(input).map(|(input, token)| { + ( + input, + Token { + kind: TokenKind::DoubleColon, + value: token, + key: None, + }, + ) + }) +} fn get_whitespace_token(input: &str) -> IResult<&str, Token<'_>> { take_while1(char::is_whitespace)(input).map(|(input, token)| { (