-
Notifications
You must be signed in to change notification settings - Fork 968
/
from_ref.rs
94 lines (79 loc) · 2.23 KB
/
from_ref.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use proc_macro2::{Ident, TokenStream};
use quote::quote_spanned;
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Field, ItemStruct, Token, Type,
};
use crate::attr_parsing::{combine_unary_attribute, parse_attrs, Combine};
pub(crate) fn expand(item: ItemStruct) -> TokenStream {
item.fields
.iter()
.enumerate()
.map(|(idx, field)| expand_field(&item.ident, idx, field))
.collect()
}
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 span = field.ty.span();
let body = if let Some(field_ident) = &field.ident {
if matches!(field_ty, Type::Reference(_)) {
quote_spanned! {span=> state.#field_ident }
} else {
quote_spanned! {span=> state.#field_ident.clone() }
}
} else {
let idx = syn::Index {
index: idx as _,
span: field.span(),
};
quote_spanned! {span=> state.#idx.clone() }
};
quote_spanned! {span=>
impl ::axum::extract::FromRef<#state> for #field_ty {
fn from_ref(state: &#state) -> Self {
#body
}
}
}
}
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]
fn ui() {
crate::run_ui_tests("from_ref");
}