192 lines
3.8 KiB
Go
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)
|
||
|
}
|