1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::{quote, quote_spanned};
4use syn::{
5 parse_macro_input, parse_quote, spanned::Spanned, token::PathSep, DeriveInput, ExprPath, Ident,
6 PathArguments, Type,
7};
8use vector_config_common::validation::Validation;
9
10use crate::ast::{Container, Data, Field, LazyCustomAttribute, Style, Tagging, Variant};
11
12pub fn derive_configurable_impl(input: TokenStream) -> TokenStream {
13 let input = parse_macro_input!(input as DeriveInput);
16 let container = match Container::from_derive_input(&input) {
17 Ok(container) => container,
18 Err(e) => {
19 return e.write_errors().into();
24 }
25 };
26
27 let mut generics = container.generics().clone();
28
29 let generic_field_types = container.generic_field_types();
34 if !generic_field_types.is_empty() {
35 let where_clause = generics.make_where_clause();
36 for typ in generic_field_types {
37 let ty = &typ.ident;
38 let predicate = parse_quote! { #ty: ::vector_config::Configurable + ::serde::Serialize + ::vector_config::ToValue };
39
40 where_clause.predicates.push(predicate);
41 }
42 }
43
44 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
45
46 let metadata_fn = build_metadata_fn(&container);
49 let generate_schema_fn = match container.virtual_newtype() {
50 Some(virtual_ty) => build_virtual_newtype_schema_fn(virtual_ty),
51 None => match container.data() {
52 Data::Struct(style, fields) => {
53 build_struct_generate_schema_fn(&container, style, fields)
54 }
55 Data::Enum(variants) => build_enum_generate_schema_fn(&container, variants),
56 },
57 };
58
59 let to_value_fn = build_to_value_fn(&container);
60
61 let name = container.ident();
62 let ref_name = container.name();
63 let configurable_impl = quote! {
64 const _: () = {
65 #[automatically_derived]
66 #[allow(unused_qualifications)]
67 impl #impl_generics ::vector_config::Configurable for #name #ty_generics #where_clause {
68 fn referenceable_name() -> Option<&'static str> {
69 let self_type_name = ::std::any::type_name::<Self>();
88 if !self_type_name.starts_with(std::module_path!()) {
89 Some(std::concat!(std::module_path!(), "::", #ref_name))
90 } else {
91 Some(self_type_name)
92 }
93 }
94
95 #metadata_fn
96
97 #generate_schema_fn
98 }
99
100 impl #impl_generics ::vector_config::ToValue for #name #ty_generics #where_clause {
101 #to_value_fn
102 }
103 };
104 };
105
106 configurable_impl.into()
107}
108
109fn build_metadata_fn(container: &Container<'_>) -> proc_macro2::TokenStream {
110 let meta_ident = Ident::new("metadata", Span::call_site());
111 let container_metadata = generate_container_metadata(&meta_ident, container);
112
113 quote! {
114 fn metadata() -> ::vector_config::Metadata {
115 #container_metadata
116 #meta_ident
117 }
118 }
119}
120
121fn build_to_value_fn(_container: &Container<'_>) -> proc_macro2::TokenStream {
122 quote! {
123 fn to_value(&self) -> ::vector_config::serde_json::Value {
124 ::vector_config::serde_json::to_value(self)
125 .expect("Could not convert value to JSON")
126 }
127 }
128}
129
130fn build_virtual_newtype_schema_fn(virtual_ty: Type) -> proc_macro2::TokenStream {
131 quote! {
132 fn generate_schema(schema_gen: &::std::cell::RefCell<::vector_config::schema::SchemaGenerator>) -> std::result::Result<::vector_config::schema::SchemaObject, ::vector_config::GenerateError> {
133 ::vector_config::schema::get_or_generate_schema(
134 &<#virtual_ty as ::vector_config::Configurable>::as_configurable_ref(),
135 schema_gen,
136 None,
137 )
138 }
139 }
140}
141
142fn build_enum_generate_schema_fn(
143 container: &Container,
144 variants: &[Variant<'_>],
145) -> proc_macro2::TokenStream {
146 let is_potentially_ambiguous = is_enum_schema_potentially_ambiguous(container, variants);
151
152 let mapped_variants = variants
156 .iter()
157 .filter(|variant| variant.visible())
159 .map(|variant| generate_enum_variant_schema(variant, is_potentially_ambiguous));
160
161 let generate_block = quote! {
168 if ::vector_config::schema::has_ambiguous_discriminants(&discriminant_map) {
169 Ok(::vector_config::schema::generate_any_of_schema(&subschemas))
170 } else {
171 Ok(::vector_config::schema::generate_one_of_schema(&subschemas))
172 }
173 };
174
175 quote! {
176 fn generate_schema(schema_gen: &::std::cell::RefCell<::vector_config::schema::SchemaGenerator>) -> std::result::Result<::vector_config::schema::SchemaObject, ::vector_config::GenerateError> {
177 let mut subschemas = ::std::vec::Vec::new();
178 let mut discriminant_map = ::std::collections::HashMap::new();
179
180 #(#mapped_variants)*
181
182 #generate_block
183 }
184 }
185}
186
187fn is_enum_schema_potentially_ambiguous(container: &Container, variants: &[Variant]) -> bool {
188 let tagging = container
189 .tagging()
190 .expect("enums must always have a tagging mode");
191 match tagging {
192 Tagging::None => {
193 if variants.len() < 2 {
195 return false;
196 }
197
198 variants.iter().all(|variant| {
201 let fields = variant.fields();
202 !fields.is_empty() && fields.iter().all(|field| field.ident().is_some())
203 })
204 }
205
206 _ => false,
208 }
209}
210
211fn build_struct_generate_schema_fn(
212 container: &Container<'_>,
213 style: &Style,
214 fields: &[Field<'_>],
215) -> proc_macro2::TokenStream {
216 match style {
217 Style::Struct => build_named_struct_generate_schema_fn(container, fields),
218 Style::Tuple => build_tuple_struct_generate_schema_fn(fields),
219 Style::Newtype => build_newtype_struct_generate_schema_fn(fields),
220 Style::Unit => panic!("unit structs should be rejected during AST parsing"),
221 }
222}
223
224fn generate_struct_field(field: &Field<'_>) -> proc_macro2::TokenStream {
225 let field_metadata_ref = Ident::new("field_metadata", Span::call_site());
226 let field_metadata = generate_field_metadata(&field_metadata_ref, field);
227 let field_schema_ty = get_field_schema_ty(field);
228
229 let spanned_generate_schema = quote_spanned! {field.span()=>
230 ::vector_config::schema::get_or_generate_schema(
231 &<#field_schema_ty as ::vector_config::Configurable>::as_configurable_ref(),
232 schema_gen,
233 Some(#field_metadata_ref),
234 )?
235 };
236
237 quote! {
238 #field_metadata
239
240 let mut subschema = #spanned_generate_schema;
241 }
242}
243
244fn generate_named_struct_field(
245 container: &Container<'_>,
246 field: &Field<'_>,
247) -> proc_macro2::TokenStream {
248 let field_name = field
249 .ident()
250 .expect("named struct fields must always have an ident");
251 let field_schema_ty = get_field_schema_ty(field);
252 let field_already_contained = format!(
253 "schema properties already contained entry for `{field_name}`, this should not occur"
254 );
255 let field_key = field.name();
256
257 let field_schema = generate_struct_field(field);
258
259 let integrate_field = if field.flatten() {
265 quote! {
266 flattened_subschemas.push(subschema);
267 }
268 } else {
269 let spanned_is_optional = quote_spanned! {field.span()=>
273 <#field_schema_ty as ::vector_config::Configurable>::is_optional()
274 };
275 let maybe_field_required =
276 if container.default_value().is_none() && field.default_value().is_none() {
277 Some(quote! {
278 if !#spanned_is_optional {
279 assert!(required.insert(#field_key.to_string()), #field_already_contained);
280 }
281 })
282 } else {
283 None
284 };
285
286 quote! {
287 if let Some(_) = properties.insert(#field_key.to_string(), subschema) {
288 panic!(#field_already_contained);
289 }
290
291 #maybe_field_required
292 }
293 };
294
295 quote! {
296 {
297 #field_schema
298 #integrate_field
299 }
300 }
301}
302
303fn generate_tuple_struct_field(field: &Field<'_>) -> proc_macro2::TokenStream {
304 let field_schema = generate_struct_field(field);
305
306 quote! {
307 {
308 #field_schema
309 subschemas.push(subschema);
310 }
311 }
312}
313
314fn build_named_struct_generate_schema_fn(
315 container: &Container<'_>,
316 fields: &[Field<'_>],
317) -> proc_macro2::TokenStream {
318 let mapped_fields = fields
319 .iter()
320 .filter(|field| field.visible())
322 .map(|field| generate_named_struct_field(container, field));
323
324 quote! {
325 fn generate_schema(schema_gen: &::std::cell::RefCell<::vector_config::schema::SchemaGenerator>) -> std::result::Result<::vector_config::schema::SchemaObject, ::vector_config::GenerateError> {
326 let mut properties = ::vector_config::indexmap::IndexMap::new();
327 let mut required = ::std::collections::BTreeSet::new();
328 let mut flattened_subschemas = ::std::vec::Vec::new();
329
330 let metadata = <Self as ::vector_config::Configurable>::metadata();
331 #(#mapped_fields)*
332
333 let had_unflatted_properties = !properties.is_empty();
334
335 let additional_properties = None;
336 let mut schema = ::vector_config::schema::generate_struct_schema(
337 properties,
338 required,
339 additional_properties,
340 );
341
342 if !flattened_subschemas.is_empty() {
344 if !had_unflatted_properties {
351 schema = flattened_subschemas.remove(0);
352 }
353
354 ::vector_config::schema::convert_to_flattened_schema(&mut schema, flattened_subschemas);
355 }
356
357 Ok(schema)
358 }
359 }
360}
361
362fn build_tuple_struct_generate_schema_fn(fields: &[Field<'_>]) -> proc_macro2::TokenStream {
363 let mapped_fields = fields
364 .iter()
365 .filter(|field| field.visible())
367 .map(generate_tuple_struct_field);
368
369 quote! {
370 fn generate_schema(schema_gen: &::std::cell::RefCell<::vector_config::schema::SchemaGenerator>) -> std::result::Result<::vector_config::schema::SchemaObject, ::vector_config::GenerateError> {
371 let mut subschemas = ::std::collections::Vec::new();
372
373 #(#mapped_fields)*
374
375 Ok(::vector_config::schema::generate_tuple_schema(&subschemas))
376 }
377 }
378}
379
380fn build_newtype_struct_generate_schema_fn(fields: &[Field<'_>]) -> proc_macro2::TokenStream {
381 let mut mapped_fields = fields
383 .iter()
384 .filter(|field| field.visible())
386 .map(generate_struct_field)
387 .collect::<Vec<_>>();
388
389 if mapped_fields.len() != 1 {
390 panic!("newtype structs should never have more than one field");
391 }
392
393 let field_schema = mapped_fields.remove(0);
394
395 quote! {
396 fn generate_schema(schema_gen: &::std::cell::RefCell<::vector_config::schema::SchemaGenerator>) -> std::result::Result<::vector_config::schema::SchemaObject, ::vector_config::GenerateError> {
397 #field_schema
398
399 Ok(subschema)
400 }
401 }
402}
403
404fn generate_container_metadata(
405 meta_ident: &Ident,
406 container: &Container<'_>,
407) -> proc_macro2::TokenStream {
408 let maybe_title = get_metadata_title(meta_ident, container.title());
409 let maybe_description = get_metadata_description(meta_ident, container.description());
410 let maybe_default_value = get_metadata_default_value(meta_ident, container.default_value());
411 let maybe_deprecated = get_metadata_deprecated(meta_ident, container.deprecated());
412 let maybe_custom_attributes = get_metadata_custom_attributes(meta_ident, container.metadata());
413
414 let enum_metadata_attrs = container
424 .tagging()
425 .map(|tagging| tagging.as_enum_metadata());
426 let enum_metadata =
427 get_metadata_custom_attributes(meta_ident, enum_metadata_attrs.into_iter().flatten());
428
429 quote! {
430 let mut #meta_ident = ::vector_config::Metadata::default();
431 #maybe_title
432 #maybe_description
433 #maybe_default_value
434 #maybe_deprecated
435 #maybe_custom_attributes
436 #enum_metadata
437 }
438}
439
440fn generate_field_metadata(meta_ident: &Ident, field: &Field<'_>) -> proc_macro2::TokenStream {
441 let field_ty = field.ty();
442 let field_schema_ty = get_field_schema_ty(field);
443
444 let maybe_title = get_metadata_title(meta_ident, field.title());
445 let maybe_description = get_metadata_description(meta_ident, field.description());
446 let maybe_clear_title_description = field
447 .title()
448 .or_else(|| field.description())
449 .is_some()
450 .then(|| {
451 quote! {
452 #meta_ident.clear_title();
457 #meta_ident.clear_description();
458 }
459 });
460 let maybe_default_value = if field_ty != field_schema_ty {
461 get_metadata_default_value_delegated(meta_ident, field_schema_ty, field.default_value())
462 } else {
463 get_metadata_default_value(meta_ident, field.default_value())
464 };
465 let maybe_deprecated = get_metadata_deprecated(meta_ident, field.deprecated());
466 let maybe_deprecated_message =
467 get_metadata_deprecated_message(meta_ident, field.deprecated_message());
468 let maybe_transparent = get_metadata_transparent(meta_ident, field.transparent());
469 let maybe_validation = get_metadata_validation(meta_ident, field.validation());
470 let maybe_custom_attributes = get_metadata_custom_attributes(meta_ident, field.metadata());
471
472 quote! {
473 let mut #meta_ident = ::vector_config::Metadata::default();
474 #maybe_clear_title_description
475 #maybe_title
476 #maybe_description
477 #maybe_default_value
478 #maybe_deprecated
479 #maybe_deprecated_message
480 #maybe_transparent
481 #maybe_validation
482 #maybe_custom_attributes
483 }
484}
485
486fn generate_variant_metadata(
487 meta_ident: &Ident,
488 variant: &Variant<'_>,
489) -> proc_macro2::TokenStream {
490 let maybe_title = get_metadata_title(meta_ident, variant.title());
491 let maybe_description = get_metadata_description(meta_ident, variant.description());
492 let maybe_deprecated = get_metadata_deprecated(meta_ident, variant.deprecated());
493
494 let maybe_transparent =
497 get_metadata_transparent(meta_ident, variant.tagging() == &Tagging::None);
498 let maybe_custom_attributes = get_metadata_custom_attributes(meta_ident, variant.metadata());
499
500 let logical_name_attrs = vec![LazyCustomAttribute::kv(
508 "logical_name",
509 variant.ident().to_string(),
510 )];
511 let variant_logical_name =
512 get_metadata_custom_attributes(meta_ident, logical_name_attrs.into_iter());
513
514 quote! {
519 let mut #meta_ident = ::vector_config::Metadata::default();
520 #maybe_title
521 #maybe_description
522 #maybe_deprecated
523 #maybe_transparent
524 #maybe_custom_attributes
525 #variant_logical_name
526 }
527}
528
529fn generate_variant_tag_metadata(
530 meta_ident: &Ident,
531 variant: &Variant<'_>,
532) -> proc_macro2::TokenStream {
533 let maybe_title = get_metadata_title(meta_ident, variant.title());
536 let maybe_description = get_metadata_description(meta_ident, variant.description());
537
538 quote! {
543 let mut #meta_ident = ::vector_config::Metadata::default();
544 #maybe_title
545 #maybe_description
546 }
547}
548
549fn get_metadata_title(
550 meta_ident: &Ident,
551 title: Option<&String>,
552) -> Option<proc_macro2::TokenStream> {
553 title.map(|title| {
554 quote! {
555 #meta_ident.set_title(#title);
556 }
557 })
558}
559
560fn get_metadata_description(
561 meta_ident: &Ident,
562 description: Option<&String>,
563) -> Option<proc_macro2::TokenStream> {
564 description.map(|description| {
565 quote! {
566 #meta_ident.set_description(#description);
567 }
568 })
569}
570
571fn get_metadata_default_value(
572 meta_ident: &Ident,
573 default_value: Option<ExprPath>,
574) -> Option<proc_macro2::TokenStream> {
575 default_value.map(|value| {
576 quote! {
577 #meta_ident.set_default_value(#value());
578 }
579 })
580}
581
582fn get_metadata_default_value_delegated(
583 meta_ident: &Ident,
584 default_ty: &syn::Type,
585 default_value: Option<ExprPath>,
586) -> Option<proc_macro2::TokenStream> {
587 default_value.map(|value| {
588 let default_ty = get_ty_for_expr_pos(default_ty);
589
590 quote! {
591 #meta_ident.set_default_value(#default_ty::from(#value()));
592 }
593 })
594}
595
596fn get_metadata_deprecated(
597 meta_ident: &Ident,
598 deprecated: bool,
599) -> Option<proc_macro2::TokenStream> {
600 deprecated.then(|| {
601 quote! {
602 #meta_ident.set_deprecated();
603 }
604 })
605}
606
607fn get_metadata_deprecated_message(
608 meta_ident: &Ident,
609 message: Option<&String>,
610) -> Option<proc_macro2::TokenStream> {
611 message.map(|message| {
612 quote! {
613 #meta_ident.set_deprecated_message(#message);
614 }
615 })
616}
617
618fn get_metadata_transparent(
619 meta_ident: &Ident,
620 transparent: bool,
621) -> Option<proc_macro2::TokenStream> {
622 transparent.then(|| {
623 quote! {
624 #meta_ident.set_transparent();
625 }
626 })
627}
628
629fn get_metadata_validation(
630 meta_ident: &Ident,
631 validation: &[Validation],
632) -> proc_macro2::TokenStream {
633 let mapped_validation = validation
634 .iter()
635 .map(|v| quote! { #meta_ident.add_validation(#v); });
636
637 quote! {
638 #(#mapped_validation)*
639 }
640}
641
642fn get_metadata_custom_attributes(
643 meta_ident: &Ident,
644 custom_attributes: impl Iterator<Item = LazyCustomAttribute>,
645) -> proc_macro2::TokenStream {
646 let mapped_custom_attributes = custom_attributes
647 .map(|attr| match attr {
648 LazyCustomAttribute::Flag(key) => quote! {
649 #meta_ident.add_custom_attribute(::vector_config::attributes::CustomAttribute::flag(#key));
650 },
651 LazyCustomAttribute::KeyValue { key, value } => quote! {
652 #meta_ident.add_custom_attribute(::vector_config::attributes::CustomAttribute::kv(
653 #key, #value
654 ));
655 },
656 });
657
658 quote! {
659 #(#mapped_custom_attributes)*
660 }
661}
662
663fn get_field_schema_ty<'a>(field: &'a Field<'a>) -> &'a syn::Type {
664 field.delegated_ty().unwrap_or_else(|| field.ty())
671}
672
673fn generate_named_enum_field(field: &Field<'_>) -> proc_macro2::TokenStream {
674 let field_name = field.ident().expect("field should be named");
675 let field_ty = field.ty();
676 let field_already_contained = format!(
677 "schema properties already contained entry for `{field_name}`, this should not occur"
678 );
679 let field_key = field.name().to_string();
680
681 let field_schema = generate_struct_field(field);
682
683 let spanned_is_optional = quote_spanned! {field.span()=>
687 <#field_ty as ::vector_config::Configurable>::is_optional()
688 };
689 let maybe_field_required = if field.default_value().is_none() {
690 Some(quote! {
691 if !#spanned_is_optional {
692 if !required.insert(#field_key.to_string()) {
693 panic!(#field_already_contained);
694 }
695 }
696 })
697 } else {
698 None
699 };
700
701 if field.flatten() {
702 quote! {
703 {
704 #field_schema
705 flattened_subschemas.push(subschema);
706 }
707 }
708 } else {
709 quote! {
710 {
711 #field_schema
712
713 if let Some(_) = properties.insert(#field_key.to_string(), subschema) {
714 panic!(#field_already_contained);
715 }
716
717 #maybe_field_required
718 }
719 }
720 }
721}
722
723fn generate_enum_struct_named_variant_schema(
724 variant: &Variant<'_>,
725 post_fields: Option<proc_macro2::TokenStream>,
726 is_potentially_ambiguous: bool,
727) -> proc_macro2::TokenStream {
728 let mapped_fields = variant.fields().iter().map(generate_named_enum_field);
729
730 let maybe_fill_discriminant_map = is_potentially_ambiguous.then(|| {
733 let variant_name = variant.ident().to_string();
734 quote! {
735 discriminant_map.insert(#variant_name, required.clone());
736 }
737 });
738
739 quote! {
740 {
741 let mut properties = ::vector_config::indexmap::IndexMap::new();
742 let mut required = ::std::collections::BTreeSet::new();
743 let mut flattened_subschemas = ::std::vec::Vec::new();
744
745 #(#mapped_fields)*
746
747 #post_fields
748
749 #maybe_fill_discriminant_map
750
751 let mut schema = ::vector_config::schema::generate_struct_schema(
752 properties,
753 required,
754 None
755 );
756
757 if !flattened_subschemas.is_empty() {
759 ::vector_config::schema::convert_to_flattened_schema(&mut schema, flattened_subschemas);
760 }
761
762 schema
763 }
764 }
765}
766
767fn generate_enum_newtype_struct_variant_schema(variant: &Variant<'_>) -> proc_macro2::TokenStream {
768 let field = variant.fields().first().expect("must exist");
773 let field_schema = generate_struct_field(field);
774
775 quote! {
776 {
777 #field_schema
778 subschema
779 }
780 }
781}
782
783fn generate_enum_variant_tag_schema(variant: &Variant<'_>) -> proc_macro2::TokenStream {
784 let variant_name = variant.name();
785 let apply_variant_tag_metadata = generate_enum_variant_tag_apply_metadata(variant);
786
787 quote! {
788 {
789 let mut tag_subschema = ::vector_config::schema::generate_const_string_schema(#variant_name.to_string());
790 #apply_variant_tag_metadata
791 tag_subschema
792 }
793 }
794}
795
796fn generate_enum_variant_schema(
797 variant: &Variant<'_>,
798 is_potentially_ambiguous: bool,
799) -> proc_macro2::TokenStream {
800 let variant_name = variant.name();
809 let variant_schema = match variant.tagging() {
810 Tagging::External => {
824 let (wrapped, variant_schema) = match variant.style() {
825 Style::Struct => (
826 true,
827 generate_enum_struct_named_variant_schema(variant, None, false),
828 ),
829 Style::Tuple => panic!("tuple variants should be rejected during AST parsing"),
830 Style::Newtype => (true, generate_enum_newtype_struct_variant_schema(variant)),
831 Style::Unit => (false, generate_enum_variant_tag_schema(variant)),
832 };
833
834 if wrapped {
840 generate_single_field_struct_schema(variant_name, variant_schema)
841 } else {
842 variant_schema
843 }
844 }
845 Tagging::Internal { tag } => match variant.style() {
858 Style::Struct => {
859 let tag_already_contained = format!("enum tag `{tag}` already contained as a field in variant; tag cannot overlap with any fields in any variant");
860
861 let tag_schema = generate_enum_variant_tag_schema(variant);
864 let tag_field = quote! {
865 {
866 if let Some(_) = properties.insert(#tag.to_string(), #tag_schema) {
867 panic!(#tag_already_contained);
868 }
869
870 if !required.insert(#tag.to_string()) {
871 panic!(#tag_already_contained);
872 }
873 }
874 };
875 generate_enum_struct_named_variant_schema(variant, Some(tag_field), false)
876 }
877 Style::Tuple => panic!("tuple variants should be rejected during AST parsing"),
878 Style::Newtype => {
879 let newtype_schema = generate_enum_newtype_struct_variant_schema(variant);
889 let tag_schema = generate_enum_variant_tag_schema(variant);
890
891 quote! {
892 let tag_schema = ::vector_config::schema::generate_internal_tagged_variant_schema(#tag.to_string(), #tag_schema);
893 let mut flattened_subschemas = ::std::vec::Vec::new();
894 flattened_subschemas.push(tag_schema);
895
896 let mut newtype_schema = #newtype_schema;
897 ::vector_config::schema::convert_to_flattened_schema(&mut newtype_schema, flattened_subschemas);
898
899 newtype_schema
900 }
901 }
902 Style::Unit => {
903 let variant_schema = generate_enum_variant_tag_schema(variant);
906 generate_single_field_struct_schema(tag, variant_schema)
907 }
908 },
909 Tagging::Adjacent { tag, content } => {
924 let tag_schema = generate_enum_variant_tag_schema(variant);
928 let maybe_content_schema = match variant.style() {
929 Style::Struct => Some(generate_enum_struct_named_variant_schema(
930 variant, None, false,
931 )),
932 Style::Tuple => panic!("tuple variants should be rejected during AST parsing"),
933 Style::Newtype => Some(generate_enum_newtype_struct_variant_schema(variant)),
934 Style::Unit => None,
935 }
936 .map(|content_schema| {
937 quote! {
938 wrapper_properties.insert(#content.to_string(), #content_schema);
939 wrapper_required.insert(#content.to_string());
940 }
941 });
942
943 quote! {
944 let mut wrapper_properties = ::vector_config::indexmap::IndexMap::new();
945 let mut wrapper_required = ::std::collections::BTreeSet::new();
946
947 wrapper_properties.insert(#tag.to_string(), #tag_schema);
948 wrapper_required.insert(#tag.to_string());
949
950 #maybe_content_schema
951
952 ::vector_config::schema::generate_struct_schema(
953 wrapper_properties,
954 wrapper_required,
955 None
956 )
957 }
958 }
959 Tagging::None => {
960 match variant.style() {
984 Style::Struct => generate_enum_struct_named_variant_schema(
985 variant,
986 None,
987 is_potentially_ambiguous,
988 ),
989 Style::Tuple => panic!("tuple variants should be rejected during AST parsing"),
990 Style::Newtype => generate_enum_newtype_struct_variant_schema(variant),
991 Style::Unit => quote! { ::vector_config::schema::generate_null_schema() },
992 }
993 }
994 };
995
996 generate_enum_variant_subschema(variant, variant_schema)
997}
998
999fn generate_single_field_struct_schema(
1000 property_name: &str,
1001 property_schema: proc_macro2::TokenStream,
1002) -> proc_macro2::TokenStream {
1003 quote! {
1004 {
1005 let mut wrapper_properties = ::vector_config::indexmap::IndexMap::new();
1006 let mut wrapper_required = ::std::collections::BTreeSet::new();
1007
1008 wrapper_properties.insert(#property_name.to_string(), #property_schema);
1009 wrapper_required.insert(#property_name.to_string());
1010
1011 ::vector_config::schema::generate_struct_schema(
1012 wrapper_properties,
1013 wrapper_required,
1014 None
1015 )
1016 }
1017 }
1018}
1019
1020fn generate_enum_variant_apply_metadata(variant: &Variant<'_>) -> proc_macro2::TokenStream {
1021 let variant_metadata_ref = Ident::new("variant_metadata", Span::call_site());
1022 let variant_metadata = generate_variant_metadata(&variant_metadata_ref, variant);
1023
1024 quote! {
1025 #variant_metadata
1026 ::vector_config::schema::apply_base_metadata(&mut subschema, #variant_metadata_ref);
1027 }
1028}
1029
1030fn generate_enum_variant_tag_apply_metadata(variant: &Variant<'_>) -> proc_macro2::TokenStream {
1031 let variant_tag_metadata_ref = Ident::new("variant_tag_metadata", Span::call_site());
1032 let variant_tag_metadata = generate_variant_tag_metadata(&variant_tag_metadata_ref, variant);
1033
1034 quote! {
1035 #variant_tag_metadata
1036 ::vector_config::schema::apply_base_metadata(&mut tag_subschema, #variant_tag_metadata_ref);
1037 }
1038}
1039
1040fn generate_enum_variant_subschema(
1041 variant: &Variant<'_>,
1042 variant_schema: proc_macro2::TokenStream,
1043) -> proc_macro2::TokenStream {
1044 let apply_variant_metadata = generate_enum_variant_apply_metadata(variant);
1045
1046 quote! {
1047 {
1048 let mut subschema = { #variant_schema };
1049 #apply_variant_metadata
1050
1051 subschemas.push(subschema);
1052 }
1053 }
1054}
1055
1056fn get_ty_for_expr_pos(ty: &syn::Type) -> syn::Type {
1071 match ty {
1072 syn::Type::Path(tp) => {
1073 let mut new_tp = tp.clone();
1074 for segment in new_tp.path.segments.iter_mut() {
1075 if let PathArguments::AngleBracketed(ab) = &mut segment.arguments {
1076 ab.colon2_token = Some(PathSep::default());
1077 }
1078 }
1079
1080 syn::Type::Path(new_tp)
1081 }
1082 _ => ty.clone(),
1083 }
1084}