Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FromCtyValueTagged #181

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cty/gocty/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ var stringType = reflect.TypeOf("")
//
// This function will panic if two fields within the struct are tagged with
// the same cty attribute name.
func structTagIndices(st reflect.Type) map[string]int {
func structTagIndices(st reflect.Type, tag string) map[string]int {
ct := st.NumField()
ret := make(map[string]int, ct)

for i := 0; i < ct; i++ {
field := st.Field(i)
attrName := field.Tag.Get("cty")
attrName := field.Tag.Get(tag)
if attrName != "" {
ret[attrName] = i
}
Expand Down
2 changes: 1 addition & 1 deletion cty/gocty/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ func toCtyObject(val reflect.Value, attrTypes map[string]cty.Type, path cty.Path
// path to give us a place to put our GetAttr step.
path = append(path, cty.PathStep(nil))

attrFields := structTagIndices(val.Type())
attrFields := structTagIndices(val.Type(), "cty")

vals := make(map[string]cty.Value, len(attrTypes))
for k, at := range attrTypes {
Expand Down
48 changes: 26 additions & 22 deletions cty/gocty/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import (
// The function will panic if given a non-pointer as the Go value target,
// since that is considered to be a bug in the calling program.
func FromCtyValue(val cty.Value, target interface{}) error {
return FromCtyValueTagged(val, target, "cty")
}

func FromCtyValueTagged(val cty.Value, target interface{}, tag string) error {
tVal := reflect.ValueOf(target)
if tVal.Kind() != reflect.Ptr {
panic("target value is not a pointer")
Expand All @@ -40,10 +44,10 @@ func FromCtyValue(val cty.Value, target interface{}) error {
// unused capacity on the end of it, depending on how deeply-recursive
// the given cty.Value is.
path := make(cty.Path, 0)
return fromCtyValue(val, tVal, path)
return fromCtyValue(val, tVal, path, tag)
}

func fromCtyValue(val cty.Value, target reflect.Value, path cty.Path) error {
func fromCtyValue(val cty.Value, target reflect.Value, path cty.Path, tag string) error {
ty := val.Type()

deepTarget := fromCtyPopulatePtr(target, false)
Expand Down Expand Up @@ -89,17 +93,17 @@ func fromCtyValue(val cty.Value, target reflect.Value, path cty.Path) error {

switch {
case ty.IsListType():
return fromCtyList(val, target, path)
return fromCtyList(val, target, path, tag)
case ty.IsMapType():
return fromCtyMap(val, target, path)
return fromCtyMap(val, target, path, tag)
case ty.IsSetType():
return fromCtySet(val, target, path)
return fromCtySet(val, target, path, tag)
case ty.IsObjectType():
return fromCtyObject(val, target, path)
return fromCtyObject(val, target, path, tag)
case ty.IsTupleType():
return fromCtyTuple(val, target, path)
return fromCtyTuple(val, target, path, tag)
case ty.IsCapsuleType():
return fromCtyCapsule(val, target, path)
return fromCtyCapsule(val, target, path, tag)
}

// We should never fall out here; reaching here indicates a bug in this
Expand Down Expand Up @@ -251,7 +255,7 @@ func fromCtyString(val cty.Value, target reflect.Value, path cty.Path) error {
}
}

func fromCtyList(val cty.Value, target reflect.Value, path cty.Path) error {
func fromCtyList(val cty.Value, target reflect.Value, path cty.Path, tag string) error {
switch target.Kind() {

case reflect.Slice:
Expand All @@ -273,7 +277,7 @@ func fromCtyList(val cty.Value, target reflect.Value, path cty.Path) error {
}

targetElem := tv.Index(i)
err = fromCtyValue(val, targetElem, path)
err = fromCtyValue(val, targetElem, path, tag)
if err != nil {
return true
}
Expand Down Expand Up @@ -310,7 +314,7 @@ func fromCtyList(val cty.Value, target reflect.Value, path cty.Path) error {
}

targetElem := target.Index(i)
err = fromCtyValue(val, targetElem, path)
err = fromCtyValue(val, targetElem, path, tag)
if err != nil {
return true
}
Expand All @@ -332,7 +336,7 @@ func fromCtyList(val cty.Value, target reflect.Value, path cty.Path) error {
}
}

func fromCtyMap(val cty.Value, target reflect.Value, path cty.Path) error {
func fromCtyMap(val cty.Value, target reflect.Value, path cty.Path, tag string) error {

switch target.Kind() {

Expand All @@ -356,7 +360,7 @@ func fromCtyMap(val cty.Value, target reflect.Value, path cty.Path) error {
ks := key.AsString()

targetElem := reflect.New(et)
err = fromCtyValue(val, targetElem, path)
err = fromCtyValue(val, targetElem, path, tag)

tv.SetMapIndex(reflect.ValueOf(ks), targetElem.Elem())

Expand All @@ -377,7 +381,7 @@ func fromCtyMap(val cty.Value, target reflect.Value, path cty.Path) error {
}
}

func fromCtySet(val cty.Value, target reflect.Value, path cty.Path) error {
func fromCtySet(val cty.Value, target reflect.Value, path cty.Path, tag string) error {
switch target.Kind() {

case reflect.Slice:
Expand All @@ -393,7 +397,7 @@ func fromCtySet(val cty.Value, target reflect.Value, path cty.Path) error {
var err error
val.ForEachElement(func(key cty.Value, val cty.Value) bool {
targetElem := tv.Index(i)
err = fromCtyValue(val, targetElem, path)
err = fromCtyValue(val, targetElem, path, tag)
if err != nil {
return true
}
Expand Down Expand Up @@ -422,7 +426,7 @@ func fromCtySet(val cty.Value, target reflect.Value, path cty.Path) error {
var err error
val.ForEachElement(func(key cty.Value, val cty.Value) bool {
targetElem := target.Index(i)
err = fromCtyValue(val, targetElem, path)
err = fromCtyValue(val, targetElem, path, tag)
if err != nil {
return true
}
Expand All @@ -444,14 +448,14 @@ func fromCtySet(val cty.Value, target reflect.Value, path cty.Path) error {
}
}

func fromCtyObject(val cty.Value, target reflect.Value, path cty.Path) error {
func fromCtyObject(val cty.Value, target reflect.Value, path cty.Path, tag string) error {

switch target.Kind() {

case reflect.Struct:

attrTypes := val.Type().AttributeTypes()
targetFields := structTagIndices(target.Type())
targetFields := structTagIndices(target.Type(), tag)

path = append(path, nil)

Expand Down Expand Up @@ -482,7 +486,7 @@ func fromCtyObject(val cty.Value, target reflect.Value, path cty.Path) error {
ev := val.GetAttr(k)

targetField := target.Field(fieldIdx)
err := fromCtyValue(ev, targetField, path)
err := fromCtyValue(ev, targetField, path, tag)
if err != nil {
return err
}
Expand All @@ -498,7 +502,7 @@ func fromCtyObject(val cty.Value, target reflect.Value, path cty.Path) error {
}
}

func fromCtyTuple(val cty.Value, target reflect.Value, path cty.Path) error {
func fromCtyTuple(val cty.Value, target reflect.Value, path cty.Path, tag string) error {

switch target.Kind() {

Expand All @@ -521,7 +525,7 @@ func fromCtyTuple(val cty.Value, target reflect.Value, path cty.Path) error {
ev := val.Index(cty.NumberIntVal(int64(i)))

targetField := target.Field(i)
err := fromCtyValue(ev, targetField, path)
err := fromCtyValue(ev, targetField, path, tag)
if err != nil {
return err
}
Expand All @@ -537,7 +541,7 @@ func fromCtyTuple(val cty.Value, target reflect.Value, path cty.Path) error {
}
}

func fromCtyCapsule(val cty.Value, target reflect.Value, path cty.Path) error {
func fromCtyCapsule(val cty.Value, target reflect.Value, path cty.Path, tag string) error {

if target.Kind() == reflect.Ptr {
// Walk through indirection until we get to the last pointer,
Expand Down
29 changes: 29 additions & 0 deletions cty/gocty/out_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,31 @@ func TestOut(t *testing.T) {
}
}

func TestOutOtherTags(t *testing.T) {
taggedCty, taggedOther := new(testStructManyTag), new(testStructManyTag)
err := FromCtyValueTagged(cty.ObjectVal(map[string]cty.Value{
"name": cty.StringVal("Eva"),
}), taggedCty, "cty")
if err != nil {
t.Fatalf("FromCtyValueTagged returned error: %s", err)
}

err = FromCtyValueTagged(cty.ObjectVal(map[string]cty.Value{
"another_name": cty.StringVal("Alice"),
}), taggedOther, "other")
if err != nil {
t.Fatalf("FromCtyValueTagged returned error: %s", err)
}

if taggedCty.Name != "Eva" {
t.Fatalf("taggedCty name mismatch: %s != Eva!", taggedCty.Name)
}

if taggedOther.Name != "Alice" {
t.Fatalf("taggedCty name mismatch: %s != Alice!", taggedOther.Name)
}
}

type testOutAssertFunc func(cty.Value, reflect.Type, interface{}, *testing.T)

func testOutAssertPtrVal(want interface{}) testOutAssertFunc {
Expand Down Expand Up @@ -409,6 +434,10 @@ type testStruct struct {
Number *int `cty:"number"`
}

type testStructManyTag struct {
Name string `cty:"name" other:"another_name"`
}

type testTupleStruct struct {
Name string
Number int
Expand Down
22 changes: 13 additions & 9 deletions cty/gocty/type_implied.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,20 @@ import (
// type, because it cannot know the capsule types supported by the calling
// program.
func ImpliedType(gv interface{}) (cty.Type, error) {
return ImpliedTypeTagged(gv, "cty")
}

func ImpliedTypeTagged(gv interface{}, tag string) (cty.Type, error) {
rt := reflect.TypeOf(gv)
var path cty.Path
return impliedType(rt, path)
return impliedType(rt, path, tag)
}

func impliedType(rt reflect.Type, path cty.Path) (cty.Type, error) {
func impliedType(rt reflect.Type, path cty.Path, tag string) (cty.Type, error) {
switch rt.Kind() {

case reflect.Ptr:
return impliedType(rt.Elem(), path)
return impliedType(rt.Elem(), path, tag)

// Primitive types
case reflect.Bool:
Expand All @@ -48,7 +52,7 @@ func impliedType(rt reflect.Type, path cty.Path) (cty.Type, error) {
// Collection types
case reflect.Slice:
path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.Number)})
ety, err := impliedType(rt.Elem(), path)
ety, err := impliedType(rt.Elem(), path, tag)
if err != nil {
return cty.NilType, err
}
Expand All @@ -58,29 +62,29 @@ func impliedType(rt reflect.Type, path cty.Path) (cty.Type, error) {
return cty.NilType, path.NewErrorf("no cty.Type for %s (must have string keys)", rt)
}
path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.String)})
ety, err := impliedType(rt.Elem(), path)
ety, err := impliedType(rt.Elem(), path, tag)
if err != nil {
return cty.NilType, err
}
return cty.Map(ety), nil

// Structural types
case reflect.Struct:
return impliedStructType(rt, path)
return impliedStructType(rt, path, tag)

default:
return cty.NilType, path.NewErrorf("no cty.Type for %s", rt)
}
}

func impliedStructType(rt reflect.Type, path cty.Path) (cty.Type, error) {
func impliedStructType(rt reflect.Type, path cty.Path, tag string) (cty.Type, error) {
if valueType.AssignableTo(rt) {
// Special case: cty.Value represents cty.DynamicPseudoType, for
// type conformance checking.
return cty.DynamicPseudoType, nil
}

fieldIdxs := structTagIndices(rt)
fieldIdxs := structTagIndices(rt, tag)
if len(fieldIdxs) == 0 {
return cty.NilType, path.NewErrorf("no cty.Type for %s (no cty field tags)", rt)
}
Expand All @@ -95,7 +99,7 @@ func impliedStructType(rt reflect.Type, path cty.Path) (cty.Type, error) {
path[len(path)-1] = cty.GetAttrStep{Name: k}

ft := rt.Field(fi).Type
aty, err := impliedType(ft, path)
aty, err := impliedType(ft, path, tag)
if err != nil {
return cty.NilType, err
}
Expand Down