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};
|
|
|
|
|
2019-06-13 02:17:59 +00:00
|
|
|
use crate::syn::{DataStruct, Fields, Data, Type, LitStr, DeriveInput, Ident, Visibility};
|
2018-07-21 22:11:08 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct DatabaseInvocation {
|
|
|
|
/// The name of the structure on which `#[database(..)] struct This(..)` was invoked.
|
|
|
|
type_name: Ident,
|
|
|
|
/// The visibility of the structure on which `#[database(..)] struct This(..)` was invoked.
|
|
|
|
visibility: Visibility,
|
|
|
|
/// The database name as passed in via #[database('database name')].
|
|
|
|
db_name: String,
|
|
|
|
/// The entire structure that the `database` attribute was called on.
|
|
|
|
structure: DataStruct,
|
|
|
|
/// The type inside the structure: struct MyDb(ThisType).
|
|
|
|
connection_type: Type,
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2020-07-20 22:02:10 +00:00
|
|
|
let string_lit = crate::syn::parse2::<LitStr>(attr_stream2)?;
|
2018-07-21 22:11:08 +00:00
|
|
|
|
2019-06-13 02:17:59 +00:00
|
|
|
let input = crate::syn::parse::<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 {
|
|
|
|
Data::Struct(s) => s,
|
|
|
|
_ => return Err(input.span().error(ONLY_ON_STRUCTS_MSG))
|
|
|
|
};
|
|
|
|
|
|
|
|
let inner_type = match structure.fields {
|
|
|
|
Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
|
|
|
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 {
|
2021-03-15 02:57:59 +00:00
|
|
|
structure,
|
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;
|
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);
|
2018-12-12 08:00:10 +00:00
|
|
|
let span = conn_type.span().into();
|
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 =>
|
2021-05-25 01:58:05 +00:00
|
|
|
#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 {
|
2018-08-15 09:07:17 +00:00
|
|
|
/// Returns a fairing that initializes the associated 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
|
|
|
}
|
|
|
|
|
2018-08-15 09:07:17 +00:00
|
|
|
/// Retrieves a connection of type `Self` from the `rocket`
|
|
|
|
/// instance. Returns `Some` as long as `Self::fairing()` has been
|
2020-07-11 18:17:43 +00:00
|
|
|
/// attached.
|
2021-05-25 01:58:05 +00:00
|
|
|
pub async fn get_one<P>(__rocket: &#rocket::Rocket<P>) -> Option<Self>
|
|
|
|
where P: #rocket::Phase,
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
{
|
2020-10-22 10:27:04 +00:00
|
|
|
<#pool>::get_one(&__rocket).await.map(Self)
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-11 18:17:43 +00:00
|
|
|
/// Runs the provided closure on a thread from a threadpool. The
|
|
|
|
/// closure will be passed an `&mut r2d2::PooledConnection`.
|
|
|
|
/// `.await`ing the return value of this function yields the value
|
|
|
|
/// returned by the closure.
|
2020-10-22 10:27:04 +00:00
|
|
|
pub async fn run<F, R>(&self, __f: F) -> R
|
2020-07-11 18:17:43 +00:00
|
|
|
where
|
|
|
|
F: FnOnce(&mut #conn_type) -> R + Send + 'static,
|
|
|
|
R: Send + 'static,
|
|
|
|
{
|
2020-10-22 10:27:04 +00:00
|
|
|
self.0.run(__f).await
|
2018-07-21 22:11:08 +00:00
|
|
|
}
|
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
|
|
|
}
|