package cty import ( "sort" "github.com/hashicorp/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.IsMarked(): return false 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 { val.assertUnmarked() 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) }