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 ProtoValueType::Float | ProtoValueType::Double => {
233 if registry.support_non_finite_vals(&field.full_name) {
234 if field.options.visibility.has_output() {
235 oneof_config.field_attribute(
236 field_name,
237 parse_quote!(#[serde(serialize_with = "::tinc::__private::serialize_floats_with_non_finite")]),
238 );
239 }
240 if field.options.visibility.has_input() {
241 oneof_config.field_attribute(field_name, parse_quote!(#[tinc(with_non_finite_values)]));
242 }
243 }
244 }
245 _ => {}
246 }
247 }
248
249 let message = registry.get_message(&oneof.message).expect("message not found");
250
251 let oneof_path = oneof.rust_path(&message.package);
252 let oneof_ident = oneof_path.segments.last().unwrap().ident.clone();
253
254 package.push_item(parse_quote! {
255 #[allow(clippy::all, dead_code, unused_imports, unused_variables, unused_parens)]
256 const _: () = {
257 #tagged_impl
258
259 #[derive(
260 ::std::fmt::Debug,
261 ::std::clone::Clone,
262 ::core::marker::Copy,
263 ::core::cmp::PartialEq,
264 ::core::cmp::Eq,
265 ::core::hash::Hash,
266 ::core::cmp::Ord,
267 ::core::cmp::PartialOrd,
268 )]
269 #[allow(non_camel_case_types)]
270 pub enum #variant_identifier_ident {
271 #(#variant_idents),*
272 }
273
274 impl ::tinc::__private::Identifier for #variant_identifier_ident {
275 const OPTIONS: &'static [&'static str] = &[#(#variant_fields),*];
276
277 fn name(&self) -> &'static str {
278 match self {
279 #(#variant_name_fn),*
280 }
281 }
282 }
283
284 impl ::core::str::FromStr for #variant_identifier_ident {
285 type Err = ();
286
287 fn from_str(s: &str) -> Result<Self, Self::Err> {
288 ::tinc::__tinc_field_from_str!(s, #(#variant_from_str_fn),*)
289 }
290 }
291
292 impl ::tinc::__private::Expected for #oneof_path {
293 fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
294 write!(formatter, stringify!(#oneof_ident))
295 }
296 }
297
298 impl ::tinc::__private::IdentifierFor for #oneof_path {
299 const NAME: &'static str = stringify!(#oneof_ident);
300 type Identifier = #oneof_identifier_for_ident;
301 }
302
303 impl ::tinc::__private::TrackedOneOfVariant for #oneof_path {
304 type Variant = #variant_identifier_ident;
305 }
306
307 type ___Tracker = <<#oneof_path as ::tinc::__private::TrackerFor>::Tracker as ::tinc::__private::TrackerWrapper>::Tracker;
308
309 impl<'de> ::tinc::__private::TrackedOneOfDeserializer<'de> for #oneof_path {
310 fn deserialize<D>(
311 value: &mut ::core::option::Option<#oneof_path>,
312 variant: #variant_identifier_ident,
313 tracker: &mut ::core::option::Option<___Tracker>,
314 deserializer: D,
315 ) -> ::core::result::Result<(), D::Error>
316 where
317 D: ::tinc::__private::DeserializeContent<'de>
318 {
319 match variant {
320 #(#deserializer_impl),*
321 }
322
323 ::core::result::Result::Ok(())
324 }
325
326 fn tracker_to_identifier(v: &___Tracker) -> #variant_identifier_ident {
327 match v {
328 #(___Tracker::#variant_enum_ident(_) => #variant_identifier_ident::#variant_idents),*
329 }
330 }
331
332 fn value_to_identifier(v: &#oneof_path) -> #variant_identifier_ident {
333 match v {
334 #(#oneof_path::#variant_enum_ident(_) => #variant_identifier_ident::#variant_idents),*
335 }
336 }
337 }
338
339 impl ::tinc::__private::TincValidate for #oneof_path {
340 fn validate(&self, tracker: Option<&<#oneof_path as ::tinc::__private::TrackerFor>::Tracker>) -> ::core::result::Result<(), ::tinc::__private::ValidationError> {
341 let tracker = tracker.and_then(|t| t.as_ref());
342 match self {
343 #(#validate_message_impl)*
344 #[allow(unreachable_patterns)]
345 _ => {}
346 }
347
348 ::core::result::Result::Ok(())
349 }
350 }
351 };
352 });
353
354 Ok(())
355}
356
357struct FieldBuilder<'a> {
358 deserializer_fields: &'a mut Vec<proc_macro2::TokenStream>,
359 field_enum_variants: &'a mut Vec<proc_macro2::TokenStream>,
360 field_enum_name_fn: &'a mut Vec<proc_macro2::TokenStream>,
361 field_enum_from_str_fn: &'a mut Vec<proc_macro2::TokenStream>,
362 field_enum_from_str_flattened_fn: &'a mut Vec<proc_macro2::TokenStream>,
363 deserializer_fn: &'a mut Vec<proc_macro2::TokenStream>,
364 cel_validation_fn: &'a mut Vec<proc_macro2::TokenStream>,
365}
366
367fn handle_message_field(
368 package: &mut Package,
369 field_name: &str,
370 field: &ProtoMessageField,
371 field_builder: FieldBuilder<'_>,
372 field_enum_ident: &syn::Ident,
373 registry: &ProtoTypeRegistry,
374) -> anyhow::Result<()> {
375 let serde_name = &field.options.serde_name;
376
377 let message_config = package.message_config(&field.message);
378
379 message_config.field_attribute(field_name, parse_quote!(#[serde(rename = #serde_name)]));
380
381 let message = registry.get_message(&field.message).expect("message not found");
382
383 let ident = quote::format_ident!("__field_{field_name}");
384 if field.options.flatten {
385 let flattened_ty_path = match &field.ty {
386 ProtoType::Modified(ProtoModifiedValueType::Optional(ProtoValueType::Message(path)))
387 | ProtoType::Value(ProtoValueType::Message(path)) => {
388 registry.resolve_rust_path(&message.package, path).expect("message not found")
389 }
390 ProtoType::Modified(ProtoModifiedValueType::OneOf(oneof)) => oneof.rust_path(&message.package),
391 _ => anyhow::bail!("flattened fields must be messages or oneofs"),
392 };
393
394 if field.options.visibility.has_output() {
395 message_config.field_attribute(field_name, parse_quote!(#[serde(flatten)]));
396 }
397
398 if field.options.visibility.has_input() {
399 let flattened_identifier = quote! {
400 <#flattened_ty_path as ::tinc::__private::IdentifierFor>::Identifier
401 };
402
403 field_builder.deserializer_fields.push(quote! {
404 <#flattened_identifier as ::tinc::__private::Identifier>::OPTIONS
405 });
406 field_builder.field_enum_variants.push(quote! {
407 #ident(#flattened_identifier)
408 });
409 field_builder.field_enum_name_fn.push(quote! {
410 #field_enum_ident::#ident(flatten) => ::tinc::__private::Identifier::name(flatten)
411 });
412 field_builder.field_enum_from_str_flattened_fn.push(quote! {
413 #ident
414 });
415 }
416 } else if field.options.visibility.has_input() {
417 field_builder.deserializer_fields.push(quote! {
418 &[#serde_name]
419 });
420 field_builder.field_enum_variants.push(quote! {
421 #ident
422 });
423 field_builder.field_enum_name_fn.push(quote! {
424 #field_enum_ident::#ident => #serde_name
425 });
426 field_builder.field_enum_from_str_fn.push(quote! {
427 #serde_name => #field_enum_ident::#ident
428 });
429 }
430
431 if field.options.visibility.has_output() {
432 if matches!(field.options.serde_omittable, ProtoFieldSerdeOmittable::True) {
433 message_config.field_attribute(
434 field_name,
435 parse_quote!(#[serde(skip_serializing_if = "::tinc::__private::serde_ser_skip_default")]),
436 );
437 }
438 } else {
439 message_config.field_attribute(field_name, parse_quote!(#[serde(skip_serializing)]));
440 }
441
442 match field.ty.value_type() {
443 Some(ProtoValueType::Enum(path)) => {
444 let path_str = registry
445 .resolve_rust_path(message.full_name.trim_last_segment(), path)
446 .expect("enum not found")
447 .to_token_stream()
448 .to_string();
449
450 if field.options.visibility.has_output() {
451 let serialize_with = format!("::tinc::__private::serialize_enum::<{path_str}, _, _>");
452 message_config.field_attribute(field_name, parse_quote!(#[serde(serialize_with = #serialize_with)]));
453 }
454
455 message_config.field_attribute(field_name, parse_quote!(#[tinc(enum = #path_str)]));
456 }
457 Some(ProtoValueType::WellKnown(_) | ProtoValueType::Bytes) => {
458 if field.options.visibility.has_output() {
459 message_config.field_attribute(
460 field_name,
461 parse_quote!(#[serde(serialize_with = "::tinc::__private::serialize_well_known")]),
462 );
463 }
464 }
465 Some(ProtoValueType::Float | ProtoValueType::Double) => {
466 if registry.support_non_finite_vals(&field.full_name) {
467 if field.options.visibility.has_output() {
468 message_config.field_attribute(
469 field_name,
470 parse_quote!(#[serde(serialize_with = "::tinc::__private::serialize_floats_with_non_finite")]),
471 );
472 }
473 if field.options.visibility.has_input() {
474 message_config.field_attribute(field_name, parse_quote!(#[tinc(with_non_finite_values)]));
475 }
476 }
477 }
478 _ => {}
479 }
480
481 if let ProtoType::Modified(ProtoModifiedValueType::OneOf(oneof)) = &field.ty {
482 handle_oneof(package, field_name, oneof, registry, field.options.visibility)?;
483 }
484
485 let field_ident = field.rust_ident();
486
487 let mut tracker = quote! {
488 &mut tracker.#field_ident
489 };
490
491 let mut value = quote! {
492 &mut self.#field_ident
493 };
494
495 if matches!(field.ty, ProtoType::Modified(ProtoModifiedValueType::Optional(_)))
498 && (!field.options.nullable || field.options.flatten)
499 {
500 tracker = quote! {
501 (#tracker).get_or_insert_default()
502 };
503
504 value = quote! {
505 (#value).get_or_insert_default()
506 };
507 }
508
509 if field.options.visibility.has_input() {
510 if field.options.flatten {
511 field_builder.deserializer_fn.push(quote! {
512 #field_enum_ident::#ident(field) => {
513 if let Err(error) = ::tinc::__private::TrackerDeserializeIdentifier::<'de>::deserialize(
514 (#tracker).get_or_insert_default(),
515 #value,
516 field,
517 deserializer,
518 ) {
519 return ::tinc::__private::report_de_error(error);
520 }
521 }
522 });
523 } else {
524 field_builder.deserializer_fn.push(quote! {
525 #field_enum_ident::#ident => {
526 let tracker = #tracker;
527
528 if !::tinc::__private::tracker_allow_duplicates(tracker.as_ref()) {
529 return ::tinc::__private::report_tracked_error(
530 ::tinc::__private::TrackedError::duplicate_field(),
531 );
532 }
533
534 if let Err(error) = ::tinc::__private::TrackerDeserializer::deserialize(
535 tracker.get_or_insert_default(),
536 #value,
537 deserializer,
538 ) {
539 return ::tinc::__private::report_de_error(error);
540 }
541 }
542 });
543 }
544 }
545
546 let push_field_token = if !field.options.flatten {
547 quote! {
548 let _token = ::tinc::__private::SerdePathToken::push_field(
549 ::tinc::__private::Identifier::name(&#field_enum_ident::#ident),
550 );
551 }
552 } else {
553 quote! {}
554 };
555
556 let missing = if matches!(field.options.serde_omittable, ProtoFieldSerdeOmittable::False) && !field.options.flatten {
557 quote! {
558 ::tinc::__private::report_tracked_error(
559 ::tinc::__private::TrackedError::missing_field(),
560 )?;
561 }
562 } else {
563 quote! {}
564 };
565
566 let mut tracker_access = quote!(tracker.and_then(|t| t.#field_ident.as_ref()));
567 if matches!(field.ty, ProtoType::Modified(ProtoModifiedValueType::Optional(_))) {
568 tracker_access = quote!(#tracker_access.and_then(|t| t.as_ref()))
569 }
570
571 if field.options.visibility.has_input() {
572 let cel_validation_fn = cel_expressions(
573 registry,
574 &field.ty,
575 &field.full_name,
576 &field.options,
577 quote!(self.#field_ident),
578 tracker_access,
579 )?;
580
581 field_builder.cel_validation_fn.push(quote!({
582 let _token = ::tinc::__private::ProtoPathToken::push_field(#field_name);
583 #push_field_token
584
585 if tracker.is_none_or(|t| t.#field_ident.is_some()) {
587 #(#cel_validation_fn)*
588 } else {
589 #missing
590 }
591 }));
592 }
593
594 Ok(())
595}
596
597fn cel_expressions(
598 registry: &ProtoTypeRegistry,
599 ty: &ProtoType,
600 field_full_name: &str,
601 options: &ProtoFieldOptions,
602 value_accessor: proc_macro2::TokenStream,
603 tracker_accessor: proc_macro2::TokenStream,
604) -> anyhow::Result<Vec<proc_macro2::TokenStream>> {
605 let compiler = Compiler::new(registry);
606 let mut cel_validation_fn = Vec::new();
607
608 let evaluate_expr = |ctx: &Compiler, expr: &CelExpression| {
609 let mut ctx = ctx.child();
610 if let Some(this) = expr.this.clone() {
611 ctx.add_variable("this", CompiledExpr::constant(this));
612 }
613 let parsed = cel_parser::parse(&expr.expression).context("expression parse")?;
614 let resolved = ctx.resolve(&parsed).context("cel expression")?;
615 let expr_str = &expr.expression;
616 let message = eval_message_fmt(field_full_name, &expr.message, &ctx).context("message")?;
617
618 anyhow::Ok(quote! {
619 if !::tinc::__private::cel::to_bool({
620 (|| {
621 ::core::result::Result::Ok::<_, ::tinc::__private::cel::CelError>(#resolved)
622 })().map_err(|err| {
623 ::tinc::__private::ValidationError::Expression {
624 error: err.to_string().into_boxed_str(),
625 field: #field_full_name,
626 expression: #expr_str,
627 }
628 })?
629 }) {
630 ::tinc::__private::report_tracked_error(
631 ::tinc::__private::TrackedError::invalid_field(#message)
632 )?;
633 }
634 })
635 };
636
637 {
638 let mut compiler = compiler.child();
639 let (value_match, field_type) = match ty {
640 ProtoType::Modified(ProtoModifiedValueType::Optional(ty)) => (quote!(Some(value)), ProtoType::Value(ty.clone())),
641 ty @ ProtoType::Modified(ProtoModifiedValueType::OneOf(_)) => (quote!(Some(value)), ty.clone()),
642 _ => (quote!(value), ty.clone()),
643 };
644
645 if let ProtoType::Value(ProtoValueType::Enum(path))
646 | ProtoType::Modified(ProtoModifiedValueType::Optional(ProtoValueType::Enum(path))) = ty
647 {
648 compiler.register_function(functions::Enum(Some(path.clone())));
649 }
650
651 let recursive_validate = matches!(
652 field_type,
653 ProtoType::Value(ProtoValueType::Message(_)) | ProtoType::Modified(ProtoModifiedValueType::OneOf(_))
654 );
655
656 compiler.add_variable(
657 "input",
658 CompiledExpr::runtime(CelType::Proto(field_type), parse_quote!(value)),
659 );
660 let mut exprs = options
661 .cel_exprs
662 .field
663 .iter()
664 .map(|expr| evaluate_expr(&compiler, expr))
665 .collect::<anyhow::Result<Vec<_>>>()?;
666
667 if recursive_validate {
668 exprs.push(quote! {
669 ::tinc::__private::TincValidate::validate(value, #tracker_accessor)?;
670 })
671 }
672
673 if !exprs.is_empty() {
674 cel_validation_fn.push(quote! {{
675 #[allow(irrefutable_let_patterns)]
676 if let #value_match = &#value_accessor {
677 #(#exprs)*
678 }
679 }});
680 }
681
682 if !options.nullable
683 && matches!(
684 &ty,
685 ProtoType::Modified(ProtoModifiedValueType::Optional(_) | ProtoModifiedValueType::OneOf(_))
686 )
687 {
688 cel_validation_fn.push(quote! {{
689 if #value_accessor.is_none() {
690 ::tinc::__private::report_tracked_error(
691 ::tinc::__private::TrackedError::missing_field()
692 )?;
693 }
694 }})
695 }
696 }
697
698 match ty {
699 ProtoType::Modified(ProtoModifiedValueType::Map(key, value))
700 if !options.cel_exprs.map_key.is_empty()
701 || !options.cel_exprs.map_value.is_empty()
702 || matches!(value, ProtoValueType::Message(_)) =>
703 {
704 let key_exprs = {
705 let mut compiler = compiler.child();
706
707 if let ProtoValueType::Enum(path) = key {
708 compiler.register_function(functions::Enum(Some(path.clone())));
709 }
710
711 compiler.add_variable(
712 "input",
713 CompiledExpr::runtime(CelType::Proto(ProtoType::Value(key.clone())), parse_quote!(key)),
714 );
715 options
716 .cel_exprs
717 .map_key
718 .iter()
719 .map(|expr| evaluate_expr(&compiler, expr))
720 .collect::<anyhow::Result<Vec<_>>>()?
721 };
722
723 let is_message = matches!(value, ProtoValueType::Message(_));
724
725 let mut value_exprs = {
726 let mut compiler = compiler.child();
727 if let ProtoValueType::Enum(path) = value {
728 compiler.register_function(functions::Enum(Some(path.clone())));
729 }
730 compiler.add_variable(
731 "input",
732 CompiledExpr::runtime(CelType::Proto(ProtoType::Value(value.clone())), parse_quote!(value)),
733 );
734 options
735 .cel_exprs
736 .map_value
737 .iter()
738 .map(|expr| evaluate_expr(&compiler, expr))
739 .collect::<anyhow::Result<Vec<_>>>()?
740 };
741
742 if is_message {
743 value_exprs.push(quote!({
744 let tracker = match #tracker_accessor {
745 ::core::option::Option::Some(t) => Some(t.get(key).expect("map tracker state missing item, this is a bug report it.")),
746 ::core::option::Option::None => None
747 };
748 ::tinc::__private::TincValidate::validate(value, tracker)?;
749 }));
750 }
751
752 cel_validation_fn.push(quote! {{
753 for (key, value) in &#value_accessor {
754 let _token = ::tinc::__private::ProtoPathToken::push_field(key);
755 let _token = ::tinc::__private::SerdePathToken::push_field(key);
756 #(#key_exprs)*
757 #(#value_exprs)*
758 }
759 }});
760 }
761 ProtoType::Modified(ProtoModifiedValueType::Repeated(item))
762 if !options.cel_exprs.repeated_item.is_empty() || matches!(item, ProtoValueType::Message(_)) =>
763 {
764 let is_message = matches!(item, ProtoValueType::Message(_));
765 let mut compiler = compiler.child();
766 if let ProtoValueType::Enum(path) = item {
767 compiler.register_function(functions::Enum(Some(path.clone())));
768 }
769 compiler.add_variable(
770 "input",
771 CompiledExpr::runtime(CelType::Proto(ProtoType::Value(item.clone())), parse_quote!(item)),
772 );
773
774 let mut exprs = options
775 .cel_exprs
776 .repeated_item
777 .iter()
778 .map(|expr| evaluate_expr(&compiler, expr))
779 .collect::<anyhow::Result<Vec<_>>>()?;
780
781 if is_message {
782 exprs.push(quote!({
783 let tracker = match #tracker_accessor {
784 ::core::option::Option::Some(t) => Some(t.get(idx).expect("repeated tracker state missing item, this is a bug report it.")),
785 ::core::option::Option::None => None
786 };
787 ::tinc::__private::TincValidate::validate(item, tracker)?;
788 }));
789 }
790
791 cel_validation_fn.push(quote! {{
792 for (idx, item) in #value_accessor.iter().enumerate() {
793 let _token = ::tinc::__private::ProtoPathToken::push_index(idx);
794 let _token = ::tinc::__private::SerdePathToken::push_index(idx);
795 #(#exprs)*
796 }
797 }});
798 }
799 _ => {}
800 }
801
802 Ok(cel_validation_fn)
803}
804
805pub(super) fn handle_message(
806 message: &ProtoMessageType,
807 package: &mut Package,
808 registry: &ProtoTypeRegistry,
809) -> anyhow::Result<()> {
810 let message_config = package.message_config(&message.full_name);
811
812 message_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_derive::Serialize)]));
813 message_config.attribute(parse_quote!(#[serde(crate = "::tinc::reexports::serde")]));
814 message_config.attribute(parse_quote!(#[derive(::tinc::__private::Tracker)]));
815
816 let field_enum_ident = quote::format_ident!("___field_enum");
817
818 let mut field_enum_variants = Vec::new();
819 let mut field_enum_name_fn = Vec::new();
820 let mut field_enum_from_str_fn = Vec::new();
821 let mut field_enum_from_str_flattened_fn = Vec::new();
822 let mut deserializer_fields = Vec::new();
823 let mut deserializer_fn = Vec::new();
824 let mut cel_validation_fn = Vec::new();
825
826 for (field_name, field) in message.fields.iter() {
827 handle_message_field(
828 package,
829 field_name,
830 field,
831 FieldBuilder {
832 deserializer_fields: &mut deserializer_fields,
833 field_enum_variants: &mut field_enum_variants,
834 field_enum_name_fn: &mut field_enum_name_fn,
835 field_enum_from_str_fn: &mut field_enum_from_str_fn,
836 field_enum_from_str_flattened_fn: &mut field_enum_from_str_flattened_fn,
837 deserializer_fn: &mut deserializer_fn,
838 cel_validation_fn: &mut cel_validation_fn,
839 },
840 &field_enum_ident,
841 registry,
842 )?;
843 }
844
845 let message_path = registry
846 .resolve_rust_path(&message.package, &message.full_name)
847 .expect("message not found");
848 let message_ident = message_path.segments.last().unwrap().ident.clone();
849
850 package.push_item(parse_quote! {
851 #[allow(clippy::all, dead_code, unused_imports, unused_variables, unused_parens)]
852 const _: () = {
853 #[derive(
854 ::std::fmt::Debug,
855 ::std::clone::Clone,
856 ::core::marker::Copy,
857 ::core::cmp::PartialEq,
858 ::core::cmp::Eq,
859 ::core::hash::Hash,
860 ::core::cmp::Ord,
861 ::core::cmp::PartialOrd,
862 )]
863 #[allow(non_camel_case_types)]
864 pub enum #field_enum_ident {
865 #(#field_enum_variants),*
866 }
867
868 impl ::tinc::__private::Identifier for #field_enum_ident {
869 const OPTIONS: &'static [&'static str] = ::tinc::__private_const_concat_str_array!(#(#deserializer_fields),*);
870
871 fn name(&self) -> &'static str {
872 match self {
873 #(#field_enum_name_fn),*
874 }
875 }
876 }
877
878 impl ::core::str::FromStr for #field_enum_ident {
879 type Err = ();
880
881 fn from_str(s: &str) -> Result<Self, Self::Err> {
882 ::tinc::__tinc_field_from_str!(s, #(#field_enum_from_str_fn),*, flattened: [#(#field_enum_from_str_flattened_fn),*])
883 }
884 }
885
886 impl ::tinc::__private::IdentifierFor for #message_path {
887 const NAME: &'static str = stringify!(#message_ident);
888 type Identifier = #field_enum_ident;
889 }
890
891 type ___Tracker = <<#message_path as ::tinc::__private::TrackerFor>::Tracker as ::tinc::__private::TrackerWrapper>::Tracker;
892
893 impl<'de> ::tinc::__private::TrackedStructDeserializer<'de> for #message_path {
894 #[allow(unused_mut, dead_code)]
895 fn deserialize<D>(
896 &mut self,
897 field: Self::Identifier,
898 mut tracker: &mut ___Tracker,
899 deserializer: D,
900 ) -> Result<(), D::Error>
901 where
902 D: ::tinc::__private::DeserializeContent<'de>,
903 {
904 match field {
905 #(#deserializer_fn),*
906 }
907
908 ::core::result::Result::Ok(())
909 }
910 }
911
912 impl ::tinc::__private::Expected for #message_path {
913 fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
914 write!(formatter, stringify!(#message_ident))
915 }
916 }
917
918 impl ::tinc::__private::TincValidate for #message_path {
919 fn validate(&self, tracker: Option<&<#message_path as ::tinc::__private::TrackerFor>::Tracker>) -> ::core::result::Result<(), ::tinc::__private::ValidationError> {
920 let tracker = tracker.map(|t| &**t);
921 #(#cel_validation_fn)*
922 ::core::result::Result::Ok(())
923 }
924 }
925 };
926 });
927
928 Ok(())
929}
930
931pub(super) fn handle_enum(enum_: &ProtoEnumType, package: &mut Package, registry: &ProtoTypeRegistry) -> anyhow::Result<()> {
932 let enum_path = registry
933 .resolve_rust_path(&enum_.package, &enum_.full_name)
934 .expect("enum not found");
935 let enum_ident = enum_path.segments.last().unwrap().ident.clone();
936 let enum_config = package.enum_config(&enum_.full_name);
937
938 if enum_.options.repr_enum {
939 enum_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_repr::Serialize_repr)]));
940 enum_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_repr::Deserialize_repr)]));
941 } else {
942 enum_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_derive::Serialize)]));
943 enum_config.attribute(parse_quote!(#[derive(::tinc::reexports::serde_derive::Deserialize)]));
944 }
945
946 enum_config.attribute(parse_quote!(#[serde(crate = "::tinc::reexports::serde")]));
947
948 let mut to_serde_matchers = if !enum_.options.repr_enum {
949 Vec::new()
950 } else {
951 vec![quote! {
952 item => ::tinc::__private::cel::CelValueConv::conv(item as i32)
953 }]
954 };
955
956 for (name, variant) in &enum_.variants {
957 if !enum_.options.repr_enum {
958 let serde_name = &variant.options.serde_name;
959 enum_config.variant_attribute(name, parse_quote!(#[serde(rename = #serde_name)]));
960 let ident = &variant.rust_ident;
961 to_serde_matchers.push(quote! {
962 #enum_path::#ident => ::tinc::__private::cel::CelValueConv::conv(#serde_name)
963 })
964 }
965
966 match variant.options.visibility {
967 ProtoVisibility::InputOnly => {
968 enum_config.variant_attribute(name, parse_quote!(#[serde(skip_serializing)]));
969 }
970 ProtoVisibility::OutputOnly => {
971 enum_config.variant_attribute(name, parse_quote!(#[serde(skip_deserializing)]));
972 }
973 ProtoVisibility::Skip => {
974 enum_config.variant_attribute(name, parse_quote!(#[serde(skip)]));
975 }
976 _ => {}
977 }
978 }
979
980 let proto_path = enum_.full_name.as_ref();
981
982 package.push_item(parse_quote! {
983 #[allow(clippy::all, dead_code, unused_imports, unused_variables, unused_parens)]
984 const _: () = {
985 impl ::tinc::__private::Expected for #enum_path {
986 fn expecting(formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
987 write!(formatter, "an enum of `")?;
988 write!(formatter, stringify!(#enum_ident))?;
989 write!(formatter, "`")
990 }
991 }
992
993 #[::tinc::reexports::linkme::distributed_slice(::tinc::__private::cel::TINC_CEL_ENUM_VTABLE)]
994 #[linkme(crate = ::tinc::reexports::linkme)]
995 static ENUM_VTABLE: ::tinc::__private::cel::EnumVtable = ::tinc::__private::cel::EnumVtable {
996 proto_path: #proto_path,
997 is_valid: |tag| {
998 <#enum_path as std::convert::TryFrom<i32>>::try_from(tag).is_ok()
999 },
1000 to_serde: |tag| {
1001 match <#enum_path as std::convert::TryFrom<i32>>::try_from(tag) {
1002 Ok(value) => match value {
1003 #(#to_serde_matchers),*
1004 }
1005 Err(_) => ::tinc::__private::cel::CelValue::Null,
1006 }
1007 },
1008 to_proto: |tag| {
1009 match <#enum_path as std::convert::TryFrom<i32>>::try_from(tag) {
1010 Ok(value) => ::tinc::__private::cel::CelValue::String(::tinc::__private::cel::CelString::Borrowed(value.as_str_name())),
1011 Err(_) => ::tinc::__private::cel::CelValue::Null,
1012 }
1013 }
1014 };
1015 };
1016 });
1017
1018 Ok(())
1019}