@@ -31,3 +31,107 @@ func dynamicFixup(wantType cty.Type) conversion {
3131func dynamicPassthrough (in cty.Value , path cty.Path ) (cty.Value , error ) {
3232 return in , nil
3333}
34+
35+ // dynamicReplace aims to return the out type unchanged, but if it finds a
36+ // dynamic type either directly or in any descendent elements it replaces them
37+ // with the equivalent type from in.
38+ //
39+ // This function assumes that in and out are compatible from a Convert
40+ // perspective, and will panic if it finds that they are not. For example if
41+ // in is an object and out is a map, this function will still attempt to iterate
42+ // through both as if they were the same.
43+ func dynamicReplace (in , out cty.Type ) cty.Type {
44+ if in == cty .DynamicPseudoType || in == cty .NilType {
45+ // Short circuit this case, there's no point worrying about this if in
46+ // is a dynamic type or a nil type. Out is the best we can do.
47+ return out
48+ }
49+
50+ switch {
51+ case out == cty .DynamicPseudoType :
52+ // So replace out with in.
53+ return in
54+ case out .IsPrimitiveType (), out .IsCapsuleType ():
55+ // out is not dynamic and it doesn't contain descendent elements so just
56+ // return it unchanged.
57+ return out
58+ case out .IsMapType ():
59+ var elemType cty.Type
60+
61+ // Maps are compatible with other maps or objects.
62+ if in .IsMapType () {
63+ elemType = dynamicReplace (in .ElementType (), out .ElementType ())
64+ }
65+
66+ if in .IsObjectType () {
67+ var types []cty.Type
68+ for _ , t := range in .AttributeTypes () {
69+ types = append (types , t )
70+ }
71+ unifiedType , _ := unify (types , true )
72+ elemType = dynamicReplace (unifiedType , out .ElementType ())
73+ }
74+
75+ return cty .Map (elemType )
76+ case out .IsObjectType ():
77+ // Objects are compatible with other objects and maps.
78+ outTypes := map [string ]cty.Type {}
79+ if in .IsMapType () {
80+ for attr , attrType := range out .AttributeTypes () {
81+ outTypes [attr ] = dynamicReplace (in .ElementType (), attrType )
82+ }
83+ }
84+
85+ if in .IsObjectType () {
86+ for attr , attrType := range out .AttributeTypes () {
87+ if ! in .HasAttribute (attr ) {
88+ // If in does not have this attribute, then it is an
89+ // optional attribute and there is nothing we can do except
90+ // to return the type from out even if it is dynamic.
91+ outTypes [attr ] = attrType
92+ continue
93+ }
94+ outTypes [attr ] = dynamicReplace (in .AttributeType (attr ), attrType )
95+ }
96+ }
97+
98+ return cty .Object (outTypes )
99+ case out .IsSetType ():
100+ var elemType cty.Type
101+
102+ // Sets are compatible with other sets, lists, tuples.
103+ if in .IsSetType () || in .IsListType () {
104+ elemType = dynamicReplace (in .ElementType (), out .ElementType ())
105+ }
106+
107+ if in .IsTupleType () {
108+ unifiedType , _ := unify (in .TupleElementTypes (), true )
109+ elemType = dynamicReplace (unifiedType , out .ElementType ())
110+ }
111+
112+ return cty .Set (elemType )
113+ case out .IsListType ():
114+ var elemType cty.Type
115+
116+ // Lists are compatible with other lists, sets, and tuples.
117+ if in .IsSetType () || in .IsListType () {
118+ elemType = dynamicReplace (in .ElementType (), out .ElementType ())
119+ }
120+
121+ if in .IsTupleType () {
122+ unifiedType , _ := unify (in .TupleElementTypes (), true )
123+ elemType = dynamicReplace (unifiedType , out .ElementType ())
124+ }
125+
126+ return cty .List (elemType )
127+ case out .IsTupleType ():
128+ // Tuples are only compatible with other tuples
129+ var types []cty.Type
130+ for ix := 0 ; ix < len (out .TupleElementTypes ()); ix ++ {
131+ types = append (types , dynamicReplace (in .TupleElementType (ix ), out .TupleElementType (ix )))
132+ }
133+ return cty .Tuple (types )
134+ default :
135+ panic ("unrecognized type " + out .FriendlyName ())
136+ }
137+ }
0 commit comments