diff --git a/cty/gocty/helpers.go b/cty/gocty/helpers.go index 98e5ba1a..b14111c5 100644 --- a/cty/gocty/helpers.go +++ b/cty/gocty/helpers.go @@ -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 } diff --git a/cty/gocty/in.go b/cty/gocty/in.go index 6cb308b5..2ee5a02f 100644 --- a/cty/gocty/in.go +++ b/cty/gocty/in.go @@ -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 { diff --git a/cty/gocty/out.go b/cty/gocty/out.go index e9c2599e..29b4b148 100644 --- a/cty/gocty/out.go +++ b/cty/gocty/out.go @@ -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") @@ -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) @@ -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 @@ -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: @@ -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 } @@ -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 } @@ -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() { @@ -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()) @@ -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: @@ -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 } @@ -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 } @@ -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) @@ -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 } @@ -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() { @@ -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 } @@ -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, diff --git a/cty/gocty/out_test.go b/cty/gocty/out_test.go index 3d2e9b9b..392e48f4 100644 --- a/cty/gocty/out_test.go +++ b/cty/gocty/out_test.go @@ -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 { @@ -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 diff --git a/cty/gocty/type_implied.go b/cty/gocty/type_implied.go index ce4c8f1e..d45f5b0a 100644 --- a/cty/gocty/type_implied.go +++ b/cty/gocty/type_implied.go @@ -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: @@ -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 } @@ -58,7 +62,7 @@ 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 } @@ -66,21 +70,21 @@ func impliedType(rt reflect.Type, path cty.Path) (cty.Type, error) { // 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) } @@ -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 }