# Tests that nested for-loop comprehensions with dynamic field assignments do
# not trigger a nil pointer dereference panic. The issue occurred in the
# evaluator's conjunct handling when processing nested iterations over structs.
# https://cuelang.org/issue/2235
// Test various orders
// https://cuelang.org/issues/2235
-- reductions.cue --
// Each of these tests triggers a slightly different path.

t1: {
	params: x.y
	x: y: {}
	for _ in x {
		if true {
			x: y: a: {}
		}
	}
}

t2: {
	out: X.y
	let X = m.x
	m: x: y: {}
	for v in m {
		if true {
			m: x: y: z: {}
		}
	}
}

t3: {
	out: foo.bar

	foo: bar: {}

	for _ in foo {
		for name, _ in foo {
			foo: (name): _
		}
	}
}
-- issue2235.cue --
package kubecluster

import (
	"strings"
	"net"
)

shorewallParams: close({
	[=~"^[A-Z0-9_]+$"]: [...net.IP]
})

#Cluster: {
	clusterName:     string
	shorewallPrefix: "KUBERNETES_\(strings.ToUpper(clusterName))"
}

clusters: [CLUSTER=string]: #Cluster & {
	clusterName: CLUSTER
}

#Cluster: {
	globalIngressControllers: [string]: #GlobalIngressController
	globalIngressControllers: admin: #GlobalIngressController & {
		class: "admin"
	}
	for _, c in globalIngressControllers {
		objects: c.objects
	}
}

for clusterName, cluster in clusters {
	for class, controller in cluster.globalIngressControllers {
		let svc = controller.objects.namespaced.ingress.Service["\(class)-nginx-ingress-controller"]

		shorewallParams: "\(cluster.shorewallPrefix)_INGRESS_\(strings.ToUpper(class))": svc.spec.externalIPs
	}
}

#GlobalIngressController: {
	class: string

	objects: namespaced: ingress: {
		Service: "\(class)-nginx-ingress-controller": {
			spec: {
				externalIPs: ["127.0.0.1"]
			}
		}
	}

	for kind, objs in objects.namespaced.ingress {
		for name, obj in objs {
			objects: namespaced: ingress: "\(kind)": "\(name)": metadata: labels: {
				"ingress-class": class
				stack:           "ingress"
				...
			}
		}
	}
}

clusters: foo: {}
-- out/compile --
--- issue2235.cue
{
  shorewallParams: close({
    [=~"^[A-Z0-9_]+$"]: [
      ...〈import;net〉.IP,
    ]
  })
  #Cluster: {
    clusterName: string
    shorewallPrefix: "KUBERNETES_\(〈import;strings〉.ToUpper(〈0;clusterName〉))"
  }
  clusters: {
    [string]: (〈1;#Cluster〉 & {
      clusterName: 〈1;-〉
    })
  }
  #Cluster: {
    globalIngressControllers: {
      [string]: 〈2;#GlobalIngressController〉
    }
    globalIngressControllers: {
      admin: (〈2;#GlobalIngressController〉 & {
        class: "admin"
      })
    }
    for _, c in 〈0;globalIngressControllers〉 {
      objects: 〈1;c〉.objects
    }
  }
  for clusterName, cluster in 〈0;clusters〉 {
    for class, controller in 〈1;cluster〉.globalIngressControllers {
      let svc#1multi = 〈1;controller〉.objects.namespaced.ingress.Service["\(〈1;class〉)-nginx-ingress-controller"]
      shorewallParams: {
        "\(〈4;cluster〉.shorewallPrefix)_INGRESS_\(〈import;strings〉.ToUpper(〈2;class〉))": 〈1;let svc#1〉.spec.externalIPs
      }
    }
  }
  #GlobalIngressController: {
    class: string
    objects: {
      namespaced: {
        ingress: {
          Service: {
            "\(〈4;class〉)-nginx-ingress-controller": {
              spec: {
                externalIPs: [
                  "127.0.0.1",
                ]
              }
            }
          }
        }
      }
    }
    for kind, objs in 〈0;objects〉.namespaced.ingress {
      for name, obj in 〈1;objs〉 {
        objects: {
          namespaced: {
            ingress: {
              "\(〈6;kind〉)": {
                "\(〈5;name〉)": {
                  metadata: {
                    labels: {
                      "ingress-class": 〈11;class〉
                      stack: "ingress"
                      ...
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  clusters: {
    foo: {}
  }
}
--- reductions.cue
{
  t1: {
    params: 〈0;x〉.y
    x: {
      y: {}
    }
    for _, _ in 〈0;x〉 {
      if true {
        x: {
          y: {
            a: {}
          }
        }
      }
    }
  }
  t2: {
    out: 〈0;let X#2〉.y
    let X#2 = 〈0;m〉.x
    m: {
      x: {
        y: {}
      }
    }
    for _, v in 〈0;m〉 {
      if true {
        m: {
          x: {
            y: {
              z: {}
            }
          }
        }
      }
    }
  }
  t3: {
    out: 〈0;foo〉.bar
    foo: {
      bar: {}
    }
    for _, _ in 〈0;foo〉 {
      for name, _ in 〈2;foo〉 {
        foo: {
          〈2;name〉: _
        }
      }
    }
  }
}
