@@ -39,11 +39,7 @@ type ReferenceField struct {
3939 ReferencedKind string
4040 ReferencedGroup string
4141 ReferencedVersion string
42- // RequiredSegments tracks whether each meaningful path segment is required.
43- // The slice aligns with the meaningful parts of FieldPath (excluding "properties", "items").
44- // For example, for FieldPath "properties.spec.properties.groupRef",
45- // RequiredSegments[0] indicates if "spec" is required, RequiredSegments[1] if "groupRef" is required.
46- RequiredSegments []bool
42+ RequiredSegments []bool
4743}
4844
4945type IndexerInfo struct {
@@ -473,17 +469,19 @@ func generateFieldExtractionCode(fields []ReferenceField) []jen.Code {
473469 // We need to convert this to: resource.Spec.<version>.GroupRef
474470 fieldAccessPath := buildFieldAccessPath (field .FieldPath )
475471
476- // Generate: if resource.Spec.<version>.GroupRef != nil && resource.Spec.<version>.GroupRef.Name != "" {
477- // keys = append(keys, types.NamespacedName{Name: resource.Spec.<version>.GroupRef.Name, Namespace: resource.Namespace}.String())
472+ // Nil check if conditions
473+ nilCheckCondition := buildNilCheckConditions (fieldAccessPath , field .RequiredSegments )
474+
475+ // Add check that the Name field is not empty
476+ condition := nilCheckCondition .Op ("&&" ).Add (
477+ jen .Id (fieldAccessPath ).Dot ("Name" ).Op ("!=" ).Lit ("" ),
478+ )
479+
480+ // Generate: if <nil checks> && resource.Spec.<version>.GroupRef.Name != "" {
481+ // keys = append(keys, types.NamespacedName{...}.String())
478482 // }
479483 code = append (code ,
480- jen .If (
481- jen .Op ("" ).Add (
482- jen .Id (fieldAccessPath ).Op ("!=" ).Nil (),
483- ).Op ("&&" ).Add (
484- jen .Id (fieldAccessPath ).Dot ("Name" ).Op ("!=" ).Lit ("" ),
485- ),
486- ).Block (
484+ jen .If (condition ).Block (
487485 jen .Id ("keys" ).Op ("=" ).Append (
488486 jen .Id ("keys" ),
489487 jen .Qual ("k8s.io/apimachinery/pkg/types" , "NamespacedName" ).Values (jen.Dict {
@@ -524,6 +522,71 @@ func capitalizeFirst(s string) string {
524522 return strings .ToUpper (s [:1 ]) + s [1 :]
525523}
526524
525+ // buildNilCheckConditions creates a compound nil check condition for a field access path
526+ // based on which segments are required (non-pointer) vs optional (pointer).
527+ // Examples:
528+ // - fieldAccessPath: "cluster.Spec.V20250312.GroupRef"
529+ // - requiredSegments: [false, true, false] (for Spec, V20250312, GroupRef - excludes variable name)
530+ func buildNilCheckConditions (fieldAccessPath string , requiredSegments []bool ) * jen.Statement {
531+ segments := strings .Split (fieldAccessPath , "." )
532+
533+ if len (requiredSegments ) == 0 {
534+ return buildDotChain (segments ).Op ("!=" ).Nil ()
535+ }
536+
537+ if len (requiredSegments ) != len (segments )- 1 {
538+ return buildDotChain (segments ).Op ("!=" ).Nil ()
539+ }
540+
541+ var conditions * jen.Statement
542+
543+ for i := 1 ; i < len (segments ); i ++ {
544+ requiredIndex := i - 1
545+
546+ // Skip if segment is required, means it's a non-pointer struct
547+ if requiredSegments [requiredIndex ] {
548+ continue
549+ }
550+
551+ // Special case: "Spec" is always a non-pointer struct in Kubernetes resources
552+ if segments [i ] == "Spec" {
553+ continue
554+ }
555+
556+ // Build the path up to this segment
557+ pathSegments := segments [:i + 1 ]
558+ nilCheck := buildDotChain (pathSegments ).Op ("!=" ).Nil ()
559+
560+ if conditions == nil {
561+ conditions = nilCheck
562+ } else {
563+ conditions = conditions .Op ("&&" ).Add (nilCheck )
564+ }
565+ }
566+
567+ // If no conditions were added (all segments required), check only the last field
568+ if conditions == nil {
569+ return buildDotChain (segments ).Op ("!=" ).Nil ()
570+ }
571+
572+ return conditions
573+ }
574+
575+ // buildDotChain creates a jen.Statement for a dot-separated path
576+ // For example: ["cluster", "Spec", "V20250312"] -> jen.Id("cluster").Dot("Spec").Dot("V20250312")
577+ func buildDotChain (segments []string ) * jen.Statement {
578+ if len (segments ) == 0 {
579+ return jen .Null ()
580+ }
581+
582+ stmt := jen .Id (segments [0 ])
583+ for i := 1 ; i < len (segments ); i ++ {
584+ stmt = stmt .Dot (segments [i ])
585+ }
586+
587+ return stmt
588+ }
589+
527590func generateMapFunc (f * jen.File , crdKind string , indexer IndexerInfo ) {
528591 f .Func ().
529592 Id (fmt .Sprintf ("New%sBy%sMapFunc" , crdKind , indexer .TargetKind )).
0 commit comments