diff --git a/src/formatter.rs b/src/formatter.rs index c538b0f..e9f6b41 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -173,6 +173,7 @@ pub(crate) fn format( .collect::(); s.into() } + TokenKind::Placeholder => format!("{} / {:?}", token.value, token.key).into(), _ => Cow::Borrowed(token.value), }; anstream::eprintln!("{k}{:21}{rk}: {d}{:50}{rd} {line}", kind, value); diff --git a/src/lib.rs b/src/lib.rs index a3ad96f..709e2d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1698,6 +1698,29 @@ mod tests { ); } + #[test] + fn it_recognizes_braced_placeholders_with_param_values() { + let input = "SELECT {a}, {b}, {c};"; + let params = vec![ + ("a".to_string(), "first".to_string()), + ("b".to_string(), "second".to_string()), + ("c".to_string(), "third".to_string()), + ]; + let options = FormatOptions::default(); + let expected = indoc!( + " + SELECT + first, + second, + third;" + ); + + assert_eq!( + format(input, &QueryParams::Named(params), &options), + expected + ); + } + #[test] fn it_formats_query_with_go_batch_separator() { let input = "SELECT 1 GO SELECT 2"; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 065c20d..9b86340 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use unicode_categories::UnicodeCategories; use winnow::ascii::{digit0, digit1, till_line_ending, Caseless}; -use winnow::combinator::{alt, dispatch, eof, fail, opt, peek, terminated}; +use winnow::combinator::{alt, delimited, dispatch, eof, fail, opt, peek, terminated}; use winnow::error::ContextError; use winnow::error::ParserError; use winnow::prelude::*; @@ -326,6 +326,7 @@ fn get_placeholder_token<'i>( get_ident_named_placeholder_token, |input: &mut _| get_string_named_placeholder_token(input, dialect), get_indexed_placeholder_token, + get_braced_named_placeholder_token, )) .parse_next(input) } else { @@ -333,6 +334,7 @@ fn get_placeholder_token<'i>( get_indexed_placeholder_token, get_ident_named_placeholder_token, |input: &mut _| get_string_named_placeholder_token(input, dialect), + get_braced_named_placeholder_token, )) .parse_next(input) } @@ -381,6 +383,25 @@ fn get_ident_named_placeholder_token<'i>(input: &mut &'i str) -> Result(input: &mut &'i str) -> Result> { + delimited( + '{', + take_while(1.., |c: char| c.is_alphanumeric() || c == '_'), + '}', + ) + .with_taken() + .parse_next(input) + .map(|(index, token)| { + let index = Cow::Borrowed(index); + Token { + kind: TokenKind::Placeholder, + value: token, + key: Some(PlaceholderKind::Named(index)), + alias: token, + } + }) +} + fn get_string_named_placeholder_token<'i>( input: &mut &'i str, dialect: Dialect,