Add #[from_ref(skip)] (#1537)

For skipping individual fields.
This commit is contained in:
David Pedersen 2022-11-18 12:05:10 +01:00 committed by GitHub
parent 64960bb19c
commit 7d58d49817
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 2 deletions

View file

@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# Unreleased # Unreleased
- None. - **added:** Add `#[from_ref(skip)]` to skip implementing `FromRef` for individual fields ([#1537])
[#1537]: https://github.com/tokio-rs/axum/pull/1537
# 0.3.0-rc.2 (8. November, 2022) # 0.3.0-rc.2 (8. November, 2022)

View file

@ -82,6 +82,21 @@ where
Ok(()) Ok(())
} }
pub(crate) fn combine_unary_attribute<K>(a: &mut Option<K>, b: Option<K>) -> syn::Result<()>
where
K: ToTokens,
{
if let Some(kw) = b {
if a.is_some() {
let kw_name = std::any::type_name::<K>().split("::").last().unwrap();
let msg = format!("`{}` specified more than once", kw_name);
return Err(syn::Error::new_spanned(kw, msg));
}
*a = Some(kw);
}
Ok(())
}
pub(crate) fn second<T, K>(tuple: (T, K)) -> K { pub(crate) fn second<T, K>(tuple: (T, K)) -> K {
tuple.1 tuple.1
} }

View file

@ -1,6 +1,12 @@
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, TokenStream};
use quote::quote_spanned; use quote::quote_spanned;
use syn::{spanned::Spanned, Field, ItemStruct}; use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Field, ItemStruct, Token,
};
use crate::attr_parsing::{combine_unary_attribute, parse_attrs, Combine};
pub(crate) fn expand(item: ItemStruct) -> TokenStream { pub(crate) fn expand(item: ItemStruct) -> TokenStream {
item.fields item.fields
@ -11,6 +17,15 @@ pub(crate) fn expand(item: ItemStruct) -> TokenStream {
} }
fn expand_field(state: &Ident, idx: usize, field: &Field) -> TokenStream { fn expand_field(state: &Ident, idx: usize, field: &Field) -> TokenStream {
let FieldAttrs { skip } = match parse_attrs("from_ref", &field.attrs) {
Ok(attrs) => attrs,
Err(err) => return err.into_compile_error(),
};
if skip.is_some() {
return TokenStream::default();
}
let field_ty = &field.ty; let field_ty = &field.ty;
let span = field.ty.span(); let span = field.ty.span();
@ -33,6 +48,42 @@ fn expand_field(state: &Ident, idx: usize, field: &Field) -> TokenStream {
} }
} }
mod kw {
syn::custom_keyword!(skip);
}
#[derive(Default)]
pub(super) struct FieldAttrs {
pub(super) skip: Option<kw::skip>,
}
impl Parse for FieldAttrs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut skip = None;
while !input.is_empty() {
let lh = input.lookahead1();
if lh.peek(kw::skip) {
skip = Some(input.parse()?);
} else {
return Err(lh.error());
}
let _ = input.parse::<Token![,]>();
}
Ok(Self { skip })
}
}
impl Combine for FieldAttrs {
fn combine(mut self, other: Self) -> syn::Result<Self> {
let Self { skip } = other;
combine_unary_attribute(&mut self.skip, skip)?;
Ok(self)
}
}
#[test] #[test]
fn ui() { fn ui() {
crate::run_ui_tests("from_ref"); crate::run_ui_tests("from_ref");

View file

@ -589,6 +589,9 @@ pub fn derive_typed_path(input: TokenStream) -> TokenStream {
/// struct AppState { /// struct AppState {
/// auth_token: AuthToken, /// auth_token: AuthToken,
/// database_pool: DatabasePool, /// database_pool: DatabasePool,
/// // fields can also be skipped
/// #[from_ref(skip)]
/// api_token: String,
/// } /// }
/// ///
/// // So those types can be extracted via `State` /// // So those types can be extracted via `State`
@ -601,6 +604,7 @@ pub fn derive_typed_path(input: TokenStream) -> TokenStream {
/// let state = AppState { /// let state = AppState {
/// auth_token, /// auth_token,
/// database_pool, /// database_pool,
/// api_token: "secret".to_owned(),
/// }; /// };
/// ///
/// let app = Router::new() /// let app = Router::new()

View file

@ -0,0 +1,10 @@
use axum_macros::FromRef;
#[derive(Clone, FromRef)]
struct AppState {
auth_token: String,
#[from_ref(skip)]
also_string: String,
}
fn main() {}