Rocket/contrib/codegen/src/database.rs

121 lines
4.5 KiB
Rust
Raw Normal View History

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};
#[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";
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";
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);
let string_lit = crate::syn::parse2::<LitStr>(attr_stream2)?;
2019-06-13 02:17:59 +00:00
let input = crate::syn::parse::<DeriveInput>(input).unwrap();
if !input.generics.params.is_empty() {
2018-12-12 08:00:10 +00:00
return Err(input.generics.span().error(NO_GENERIC_STRUCTS));
}
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");
first.ty.clone()
}
_ => return Err(structure.fields.span().error(ONLY_UNNAMED_FIELDS).help(EXAMPLE))
};
Ok(DatabaseInvocation {
structure,
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)]
pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
let invocation = parse_invocation(attr, input)?;
// Store everything we're going to need to generate code.
2018-12-12 08:00:10 +00:00
let conn_type = &invocation.connection_type;
let name = &invocation.db_name;
2018-12-12 08:00:10 +00:00
let guard_type = &invocation.type_name;
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();
// A few useful paths.
2018-12-12 08:00:10 +00:00
let databases = quote_spanned!(span => ::rocket_contrib::databases);
let request = quote!(::rocket::request);
Revamp configuration. This commit completely overhauls Rocket's configuration systems, basing it on the new Figment library. It includes many breaking changes pertaining to configuration. They are: * "Environments" are replaced by "profiles". * 'ROCKET_PROFILE' takes the place of 'ROCKET_ENV'. * Profile names are now arbitrary, but 'debug' and 'release' are given special treatment as default profiles for the debug and release compilation profiles. * A 'default' profile now sits along-side the meta 'global' profile. * The concept of "extras" is no longer present; users can extract any values they want from the configured 'Figment'. * The 'Poolable' trait takes an '&Config'. * The 'secrets' feature is disabled by default. * It is a hard error if 'secrets' is enabled under the 'release' profile and no 'secret_key' is configured. * 'ConfigBuilder' no longer exists: all fields of 'Config' are public with public constructors for each type. * 'keep_alive' is disabled with '0', not 'false' or 'off'. * Inlined error variants into the 'Error' structure. * 'LoggingLevel' is now 'LogLevel'. * Limits can now be specified in SI units: "1 MiB". The summary of other changes are: * The default config file can be configured with 'ROCKET_CONFIG'. * HTTP/1 and HTTP/2 keep-alive configuration is restored. * 'ctrlc' is now a recognized config option. * 'serde' is now a core dependency. * TLS misconfiguration errors are improved. * Several example use '_' as the return type of '#[launch]' fns. * 'AdHoc::config()' was added for simple config extraction. * Added more documentation for using 'Limits'. * Launch information is no longer treated specially. * The configuration guide was rewritten. Resolves #852. Resolves #209. Closes #1404. Closes #652.
2020-09-03 05:41:31 +00:00
let request_guard_type = quote_spanned! { span =>
#vis struct #guard_type(#databases::Connection<Self, #conn_type>);
2018-12-12 08:00:10 +00:00
};
let pool = quote_spanned!(span => #databases::ConnectionPool<Self, #conn_type>);
let conn = quote_spanned!(span => #databases::Connection<Self, #conn_type>);
2018-12-12 08:00:10 +00:00
Ok(quote! {
Revamp configuration. This commit completely overhauls Rocket's configuration systems, basing it on the new Figment library. It includes many breaking changes pertaining to configuration. They are: * "Environments" are replaced by "profiles". * 'ROCKET_PROFILE' takes the place of 'ROCKET_ENV'. * Profile names are now arbitrary, but 'debug' and 'release' are given special treatment as default profiles for the debug and release compilation profiles. * A 'default' profile now sits along-side the meta 'global' profile. * The concept of "extras" is no longer present; users can extract any values they want from the configured 'Figment'. * The 'Poolable' trait takes an '&Config'. * The 'secrets' feature is disabled by default. * It is a hard error if 'secrets' is enabled under the 'release' profile and no 'secret_key' is configured. * 'ConfigBuilder' no longer exists: all fields of 'Config' are public with public constructors for each type. * 'keep_alive' is disabled with '0', not 'false' or 'off'. * Inlined error variants into the 'Error' structure. * 'LoggingLevel' is now 'LogLevel'. * Limits can now be specified in SI units: "1 MiB". The summary of other changes are: * The default config file can be configured with 'ROCKET_CONFIG'. * HTTP/1 and HTTP/2 keep-alive configuration is restored. * 'ctrlc' is now a recognized config option. * 'serde' is now a core dependency. * TLS misconfiguration errors are improved. * Several example use '_' as the return type of '#[launch]' fns. * 'AdHoc::config()' was added for simple config extraction. * Added more documentation for using 'Limits'. * Launch information is no longer treated specially. * The configuration guide was rewritten. Resolves #852. Resolves #209. Closes #1404. Closes #652.
2020-09-03 05:41:31 +00:00
#request_guard_type
2018-12-12 08:00:10 +00:00
impl #guard_type {
/// Returns a fairing that initializes the associated database
/// connection pool.
pub fn fairing() -> impl ::rocket::fairing::Fairing {
<#pool>::fairing(#fairing_name, #name)
}
/// Retrieves a connection of type `Self` from the `rocket`
/// instance. Returns `Some` as long as `Self::fairing()` has been
/// attached.
pub async fn get_one(__rocket: &::rocket::Rocket) -> Option<Self> {
<#pool>::get_one(&__rocket).await.map(Self)
}
/// 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.
pub async fn run<F, R>(&self, __f: F) -> R
where
F: FnOnce(&mut #conn_type) -> R + Send + 'static,
R: Send + 'static,
{
self.0.run(__f).await
}
}
#[::rocket::async_trait]
impl<'r> #request::FromRequest<'r> for #guard_type {
type Error = ();
async fn from_request(__r: &'r #request::Request<'_>) -> #request::Outcome<Self, ()> {
<#conn>::from_request(__r).await.map(Self)
}
}
}.into())
}