2018-08-15 09:07:17 +00:00
|
|
|
use proc_macro::TokenStream;
|
2019-09-21 20:36:57 +00:00
|
|
|
use devise::{Spanned, Result, ext::SpanDiagnosticExt};
|
|
|
|
|
2022-05-24 23:47:09 +00:00
|
|
|
use crate::syn;
|
2018-07-21 22:11:08 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct DatabaseInvocation {
|
2022-05-24 23:47:09 +00:00
|
|
|
/// The attributes on the attributed structure.
|
|
|
|
attrs: Vec<syn::Attribute>,
|
2018-07-21 22:11:08 +00:00
|
|
|
/// The name of the structure on which `#[database(..)] struct This(..)` was invoked.
|
2022-05-24 23:47:09 +00:00
|
|
|
type_name: syn::Ident,
|
2018-07-21 22:11:08 +00:00
|
|
|
/// The visibility of the structure on which `#[database(..)] struct This(..)` was invoked.
|
2022-05-24 23:47:09 +00:00
|
|
|
visibility: syn::Visibility,
|
2018-07-21 22:11:08 +00:00
|
|
|
/// The database name as passed in via #[database('database name')].
|
|
|
|
db_name: String,
|
|
|
|
/// The type inside the structure: struct MyDb(ThisType).
|
2022-05-24 23:47:09 +00:00
|
|
|
connection_type: syn::Type,
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const EXAMPLE: &str = "example: `struct MyDatabase(diesel::SqliteConnection);`";
|
|
|
|
const ONLY_ON_STRUCTS_MSG: &str = "`database` attribute can only be used on structs";
|
2018-08-15 09:07:17 +00:00
|
|
|
const ONLY_UNNAMED_FIELDS: &str = "`database` attribute can only be applied to \
|
|
|
|
structs with exactly one unnamed field";
|
2018-12-12 08:00:10 +00:00
|
|
|
const NO_GENERIC_STRUCTS: &str = "`database` attribute cannot be applied to structs \
|
|
|
|
with generics";
|
2018-07-21 22:11:08 +00:00
|
|
|
|
|
|
|
fn parse_invocation(attr: TokenStream, input: TokenStream) -> Result<DatabaseInvocation> {
|
2019-06-13 02:17:59 +00:00
|
|
|
let attr_stream2 = crate::proc_macro2::TokenStream::from(attr);
|
2022-05-24 23:47:09 +00:00
|
|
|
let string_lit = crate::syn::parse2::<syn::LitStr>(attr_stream2)?;
|
2018-07-21 22:11:08 +00:00
|
|
|
|
2022-05-24 23:47:09 +00:00
|
|
|
let input = crate::syn::parse::<syn::DeriveInput>(input).unwrap();
|
2018-07-21 22:11:08 +00:00
|
|
|
if !input.generics.params.is_empty() {
|
2018-12-12 08:00:10 +00:00
|
|
|
return Err(input.generics.span().error(NO_GENERIC_STRUCTS));
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let structure = match input.data {
|
2022-05-24 23:47:09 +00:00
|
|
|
syn::Data::Struct(s) => s,
|
2018-07-21 22:11:08 +00:00
|
|
|
_ => return Err(input.span().error(ONLY_ON_STRUCTS_MSG))
|
|
|
|
};
|
|
|
|
|
|
|
|
let inner_type = match structure.fields {
|
2022-05-24 23:47:09 +00:00
|
|
|
syn::Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
2018-07-21 22:11:08 +00:00
|
|
|
let first = fields.unnamed.first().expect("checked length");
|
2019-09-05 22:43:57 +00:00
|
|
|
first.ty.clone()
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
|
|
|
_ => return Err(structure.fields.span().error(ONLY_UNNAMED_FIELDS).help(EXAMPLE))
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(DatabaseInvocation {
|
2022-05-24 23:47:09 +00:00
|
|
|
attrs: input.attrs,
|
2018-07-21 22:11:08 +00:00
|
|
|
type_name: input.ident,
|
|
|
|
visibility: input.vis,
|
|
|
|
db_name: string_lit.value(),
|
|
|
|
connection_type: inner_type,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-12-12 08:00:10 +00:00
|
|
|
#[allow(non_snake_case)]
|
2018-07-21 22:11:08 +00:00
|
|
|
pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
|
|
|
|
let invocation = parse_invocation(attr, input)?;
|
|
|
|
|
2018-08-15 09:07:17 +00:00
|
|
|
// Store everything we're going to need to generate code.
|
2018-12-12 08:00:10 +00:00
|
|
|
let conn_type = &invocation.connection_type;
|
2018-08-15 09:07:17 +00:00
|
|
|
let name = &invocation.db_name;
|
2022-05-24 23:47:09 +00:00
|
|
|
let attrs = &invocation.attrs;
|
2018-12-12 08:00:10 +00:00
|
|
|
let guard_type = &invocation.type_name;
|
2018-08-15 09:07:17 +00:00
|
|
|
let vis = &invocation.visibility;
|
|
|
|
let fairing_name = format!("'{}' Database Pool", name);
|
2021-06-30 20:36:23 +00:00
|
|
|
let span = conn_type.span();
|
2018-08-15 09:07:17 +00:00
|
|
|
|
|
|
|
// A few useful paths.
|
2021-05-25 01:58:05 +00:00
|
|
|
let root = quote_spanned!(span => ::rocket_sync_db_pools);
|
|
|
|
let rocket = quote!(#root::rocket);
|
2018-07-21 22:11:08 +00:00
|
|
|
|
2020-09-03 05:41:31 +00:00
|
|
|
let request_guard_type = quote_spanned! { span =>
|
2022-05-24 23:47:09 +00:00
|
|
|
#(#attrs)* #vis struct #guard_type(#root::Connection<Self, #conn_type>);
|
2018-12-12 08:00:10 +00:00
|
|
|
};
|
|
|
|
|
2021-05-25 01:58:05 +00:00
|
|
|
let pool = quote_spanned!(span => #root::ConnectionPool<Self, #conn_type>);
|
|
|
|
let conn = quote_spanned!(span => #root::Connection<Self, #conn_type>);
|
2020-10-22 10:27:04 +00:00
|
|
|
|
2018-12-12 08:00:10 +00:00
|
|
|
Ok(quote! {
|
2020-09-03 05:41:31 +00:00
|
|
|
#request_guard_type
|
2018-07-21 22:11:08 +00:00
|
|
|
|
2018-12-12 08:00:10 +00:00
|
|
|
impl #guard_type {
|
2022-05-24 23:47:09 +00:00
|
|
|
/// Returns a fairing that initializes the database connection pool.
|
2021-05-25 01:58:05 +00:00
|
|
|
pub fn fairing() -> impl #rocket::fairing::Fairing {
|
2020-10-22 10:27:04 +00:00
|
|
|
<#pool>::fairing(#fairing_name, #name)
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-24 23:47:09 +00:00
|
|
|
/// Returns an opaque type that represents the connection pool
|
|
|
|
/// backing connections of type `Self`.
|
|
|
|
pub fn pool<P: #rocket::Phase>(__rocket: &#rocket::Rocket<P>) -> Option<&#pool> {
|
|
|
|
<#pool>::pool(&__rocket)
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-24 23:47:09 +00:00
|
|
|
/// Runs the provided function `__f` in an async-safe blocking
|
|
|
|
/// thread.
|
2020-10-22 10:27:04 +00:00
|
|
|
pub async fn run<F, R>(&self, __f: F) -> R
|
2022-05-24 23:47:09 +00:00
|
|
|
where F: FnOnce(&mut #conn_type) -> R + Send + 'static,
|
|
|
|
R: Send + 'static,
|
2020-07-11 18:17:43 +00:00
|
|
|
{
|
2020-10-22 10:27:04 +00:00
|
|
|
self.0.run(__f).await
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
2022-05-24 23:47:09 +00:00
|
|
|
|
|
|
|
/// Retrieves a connection of type `Self` from the `rocket` instance.
|
|
|
|
pub async fn get_one<P: #rocket::Phase>(__rocket: &#rocket::Rocket<P>) -> Option<Self> {
|
|
|
|
<#pool>::get_one(&__rocket).await.map(Self)
|
|
|
|
}
|
2018-12-11 21:57:23 +00:00
|
|
|
}
|
|
|
|
|
2021-05-25 01:58:05 +00:00
|
|
|
#[#rocket::async_trait]
|
|
|
|
impl<'r> #rocket::request::FromRequest<'r> for #guard_type {
|
2018-07-21 22:11:08 +00:00
|
|
|
type Error = ();
|
|
|
|
|
2021-05-25 01:58:05 +00:00
|
|
|
async fn from_request(
|
|
|
|
__r: &'r #rocket::request::Request<'_>
|
|
|
|
) -> #rocket::request::Outcome<Self, ()> {
|
2020-10-22 10:27:04 +00:00
|
|
|
<#conn>::from_request(__r).await.map(Self)
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
|
|
|
}
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
|
2021-05-25 01:58:05 +00:00
|
|
|
impl #rocket::Sentinel for #guard_type {
|
|
|
|
fn abort(__r: &#rocket::Rocket<#rocket::Ignite>) -> bool {
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
<#conn>::abort(__r)
|
|
|
|
}
|
|
|
|
}
|
2018-08-15 09:07:17 +00:00
|
|
|
}.into())
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|