tinc_build/codegen/
serde.rs

1use anyhow::Context;
2use quote::{ToTokens, quote};
3use syn::parse_quote;
4
5use super::Package;
6use super::cel::compiler::{CompiledExpr, Compiler};
7use super::cel::types::CelType;
8use super::cel::{CelExpression, eval_message_fmt, functions};
9use crate::types::{
10    ProtoEnumType, ProtoFieldOptions, ProtoFieldSerdeOmittable, ProtoMessageField, ProtoMessageType, ProtoModifiedValueType,
11    ProtoOneOfType, ProtoType, ProtoTypeRegistry, ProtoValueType, ProtoVisibility, Tagged,
12};
13
14fn handle_oneof(
15    package: &mut Package,
16    field_name: &str,
17    oneof: &ProtoOneOfType,
18    registry: &ProtoTypeRegistry,
19    visibility: ProtoVisibility,
20) -> anyhow::Result<()> {
21    let message_config = package.message_config(&oneof.message);
22    message_config.field_attribute(field_name, parse_quote!(#[tinc(oneof)]));
23
24    let oneof_config = message_config.oneof_config(field_name);
25
26    if visibility.has_output() {
27        oneof_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_derive::Serialize)]));
28    }
29
30    oneof_config.attribute(parse_quote!(#[derive(::tinc::__private::Tracker)]));
31
32    let variant_identifier_ident = quote::format_ident!("___identifier");
33    let mut oneof_identifier_for_ident = variant_identifier_ident.clone();
34    let mut variant_idents = Vec::new();
35    let mut variant_name_fn = Vec::new();
36    let mut variant_from_str_fn = Vec::new();
37    let mut variant_fields = Vec::new();
38    let mut variant_enum_ident = Vec::new();
39    let mut deserializer_impl = Vec::new();
40    let mut validate_message_impl = Vec::new();
41
42    let tagged_impl = if let Some(Tagged { tag, content }) = &oneof.options.tagged {
43        oneof_config.attribute(parse_quote!(#[serde(tag = #tag, content = #content)]));
44        oneof_config.attribute(parse_quote!(#[tinc(tagged)]));
45        oneof_identifier_for_ident = quote::format_ident!("___tagged_identifier");
46        quote! {
47            #[derive(
48                ::std::fmt::Debug,
49                ::std::clone::Clone,
50                ::core::marker::Copy,
51                ::core::cmp::PartialEq,
52                ::core::cmp::Eq,
53                ::core::hash::Hash,
54                ::core::cmp::Ord,
55                ::core::cmp::PartialOrd,
56            )]
57            #[allow(non_camel_case_types)]
58            pub enum #oneof_identifier_for_ident {
59                ___tag,
60                ___content,
61            }
62
63            impl ::tinc::__private::Identifier for #oneof_identifier_for_ident {
64                const OPTIONS: &'static [&'static str] = &[
65                    #tag,
66                    #content,
67                ];
68
69                fn name(&self) -> &'static str {
70                    match self {
71                        #oneof_identifier_for_ident::___tag => #tag,
72                        #oneof_identifier_for_ident::___content => #content,
73                    }
74                }
75            }
76
77            impl ::core::str::FromStr for #oneof_identifier_for_ident {
78                type Err = ();
79
80                fn from_str(s: &str) -> Result<Self, Self::Err> {
81                    ::tinc::__tinc_field_from_str!(s,
82                        #tag => #oneof_identifier_for_ident::___tag,
83                        #content => #oneof_identifier_for_ident::___content,
84                    )
85                }
86            }
87
88            impl ::tinc::__private::TaggedOneOfIdentifier for #oneof_identifier_for_ident {
89                const TAG: Self = #oneof_identifier_for_ident::___tag;
90                const CONTENT: Self = #oneof_identifier_for_ident::___content;
91            }
92        }
93    } else {
94        quote! {}
95    };
96
97    for (field_name, field) in &oneof.fields {
98        anyhow::ensure!(!field.options.flatten, "oneof fields cannot be flattened");
99
100        let ident = quote::format_ident!("__field_{field_name}");
101        let serde_name = &field.options.serde_name;
102
103        oneof_config.field_attribute(field_name, parse_quote!(#[serde(rename = #serde_name)]));
104        if visibility.has_output() && !field.options.visibility.has_output() {
105            oneof_config.field_attribute(field_name, parse_quote!(#[serde(skip_serializing)]));
106        }
107
108        if field.options.visibility.has_input() {
109            variant_idents.push(ident.clone());
110            variant_name_fn.push(quote! {
111                #variant_identifier_ident::#ident => #serde_name
112            });
113            variant_from_str_fn.push(quote! {
114                #serde_name => #variant_identifier_ident::#ident
115            });
116            variant_fields.push(quote! {
117                #serde_name
118            });
119            let enum_ident = field.rust_ident();
120            variant_enum_ident.push(enum_ident.clone());
121            deserializer_impl.push(quote! {
122                #variant_identifier_ident::#ident => {
123                    let tracker = match tracker {
124                        ::core::option::Option::None => {
125                            let ___Tracker::#enum_ident(tracker) = tracker.get_or_insert_with(|| ___Tracker::#enum_ident(Default::default())) else {
126                                ::core::unreachable!()
127                            };
128
129                            tracker
130                        },
131                        ::core::option::Option::Some(___Tracker::#enum_ident(tracker)) => {
132                            if !::tinc::__private::tracker_allow_duplicates(Some(tracker)) {
133                                return ::tinc::__private::report_tracked_error(
134                                    ::tinc::__private::TrackedError::duplicate_field(),
135                                );
136                            }
137
138                            tracker
139                        },
140                        ::core::option::Option::Some(tracker) => {
141                            return ::core::result::Result::Err(
142                                ::tinc::reexports::serde::de::Error::invalid_type(
143                                    ::tinc::reexports::serde::de::Unexpected::Other(
144                                        ::tinc::__private::Identifier::name(&Self::tracker_to_identifier(tracker)),
145                                    ),
146                                    &::tinc::__private::Identifier::name(&#variant_identifier_ident::#ident),
147                                ),
148                            );
149                        }
150                    };
151
152                    let value = match value.get_or_insert_with(|| Self::#enum_ident(Default::default())) {
153                        Self::#enum_ident(value) => value,
154                        value => {
155                            return ::core::result::Result::Err(
156                                ::tinc::reexports::serde::de::Error::invalid_type(
157                                    ::tinc::reexports::serde::de::Unexpected::Other(
158                                        ::tinc::__private::Identifier::name(&Self::value_to_identifier(value)),
159                                    ),
160                                    &::tinc::__private::Identifier::name(&#variant_identifier_ident::#ident),
161                                ),
162                            );
163                        }
164                    };
165
166                    ::tinc::__private::TrackerDeserializer::deserialize(
167                        tracker,
168                        value,
169                        deserializer,
170                    )?;
171                }
172            });
173
174            let cel_validation_fn = cel_expressions(
175                registry,
176                &ProtoType::Value(field.ty.clone()),
177                &field.full_name,
178                &field.options,
179                quote!(*value),
180                quote!(tracker),
181            )?;
182
183            let serde_name = if let Some(tagged) = &oneof.options.tagged {
184                tagged.content.as_str()
185            } else {
186                field.options.serde_name.as_str()
187            };
188
189            validate_message_impl.push(quote! {
190                (Self::#enum_ident(value)) => {
191                    let _token = ::tinc::__private::ProtoPathToken::push_field(#field_name);
192                    let _token = ::tinc::__private::SerdePathToken::push_field(#serde_name);
193                    let tracker = match tracker {
194                        ::core::option::Option::Some(___Tracker::#enum_ident(tracker)) => ::core::option::Option::Some(tracker),
195                        ::core::option::Option::Some(t) => return ::core::result::Result::Err(
196                            ::tinc::reexports::serde::de::Error::custom(format!(
197                                "tracker and value do not match: {:?} != {:?}",
198                                ::tinc::__private::Identifier::name(&<Self as ::tinc::__private::TrackedOneOfDeserializer<'_>>::tracker_to_identifier(t)),
199                                ::tinc::__private::Identifier::name(&<Self as ::tinc::__private::TrackedOneOfDeserializer<'_>>::value_to_identifier(self)),
200                            )),
201                        ),
202                        ::core::option::Option::None => ::core::option::Option::None,
203                    };
204                    #(#cel_validation_fn)*
205                }
206            });
207        }
208
209        match &field.ty {
210            ProtoValueType::Enum(path) => {
211                let path_str = registry
212                    .resolve_rust_path(&oneof.message, path)
213                    .expect("enum not found")
214                    .to_token_stream()
215                    .to_string();
216
217                if field.options.visibility.has_output() {
218                    let serialize_with = format!("::tinc::__private::serialize_enum::<{path_str}, _, _>");
219                    oneof_config.field_attribute(field_name, parse_quote!(#[serde(serialize_with = #serialize_with)]));
220                }
221
222                oneof_config.field_attribute(field_name, parse_quote!(#[tinc(enum = #path_str)]));
223            }
224            ProtoValueType::WellKnown(_) | ProtoValueType::Bytes => {
225                if field.options.visibility.has_output() {
226                    oneof_config.field_attribute(
227                        field_name,
228                        parse_quote!(#[serde(serialize_with = "::tinc::__private::serialize_well_known")]),
229                    );
230                }
231            }
232            _ => {}
233        }
234    }
235
236    let message = registry.get_message(&oneof.message).expect("message not found");
237
238    let oneof_path = oneof.rust_path(&message.package);
239    let oneof_ident = oneof_path.segments.last().unwrap().ident.clone();
240
241    package.push_item(parse_quote! {
242        #[allow(clippy::all, dead_code, unused_imports, unused_variables, unused_parens)]
243        const _: () = {
244            #tagged_impl
245
246            #[derive(
247                ::std::fmt::Debug,
248                ::std::clone::Clone,
249                ::core::marker::Copy,
250                ::core::cmp::PartialEq,
251                ::core::cmp::Eq,
252                ::core::hash::Hash,
253                ::core::cmp::Ord,
254                ::core::cmp::PartialOrd,
255            )]
256            #[allow(non_camel_case_types)]
257            pub enum #variant_identifier_ident {
258                #(#variant_idents),*
259            }
260
261            impl ::tinc::__private::Identifier for #variant_identifier_ident {
262                const OPTIONS: &'static [&'static str] = &[#(#variant_fields),*];
263
264                fn name(&self) -> &'static str {
265                    match self {
266                        #(#variant_name_fn),*
267                    }
268                }
269            }
270
271            impl ::core::str::FromStr for #variant_identifier_ident {
272                type Err = ();
273
274                fn from_str(s: &str) -> Result<Self, Self::Err> {
275                    ::tinc::__tinc_field_from_str!(s, #(#variant_from_str_fn),*)
276                }
277            }
278
279            impl ::tinc::__private::Expected for #oneof_path {
280                fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
281                    write!(formatter, stringify!(#oneof_ident))
282                }
283            }
284
285            impl ::tinc::__private::IdentifierFor for #oneof_path {
286                const NAME: &'static str = stringify!(#oneof_ident);
287                type Identifier = #oneof_identifier_for_ident;
288            }
289
290            impl ::tinc::__private::TrackedOneOfVariant for #oneof_path {
291                type Variant = #variant_identifier_ident;
292            }
293
294            type ___Tracker = <<#oneof_path as ::tinc::__private::TrackerFor>::Tracker as ::tinc::__private::TrackerWrapper>::Tracker;
295
296            impl<'de> ::tinc::__private::TrackedOneOfDeserializer<'de> for #oneof_path {
297                fn deserialize<D>(
298                    value: &mut ::core::option::Option<#oneof_path>,
299                    variant: #variant_identifier_ident,
300                    tracker: &mut ::core::option::Option<___Tracker>,
301                    deserializer: D,
302                ) -> ::core::result::Result<(), D::Error>
303                where
304                    D: ::tinc::__private::DeserializeContent<'de>
305                {
306                    match variant {
307                        #(#deserializer_impl),*
308                    }
309
310                    ::core::result::Result::Ok(())
311                }
312
313                fn tracker_to_identifier(v: &___Tracker) -> #variant_identifier_ident {
314                    match v {
315                        #(___Tracker::#variant_enum_ident(_) => #variant_identifier_ident::#variant_idents),*
316                    }
317                }
318
319                fn value_to_identifier(v: &#oneof_path) -> #variant_identifier_ident {
320                    match v {
321                        #(#oneof_path::#variant_enum_ident(_) => #variant_identifier_ident::#variant_idents),*
322                    }
323                }
324            }
325
326            impl ::tinc::__private::TincValidate for #oneof_path {
327                fn validate(&self, tracker: Option<&<#oneof_path as ::tinc::__private::TrackerFor>::Tracker>) -> ::core::result::Result<(), ::tinc::__private::ValidationError> {
328                    let tracker = tracker.and_then(|t| t.as_ref());
329                    match self {
330                        #(#validate_message_impl)*
331                        #[allow(unreachable_patterns)]
332                        _ => {}
333                    }
334
335                    ::core::result::Result::Ok(())
336                }
337            }
338        };
339    });
340
341    Ok(())
342}
343
344struct FieldBuilder<'a> {
345    deserializer_fields: &'a mut Vec<proc_macro2::TokenStream>,
346    field_enum_variants: &'a mut Vec<proc_macro2::TokenStream>,
347    field_enum_name_fn: &'a mut Vec<proc_macro2::TokenStream>,
348    field_enum_from_str_fn: &'a mut Vec<proc_macro2::TokenStream>,
349    field_enum_from_str_flattened_fn: &'a mut Vec<proc_macro2::TokenStream>,
350    deserializer_fn: &'a mut Vec<proc_macro2::TokenStream>,
351    cel_validation_fn: &'a mut Vec<proc_macro2::TokenStream>,
352}
353
354fn handle_message_field(
355    package: &mut Package,
356    field_name: &str,
357    field: &ProtoMessageField,
358    field_builder: FieldBuilder<'_>,
359    field_enum_ident: &syn::Ident,
360    registry: &ProtoTypeRegistry,
361) -> anyhow::Result<()> {
362    let serde_name = &field.options.serde_name;
363
364    let message_config = package.message_config(&field.message);
365
366    message_config.field_attribute(field_name, parse_quote!(#[serde(rename = #serde_name)]));
367
368    let message = registry.get_message(&field.message).expect("message not found");
369
370    let ident = quote::format_ident!("__field_{field_name}");
371    if field.options.flatten {
372        let flattened_ty_path = match &field.ty {
373            ProtoType::Modified(ProtoModifiedValueType::Optional(ProtoValueType::Message(path)))
374            | ProtoType::Value(ProtoValueType::Message(path)) => {
375                registry.resolve_rust_path(&message.package, path).expect("message not found")
376            }
377            ProtoType::Modified(ProtoModifiedValueType::OneOf(oneof)) => oneof.rust_path(&message.package),
378            _ => anyhow::bail!("flattened fields must be messages or oneofs"),
379        };
380
381        if field.options.visibility.has_output() {
382            message_config.field_attribute(field_name, parse_quote!(#[serde(flatten)]));
383        }
384
385        if field.options.visibility.has_input() {
386            let flattened_identifier = quote! {
387                <#flattened_ty_path as ::tinc::__private::IdentifierFor>::Identifier
388            };
389
390            field_builder.deserializer_fields.push(quote! {
391                <#flattened_identifier as ::tinc::__private::Identifier>::OPTIONS
392            });
393            field_builder.field_enum_variants.push(quote! {
394                #ident(#flattened_identifier)
395            });
396            field_builder.field_enum_name_fn.push(quote! {
397                #field_enum_ident::#ident(flatten) => ::tinc::__private::Identifier::name(flatten)
398            });
399            field_builder.field_enum_from_str_flattened_fn.push(quote! {
400                #ident
401            });
402        }
403    } else if field.options.visibility.has_input() {
404        field_builder.deserializer_fields.push(quote! {
405            &[#serde_name]
406        });
407        field_builder.field_enum_variants.push(quote! {
408            #ident
409        });
410        field_builder.field_enum_name_fn.push(quote! {
411            #field_enum_ident::#ident => #serde_name
412        });
413        field_builder.field_enum_from_str_fn.push(quote! {
414            #serde_name => #field_enum_ident::#ident
415        });
416    }
417
418    if field.options.visibility.has_output() {
419        if matches!(field.options.serde_omittable, ProtoFieldSerdeOmittable::True) {
420            message_config.field_attribute(
421                field_name,
422                parse_quote!(#[serde(skip_serializing_if = "::tinc::__private::serde_ser_skip_default")]),
423            );
424        }
425    } else {
426        message_config.field_attribute(field_name, parse_quote!(#[serde(skip_serializing)]));
427    }
428
429    match field.ty.value_type() {
430        Some(ProtoValueType::Enum(path)) => {
431            let path_str = registry
432                .resolve_rust_path(message.full_name.trim_last_segment(), path)
433                .expect("enum not found")
434                .to_token_stream()
435                .to_string();
436
437            if field.options.visibility.has_output() {
438                let serialize_with = format!("::tinc::__private::serialize_enum::<{path_str}, _, _>");
439                message_config.field_attribute(field_name, parse_quote!(#[serde(serialize_with = #serialize_with)]));
440            }
441
442            message_config.field_attribute(field_name, parse_quote!(#[tinc(enum = #path_str)]));
443        }
444        Some(ProtoValueType::WellKnown(_) | ProtoValueType::Bytes) => {
445            if field.options.visibility.has_output() {
446                message_config.field_attribute(
447                    field_name,
448                    parse_quote!(#[serde(serialize_with = "::tinc::__private::serialize_well_known")]),
449                );
450            }
451        }
452        _ => {}
453    }
454
455    if let ProtoType::Modified(ProtoModifiedValueType::OneOf(oneof)) = &field.ty {
456        handle_oneof(package, field_name, oneof, registry, field.options.visibility)?;
457    }
458
459    let field_ident = field.rust_ident();
460
461    let mut tracker = quote! {
462        &mut tracker.#field_ident
463    };
464
465    let mut value = quote! {
466        &mut self.#field_ident
467    };
468
469    // When a field is not nullable but prost generates an option<T>, we need to
470    // remove the option before deserializing otherwise null will be a valid input.
471    if matches!(field.ty, ProtoType::Modified(ProtoModifiedValueType::Optional(_)))
472        && (!field.options.nullable || field.options.flatten)
473    {
474        tracker = quote! {
475            (#tracker).get_or_insert_default()
476        };
477
478        value = quote! {
479            (#value).get_or_insert_default()
480        };
481    }
482
483    if field.options.visibility.has_input() {
484        if field.options.flatten {
485            field_builder.deserializer_fn.push(quote! {
486                #field_enum_ident::#ident(field) => {
487                    if let Err(error) = ::tinc::__private::TrackerDeserializeIdentifier::<'de>::deserialize(
488                        (#tracker).get_or_insert_default(),
489                        #value,
490                        field,
491                        deserializer,
492                    ) {
493                        return ::tinc::__private::report_de_error(error);
494                    }
495                }
496            });
497        } else {
498            field_builder.deserializer_fn.push(quote! {
499                #field_enum_ident::#ident => {
500                    let tracker = #tracker;
501
502                    if !::tinc::__private::tracker_allow_duplicates(tracker.as_ref()) {
503                        return ::tinc::__private::report_tracked_error(
504                            ::tinc::__private::TrackedError::duplicate_field(),
505                        );
506                    }
507
508                    if let Err(error) = ::tinc::__private::TrackerDeserializer::deserialize(
509                        tracker.get_or_insert_default(),
510                        #value,
511                        deserializer,
512                    ) {
513                        return ::tinc::__private::report_de_error(error);
514                    }
515                }
516            });
517        }
518    }
519
520    let push_field_token = if !field.options.flatten {
521        quote! {
522            let _token = ::tinc::__private::SerdePathToken::push_field(
523                ::tinc::__private::Identifier::name(&#field_enum_ident::#ident),
524            );
525        }
526    } else {
527        quote! {}
528    };
529
530    let missing = if matches!(field.options.serde_omittable, ProtoFieldSerdeOmittable::False) && !field.options.flatten {
531        quote! {
532            ::tinc::__private::report_tracked_error(
533                ::tinc::__private::TrackedError::missing_field(),
534            )?;
535        }
536    } else {
537        quote! {}
538    };
539
540    let mut tracker_access = quote!(tracker.and_then(|t| t.#field_ident.as_ref()));
541    if matches!(field.ty, ProtoType::Modified(ProtoModifiedValueType::Optional(_))) {
542        tracker_access = quote!(#tracker_access.and_then(|t| t.as_ref()))
543    }
544
545    if field.options.visibility.has_input() {
546        let cel_validation_fn = cel_expressions(
547            registry,
548            &field.ty,
549            &field.full_name,
550            &field.options,
551            quote!(self.#field_ident),
552            tracker_access,
553        )?;
554
555        field_builder.cel_validation_fn.push(quote!({
556            let _token = ::tinc::__private::ProtoPathToken::push_field(#field_name);
557            #push_field_token
558
559            // TODO: this seems wrong. I feel as if we should validate it even if omittable is true.
560            if tracker.is_none_or(|t| t.#field_ident.is_some()) {
561                #(#cel_validation_fn)*
562            } else {
563                #missing
564            }
565        }));
566    }
567
568    Ok(())
569}
570
571fn cel_expressions(
572    registry: &ProtoTypeRegistry,
573    ty: &ProtoType,
574    field_full_name: &str,
575    options: &ProtoFieldOptions,
576    value_accessor: proc_macro2::TokenStream,
577    tracker_accessor: proc_macro2::TokenStream,
578) -> anyhow::Result<Vec<proc_macro2::TokenStream>> {
579    let compiler = Compiler::new(registry);
580    let mut cel_validation_fn = Vec::new();
581
582    let evaluate_expr = |ctx: &Compiler, expr: &CelExpression| {
583        let mut ctx = ctx.child();
584        if let Some(this) = expr.this.clone() {
585            ctx.add_variable("this", CompiledExpr::constant(this));
586        }
587        let parsed = cel_parser::parse(&expr.expression).context("expression parse")?;
588        let resolved = ctx.resolve(&parsed).context("cel expression")?;
589        let expr_str = &expr.expression;
590        let message = eval_message_fmt(field_full_name, &expr.message, &ctx).context("message")?;
591
592        anyhow::Ok(quote! {
593            if !::tinc::__private::cel::to_bool({
594                (|| {
595                    ::core::result::Result::Ok::<_, ::tinc::__private::cel::CelError>(#resolved)
596                })().map_err(|err| {
597                    ::tinc::__private::ValidationError::Expression {
598                        error: err.to_string().into_boxed_str(),
599                        field: #field_full_name,
600                        expression: #expr_str,
601                    }
602                })?
603            }) {
604                ::tinc::__private::report_tracked_error(
605                    ::tinc::__private::TrackedError::invalid_field(#message)
606                )?;
607            }
608        })
609    };
610
611    {
612        let mut compiler = compiler.child();
613        let (value_match, field_type) = match ty {
614            ProtoType::Modified(ProtoModifiedValueType::Optional(ty)) => (quote!(Some(value)), ProtoType::Value(ty.clone())),
615            ty @ ProtoType::Modified(ProtoModifiedValueType::OneOf(_)) => (quote!(Some(value)), ty.clone()),
616            _ => (quote!(value), ty.clone()),
617        };
618
619        if let ProtoType::Value(ProtoValueType::Enum(path))
620        | ProtoType::Modified(ProtoModifiedValueType::Optional(ProtoValueType::Enum(path))) = ty
621        {
622            compiler.register_function(functions::Enum(Some(path.clone())));
623        }
624
625        let recursive_validate = matches!(
626            field_type,
627            ProtoType::Value(ProtoValueType::Message(_)) | ProtoType::Modified(ProtoModifiedValueType::OneOf(_))
628        );
629
630        compiler.add_variable(
631            "input",
632            CompiledExpr::runtime(CelType::Proto(field_type), parse_quote!(value)),
633        );
634        let mut exprs = options
635            .cel_exprs
636            .field
637            .iter()
638            .map(|expr| evaluate_expr(&compiler, expr))
639            .collect::<anyhow::Result<Vec<_>>>()?;
640
641        if recursive_validate {
642            exprs.push(quote! {
643                ::tinc::__private::TincValidate::validate(value, #tracker_accessor)?;
644            })
645        }
646
647        if !exprs.is_empty() {
648            cel_validation_fn.push(quote! {{
649                #[allow(irrefutable_let_patterns)]
650                if let #value_match = &#value_accessor {
651                    #(#exprs)*
652                }
653            }});
654        }
655
656        if !options.nullable
657            && matches!(
658                &ty,
659                ProtoType::Modified(ProtoModifiedValueType::Optional(_) | ProtoModifiedValueType::OneOf(_))
660            )
661        {
662            cel_validation_fn.push(quote! {{
663                if #value_accessor.is_none() {
664                    ::tinc::__private::report_tracked_error(
665                        ::tinc::__private::TrackedError::missing_field()
666                    )?;
667                }
668            }})
669        }
670    }
671
672    match ty {
673        ProtoType::Modified(ProtoModifiedValueType::Map(key, value))
674            if !options.cel_exprs.map_key.is_empty()
675                || !options.cel_exprs.map_value.is_empty()
676                || matches!(value, ProtoValueType::Message(_)) =>
677        {
678            let key_exprs = {
679                let mut compiler = compiler.child();
680
681                if let ProtoValueType::Enum(path) = key {
682                    compiler.register_function(functions::Enum(Some(path.clone())));
683                }
684
685                compiler.add_variable(
686                    "input",
687                    CompiledExpr::runtime(CelType::Proto(ProtoType::Value(key.clone())), parse_quote!(key)),
688                );
689                options
690                    .cel_exprs
691                    .map_key
692                    .iter()
693                    .map(|expr| evaluate_expr(&compiler, expr))
694                    .collect::<anyhow::Result<Vec<_>>>()?
695            };
696
697            let is_message = matches!(value, ProtoValueType::Message(_));
698
699            let mut value_exprs = {
700                let mut compiler = compiler.child();
701                if let ProtoValueType::Enum(path) = value {
702                    compiler.register_function(functions::Enum(Some(path.clone())));
703                }
704                compiler.add_variable(
705                    "input",
706                    CompiledExpr::runtime(CelType::Proto(ProtoType::Value(value.clone())), parse_quote!(value)),
707                );
708                options
709                    .cel_exprs
710                    .map_value
711                    .iter()
712                    .map(|expr| evaluate_expr(&compiler, expr))
713                    .collect::<anyhow::Result<Vec<_>>>()?
714            };
715
716            if is_message {
717                value_exprs.push(quote!({
718                    let tracker = match #tracker_accessor {
719                        ::core::option::Option::Some(t) => Some(t.get(key).expect("map tracker state missing item, this is a bug report it.")),
720                        ::core::option::Option::None => None
721                    };
722                    ::tinc::__private::TincValidate::validate(value, tracker)?;
723                }));
724            }
725
726            cel_validation_fn.push(quote! {{
727                for (key, value) in &#value_accessor {
728                    let _token = ::tinc::__private::ProtoPathToken::push_field(key);
729                    let _token = ::tinc::__private::SerdePathToken::push_field(key);
730                    #(#key_exprs)*
731                    #(#value_exprs)*
732                }
733            }});
734        }
735        ProtoType::Modified(ProtoModifiedValueType::Repeated(item))
736            if !options.cel_exprs.repeated_item.is_empty() || matches!(item, ProtoValueType::Message(_)) =>
737        {
738            let is_message = matches!(item, ProtoValueType::Message(_));
739            let mut compiler = compiler.child();
740            if let ProtoValueType::Enum(path) = item {
741                compiler.register_function(functions::Enum(Some(path.clone())));
742            }
743            compiler.add_variable(
744                "input",
745                CompiledExpr::runtime(CelType::Proto(ProtoType::Value(item.clone())), parse_quote!(item)),
746            );
747
748            let mut exprs = options
749                .cel_exprs
750                .repeated_item
751                .iter()
752                .map(|expr| evaluate_expr(&compiler, expr))
753                .collect::<anyhow::Result<Vec<_>>>()?;
754
755            if is_message {
756                exprs.push(quote!({
757                    let tracker = match #tracker_accessor {
758                        ::core::option::Option::Some(t) => Some(t.get(idx).expect("repeated tracker state missing item, this is a bug report it.")),
759                        ::core::option::Option::None => None
760                    };
761                    ::tinc::__private::TincValidate::validate(item, tracker)?;
762                }));
763            }
764
765            cel_validation_fn.push(quote! {{
766                for (idx, item) in #value_accessor.iter().enumerate() {
767                    let _token = ::tinc::__private::ProtoPathToken::push_index(idx);
768                    let _token = ::tinc::__private::SerdePathToken::push_index(idx);
769                    #(#exprs)*
770                }
771            }});
772        }
773        _ => {}
774    }
775
776    Ok(cel_validation_fn)
777}
778
779pub(super) fn handle_message(
780    message: &ProtoMessageType,
781    package: &mut Package,
782    registry: &ProtoTypeRegistry,
783) -> anyhow::Result<()> {
784    let message_config = package.message_config(&message.full_name);
785
786    message_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_derive::Serialize)]));
787    message_config.attribute(parse_quote!(#[serde(crate = "::tinc::reexports::serde")]));
788    message_config.attribute(parse_quote!(#[derive(::tinc::__private::Tracker)]));
789
790    let field_enum_ident = quote::format_ident!("___field_enum");
791
792    let mut field_enum_variants = Vec::new();
793    let mut field_enum_name_fn = Vec::new();
794    let mut field_enum_from_str_fn = Vec::new();
795    let mut field_enum_from_str_flattened_fn = Vec::new();
796    let mut deserializer_fields = Vec::new();
797    let mut deserializer_fn = Vec::new();
798    let mut cel_validation_fn = Vec::new();
799
800    for (field_name, field) in message.fields.iter() {
801        handle_message_field(
802            package,
803            field_name,
804            field,
805            FieldBuilder {
806                deserializer_fields: &mut deserializer_fields,
807                field_enum_variants: &mut field_enum_variants,
808                field_enum_name_fn: &mut field_enum_name_fn,
809                field_enum_from_str_fn: &mut field_enum_from_str_fn,
810                field_enum_from_str_flattened_fn: &mut field_enum_from_str_flattened_fn,
811                deserializer_fn: &mut deserializer_fn,
812                cel_validation_fn: &mut cel_validation_fn,
813            },
814            &field_enum_ident,
815            registry,
816        )?;
817    }
818
819    let message_path = registry
820        .resolve_rust_path(&message.package, &message.full_name)
821        .expect("message not found");
822    let message_ident = message_path.segments.last().unwrap().ident.clone();
823
824    package.push_item(parse_quote! {
825        #[allow(clippy::all, dead_code, unused_imports, unused_variables, unused_parens)]
826        const _: () = {
827            #[derive(
828                ::std::fmt::Debug,
829                ::std::clone::Clone,
830                ::core::marker::Copy,
831                ::core::cmp::PartialEq,
832                ::core::cmp::Eq,
833                ::core::hash::Hash,
834                ::core::cmp::Ord,
835                ::core::cmp::PartialOrd,
836            )]
837            #[allow(non_camel_case_types)]
838            pub enum #field_enum_ident {
839                #(#field_enum_variants),*
840            }
841
842            impl ::tinc::__private::Identifier for #field_enum_ident {
843                const OPTIONS: &'static [&'static str] = ::tinc::__private_const_concat_str_array!(#(#deserializer_fields),*);
844
845                fn name(&self) -> &'static str {
846                    match self {
847                        #(#field_enum_name_fn),*
848                    }
849                }
850            }
851
852            impl ::core::str::FromStr for #field_enum_ident {
853                type Err = ();
854
855                fn from_str(s: &str) -> Result<Self, Self::Err> {
856                    ::tinc::__tinc_field_from_str!(s, #(#field_enum_from_str_fn),*, flattened: [#(#field_enum_from_str_flattened_fn),*])
857                }
858            }
859
860            impl ::tinc::__private::IdentifierFor for #message_path {
861                const NAME: &'static str = stringify!(#message_ident);
862                type Identifier = #field_enum_ident;
863            }
864
865            type ___Tracker = <<#message_path as ::tinc::__private::TrackerFor>::Tracker as ::tinc::__private::TrackerWrapper>::Tracker;
866
867            impl<'de> ::tinc::__private::TrackedStructDeserializer<'de> for #message_path {
868                #[allow(unused_mut, dead_code)]
869                fn deserialize<D>(
870                    &mut self,
871                    field: Self::Identifier,
872                    mut tracker: &mut ___Tracker,
873                    deserializer: D,
874                ) -> Result<(), D::Error>
875                where
876                    D: ::tinc::__private::DeserializeContent<'de>,
877                {
878                    match field {
879                        #(#deserializer_fn),*
880                    }
881
882                    ::core::result::Result::Ok(())
883                }
884            }
885
886            impl ::tinc::__private::Expected for #message_path {
887                fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
888                    write!(formatter, stringify!(#message_ident))
889                }
890            }
891
892            impl ::tinc::__private::TincValidate for #message_path {
893                fn validate(&self, tracker: Option<&<#message_path as ::tinc::__private::TrackerFor>::Tracker>) -> ::core::result::Result<(), ::tinc::__private::ValidationError> {
894                    let tracker = tracker.map(|t| &**t);
895                    #(#cel_validation_fn)*
896                    ::core::result::Result::Ok(())
897                }
898            }
899        };
900    });
901
902    Ok(())
903}
904
905pub(super) fn handle_enum(enum_: &ProtoEnumType, package: &mut Package, registry: &ProtoTypeRegistry) -> anyhow::Result<()> {
906    let enum_path = registry
907        .resolve_rust_path(&enum_.package, &enum_.full_name)
908        .expect("enum not found");
909    let enum_ident = enum_path.segments.last().unwrap().ident.clone();
910    let enum_config = package.enum_config(&enum_.full_name);
911
912    if enum_.options.repr_enum {
913        enum_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_repr::Serialize_repr)]));
914        enum_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_repr::Deserialize_repr)]));
915    } else {
916        enum_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_derive::Serialize)]));
917        enum_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_derive::Deserialize)]));
918    }
919
920    enum_config.attribute(parse_quote!(#[serde(crate = "::tinc::reexports::serde")]));
921
922    let mut to_serde_matchers = if !enum_.options.repr_enum {
923        Vec::new()
924    } else {
925        vec![quote! {
926            item => ::tinc::__private::cel::CelValueConv::conv(item as i32)
927        }]
928    };
929
930    for (name, variant) in &enum_.variants {
931        if !enum_.options.repr_enum {
932            let serde_name = &variant.options.serde_name;
933            enum_config.variant_attribute(name, parse_quote!(#[serde(rename = #serde_name)]));
934            let ident = &variant.rust_ident;
935            to_serde_matchers.push(quote! {
936                #enum_path::#ident => ::tinc::__private::cel::CelValueConv::conv(#serde_name)
937            })
938        }
939
940        match variant.options.visibility {
941            ProtoVisibility::InputOnly => {
942                enum_config.variant_attribute(name, parse_quote!(#[serde(skip_serializing)]));
943            }
944            ProtoVisibility::OutputOnly => {
945                enum_config.variant_attribute(name, parse_quote!(#[serde(skip_deserializing)]));
946            }
947            ProtoVisibility::Skip => {
948                enum_config.variant_attribute(name, parse_quote!(#[serde(skip)]));
949            }
950            _ => {}
951        }
952    }
953
954    let proto_path = enum_.full_name.as_ref();
955
956    package.push_item(parse_quote! {
957        #[allow(clippy::all, dead_code, unused_imports, unused_variables, unused_parens)]
958        const _: () = {
959            impl ::tinc::__private::Expected for #enum_path {
960                fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
961                    write!(formatter, "an enum of `")?;
962                    write!(formatter, stringify!(#enum_ident))?;
963                    write!(formatter, "`")
964                }
965            }
966
967            #[::tinc::reexports::linkme::distributed_slice(::tinc::__private::cel::TINC_CEL_ENUM_VTABLE)]
968            #[linkme(crate = ::tinc::reexports::linkme)]
969            static ENUM_VTABLE: ::tinc::__private::cel::EnumVtable = ::tinc::__private::cel::EnumVtable {
970                proto_path: #proto_path,
971                is_valid: |tag| {
972                    <#enum_path as std::convert::TryFrom<i32>>::try_from(tag).is_ok()
973                },
974                to_serde: |tag| {
975                    match <#enum_path as std::convert::TryFrom<i32>>::try_from(tag) {
976                        Ok(value) => match value {
977                            #(#to_serde_matchers),*
978                        }
979                        Err(_) => ::tinc::__private::cel::CelValue::Null,
980                    }
981                },
982                to_proto: |tag| {
983                    match <#enum_path as std::convert::TryFrom<i32>>::try_from(tag) {
984                        Ok(value) => ::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed(value.as_str_name())),
985                        Err(_) => ::tinc::__private::cel::CelValue::Null,
986                    }
987                }
988            };
989        };
990    });
991
992    Ok(())
993}