package main import ( "fmt" "reflect" "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" ) var typeType = cty.Capsule("type", reflect.TypeOf(cty.NilType)) var typeEvalCtx = &hcl.EvalContext{ Variables: map[string]cty.Value{ "string": wrapTypeType(cty.String), "bool": wrapTypeType(cty.Bool), "number": wrapTypeType(cty.Number), "any": wrapTypeType(cty.DynamicPseudoType), }, Functions: map[string]function.Function{ "list": function.New(&function.Spec{ Params: []function.Parameter{ { Name: "element_type", Type: typeType, }, }, Type: function.StaticReturnType(typeType), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { ety := unwrapTypeType(args[0]) ty := cty.List(ety) return wrapTypeType(ty), nil }, }), "set": function.New(&function.Spec{ Params: []function.Parameter{ { Name: "element_type", Type: typeType, }, }, Type: function.StaticReturnType(typeType), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { ety := unwrapTypeType(args[0]) ty := cty.Set(ety) return wrapTypeType(ty), nil }, }), "map": function.New(&function.Spec{ Params: []function.Parameter{ { Name: "element_type", Type: typeType, }, }, Type: function.StaticReturnType(typeType), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { ety := unwrapTypeType(args[0]) ty := cty.Map(ety) return wrapTypeType(ty), nil }, }), "tuple": function.New(&function.Spec{ Params: []function.Parameter{ { Name: "element_types", Type: cty.List(typeType), }, }, Type: function.StaticReturnType(typeType), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { etysVal := args[0] etys := make([]cty.Type, 0, etysVal.LengthInt()) for it := etysVal.ElementIterator(); it.Next(); { _, wrapEty := it.Element() etys = append(etys, unwrapTypeType(wrapEty)) } ty := cty.Tuple(etys) return wrapTypeType(ty), nil }, }), "object": function.New(&function.Spec{ Params: []function.Parameter{ { Name: "attribute_types", Type: cty.Map(typeType), }, }, Type: function.StaticReturnType(typeType), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { atysVal := args[0] atys := make(map[string]cty.Type) for it := atysVal.ElementIterator(); it.Next(); { nameVal, wrapAty := it.Element() name := nameVal.AsString() atys[name] = unwrapTypeType(wrapAty) } ty := cty.Object(atys) return wrapTypeType(ty), nil }, }), }, } func evalTypeExpr(expr hcl.Expression) (cty.Type, hcl.Diagnostics) { result, diags := expr.Value(typeEvalCtx) if result.IsNull() { return cty.DynamicPseudoType, diags } if !result.Type().Equals(typeType) { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid type expression", Detail: fmt.Sprintf("A type is required, not %s.", result.Type().FriendlyName()), }) return cty.DynamicPseudoType, diags } return unwrapTypeType(result), diags } func wrapTypeType(ty cty.Type) cty.Value { return cty.CapsuleVal(typeType, &ty) } func unwrapTypeType(val cty.Value) cty.Type { return *(val.EncapsulatedValue().(*cty.Type)) }