power_protobuf_lib/
expand.rs

1use convert_case::Case;
2use proc_macro2::TokenStream;
3use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
4use syn::{punctuated::Punctuated, spanned::Spanned, Ident, LitStr};
5use syn_prelude::{ToIdent, ToIdentWithCase, ToLitStr, WithPrefix, WithSuffix};
6
7use crate::model::{
8    DeclIndex, EnumValue, Enumeration, Field, FieldType, GetOption, Message, MessageElement,
9    Method, Modifier, NestedTypeIndex, OneOf, Package, ProtobufOption, ProtobufPath, Protocol,
10    Service, Type,
11};
12
13impl ToTokens for Protocol {
14    fn to_tokens(&self, tokens: &mut TokenStream) {
15        self.to_token_stream_with_extra(tokens, TokenStream::new())
16    }
17}
18
19impl Protocol {
20    pub fn to_token_stream_with_extra(&self, tokens: &mut TokenStream, extra: TokenStream) {
21        let decls = self
22            .decls
23            .iter()
24            .map(|index| match index {
25                DeclIndex::Message(idx) => self.messages.get(*idx).map(Message::to_tokens),
26                DeclIndex::Enum(idx) => self.enums.get(*idx).map(Enumeration::to_tokens),
27                DeclIndex::Service(idx) => {
28                    self.services.get(*idx).map(|s| s.to_tokens(&self.package))
29                }
30            })
31            .collect::<Vec<_>>();
32        if let Some(Package { package }) = &self.package {
33            tokens.append_all(quote! {
34                pub mod #package {
35                    #(#decls)*
36                    #extra
37                }
38            })
39        } else {
40            tokens.append_all(quote! {
41                #(#decls)*
42                #extra
43            })
44        }
45    }
46}
47
48impl Message {
49    fn to_tokens(&self) -> TokenStream {
50        let Self {
51            name,
52            struct_name,
53            messages,
54            enums,
55            fields,
56            nested_types,
57            ..
58        } = self;
59
60        let field_tokens = fields.iter().map(|field| match field {
61            MessageElement::Field(field) => field.to_tokens(),
62            MessageElement::OneOf(OneOf {
63                field_name,
64                enum_name: type_name,
65                field_lit,
66                tags,
67                options,
68                ..
69            }) => {
70                let deprecated = options.deprecated();
71                let nested_mod_name = name.to_ident_with_case(Case::Snake);
72                quote! {
73                    #deprecated
74                    #[prost(oneof=#field_lit, tags=#tags)]
75                    pub #field_name: Option<#nested_mod_name::#type_name>
76                }
77            }
78        });
79
80        let nested = if !nested_types.is_empty() {
81            let nested = nested_types
82                .iter()
83                .filter_map(|i| match i {
84                    NestedTypeIndex::Message(idx) => {
85                        messages.get(*idx).map(|mesage| mesage.to_tokens())
86                    }
87                    NestedTypeIndex::Enum(idx) => {
88                        enums.get(*idx).map(|enumeration| enumeration.to_tokens())
89                    }
90                    NestedTypeIndex::Oneof(idx) => fields
91                        .get(*idx)
92                        .map(|f| match f {
93                            MessageElement::Field(_) => None,
94                            MessageElement::OneOf(oneof) => Some(oneof.to_tokens()),
95                        })
96                        .flatten(),
97                })
98                .collect::<Vec<_>>();
99
100            let nested_mod_name = name.to_ident_with_case(Case::Snake);
101            Some(quote! {
102                pub mod #nested_mod_name {
103                    #(#nested)*
104                }
105            })
106        } else {
107            None
108        };
109
110        let mut derives = vec![quote!(Clone), quote!(PartialEq), quote!(prost::Message)];
111        if cfg!(feature = "derive_serde") {
112            derives.push(quote!(serde::Deserialize));
113            derives.push(quote!(serde::Serialize));
114        };
115
116        quote! {
117            #[allow(clippy::derive_partial_eq_without_eq)]
118            #[derive(#(#derives),*)]
119            pub struct #struct_name {
120                #(#field_tokens),*
121            }
122
123            #nested
124        }
125    }
126}
127
128impl Field {
129    fn to_tokens(&self) -> TokenStream {
130        let Field {
131            field_name,
132            typ,
133            tag: number,
134            options,
135            ..
136        } = self;
137        let deprecated = options.deprecated();
138        let tag = number.to_lit_str();
139
140        let (optional, repeated, field_type) = self.to_type_tokens();
141
142        let mut prost_args = vec![];
143        prost_args.push(typ.to_prost_type());
144        if optional {
145            prost_args.push(quote!(optional));
146        } else if repeated {
147            prost_args.push(quote!(repeated));
148        }
149        prost_args.push(quote!(tag=#tag));
150
151        quote! {
152            #deprecated
153            #[prost(#(#prost_args),*)]
154            pub #field_name: #field_type
155        }
156    }
157
158    fn to_type_tokens(&self) -> (bool, bool, TokenStream) {
159        let Field {
160            modifier,
161            typ,
162            options,
163            enum_field,
164            ..
165        } = self;
166        let field_type = typ.to_tokens(Some(options));
167        let mut optional = false;
168        let mut repeated = false;
169        let typ = if let Some(modifier) = modifier {
170            match modifier {
171                Modifier::Optional => {
172                    optional = true;
173                    quote!(Option<#field_type>)
174                }
175                Modifier::Repeated => {
176                    repeated = true;
177                    quote!(::prost::alloc::vec::Vec<#field_type>)
178                }
179                Modifier::Required => field_type,
180            }
181        } else if !enum_field && typ.is_message() {
182            quote!(Option<#field_type>)
183        } else {
184            field_type
185        };
186        (optional, repeated, typ)
187    }
188}
189
190impl Service {
191    fn to_tokens(&self, package: &Option<Package>) -> TokenStream {
192        let Self { name, .. } = self;
193
194        let service_name = if let Some(package) = package.as_ref() {
195            name.to_lit_str().with_prefix(package.package.to_string())
196        } else {
197            name.to_lit_str()
198        };
199
200        let client = self.impl_client(&service_name);
201        let server = self.impl_server(&service_name);
202
203        quote! {
204            #client
205            #server
206        }
207    }
208
209    fn impl_client(&self, service_name: &LitStr) -> TokenStream {
210        let client_mod = self
211            .name
212            .to_ident_with_case(Case::Snake)
213            .with_suffix("_client");
214
215        let client_name = self.code_name.with_suffix("Client");
216        let methods = self
217            .methods
218            .iter()
219            .map(|m| m.impl_client_method(service_name))
220            .collect::<Vec<_>>();
221
222        quote! {
223            pub mod #client_mod {
224                #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
225                use tonic::codegen::*;
226                use tonic::codegen::http::Uri;
227
228                pub struct #client_name<T> {
229                    inner: tonic::client::Grpc<T>,
230                }
231
232                impl #client_name<tonic::transport::Channel> {
233                    pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
234                    where
235                        D: TryInto<tonic::transport::Endpoint>,
236                        D::Error: Into<StdError>,
237                    {
238                        let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
239                        Ok(Self::new(conn))
240                    }
241                }
242
243                impl<T> #client_name<T>
244                where
245                    T: tonic::client::GrpcService<tonic::body::BoxBody>,
246                    T::Error: Into<StdError>,
247                    T::ResponseBody: Body<Data = Bytes> + Send + 'static,
248                    <T::ResponseBody as Body>::Error: Into<StdError> + Send,
249                {
250                    pub fn new(inner: T) -> Self {
251                        let inner = tonic::client::Grpc::new(inner);
252                        Self { inner }
253                    }
254                    pub fn with_origin(inner: T, origin: Uri) -> Self {
255                        let inner = tonic::client::Grpc::with_origin(inner, origin);
256                        Self { inner }
257                    }
258                    pub fn with_interceptor<F>(
259                        inner: T,
260                        interceptor: F,
261                    ) -> #client_name<InterceptedService<T, F>>
262                    where
263                        F: tonic::service::Interceptor,
264                        T::ResponseBody: Default,
265                        T: tonic::codegen::Service<
266                            http::Request<tonic::body::BoxBody>,
267                            Response = http::Response<
268                                <T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
269                            >,
270                        >,
271                        <T as tonic::codegen::Service<
272                            http::Request<tonic::body::BoxBody>,
273                        >>::Error: Into<StdError> + Send + Sync,
274                    {
275                        #client_name::new(InterceptedService::new(inner, interceptor))
276                    }
277                    /// Compress requests with the given encoding.
278                    ///
279                    /// This requires the server to support it otherwise it might respond with an
280                    /// error.
281                    #[must_use]
282                    pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
283                        self.inner = self.inner.send_compressed(encoding);
284                        self
285                    }
286                    /// Enable decompressing responses.
287                    #[must_use]
288                    pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
289                        self.inner = self.inner.accept_compressed(encoding);
290                        self
291                    }
292                    /// Limits the maximum size of a decoded message.
293                    ///
294                    /// Default: `4MB`
295                    #[must_use]
296                    pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
297                        self.inner = self.inner.max_decoding_message_size(limit);
298                        self
299                    }
300                    /// Limits the maximum size of an encoded message.
301                    ///
302                    /// Default: `usize::MAX`
303                    #[must_use]
304                    pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
305                        self.inner = self.inner.max_encoding_message_size(limit);
306                        self
307                    }
308
309                    #(#methods)*
310                }
311            }
312        }
313    }
314
315    fn impl_server(&self, service_name: &LitStr) -> TokenStream {
316        let Self {
317            name,
318            code_name,
319            methods,
320            ..
321        } = self;
322
323        let server_mod = name.to_ident_with_case(Case::Snake).with_suffix("_server");
324        let trait_methods = methods
325            .iter()
326            .map(|m| m.impl_server_trait_method())
327            .collect::<Vec<_>>();
328
329        let server_name = code_name.with_suffix("Server");
330        let svc_cases = methods
331            .iter()
332            .map(|m| m.impl_server_method_switch(service_name, code_name))
333            .collect::<Vec<_>>();
334
335        quote! {
336            pub mod #server_mod {
337                #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
338                use tonic::codegen::*;
339
340                #[async_trait]
341                pub trait #code_name: Send + Sync + 'static {
342                    #(#trait_methods)*
343                }
344
345                #[derive(Debug)]
346                pub struct #server_name<T: #code_name> {
347                    inner: _Inner<T>,
348                    accept_compression_encodings: EnabledCompressionEncodings,
349                    send_compression_encodings: EnabledCompressionEncodings,
350                    max_decoding_message_size: Option<usize>,
351                    max_encoding_message_size: Option<usize>,
352                }
353
354                struct _Inner<T>(Arc<T>);
355
356                impl<T: #code_name> #server_name<T> {
357                    pub fn new(inner: T) -> Self {
358                        Self::from_arc(Arc::new(inner))
359                    }
360                    pub fn from_arc(inner: Arc<T>) -> Self {
361                        let inner = _Inner(inner);
362                        Self {
363                            inner,
364                            accept_compression_encodings: Default::default(),
365                            send_compression_encodings: Default::default(),
366                            max_decoding_message_size: None,
367                            max_encoding_message_size: None,
368                        }
369                    }
370                    pub fn with_interceptor<F>(
371                        inner: T,
372                        interceptor: F,
373                    ) -> InterceptedService<Self, F>
374                    where
375                        F: tonic::service::Interceptor,
376                    {
377                        InterceptedService::new(Self::new(inner), interceptor)
378                    }
379                    /// Enable decompressing requests with the given encoding.
380                    #[must_use]
381                    pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
382                        self.accept_compression_encodings.enable(encoding);
383                        self
384                    }
385                    /// Compress responses with the given encoding, if the client supports it.
386                    #[must_use]
387                    pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
388                        self.send_compression_encodings.enable(encoding);
389                        self
390                    }
391                    /// Limits the maximum size of a decoded message.
392                    ///
393                    /// Default: `4MB`
394                    #[must_use]
395                    pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
396                        self.max_decoding_message_size = Some(limit);
397                        self
398                    }
399                    /// Limits the maximum size of an encoded message.
400                    ///
401                    /// Default: `usize::MAX`
402                    #[must_use]
403                    pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
404                        self.max_encoding_message_size = Some(limit);
405                        self
406                    }
407                }
408                impl<T, B> tonic::codegen::Service<http::Request<B>> for #server_name<T>
409                where
410                    T: #code_name,
411                    B: Body + Send + 'static,
412                    B::Error: Into<StdError> + Send + 'static,
413                {
414                    type Response = http::Response<tonic::body::BoxBody>;
415                    type Error = std::convert::Infallible;
416                    type Future = BoxFuture<Self::Response, Self::Error>;
417                    fn poll_ready(
418                        &mut self,
419                        _cx: &mut Context<'_>,
420                    ) -> Poll<std::result::Result<(), Self::Error>> {
421                        Poll::Ready(Ok(()))
422                    }
423                    fn call(&mut self, req: http::Request<B>) -> Self::Future {
424                        let inner = self.inner.clone();
425                        match req.uri().path() {
426                            #(#svc_cases)*
427                            _ => {
428                                Box::pin(async move {
429                                    Ok(
430                                        http::Response::builder()
431                                            .status(200)
432                                            .header("grpc-status", "12")
433                                            .header("content-type", "application/grpc")
434                                            .body(empty_body())
435                                            .unwrap(),
436                                    )
437                                })
438                            }
439                        }
440                    }
441                }
442                impl<T: #code_name> Clone for #server_name<T> {
443                    fn clone(&self) -> Self {
444                        let inner = self.inner.clone();
445                        Self {
446                            inner,
447                            accept_compression_encodings: self.accept_compression_encodings,
448                            send_compression_encodings: self.send_compression_encodings,
449                            max_decoding_message_size: self.max_decoding_message_size,
450                            max_encoding_message_size: self.max_encoding_message_size,
451                        }
452                    }
453                }
454                impl<T: #code_name> Clone for _Inner<T> {
455                    fn clone(&self) -> Self {
456                        Self(Arc::clone(&self.0))
457                    }
458                }
459                impl<T: std::fmt::Debug> std::fmt::Debug for _Inner<T> {
460                    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
461                        write!(f, "{:?}", self.0)
462                    }
463                }
464                impl<T: #code_name> tonic::server::NamedService for #server_name<T> {
465                    const NAME: &'static str = #service_name;
466                }
467            }
468        }
469    }
470}
471
472impl Method {
473    fn impl_client_method(&self, service_name: &LitStr) -> TokenStream {
474        let Self {
475            method_name,
476            input_type,
477            output_type,
478            ..
479        } = self;
480
481        let method_name_lit = method_name
482            .to_ident_with_case(Case::UpperCamel)
483            .to_lit_str();
484        let request_path = method_name_lit.with_prefix(format!("/{}/", service_name.value()));
485
486        quote! {
487            pub async fn #method_name(&mut self, request: impl tonic::IntoRequest<#input_type>) -> std::result::Result<tonic::Response<#output_type>, tonic::Status> {
488                self.inner
489                    .ready()
490                    .await
491                    .map_err(|e| {
492                        tonic::Status::new(
493                            tonic::Code::Unknown,
494                            format!("Service was not ready: {}", e.into()),
495                        )
496                    })?;
497                let codec = tonic::codec::ProstCodec::default();
498                let path = http::uri::PathAndQuery::from_static(#request_path);
499                let mut req = request.into_request();
500                req.extensions_mut().insert(GrpcMethod::new(#service_name, #method_name_lit));
501                self.inner.unary(req, path, codec).await
502            }
503        }
504    }
505
506    fn impl_server_trait_method(&self) -> TokenStream {
507        let Self {
508            method_name,
509            input_type: Type { ty: input_type, .. },
510            output_type: Type {
511                ty: output_type, ..
512            },
513            ..
514        } = self;
515
516        quote! {
517            async fn #method_name(
518                &self,
519                request: tonic::Request<#input_type>,
520            ) -> std::result::Result<
521                tonic::Response<#output_type>,
522                tonic::Status,
523            >;
524        }
525    }
526
527    fn impl_server_method_switch(
528        &self,
529        service_name: &LitStr,
530        server_trait: &Ident,
531    ) -> TokenStream {
532        let Self {
533            name,
534            method_name,
535            input_type,
536            output_type,
537            ..
538        } = self;
539        let case_lit = service_name
540            .with_prefix("/")
541            .with_suffix("/")
542            .with_suffix(name.to_ident_with_case(Case::UpperCamel).to_string());
543
544        let struct_name = name.to_ident_with_case(Case::Camel).with_suffix("Svc");
545
546        quote! {
547            #case_lit => {
548                #[allow(non_camel_case_types)]
549                struct #struct_name<T: #server_trait>(pub Arc<T>);
550                impl<T: #server_trait> tonic::server::UnaryService<#input_type>
551                for #struct_name<T> {
552                    type Response = #output_type;
553                    type Future = BoxFuture<
554                        tonic::Response<Self::Response>,
555                        tonic::Status,
556                    >;
557                    fn call(
558                        &mut self,
559                        request: tonic::Request<#input_type>,
560                    ) -> Self::Future {
561                        let inner = Arc::clone(&self.0);
562                        let fut = async move {
563                            <T as #server_trait>::#method_name(&inner, request).await
564                        };
565                        Box::pin(fut)
566                    }
567                }
568                let accept_compression_encodings = self.accept_compression_encodings;
569                let send_compression_encodings = self.send_compression_encodings;
570                let max_decoding_message_size = self.max_decoding_message_size;
571                let max_encoding_message_size = self.max_encoding_message_size;
572                let inner = self.inner.clone();
573                let fut = async move {
574                    let inner = inner.0;
575                    let method = #struct_name(inner);
576                    let codec = tonic::codec::ProstCodec::default();
577                    let mut grpc = tonic::server::Grpc::new(codec)
578                        .apply_compression_config(
579                            accept_compression_encodings,
580                            send_compression_encodings,
581                        )
582                        .apply_max_message_size_config(
583                            max_decoding_message_size,
584                            max_encoding_message_size,
585                        );
586                    let res = grpc.unary(method, req).await;
587                    Ok(res)
588                };
589                Box::pin(fut)
590            }
591        }
592    }
593}
594
595impl Enumeration {
596    fn to_tokens(&self) -> TokenStream {
597        let Self { name, values, .. } = self;
598        let variants = values
599            .iter()
600            .map(
601                |EnumValue {
602                     variant_name,
603                     tag: number,
604                     ..
605                 }| {
606                    quote! { #variant_name = #number }
607                },
608            )
609            .collect::<Vec<_>>();
610
611        let name = name.to_ident_with_case(Case::UpperCamel);
612
613        let as_str_cases = values
614            .iter()
615            .map(
616                |EnumValue {
617                     variant_name,
618                     proto_name,
619                     ..
620                 }| {
621                    quote! {Self::#variant_name => #proto_name}
622                },
623            )
624            .collect::<Vec<_>>();
625
626        let from_str_cases = values.iter().map(
627            |EnumValue {
628                 variant_name,
629                 proto_name,
630                 ..
631             }| {
632                quote! { #proto_name => Some(Self::#variant_name) }
633            },
634        );
635
636        let mut derives = vec![
637            quote!(Clone),
638            quote!(Copy),
639            quote!(Debug),
640            quote!(Eq),
641            quote!(PartialEq),
642            quote!(PartialOrd),
643            quote!(Ord),
644            quote!(Hash),
645            quote!(prost::Enumeration),
646        ];
647        if cfg!(feature = "derive_serde") {
648            derives.push(quote!(serde::Deserialize));
649            derives.push(quote!(serde::Serialize));
650        }
651
652        quote! {
653            #[derive(#(#derives),*)]
654            #[repr(i32)]
655            pub enum #name {
656                #(#variants),*
657            }
658
659            impl #name {
660                /// String value of the enum field names used in the ProtoBuf definition.
661                ///
662                /// The values are not transformed in any way and thus are considered stable
663                ///(if the ProtoBuf definition does not change) and safe for programmatic use.
664                pub fn as_str_name(&self) -> &'static str {
665                    match self {
666                        #(#as_str_cases),*
667                    }
668                }
669
670                /// Creates an enum from field names used in the ProtoBuf definition.
671                pub fn from_str_name(value: &str) -> Option<Self> {
672                    match value {
673                        #(#from_str_cases,)*
674                        _ => None,
675                    }
676                }
677            }
678
679        }
680    }
681}
682
683impl OneOf {
684    fn to_tokens(&self) -> TokenStream {
685        let Self {
686            enum_name, fields, ..
687        } = self;
688        let field_tokens = fields.iter().map(|field| {
689            let Field { name, typ, tag, .. } = field;
690            let prost_type = typ.to_prost_type();
691            let (_, _, typ) = field.to_type_tokens();
692            let name = name.to_ident_with_case(Case::UpperCamel);
693            let tag = tag.to_lit_str();
694            quote! {
695                #[prost(#prost_type, tag = #tag)]
696                #name(#typ)
697            }
698        });
699
700        let mut derives = vec![quote!(Clone), quote!(PartialEq), quote!(prost::Oneof)];
701        if cfg!(feature = "derive_serde") {
702            derives.push(quote!(serde::Deserialize));
703            derives.push(quote!(serde::Serialize));
704        }
705
706        quote! {
707            #[allow(clippy::derive_partial_eq_without_eq)]
708            #[derive(#(#derives),*)]
709            pub enum #enum_name {
710                #(#field_tokens),*
711            }
712        }
713    }
714}
715
716impl ToTokens for ProtobufPath {
717    fn to_tokens(&self, tokens: &mut TokenStream) {
718        let idents = self.segments.iter().collect::<Vec<_>>();
719        tokens.append_all(quote!(#(#idents)::*))
720    }
721}
722
723impl ToTokens for Type {
724    fn to_tokens(&self, tokens: &mut TokenStream) {
725        let Self {
726            ty: complete_path, ..
727        } = self;
728        tokens.append_all(quote!(#complete_path))
729    }
730}
731
732impl FieldType {
733    fn to_tokens(&self, options: Option<&Vec<ProtobufOption>>) -> TokenStream {
734        match self {
735            FieldType::Int32(span) => Ident::new("i32", *span).to_token_stream(),
736            FieldType::Int64(span) => Ident::new("i64", *span).to_token_stream(),
737            FieldType::Uint32(span) => Ident::new("u32", *span).to_token_stream(),
738            FieldType::Uint64(span) => Ident::new("u64", *span).to_token_stream(),
739            FieldType::Sint32(span) => Ident::new("i32", *span).to_token_stream(),
740            FieldType::Sint64(span) => Ident::new("i64", *span).to_token_stream(),
741            FieldType::Bool(span) => Ident::new("bool", *span).to_token_stream(),
742            FieldType::Fixed32(span) => Ident::new("u32", *span).to_token_stream(),
743            FieldType::Sfixed32(span) => Ident::new("i32", *span).to_token_stream(),
744            FieldType::Fixed64(span) => Ident::new("u64", *span).to_token_stream(),
745            FieldType::Sfixed64(span) => Ident::new("i64", *span).to_token_stream(),
746            FieldType::Float(span) => Ident::new("f32", *span).to_token_stream(),
747            FieldType::Double(span) => Ident::new("f64", *span).to_token_stream(),
748            FieldType::String(span) => quote_spanned!(*span => ::prost::alloc::string::String),
749            FieldType::Bytes(span) => quote_spanned!(*span => ::prost::alloc::vec::Vec<u8>),
750            FieldType::MessageOrEnum(ty) => {
751                if ty.target_is_message {
752                    ty.ty.to_token_stream()
753                } else {
754                    Ident::new("i32", ty.type_path.span()).to_token_stream()
755                }
756            }
757            FieldType::Map(map) => {
758                let key_type = map.key.as_ref().to_tokens(None);
759                let value_type = map.value.as_ref().to_tokens(None);
760
761                if let Some(value) = options
762                    .map(|opts| opts.get_string_option("map_type"))
763                    .flatten()
764                {
765                    let opt = value.value();
766                    if opt.eq("hash") || opt.eq("HashMap") {
767                        quote_spanned!(value.span() => std::collections::HashMap<#key_type, #value_type>)
768                    } else if opt.eq("btree_map") || opt.eq("BTreeMap") {
769                        quote_spanned!(value.span() => std::collections::BTreeMap<#key_type, #value_type>)
770                    } else {
771                        // default is hash map
772                        quote_spanned!(value.span() => std::collections::HashMap<#key_type, #value_type>)
773                    }
774                } else {
775                    quote!(std::collections::HashMap<#key_type, #value_type>)
776                }
777            }
778            FieldType::Group(_group) => todo!(),
779        }
780    }
781
782    fn to_prost_type(&self) -> TokenStream {
783        match self {
784            Self::Int32(span) => ("int32", *span).to_ident().to_token_stream(),
785            Self::Int64(span) => ("int64", *span).to_ident().to_token_stream(),
786            Self::Uint32(span) => ("uint32", *span).to_ident().to_token_stream(),
787            Self::Uint64(span) => ("uint64", *span).to_ident().to_token_stream(),
788            Self::Sint32(span) => ("sint32", *span).to_ident().to_token_stream(),
789            Self::Sint64(span) => ("sint64", *span).to_ident().to_token_stream(),
790            Self::Bool(span) => ("bool", *span).to_ident().to_token_stream(),
791            Self::Fixed64(span) => ("fixed64", *span).to_ident().to_token_stream(),
792            Self::Sfixed64(span) => ("sfixed64", *span).to_ident().to_token_stream(),
793            Self::Double(span) => ("double", *span).to_ident().to_token_stream(),
794            Self::String(span) => ("string", *span).to_ident().to_token_stream(),
795            Self::Bytes(span) => ("bytes", *span).to_ident().to_token_stream(),
796            Self::Fixed32(span) => ("fixed32", *span).to_ident().to_token_stream(),
797            Self::Sfixed32(span) => ("sfixed32", *span).to_ident().to_token_stream(),
798            Self::Float(span) => ("float", *span).to_ident().to_token_stream(),
799            Self::MessageOrEnum(ty) => {
800                if ty.target_is_message {
801                    ("message", ty.ty.span()).to_ident().to_token_stream()
802                } else {
803                    let enum_type = ty.type_path.local_name().to_lit_str();
804                    quote!(enumeration = #enum_type)
805                }
806            }
807            Self::Group(g) => ("group", g.name.span()).to_ident().to_token_stream(),
808            Self::Map(map) => {
809                let key_type = map.key.to_prost_type().to_string();
810                let value_type = map.value.to_prost_type().to_string();
811                let map_type = (
812                    format!("{}, {}", key_type.trim(), value_type.trim()),
813                    map.key.span(),
814                )
815                    .to_lit_str();
816                quote!(map = #map_type)
817            }
818        }
819    }
820}
821
822trait Deprecated: GetOption {
823    fn deprecated(&self) -> Option<TokenStream> {
824        if self
825            .get_bool_option("deprecated")
826            .map(|opt| opt.value())
827            .unwrap_or_default()
828        {
829            Some(quote!(#[deprecated]))
830        } else {
831            None
832        }
833    }
834}
835
836impl Deprecated for Vec<ProtobufOption> {}
837impl<P> Deprecated for Punctuated<ProtobufOption, P> {}