plans/objchange: Decompose assertNestedBlockCompatible

The main function is now just a jump table into a separate function for
each nesting mode. The observable behavior is unchanged.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
Martin Atkins 2025-01-08 10:32:46 -08:00
parent 55ff663aff
commit d49f997b65

View File

@ -95,31 +95,49 @@ func assertAttributeCompatible(plannedV, actualV cty.Value, attrS *configschema.
}
func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
var errs []error
switch blockS.Nesting {
case configschema.NestingSingle, configschema.NestingGroup:
return assertNestedBlockCompatibleSingle(plannedV, actualV, blockS, path)
case configschema.NestingList:
return assertNestedBlockCompatibleList(plannedV, actualV, blockS, path)
case configschema.NestingMap:
return assertNestedBlockCompatibleMap(plannedV, actualV, blockS, path)
case configschema.NestingSet:
return assertNestedBlockCompatibleSet(plannedV, actualV, blockS, path)
default:
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
}
}
func assertNestedBlockCompatibleSingle(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
// If an unknown block placeholder was present then the placeholder
// may have expanded out into zero blocks, which is okay.
if !plannedV.IsKnown() && actualV.IsNull() {
break
return nil
}
var errs []error
moreErrs := assertObjectCompatible(&blockS.Block, plannedV, actualV, path)
errs = append(errs, moreErrs...)
case configschema.NestingList:
return errs
}
func assertNestedBlockCompatibleList(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
// A NestingList might either be a list or a tuple, depending on
// whether there are dynamically-typed attributes inside. However,
// both support a similar-enough API that we can treat them the
// same for our purposes here.
if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
break
return nil
}
var errs []error
plannedL := plannedV.LengthInt()
actualL := actualV.LengthInt()
if plannedL != actualL {
errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
break
return errs
}
for it := plannedV.ElementIterator(); it.Next(); {
idx, plannedEV := it.Element()
@ -130,11 +148,17 @@ func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configsche
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx}))
errs = append(errs, moreErrs...)
}
case configschema.NestingMap:
return errs
}
func assertNestedBlockCompatibleMap(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
// A NestingMap might either be a map or an object, depending on
// whether there are dynamically-typed attributes inside, but
// that's decided statically and so both values will have the same
// kind.
var errs []error
if plannedV.Type().IsObjectType() {
plannedAtys := plannedV.Type().AttributeTypes()
actualAtys := actualV.Type().AttributeTypes()
@ -159,13 +183,13 @@ func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configsche
}
} else {
if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
break
return errs
}
plannedL := plannedV.LengthInt()
actualL := actualV.LengthInt()
if plannedL != actualL && plannedV.IsKnown() { // new blocks may appear if unknown blocks were present in the plan
errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
break
return errs
}
for it := plannedV.ElementIterator(); it.Next(); {
idx, plannedEV := it.Element()
@ -177,9 +201,13 @@ func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configsche
errs = append(errs, moreErrs...)
}
}
case configschema.NestingSet:
return errs
}
func assertNestedBlockCompatibleSet(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
break
return nil
}
if !plannedV.IsKnown() {
@ -189,12 +217,14 @@ func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configsche
// a dynamic block. Unfortunately this means we can't do our
// usual checks in this case without generating false
// negatives.
break
return nil
}
var errs []error
setErrs := assertSetValuesCompatible(plannedV, actualV, path, func(plannedEV, actualEV cty.Value) bool {
errs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: actualEV}))
return len(errs) == 0
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: actualEV}))
return len(moreErrs) == 0
})
errs = append(errs, setErrs...)
@ -206,9 +236,6 @@ func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configsche
if plannedL < actualL {
errs = append(errs, path.NewErrorf("block set length changed from %d to %d", plannedL, actualL))
}
default:
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
}
return errs
}