1- use std:: ops:: Not ;
1+ use std:: { cell :: OnceCell , ops:: Not } ;
22
33use darling:: { FromAttributes , Result } ;
44use proc_macro2:: TokenStream ;
55use quote:: quote;
6- use syn:: { Generics , ItemEnum } ;
6+ use syn:: { Generics , ItemEnum , parse_quote } ;
77
88use crate :: {
99 attrs:: container:: ContainerAttributes ,
@@ -19,12 +19,17 @@ use crate::{
1919} ;
2020
2121impl Container {
22- pub fn new_enum ( item_enum : ItemEnum , versions : & [ VersionDefinition ] ) -> Result < Self > {
22+ pub fn new_enum (
23+ item_enum : ItemEnum ,
24+ versions : & [ VersionDefinition ] ,
25+ experimental_conversion_tracking : bool ,
26+ ) -> Result < Self > {
2327 let attributes = ContainerAttributes :: from_attributes ( & item_enum. attrs ) ?;
2428
2529 let mut versioned_variants = Vec :: new ( ) ;
2630 for variant in item_enum. variants {
27- let mut versioned_variant = VersionedVariant :: new ( variant, versions) ?;
31+ let mut versioned_variant =
32+ VersionedVariant :: new ( variant, versions, experimental_conversion_tracking) ?;
2833 versioned_variant. insert_container_versions ( versions) ;
2934 versioned_variants. push ( versioned_variant) ;
3035 }
@@ -45,6 +50,7 @@ impl Container {
4550 } ;
4651
4752 Ok ( Self :: Enum ( Enum {
53+ tracking_generics : OnceCell :: new ( ) ,
4854 generics : item_enum. generics ,
4955 variants : versioned_variants,
5056 common,
@@ -63,14 +69,25 @@ pub struct Enum {
6369
6470 /// Generic types of the enum
6571 pub generics : Generics ,
72+
73+ /// Generic types of the enum which additionally also include the tracking state generic type
74+ /// and where clause.
75+ ///
76+ /// We use a [`OnceCell`] here to avoid outer mutability and instead rely on interior mutability
77+ /// to lazily initialize this field. This is done to avoid the macro the re-clone and
78+ /// re-construct these generics over and over again.
79+ ///
80+ /// SAFETY: Using [`OnceCell`] instead of `OnceLock` is fine here, because proc-macros run in a
81+ /// single thread.
82+ tracking_generics : OnceCell < Generics > ,
6683}
6784
6885// Common token generation
6986impl Enum {
7087 pub fn generate_tokens < ' a > (
7188 & ' a self ,
7289 versions : & ' a [ VersionDefinition ] ,
73- gen_ctx : ModuleGenerationContext < ' a > ,
90+ mod_gen_ctx : ModuleGenerationContext < ' a > ,
7491 ) -> ContainerTokens < ' a > {
7592 let mut versions = versions. iter ( ) . peekable ( ) ;
7693 let mut container_tokens = ContainerTokens :: default ( ) ;
@@ -80,8 +97,9 @@ impl Enum {
8097 let ver_ctx = VersionContext :: new ( version, next_version) ;
8198
8299 let enum_definition = self . generate_definition ( ver_ctx) ;
83- let upgrade_from = self . generate_from_impl ( Direction :: Upgrade , ver_ctx, gen_ctx) ;
84- let downgrade_from = self . generate_from_impl ( Direction :: Downgrade , ver_ctx, gen_ctx) ;
100+ let upgrade_from = self . generate_from_impl ( Direction :: Upgrade , ver_ctx, mod_gen_ctx) ;
101+ let downgrade_from =
102+ self . generate_from_impl ( Direction :: Downgrade , ver_ctx, mod_gen_ctx) ;
85103
86104 container_tokens
87105 . extend_inner ( & version. inner , enum_definition)
@@ -116,11 +134,11 @@ impl Enum {
116134 }
117135
118136 // TODO (@Techassi): Add doc comments
119- pub fn generate_from_impl (
137+ fn generate_from_impl (
120138 & self ,
121139 direction : Direction ,
122140 ver_ctx : VersionContext < ' _ > ,
123- gen_ctx : ModuleGenerationContext < ' _ > ,
141+ mod_gen_ctx : ModuleGenerationContext < ' _ > ,
124142 ) -> Option < TokenStream > {
125143 if ver_ctx. version . skip_from || self . common . options . skip_from {
126144 return None ;
@@ -149,15 +167,15 @@ impl Enum {
149167
150168 // Only add the #[automatically_derived] attribute only if this impl is used
151169 // outside of a module (in standalone mode).
152- let automatically_derived = gen_ctx . add_attributes
170+ let automatically_derived = mod_gen_ctx . add_attributes
153171 . not ( )
154172 . then ( || quote ! { #[ automatically_derived] } ) ;
155173
156174 let variants = |direction : Direction | -> TokenStream {
157175 self . variants
158176 . iter ( )
159177 . filter_map ( |v| {
160- v. generate_for_from_impl ( direction, version, next_version, enum_ident)
178+ v. generate_for_from_impl ( direction, version, next_version, enum_ident, mod_gen_ctx )
161179 } )
162180 . collect ( )
163181 } ;
@@ -177,22 +195,68 @@ impl Enum {
177195 } ,
178196 } ;
179197
180- quote ! {
181- #automatically_derived
182- #allow_attribute
183- impl #impl_generics :: core:: convert:: From <#from_module_ident:: #enum_ident #type_generics> for #for_module_ident:: #enum_ident #type_generics
184- #where_clause
185- {
186- fn from( #from_enum_ident: #from_module_ident:: #enum_ident #type_generics) -> Self {
187- match #from_enum_ident {
188- #variants
198+ // TODO (@Techassi): This currently does NOT support tracking changes to the enum itself,
199+ // only changes in nested structs are tracked.
200+ if mod_gen_ctx. kubernetes_options . experimental_conversion_tracking . is_present ( ) {
201+ let versioned_path = & * mod_gen_ctx. crates . versioned ;
202+
203+ let ( impl_generics, _, where_clause) = self . get_tracking_generics ( mod_gen_ctx) . split_for_impl ( ) ;
204+
205+ quote ! {
206+ #automatically_derived
207+ #allow_attribute
208+ impl #impl_generics #versioned_path:: TrackingFrom <#from_module_ident:: #enum_ident #type_generics, S > for #for_module_ident:: #enum_ident #type_generics
209+ #where_clause
210+ {
211+ fn tracking_from( #from_enum_ident: #from_module_ident:: #enum_ident #type_generics, status: & mut S , parent: & str ) -> Self {
212+ // TODO (@Techassi): Only emit this if any of the variants below need it
213+ use #versioned_path:: TrackingInto as _;
214+
215+ match #from_enum_ident {
216+ #variants
217+ }
218+ }
219+ }
220+ }
221+ } else {
222+ quote ! {
223+ #automatically_derived
224+ #allow_attribute
225+ impl #impl_generics :: core:: convert:: From <#from_module_ident:: #enum_ident #type_generics> for #for_module_ident:: #enum_ident #type_generics
226+ #where_clause
227+ {
228+ fn from( #from_enum_ident: #from_module_ident:: #enum_ident #type_generics) -> Self {
229+ match #from_enum_ident {
230+ #variants
231+ }
189232 }
190233 }
191234 }
192235 }
193236 } )
194237 }
195238
239+ /// Construct (if needed) and return the [`Enum`]'s generics extended with the tracking status
240+ /// generic type and where clause.
241+ ///
242+ /// This function lazily initializes the underlying field in [`Enum`]. Afterwards, the value is
243+ /// re-used without constructing the value from scratch over and over again. This is achieved
244+ /// through interior mutability via [`OnceCell`]. See [`Enum`]'s `tracking_generics` field for
245+ /// more details.
246+ pub fn get_tracking_generics ( & self , mod_gen_ctx : ModuleGenerationContext < ' _ > ) -> & Generics {
247+ self . tracking_generics . get_or_init ( || {
248+ let versioned_path = & * mod_gen_ctx. crates . versioned ;
249+
250+ let mut tracking_generics = self . generics . clone ( ) ;
251+ tracking_generics. params . push ( parse_quote ! { S } ) ;
252+ tracking_generics. make_where_clause ( ) . predicates . push (
253+ parse_quote ! { S : #versioned_path:: TrackingStatus + :: core:: default :: Default } ,
254+ ) ;
255+
256+ tracking_generics
257+ } )
258+ }
259+
196260 /// Returns whether any variant is deprecated in the provided `version`.
197261 fn is_any_variant_deprecated ( & self , version : & VersionDefinition ) -> bool {
198262 // First, iterate over all variants. The `any` function will return true
0 commit comments