prost_reflect_derive/
lib.rs1#![doc(html_root_url = "https://docs.rs/prost-reflect-derive/0.14.0/")]
5
6use proc_macro::TokenStream;
7use proc_macro2::Span;
8use quote::{quote, ToTokens};
9use syn::spanned::Spanned;
10
11#[proc_macro_derive(ReflectMessage, attributes(prost_reflect))]
15pub fn reflect_message(input: TokenStream) -> TokenStream {
16 let input = syn::parse_macro_input!(input as syn::DeriveInput);
17
18 match reflect_message_impl(input) {
19 Ok(tokens) => tokens.into(),
20 Err(err) => err.to_compile_error().into(),
21 }
22}
23
24struct Args {
25 args_span: Span,
26 message_name: Option<syn::Lit>,
27 descriptor_pool: Option<syn::LitStr>,
28 file_descriptor_set: Option<syn::LitStr>,
29}
30
31fn reflect_message_impl(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> {
32 match &input.data {
33 syn::Data::Struct(_) => (),
34 syn::Data::Enum(_) => return Ok(Default::default()),
35 syn::Data::Union(_) => return Ok(Default::default()),
36 };
37
38 let args = Args::parse(input.ident.span(), &input.attrs)?;
39
40 let name = &input.ident;
41 let descriptor_pool = args.descriptor_pool()?;
42 let message_name = args.message_name()?;
43
44 Ok(quote! {
45 impl ::prost_reflect::ReflectMessage for #name {
46 fn descriptor(&self) -> ::prost_reflect::MessageDescriptor {
47 #descriptor_pool
48 .get_message_by_name(#message_name)
49 .expect(concat!("descriptor for message type `", #message_name, "` not found"))
50 }
51 }
52 })
53}
54
55fn is_prost_reflect_attribute(attr: &syn::Attribute) -> bool {
56 attr.path().is_ident("prost_reflect")
57}
58
59impl Args {
60 fn parse(input_span: proc_macro2::Span, attrs: &[syn::Attribute]) -> Result<Args, syn::Error> {
61 let reflect_attrs: Vec<_> = attrs
62 .iter()
63 .filter(|attr| is_prost_reflect_attribute(attr))
64 .collect();
65
66 if reflect_attrs.is_empty() {
67 return Err(syn::Error::new(
68 input_span,
69 "missing #[prost_reflect] attribute",
70 ));
71 }
72
73 let mut args = Args {
74 args_span: reflect_attrs
75 .iter()
76 .map(|a| a.span())
77 .reduce(|l, r| l.join(r).unwrap_or(l))
78 .unwrap(),
79 message_name: None,
80 descriptor_pool: None,
81 file_descriptor_set: None,
82 };
83
84 for attr in reflect_attrs {
85 attr.parse_nested_meta(|nested| {
86 if nested.path.is_ident("descriptor_pool") {
87 args.descriptor_pool = nested.value()?.parse()?;
88 Ok(())
89 } else if nested.path.is_ident("file_descriptor_set_bytes") {
90 args.file_descriptor_set = nested.value()?.parse()?;
91 Ok(())
92 } else if nested.path.is_ident("message_name") {
93 args.message_name = nested.value()?.parse()?;
94 Ok(())
95 } else {
96 Err(syn::Error::new(
97 nested.path.span(),
98 "unknown argument (expected 'descriptor_pool', 'file_descriptor_set_bytes' or 'message_name')",
99 ))
100 }
101 })?;
102 }
103
104 Ok(args)
105 }
106
107 fn descriptor_pool(&self) -> Result<proc_macro2::TokenStream, syn::Error> {
108 if let Some(descriptor_pool) = &self.descriptor_pool {
109 let expr: syn::Expr = syn::parse_str(&descriptor_pool.value())?;
110 Ok(expr.to_token_stream())
111 } else if let Some(file_descriptor_set) = &self.file_descriptor_set {
112 let expr: syn::Expr = syn::parse_str(&file_descriptor_set.value())?;
113
114 Ok(quote!({
115 static INIT: ::std::sync::Once = ::std::sync::Once::new();
116 INIT.call_once(|| ::prost_reflect::DescriptorPool::decode_global_file_descriptor_set(#expr).unwrap());
117 ::prost_reflect::DescriptorPool::global()
118 }))
119 } else {
120 Err(syn::Error::new(
121 self.args_span,
122 "missing required argument 'descriptor_pool'",
123 ))
124 }
125 }
126
127 fn message_name(&self) -> Result<proc_macro2::TokenStream, syn::Error> {
128 if let Some(message_name) = &self.message_name {
129 Ok(message_name.to_token_stream())
130 } else {
131 Err(syn::Error::new(
132 self.args_span,
133 "missing required argument 'message_name'",
134 ))
135 }
136 }
137}