mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-10-02 15:25:32 +00:00
202 lines
6.5 KiB
Rust
202 lines
6.5 KiB
Rust
#![cfg_attr(
|
|
not(any(feature = "postgres", feature = "mysql")),
|
|
allow(dead_code, unused_macros, unused_imports)
|
|
)]
|
|
extern crate proc_macro;
|
|
|
|
use proc_macro::TokenStream;
|
|
|
|
use quote::quote;
|
|
|
|
#[cfg(feature = "runtime-async-std")]
|
|
use async_std::task::block_on;
|
|
|
|
use url::Url;
|
|
|
|
type Error = Box<dyn std::error::Error>;
|
|
|
|
type Result<T> = std::result::Result<T, Error>;
|
|
|
|
mod database;
|
|
mod derives;
|
|
mod query_macros;
|
|
mod runtime;
|
|
|
|
use query_macros::*;
|
|
|
|
#[cfg(feature = "runtime-tokio")]
|
|
lazy_static::lazy_static! {
|
|
static ref BASIC_RUNTIME: tokio::runtime::Runtime = {
|
|
tokio::runtime::Builder::new()
|
|
.threaded_scheduler()
|
|
.enable_io()
|
|
.enable_time()
|
|
.build()
|
|
.expect("failed to build tokio runtime")
|
|
};
|
|
}
|
|
|
|
#[cfg(feature = "runtime-tokio")]
|
|
fn block_on<F: std::future::Future>(future: F) -> F::Output {
|
|
BASIC_RUNTIME.enter(|| futures::executor::block_on(future))
|
|
}
|
|
|
|
fn macro_result(tokens: proc_macro2::TokenStream) -> TokenStream {
|
|
quote!(
|
|
macro_rules! macro_result {
|
|
($($args:tt)*) => (#tokens)
|
|
}
|
|
)
|
|
.into()
|
|
}
|
|
|
|
macro_rules! async_macro (
|
|
($db:ident, $input:ident: $ty:ty => $expr:expr) => {{
|
|
let $input = match syn::parse::<$ty>($input) {
|
|
Ok(input) => input,
|
|
Err(e) => return macro_result(e.to_compile_error()),
|
|
};
|
|
|
|
let res: Result<proc_macro2::TokenStream> = block_on(async {
|
|
use sqlx::connection::Connect;
|
|
|
|
let db_url = Url::parse(&dotenv::var("DATABASE_URL").map_err(|_| "DATABASE_URL not set")?)?;
|
|
|
|
match db_url.scheme() {
|
|
#[cfg(feature = "sqlite")]
|
|
"sqlite" => {
|
|
let $db = sqlx::sqlite::SqliteConnection::connect(db_url.as_str())
|
|
.await
|
|
.map_err(|e| format!("failed to connect to database: {}", e))?;
|
|
|
|
$expr.await
|
|
}
|
|
#[cfg(not(feature = "sqlite"))]
|
|
"sqlite" => Err(format!(
|
|
"DATABASE_URL {} has the scheme of a SQLite database but the `sqlite` \
|
|
feature of sqlx was not enabled",
|
|
db_url
|
|
).into()),
|
|
#[cfg(feature = "postgres")]
|
|
"postgresql" | "postgres" => {
|
|
let $db = sqlx::postgres::PgConnection::connect(db_url.as_str())
|
|
.await
|
|
.map_err(|e| format!("failed to connect to database: {}", e))?;
|
|
|
|
$expr.await
|
|
}
|
|
#[cfg(not(feature = "postgres"))]
|
|
"postgresql" | "postgres" => Err(format!(
|
|
"DATABASE_URL {} has the scheme of a Postgres database but the `postgres` \
|
|
feature of sqlx was not enabled",
|
|
db_url
|
|
).into()),
|
|
#[cfg(feature = "mysql")]
|
|
"mysql" | "mariadb" => {
|
|
let $db = sqlx::mysql::MySqlConnection::connect(db_url.as_str())
|
|
.await
|
|
.map_err(|e| format!("failed to connect to database: {}", e))?;
|
|
|
|
$expr.await
|
|
}
|
|
#[cfg(not(feature = "mysql"))]
|
|
"mysql" | "mariadb" => Err(format!(
|
|
"DATABASE_URL {} has the scheme of a MySQL/MariaDB database but the `mysql` \
|
|
feature of sqlx was not enabled",
|
|
db_url
|
|
).into()),
|
|
scheme => Err(format!("unexpected scheme {:?} in DATABASE_URL {}", scheme, db_url).into()),
|
|
}
|
|
});
|
|
|
|
match res {
|
|
Ok(ts) => ts.into(),
|
|
Err(e) => {
|
|
if let Some(parse_err) = e.downcast_ref::<syn::Error>() {
|
|
macro_result(parse_err.to_compile_error())
|
|
} else {
|
|
let msg = e.to_string();
|
|
macro_result(quote!(compile_error!(#msg)))
|
|
}
|
|
}
|
|
}
|
|
}}
|
|
);
|
|
|
|
#[proc_macro]
|
|
#[allow(unused_variables)]
|
|
pub fn query(input: TokenStream) -> TokenStream {
|
|
#[allow(unused_variables)]
|
|
async_macro!(db, input: QueryMacroInput => expand_query(input, db))
|
|
}
|
|
|
|
#[proc_macro]
|
|
#[allow(unused_variables)]
|
|
pub fn query_file(input: TokenStream) -> TokenStream {
|
|
#[allow(unused_variables)]
|
|
async_macro!(db, input: QueryMacroInput => expand_query_file(input, db))
|
|
}
|
|
|
|
#[proc_macro]
|
|
#[allow(unused_variables)]
|
|
pub fn query_as(input: TokenStream) -> TokenStream {
|
|
#[allow(unused_variables)]
|
|
async_macro!(db, input: QueryAsMacroInput => expand_query_as(input, db, true))
|
|
}
|
|
|
|
#[proc_macro]
|
|
#[allow(unused_variables)]
|
|
pub fn query_file_as(input: TokenStream) -> TokenStream {
|
|
async_macro!(db, input: QueryAsMacroInput => expand_query_file_as(input, db, true))
|
|
}
|
|
|
|
#[proc_macro]
|
|
#[allow(unused_variables)]
|
|
pub fn query_as_unchecked(input: TokenStream) -> TokenStream {
|
|
#[allow(unused_variables)]
|
|
async_macro!(db, input: QueryAsMacroInput => expand_query_as(input, db, false))
|
|
}
|
|
|
|
#[proc_macro]
|
|
#[allow(unused_variables)]
|
|
pub fn query_file_as_unchecked(input: TokenStream) -> TokenStream {
|
|
async_macro!(db, input: QueryAsMacroInput => expand_query_file_as(input, db, false))
|
|
}
|
|
|
|
#[proc_macro_derive(Encode, attributes(sqlx))]
|
|
pub fn derive_encode(tokenstream: TokenStream) -> TokenStream {
|
|
let input = syn::parse_macro_input!(tokenstream as syn::DeriveInput);
|
|
match derives::expand_derive_encode(&input) {
|
|
Ok(ts) => ts.into(),
|
|
Err(e) => e.to_compile_error().into(),
|
|
}
|
|
}
|
|
|
|
#[proc_macro_derive(Decode, attributes(sqlx))]
|
|
pub fn derive_decode(tokenstream: TokenStream) -> TokenStream {
|
|
let input = syn::parse_macro_input!(tokenstream as syn::DeriveInput);
|
|
match derives::expand_derive_decode(&input) {
|
|
Ok(ts) => ts.into(),
|
|
Err(e) => e.to_compile_error().into(),
|
|
}
|
|
}
|
|
|
|
#[proc_macro_derive(Type, attributes(sqlx))]
|
|
pub fn derive_type(tokenstream: TokenStream) -> TokenStream {
|
|
let input = syn::parse_macro_input!(tokenstream as syn::DeriveInput);
|
|
match derives::expand_derive_type_encode_decode(&input) {
|
|
Ok(ts) => ts.into(),
|
|
Err(e) => e.to_compile_error().into(),
|
|
}
|
|
}
|
|
|
|
#[proc_macro_derive(FromRow, attributes(sqlx))]
|
|
pub fn derive_from_row(input: TokenStream) -> TokenStream {
|
|
let input = syn::parse_macro_input!(input as syn::DeriveInput);
|
|
|
|
match derives::expand_derive_from_row(&input) {
|
|
Ok(ts) => ts.into(),
|
|
Err(e) => e.to_compile_error().into(),
|
|
}
|
|
}
|