From 33b5ea3c4398e6c87725cbdadeb3786b1261f83f Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 23 Dec 2023 12:10:36 +0100 Subject: [PATCH] Handle raw string literal --- rusqlite-macros/Cargo.toml | 1 + rusqlite-macros/src/lib.rs | 38 +++++++---------------------------- rusqlite-macros/tests/test.rs | 16 +++++++-------- 3 files changed, 16 insertions(+), 39 deletions(-) diff --git a/rusqlite-macros/Cargo.toml b/rusqlite-macros/Cargo.toml index ab3ea51..67f2d20 100644 --- a/rusqlite-macros/Cargo.toml +++ b/rusqlite-macros/Cargo.toml @@ -14,3 +14,4 @@ proc-macro = true [dependencies] sqlite3-parser = { version = "0.12", default-features = false, features = ["YYNOERRORRECOVERY"] } fallible-iterator = "0.3" +litrs = { version = "0.4", default-features = false } diff --git a/rusqlite-macros/src/lib.rs b/rusqlite-macros/src/lib.rs index 308d5fd..15a7681 100644 --- a/rusqlite-macros/src/lib.rs +++ b/rusqlite-macros/src/lib.rs @@ -1,6 +1,7 @@ //! Private implementation details of `rusqlite`. -use proc_macro::{Delimiter, Group, Literal, Span, TokenStream, TokenTree}; +use litrs::StringLit; +use proc_macro::{Group, Span, TokenStream, TokenTree}; use fallible_iterator::FallibleIterator; use sqlite3_parser::ast::{ParameterInfo, ToTokens}; @@ -25,15 +26,12 @@ fn try_bind(input: TokenStream) -> Result { (stmt, literal) }; - let literal = match into_literal(&literal) { - Some(it) => it, - None => return Err("expected a plain string literal".to_string()), + let call_site = literal.span(); + let string_lit = match StringLit::try_from(literal) { + Ok(string_lit) => string_lit, + Err(e) => return Ok(e.to_compile_error()), }; - let sql = literal.to_string(); - if !sql.starts_with('"') { - return Err("expected a plain string literal".to_string()); - } - let sql = strip_matches(&sql, "\""); + let sql = string_lit.value(); let mut parser = Parser::new(sql.as_bytes()); let ast = match parser.next() { @@ -54,7 +52,6 @@ fn try_bind(input: TokenStream) -> Result { return Err("Mixing named and numbered parameters is not supported.".to_string()); } - let call_site = literal.span(); let mut res = TokenStream::new(); for (i, name) in info.names.iter().enumerate() { res.extend(Some(stmt.clone())); @@ -71,27 +68,6 @@ fn try_bind(input: TokenStream) -> Result { Ok(res) } -fn into_literal(ts: &TokenTree) -> Option { - match ts { - TokenTree::Literal(l) => Some(l.clone()), - TokenTree::Group(g) => match g.delimiter() { - Delimiter::None => match g.stream().into_iter().collect::>().as_slice() { - [TokenTree::Literal(l)] => Some(l.clone()), - _ => None, - }, - Delimiter::Parenthesis | Delimiter::Brace | Delimiter::Bracket => None, - }, - _ => None, - } -} - -fn strip_matches<'a>(s: &'a str, pattern: &str) -> &'a str { - s.strip_prefix(pattern) - .unwrap_or(s) - .strip_suffix(pattern) - .unwrap_or(s) -} - fn respan(ts: TokenStream, span: Span) -> TokenStream { let mut res = TokenStream::new(); for tt in ts { diff --git a/rusqlite-macros/tests/test.rs b/rusqlite-macros/tests/test.rs index f9e9782..3a47718 100644 --- a/rusqlite-macros/tests/test.rs +++ b/rusqlite-macros/tests/test.rs @@ -21,19 +21,19 @@ fn test_literal() -> Result { } #[test] -fn test_no_placeholder() -> Result { +fn test_no_placeholder() { let _stmt = Stmt; __bind!(_stmt "SELECT 1"); - Ok(()) +} + +#[test] +fn test_raw_string() { + let _stmt = Stmt; + __bind!(_stmt r"SELECT 1"); + __bind!(_stmt r#"SELECT 1"#); } /* FIXME -#[test] -fn test_raw_string() { - let stmt = (); - __bind!(stmt r#"SELECT 1"#); -} - #[test] fn test_const() { const SQL: &str = "SELECT 1";