Rust中如何替换插值分隔符以嵌入EdgeQL查询?
在Rust中嵌入EdgeQL时避免花括号转义的编译时方案
1. 自定义编译时Proc宏(支持${}插值)
你可以实现一个自定义proc宏,比如edgeql!,让它识别${var}风格的插值标记,自动处理EdgeQL原生花括号的转义,且逻辑在编译时完成。
首先在Cargo.toml中添加依赖:
[dependencies] proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full"] } [lib] proc-macro = true
然后编写宏实现:
use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; use syn::{parse::Parse, parse_macro_input, LitStr, Token}; struct EdgeqlQuery { query: LitStr, args: Vec<syn::Expr>, } impl Parse for EdgeqlQuery { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { let query = input.parse::<LitStr>()?; let mut args = Vec::new(); while !input.is_empty() { input.parse::<Token![,]>()?; args.push(input.parse::<syn::Expr>()?); } Ok(Self { query, args }) } } #[proc_macro] pub fn edgeql(input: TokenStream) -> TokenStream { let EdgeqlQuery { query, args } = parse_macro_input!(input as EdgeqlQuery); let query_str = query.value(); // 替换${var}为{},保留原生EdgeQL花括号 let formatted_query = query_str.replace("${", "{"); // 生成format!调用代码 let expanded = quote! { format!(#formatted_query, #(#args),*) }; expanded.into() }
使用示例:
use your_crate::edgeql; fn main() { let user_id = 123; let query = edgeql!(r#" SELECT User {{ name, posts := (SELECT Post WHERE author.id = ${user_id}) }} "#, user_id); println!("{}", query); }
这个宏会在编译时完成插值标记替换,原生EdgeQL花括号无需转义。
2. 使用EdgeQL官方库的编译时宏
edgeql-rs库提供的query!宏原生支持编译时查询插值,完全不需要转义EdgeQL中的花括号,还能做语法和类型检查。
在Cargo.toml添加依赖:
[dependencies] edgeql = "0.10"
使用示例:
use edgeql::query; #[tokio::main] async fn main() -> Result<(), edgeql::Error> { let client = edgeql::Client::new().await?; let user_id = 123; // 直接写EdgeQL原生语法,用$user_id做插值 let users = query!(r#" SELECT User {{ name, posts := (SELECT Post WHERE author.id = $user_id) }} "#, user_id).fetch_all(&client).await?; for user in users { println!("User: {}", user.name); } Ok(()) }
这是最推荐的方案,编译时就能校验查询合法性和参数类型匹配。
3. 利用EdgeQL命名参数(无需宏)
如果不想依赖proc宏,可直接用EdgeQL的命名参数特性,查询写为原始字符串字面量,执行时传入参数映射:
use edgeql::Client; #[tokio::main] async fn main() -> Result<(), edgeql::Error> { let client = Client::new().await?; let user_id = 123; let query = r#" SELECT User {{ name, posts := (SELECT Post WHERE author.id := <int64>$user_id) }} "#; let users = client.query(query, &[("user_id", &user_id)]).await?; Ok(()) }
这种方式不用转义花括号,参数通过命名映射传递,虽无编译时查询检查,但能解决转义麻烦。
内容的提问来源于stack exchange,提问作者FoxPro
相关产品推荐
相关产品推荐

