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 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 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}