opentofu/vendor/github.com/zclconf/go-cty/cty/element_iterator.go
2017-10-03 17:47:01 -07:00

192 lines
3.8 KiB
Go

package cty
import (
"sort"
"github.com/zclconf/go-cty/cty/set"
)
// ElementIterator is the interface type returned by Value.ElementIterator to
// allow the caller to iterate over elements of a collection-typed value.
//
// Its usage pattern is as follows:
//
// it := val.ElementIterator()
// for it.Next() {
// key, val := it.Element()
// // ...
// }
type ElementIterator interface {
Next() bool
Element() (key Value, value Value)
}
func canElementIterator(val Value) bool {
switch {
case val.ty.IsListType():
return true
case val.ty.IsMapType():
return true
case val.ty.IsSetType():
return true
case val.ty.IsTupleType():
return true
case val.ty.IsObjectType():
return true
default:
return false
}
}
func elementIterator(val Value) ElementIterator {
switch {
case val.ty.IsListType():
return &listElementIterator{
ety: val.ty.ElementType(),
vals: val.v.([]interface{}),
idx: -1,
}
case val.ty.IsMapType():
// We iterate the keys in a predictable lexicographical order so
// that results will always be stable given the same input map.
rawMap := val.v.(map[string]interface{})
keys := make([]string, 0, len(rawMap))
for key := range rawMap {
keys = append(keys, key)
}
sort.Strings(keys)
return &mapElementIterator{
ety: val.ty.ElementType(),
vals: rawMap,
keys: keys,
idx: -1,
}
case val.ty.IsSetType():
rawSet := val.v.(set.Set)
return &setElementIterator{
ety: val.ty.ElementType(),
setIt: rawSet.Iterator(),
}
case val.ty.IsTupleType():
return &tupleElementIterator{
etys: val.ty.TupleElementTypes(),
vals: val.v.([]interface{}),
idx: -1,
}
case val.ty.IsObjectType():
// We iterate the keys in a predictable lexicographical order so
// that results will always be stable given the same object type.
atys := val.ty.AttributeTypes()
keys := make([]string, 0, len(atys))
for key := range atys {
keys = append(keys, key)
}
sort.Strings(keys)
return &objectElementIterator{
atys: atys,
vals: val.v.(map[string]interface{}),
attrNames: keys,
idx: -1,
}
default:
panic("attempt to iterate on non-collection, non-tuple type")
}
}
type listElementIterator struct {
ety Type
vals []interface{}
idx int
}
func (it *listElementIterator) Element() (Value, Value) {
i := it.idx
return NumberIntVal(int64(i)), Value{
ty: it.ety,
v: it.vals[i],
}
}
func (it *listElementIterator) Next() bool {
it.idx++
return it.idx < len(it.vals)
}
type mapElementIterator struct {
ety Type
vals map[string]interface{}
keys []string
idx int
}
func (it *mapElementIterator) Element() (Value, Value) {
key := it.keys[it.idx]
return StringVal(key), Value{
ty: it.ety,
v: it.vals[key],
}
}
func (it *mapElementIterator) Next() bool {
it.idx++
return it.idx < len(it.keys)
}
type setElementIterator struct {
ety Type
setIt *set.Iterator
}
func (it *setElementIterator) Element() (Value, Value) {
val := Value{
ty: it.ety,
v: it.setIt.Value(),
}
return val, val
}
func (it *setElementIterator) Next() bool {
return it.setIt.Next()
}
type tupleElementIterator struct {
etys []Type
vals []interface{}
idx int
}
func (it *tupleElementIterator) Element() (Value, Value) {
i := it.idx
return NumberIntVal(int64(i)), Value{
ty: it.etys[i],
v: it.vals[i],
}
}
func (it *tupleElementIterator) Next() bool {
it.idx++
return it.idx < len(it.vals)
}
type objectElementIterator struct {
atys map[string]Type
vals map[string]interface{}
attrNames []string
idx int
}
func (it *objectElementIterator) Element() (Value, Value) {
key := it.attrNames[it.idx]
return StringVal(key), Value{
ty: it.atys[key],
v: it.vals[key],
}
}
func (it *objectElementIterator) Next() bool {
it.idx++
return it.idx < len(it.attrNames)
}