@@ -45,6 +45,18 @@ const (
4545 mode = packages .NeedTypes | packages .NeedName | packages .NeedSyntax | packages .NeedTypesInfo | packages .NeedImports
4646)
4747
48+ type partialImport struct {
49+ Name string
50+ Path string
51+ }
52+
53+ var packagePartials = map [string ]partialImport {
54+ "k8s.io/api/core/v1" : {
55+ Name : "applycorev1" ,
56+ Path : "k8s.io/client-go/applyconfigurations/core/v1" ,
57+ },
58+ }
59+
4860func Cmd () * cobra.Command {
4961 var outFlag string
5062 var headerFlag string
@@ -127,7 +139,7 @@ func (g *Generator) Generate(t types.Type) []ast.Node {
127139 // reference needs to be a pointer or changed to a newly generated
128140 // type. Partialization of (anonymous) structs, is generation of a new
129141 // struct type.
130- partialized := g .partialize (named .Underlying ())
142+ partialized := g .partialize (named .Underlying (), nil )
131143
132144 var params * ast.FieldList
133145 if named .TypeParams ().Len () > 0 {
@@ -185,22 +197,22 @@ func (g *Generator) typeToNode(t types.Type) ast.Node {
185197 return node
186198}
187199
188- func (g * Generator ) partialize (t types.Type ) types.Type {
200+ func (g * Generator ) partialize (t types.Type , tag * StructTag ) types.Type {
189201 // TODO cache me.
190202
191203 switch t := t .(type ) {
192204 case * types.Basic , * types.Interface , * types.Alias :
193205 return t
194206 case * types.Pointer :
195- return types .NewPointer (g .partialize (t .Elem ()))
207+ return types .NewPointer (g .partialize (t .Elem (), tag ))
196208 case * types.Map :
197- return types .NewMap (t .Key (), g .partialize (t .Elem ()))
209+ return types .NewMap (t .Key (), g .partialize (t .Elem (), nil ))
198210 case * types.Slice :
199- return types .NewSlice (g .partialize (t .Elem ()))
211+ return types .NewSlice (g .partialize (t .Elem (), tag ))
200212 case * types.Struct :
201213 return g .partializeStruct (t )
202214 case * types.Named :
203- return g .partializeNamed (t )
215+ return g .partializeNamed (t , tag )
204216 case * types.TypeParam :
205217 return t // TODO this isn't super easy to fully support without a lot of additional information......
206218 default :
@@ -214,7 +226,7 @@ func (g *Generator) partializeStruct(t *types.Struct) *types.Struct {
214226 for i := 0 ; i < t .NumFields (); i ++ {
215227 field := t .Field (i )
216228
217- partialized := g .partialize (field .Type ())
229+ partialized := g .partialize (field .Type (), parseTag ( t . Tag ( i )). Named ( "partial" ) )
218230 switch partialized .Underlying ().(type ) {
219231 case * types.Basic :
220232 partialized = types .NewPointer (partialized )
@@ -240,7 +252,7 @@ func (g *Generator) partializeStruct(t *types.Struct) *types.Struct {
240252 return types .NewStruct (fields , tags )
241253}
242254
243- func (g * Generator ) partializeNamed (t * types.Named ) types.Type {
255+ func (g * Generator ) partializeNamed (t * types.Named , tag * StructTag ) types.Type {
244256 // If there exists a Partial___ variant of the type, we'll use this. This
245257 // allows Partial structs to references partial structs from other packages
246258 // that contain Partialized structs and/or allows end users to provide
@@ -266,6 +278,38 @@ func (g *Generator) partializeNamed(t *types.Named) types.Type {
266278 // NB: This check MUST match the check in FindAllNames.
267279 isPartialized := inPkg && ! IsType [* types.Basic ](t .Underlying ())
268280 if ! isPartialized {
281+ if tag != nil {
282+ for _ , value := range tag .Values {
283+ if value == "builtin" {
284+ path := t .Obj ().Pkg ().Path ()
285+ if override , ok := packagePartials [path ]; ok {
286+ var args []types.Type
287+ for i := 0 ; i < t .TypeArgs ().Len (); i ++ {
288+ args = append (args , g .partialize (t .TypeArgs ().At (i ), nil ))
289+ }
290+
291+ params := make ([]* types.TypeParam , t .TypeParams ().Len ())
292+ for i := 0 ; i < t .TypeParams ().Len (); i ++ {
293+ param := t .TypeParams ().At (i )
294+ // Might need to clone the typename here
295+ params [i ] = types .NewTypeParam (param .Obj (), param .Constraint ())
296+ }
297+
298+ named := types .NewNamed (types .NewTypeName (0 , types .NewPackage (override .Path , override .Name ), t .Obj ().Name ()+ "ApplyConfiguration" , t .Underlying ()), t .Underlying (), nil )
299+ if len (args ) < 1 {
300+ return named
301+ }
302+ named .SetTypeParams (params )
303+ result , err := types .Instantiate (nil , named , args , true )
304+ if err != nil {
305+ panic (err )
306+ }
307+ return result
308+ }
309+ }
310+ }
311+ }
312+
269313 // If we haven't partialized this type, there's nothing we can do. Noop.
270314 return t
271315 }
@@ -277,7 +321,7 @@ func (g *Generator) partializeNamed(t *types.Named) types.Type {
277321
278322 var args []types.Type
279323 for i := 0 ; i < t .TypeArgs ().Len (); i ++ {
280- args = append (args , g .partialize (t .TypeArgs ().At (i )))
324+ args = append (args , g .partialize (t .TypeArgs ().At (i ), nil ))
281325 }
282326
283327 params := make ([]* types.TypeParam , t .TypeParams ().Len ())
@@ -369,6 +413,14 @@ func GeneratePartial(pkg *packages.Package, structName string, outPackage string
369413 if pkg , ok := originalImports [parent .Name ]; ok {
370414 imports [parent .Name ] = pkg
371415 }
416+
417+ for _ , pkg := range packagePartials {
418+ if pkg .Name == parent .Name {
419+ // NB: we don't actually use the import name below, so
420+ // we just set it to empty here
421+ imports [pkg .Name ] = types .NewPackage (pkg .Path , "" )
422+ }
423+ }
372424 }
373425 return true
374426 })
@@ -524,6 +576,9 @@ func EnsureOmitEmpty(tag string) string {
524576
525577 var out strings.Builder
526578 for i , p := range parts {
579+ if p .Name == "partial" {
580+ continue
581+ }
527582 if i > 0 {
528583 _ , _ = out .WriteRune (' ' )
529584 }
@@ -547,7 +602,18 @@ func IsType[T types.Type](typ types.Type) bool {
547602
548603var tagRe = regexp .MustCompile (`([a-z_]+):"([^"]+)"` )
549604
550- func parseTag (tag string ) []StructTag {
605+ type StructTags []StructTag
606+
607+ func (t StructTags ) Named (name string ) * StructTag {
608+ for i , tag := range t {
609+ if tag .Name == name {
610+ return & t [i ]
611+ }
612+ }
613+ return nil
614+ }
615+
616+ func parseTag (tag string ) StructTags {
551617 matches := tagRe .FindAllStringSubmatch (tag , - 1 )
552618
553619 tags := make ([]StructTag , len (matches ))
0 commit comments