rocket_codegen/derive/
uri_display.rs1use proc_macro2::TokenStream;
2use devise::{*, ext::SpanDiagnosticExt};
3
4use crate::exports::*;
5use crate::derive::form_field::{FieldExt, VariantExt};
6use crate::syn_ext::{GenericsExt as _, TypeExt as _};
7use crate::http::uri::fmt;
8
9const NO_EMPTY_FIELDS: &str = "fieldless structs are not supported";
10const NO_NULLARY: &str = "nullary items are not supported";
11const NO_EMPTY_ENUMS: &str = "empty enums are not supported";
12const ONLY_ONE_UNNAMED: &str = "tuple structs or variants must have exactly one field";
13const EXACTLY_ONE_FIELD: &str = "struct must have exactly one field";
14
15const Q_URI_DISPLAY: StaticTokens = quote_static!(#_fmt::UriDisplay<#_fmt::Query>);
16const Q_FORMATTER: StaticTokens = quote_static!(#_fmt::Formatter<#_fmt::Query>);
17
18const P_URI_DISPLAY: StaticTokens = quote_static!(#_fmt::UriDisplay<#_fmt::Path>);
19const P_FORMATTER: StaticTokens = quote_static!(#_fmt::Formatter<#_fmt::Path>);
20
21fn generic_bounds_mapper(bound: StaticTokens) -> MapperBuild {
22 MapperBuild::new()
23 .try_enum_map(|m, e| mapper::enum_null(m, e))
24 .try_fields_map(move |_, fields| {
25 let generic_idents = fields.parent.input().generics().type_idents();
26
27 let bounds = fields.iter()
28 .filter(|f| !f.ty.is_concrete(&generic_idents))
29 .map(|f| &f.field.inner.ty)
30 .map(move |ty| quote_spanned!(ty.span() => #ty: #bound));
31
32 Ok(quote!(#(#bounds,)*))
33 })
34}
35
36pub fn derive_uri_display_query(input: proc_macro::TokenStream) -> TokenStream {
37 let uri_display = DeriveGenerator::build_for(input.clone(), quote!(impl #Q_URI_DISPLAY))
38 .support(Support::Struct | Support::Enum | Support::Type | Support::Lifetime)
39 .validator(ValidatorBuild::new()
40 .enum_validate(|_, data| {
41 if data.variants().count() == 0 {
42 Err(data.brace_token.span.join().error(NO_EMPTY_ENUMS))
43 } else {
44 Ok(())
45 }
46 })
47 .struct_validate(|_, data| {
48 let fields = data.fields();
49 if fields.is_empty() {
50 Err(data.span().error(NO_EMPTY_FIELDS))
51 } else if fields.are_unit() {
52 Err(data.span().error(NO_NULLARY))
53 } else {
54 Ok(())
55 }
56 })
57 .fields_validate(|_, fields| {
58 if fields.are_unnamed() && fields.count() > 1 {
59 Err(fields.span().error(ONLY_ONE_UNNAMED))
60 } else {
61 Ok(())
62 }
63 })
64 )
65 .type_bound_mapper(generic_bounds_mapper(Q_URI_DISPLAY))
66 .inner_mapper(MapperBuild::new()
67 .with_output(|_, output| quote! {
68 fn fmt(&self, f: &mut #Q_FORMATTER) -> ::std::fmt::Result {
69 #output
70 Ok(())
71 }
72 })
73 .try_variant_map(|mapper, variant| {
74 if !variant.fields().is_empty() {
75 return mapper::variant_default(mapper, variant);
76 }
77
78 let value = variant.first_form_field_value()?;
79 Ok(quote_spanned! { variant.span() =>
80 f.write_value(#value)?;
81 })
82 })
83 .try_field_map(|_, field| {
84 let span = field.span();
85 let accessor = field.accessor();
86 let tokens = if let Some(name) = field.first_field_name()? {
87 quote_spanned!(span => f.write_named_value(#name, &#accessor)?;)
88 } else {
89 quote_spanned!(span => f.write_value(&#accessor)?;)
90 };
91
92 Ok(tokens)
93 })
94 )
95 .try_to_tokens::<TokenStream>();
96
97 let uri_display = match uri_display {
98 Ok(tokens) => tokens,
99 Err(diag) => return diag.emit_as_item_tokens()
100 };
101
102 let from_self = from_uri_param::<fmt::Query>(input.clone(), quote!(Self));
103 let from_ref = from_uri_param::<fmt::Query>(input.clone(), quote!(&'__r Self));
104 let from_mut = from_uri_param::<fmt::Query>(input, quote!(&'__r mut Self));
105
106 let mut ts = uri_display;
107 ts.extend(from_self);
108 ts.extend(from_ref);
109 ts.extend(from_mut);
110 ts
111}
112
113pub fn derive_uri_display_path(input: proc_macro::TokenStream) -> TokenStream {
114 let uri_display = DeriveGenerator::build_for(input.clone(), quote!(impl #P_URI_DISPLAY))
115 .support(Support::TupleStruct | Support::Type | Support::Lifetime)
116 .type_bound_mapper(generic_bounds_mapper(P_URI_DISPLAY))
117 .validator(ValidatorBuild::new()
118 .fields_validate(|_, fields| match fields.count() {
119 1 => Ok(()),
120 _ => Err(fields.span().error(EXACTLY_ONE_FIELD))
121 })
122 )
123 .inner_mapper(MapperBuild::new()
124 .with_output(|_, output| quote! {
125 fn fmt(&self, f: &mut #P_FORMATTER) -> ::std::fmt::Result {
126 #output
127 Ok(())
128 }
129 })
130 .field_map(|_, field| {
131 let accessor = field.accessor();
132 quote_spanned!(field.span() => f.write_value(&#accessor)?;)
133 })
134 )
135 .try_to_tokens::<TokenStream>();
136
137 let uri_display = match uri_display {
138 Ok(tokens) => tokens,
139 Err(diag) => return diag.emit_as_item_tokens()
140 };
141
142 let from_self = from_uri_param::<fmt::Path>(input.clone(), quote!(Self));
143 let from_ref = from_uri_param::<fmt::Path>(input.clone(), quote!(&'__r Self));
144 let from_mut = from_uri_param::<fmt::Path>(input, quote!(&'__r mut Self));
145
146 let mut ts = uri_display;
147 ts.extend(from_self);
148 ts.extend(from_ref);
149 ts.extend(from_mut);
150 ts
151}
152
153fn from_uri_param<P: fmt::Part>(input: proc_macro::TokenStream, ty: TokenStream) -> TokenStream {
154 let part = match P::KIND {
155 fmt::Kind::Path => quote!(#_fmt::Path),
156 fmt::Kind::Query => quote!(#_fmt::Query),
157 };
158
159 let display_trait = match P::KIND {
160 fmt::Kind::Path => P_URI_DISPLAY,
161 fmt::Kind::Query => Q_URI_DISPLAY,
162 };
163
164 let ty: syn::Type = syn::parse2(ty).expect("valid type");
165 let gen = match ty {
166 syn::Type::Reference(ref r) => r.lifetime.as_ref().map(|l| quote!(<#l>)),
167 _ => None
168 };
169
170 let param_trait = quote!(impl #gen #_fmt::FromUriParam<#part, #ty>);
171 DeriveGenerator::build_for(input, param_trait)
172 .support(Support::All)
173 .type_bound_mapper(generic_bounds_mapper(display_trait))
174 .inner_mapper(MapperBuild::new()
175 .with_output(move |_, _| quote! {
176 type Target = #ty;
177 #[inline(always)] fn from_uri_param(_p: #ty) -> #ty { _p }
178 })
179 )
180 .to_tokens()
181}