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