prost_reflect_derive/
lib.rs

1//! This crate provides the [`ReflectMessage`](https://docs.rs/prost-reflect/latest/prost_reflect/derive.ReflectMessage.html) derive macro
2//!
3//! For documentation, see the example in the [`prost-reflect` crate docs](https://docs.rs/prost-reflect/latest/prost_reflect/index.html#deriving-reflectmessage).
4#![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/// A derive macro for the [`ReflectMessage`](https://docs.rs/prost-reflect/latest/prost_reflect/trait.ReflectMessage.html) trait.
12///
13/// For documentation, see the example in the [`prost-reflect` crate docs](https://docs.rs/prost-reflect/latest/prost_reflect/index.html#deriving-reflectmessage).
14#[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}