command/format: Support list/map blocks with dynamic-typed attrs
Our null-to-empty normalization was previously assuming these would always be collection types, but that isn't true when a block contains something dynamic since we must then use tuple or object types instead to properly represent all of the individual element types.
This commit is contained in:
parent
69772b11b1
commit
dd1fa322a7
|
@ -299,12 +299,8 @@ func (p *blockBodyDiffPrinter) writeNestedBlockDiffs(name string, blockS *config
|
|||
// For the sake of handling nested blocks, we'll treat a null list
|
||||
// the same as an empty list since the config language doesn't
|
||||
// distinguish these anyway.
|
||||
if old.IsNull() {
|
||||
old = cty.ListValEmpty(old.Type().ElementType())
|
||||
}
|
||||
if new.IsNull() {
|
||||
new = cty.ListValEmpty(new.Type().ElementType())
|
||||
}
|
||||
old = ctyNullBlockListAsEmpty(old)
|
||||
new = ctyNullBlockListAsEmpty(new)
|
||||
|
||||
oldItems := ctyCollectionValues(old)
|
||||
newItems := ctyCollectionValues(new)
|
||||
|
@ -358,12 +354,8 @@ func (p *blockBodyDiffPrinter) writeNestedBlockDiffs(name string, blockS *config
|
|||
// For the sake of handling nested blocks, we'll treat a null set
|
||||
// the same as an empty set since the config language doesn't
|
||||
// distinguish these anyway.
|
||||
if old.IsNull() {
|
||||
old = cty.SetValEmpty(old.Type().ElementType())
|
||||
}
|
||||
if new.IsNull() {
|
||||
new = cty.SetValEmpty(new.Type().ElementType())
|
||||
}
|
||||
old = ctyNullBlockSetAsEmpty(old)
|
||||
new = ctyNullBlockSetAsEmpty(new)
|
||||
|
||||
oldItems := ctyCollectionValues(old)
|
||||
newItems := ctyCollectionValues(new)
|
||||
|
@ -1101,3 +1093,46 @@ func ctyEnsurePathCapacity(path cty.Path, minExtra int) cty.Path {
|
|||
copy(newPath, path)
|
||||
return newPath
|
||||
}
|
||||
|
||||
// ctyNullBlockListAsEmpty either returns the given value verbatim if it is non-nil
|
||||
// or returns an empty value of a suitable type to serve as a placeholder for it.
|
||||
//
|
||||
// In particular, this function handles the special situation where a "list" is
|
||||
// actually represented as a tuple type where nested blocks contain
|
||||
// dynamically-typed values.
|
||||
func ctyNullBlockListAsEmpty(in cty.Value) cty.Value {
|
||||
if !in.IsNull() {
|
||||
return in
|
||||
}
|
||||
if ty := in.Type(); ty.IsListType() {
|
||||
return cty.ListValEmpty(ty.ElementType())
|
||||
}
|
||||
return cty.EmptyTupleVal // must need a tuple, then
|
||||
}
|
||||
|
||||
// ctyNullBlockMapAsEmpty either returns the given value verbatim if it is non-nil
|
||||
// or returns an empty value of a suitable type to serve as a placeholder for it.
|
||||
//
|
||||
// In particular, this function handles the special situation where a "map" is
|
||||
// actually represented as an object type where nested blocks contain
|
||||
// dynamically-typed values.
|
||||
func ctyNullBlockMapAsEmpty(in cty.Value) cty.Value {
|
||||
if !in.IsNull() {
|
||||
return in
|
||||
}
|
||||
if ty := in.Type(); ty.IsMapType() {
|
||||
return cty.MapValEmpty(ty.ElementType())
|
||||
}
|
||||
return cty.EmptyObjectVal // must need an object, then
|
||||
}
|
||||
|
||||
// ctyNullBlockSetAsEmpty either returns the given value verbatim if it is non-nil
|
||||
// or returns an empty value of a suitable type to serve as a placeholder for it.
|
||||
func ctyNullBlockSetAsEmpty(in cty.Value) cty.Value {
|
||||
if !in.IsNull() {
|
||||
return in
|
||||
}
|
||||
// Dynamically-typed attributes are not supported inside blocks backed by
|
||||
// sets, so our result here is always a set.
|
||||
return cty.SetValEmpty(in.Type().ElementType())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue