gproxy_protocol_macros/
lib.rs1use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::{Data, DeriveInput, Fields, GenericArgument, PathArguments, Type, parse_macro_input};
6
7mod declared_fields;
8
9#[proc_macro_derive(DeclaredFields, attributes(declared))]
18pub fn declared_fields(input: TokenStream) -> TokenStream {
19 declared_fields::expand(parse_macro_input!(input as DeriveInput))
20 .unwrap_or_else(syn::Error::into_compile_error)
21 .into()
22}
23
24#[proc_macro_derive(WireBuilder, attributes(wire))]
32pub fn wire_builder(input: TokenStream) -> TokenStream {
33 let input = parse_macro_input!(input as DeriveInput);
34 expand(input)
35 .unwrap_or_else(syn::Error::into_compile_error)
36 .into()
37}
38
39fn expand(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
40 let Data::Struct(data) = &input.data else {
41 return Err(syn::Error::new_spanned(
42 &input,
43 "WireBuilder requires a struct",
44 ));
45 };
46 let Fields::Named(fields) = &data.fields else {
47 return Err(syn::Error::new_spanned(
48 &input,
49 "WireBuilder requires named fields",
50 ));
51 };
52 let name = &input.ident;
53 let builder = format_ident!("{}Builder", name);
54 let visibility = &input.vis;
55 let generics = &input.generics;
56 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
57 let mut arguments = Vec::new();
58 let mut initializers = Vec::new();
59 let mut setters = Vec::new();
60
61 for field in &fields.named {
62 let ident = field.ident.as_ref().expect("named field");
63 let ty = &field.ty;
64 let mut required = false;
65 for attr in field
66 .attrs
67 .iter()
68 .filter(|attr| attr.path().is_ident("wire"))
69 {
70 attr.parse_nested_meta(|meta| {
71 if meta.path.is_ident("required") {
72 required = true;
73 Ok(())
74 } else {
75 Err(meta.error("expected `required`"))
76 }
77 })?;
78 }
79 if required {
80 arguments.push(quote!(#ident: #ty));
81 initializers.push(quote!(#ident));
82 continue;
83 }
84 if let Some(inner) = option_inner(ty) {
85 initializers.push(quote!(#ident: ::core::option::Option::None));
86 setters.push(quote! {
87 #[must_use]
88 pub fn #ident(mut self, value: impl ::core::convert::Into<#inner>) -> Self {
89 self.0.#ident = ::core::option::Option::Some(value.into());
90 self
91 }
92 });
93 } else if ident == "rest" {
94 initializers.push(quote!(#ident: ::core::default::Default::default()));
95 setters.push(quote! {
96 #[must_use]
97 pub fn #ident(mut self, value: #ty) -> Self {
98 self.0.#ident = value;
99 self
100 }
101 });
102 } else {
103 arguments.push(quote!(#ident: #ty));
104 initializers.push(quote!(#ident));
105 }
106 }
107
108 Ok(quote! {
109 impl #impl_generics #name #type_generics #where_clause {
110 #[allow(clippy::too_many_arguments)]
112 pub fn builder(#(#arguments),*) -> #builder #type_generics {
113 #builder(Self { #(#initializers),* })
114 }
115 }
116
117 #[doc = concat!("Builder for [`", stringify!(#name), "`].")]
118 #[must_use]
119 #visibility struct #builder #generics (#name #type_generics) #where_clause;
120
121 impl #impl_generics #builder #type_generics #where_clause {
122 #(#setters)*
123
124 pub fn build(self) -> #name #type_generics {
126 self.0
127 }
128 }
129 })
130}
131
132fn option_inner(ty: &Type) -> Option<&Type> {
133 let Type::Path(path) = ty else { return None };
134 let segment = path.path.segments.last()?;
135 if segment.ident != "Option" {
136 return None;
137 }
138 let PathArguments::AngleBracketed(arguments) = &segment.arguments else {
139 return None;
140 };
141 match arguments.args.first()? {
142 GenericArgument::Type(inner) => Some(inner),
143 _ => None,
144 }
145}