diff --git a/.github/workflows/sqlformat.yml b/.github/workflows/sqlformat.yml index 784a2ba..bc4eefc 100644 --- a/.github/workflows/sqlformat.yml +++ b/.github/workflows/sqlformat.yml @@ -28,16 +28,14 @@ jobs: - conf: latest-nightly toolchain: nightly steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Install ${{ matrix.toolchain }} - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - profile: minimal toolchain: ${{ matrix.toolchain }} - override: true components: clippy, rustfmt - name: Cache cargo registry - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.cargo/registry/cache key: ${{ runner.os }}-${{ matrix.conf }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} @@ -45,16 +43,13 @@ jobs: ${{ runner.os }}-${{ matrix.conf }}-cargo-registry- - name: Run rustfmt if: matrix.toolchain == 'stable' - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check + run: | + cargo fmt --all -- --check - name: Run clippy if: matrix.toolchain == 'stable' - uses: actions-rs/clippy-check@v1 + uses: actions-rs-plus/clippy-check@v2.3.0 with: - token: ${{ secrets.GITHUB_TOKEN }} - args: -- -D warnings + args: --all -- -D warnings # FIXME: criterion and its dependencies require a newer version than 1.65, but it is only used for benchmarks. # Is there a way to not have criterion built when we run tests? - name: Run cargo check diff --git a/src/formatter.rs b/src/formatter.rs index 07f9cc2..afbfa97 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -323,6 +323,8 @@ impl<'a> Formatter<'a> { if !self.inline_block.is_active() { self.add_new_line(query); + } else if token.value.to_lowercase() == "case" { + query.push(' '); } } diff --git a/src/lib.rs b/src/lib.rs index 2d13616..d665f11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1599,6 +1599,33 @@ mod tests { assert_eq!(format(input, &QueryParams::None, &options), expected); } + #[test] + fn it_formats_case_when_inside_an_order_by() { + let input = "SELECT a, created_at FROM b ORDER BY (CASE $3 WHEN 'created_at_asc' THEN created_at END) ASC, (CASE $3 WHEN 'created_at_desc' THEN created_at END) DESC;"; + let max_line = 120; + let options = FormatOptions { + max_inline_block: max_line, + max_inline_arguments: Some(max_line), + joins_as_top_level: true, + uppercase: Some(true), + ignore_case_convert: Some(vec!["status"]), + ..Default::default() + }; + + let expected = indoc!( + " + SELECT + a, created_at + FROM + b + ORDER BY + (CASE $3 WHEN 'created_at_asc' THEN created_at END) ASC, + (CASE $3 WHEN 'created_at_desc' THEN created_at END) DESC;" + ); + + assert_eq!(format(input, &QueryParams::None, &options), expected); + } + #[test] fn it_recognizes_lowercase_case_end() { let input = "case when option = 'foo' then 1 else 2 end;"; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 4bcbb02..7b1757b 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -557,7 +557,7 @@ fn get_join_token<'a>() -> impl Parser<&'a str, Token<'a>, ContextError> { alt((standard_joins, specific_joins, special_joins)).parse_next(&mut uc_input); if let Ok(token) = result { - let final_word = token.split(' ').last().unwrap(); + let final_word = token.split(' ').next_back().unwrap(); let input_end_pos = input.to_ascii_uppercase().find(final_word).unwrap() + final_word.len(); let token = input.next_slice(input_end_pos); @@ -607,7 +607,7 @@ fn get_newline_reserved_token<'a>( let result: Result<&str> = alt((operators, alter_table_actions)).parse_next(&mut uc_input); if let Ok(token) = result { - let final_word = token.split(' ').last().unwrap(); + let final_word = token.split(' ').next_back().unwrap(); let input_end_pos = input.to_ascii_uppercase().find(final_word).unwrap() + final_word.len(); let token = input.next_slice(input_end_pos); @@ -648,7 +648,7 @@ fn get_top_level_reserved_token_no_indent<'i>(input: &mut &'i str) -> Result(input: &mut &'i str) -> Result> { )) .parse_next(&mut uc_input); if let Ok(token) = result { - let final_word = token.split(' ').last().unwrap(); + let final_word = token.split(' ').next_back().unwrap(); let input_end_pos = input.to_ascii_uppercase().find(final_word).unwrap() + final_word.len(); let token = input.next_slice(input_end_pos); Ok(Token {