github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/views/json/function.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package json 5 6 import ( 7 "encoding/json" 8 9 "github.com/zclconf/go-cty/cty" 10 "github.com/zclconf/go-cty/cty/function" 11 ) 12 13 // Function is a description of the JSON representation of the signature of 14 // a function callable from the Terraform language. 15 type Function struct { 16 // Name is the leaf name of the function, without any namespace prefix. 17 Name string `json:"name"` 18 19 Params []FunctionParam `json:"params"` 20 VariadicParam *FunctionParam `json:"variadic_param,omitempty"` 21 22 // ReturnType is type constraint which is a static approximation of the 23 // possibly-dynamic return type of the function. 24 ReturnType json.RawMessage `json:"return_type"` 25 26 Description string `json:"description,omitempty"` 27 DescriptionKind string `json:"description_kind,omitempty"` 28 } 29 30 // FunctionParam represents a single parameter to a function, as represented 31 // by type Function. 32 type FunctionParam struct { 33 // Name is a name for the function which is used primarily for 34 // documentation purposes, because function arguments are positional 35 // and therefore don't appear directly in configuration source code. 36 Name string `json:"name"` 37 38 // Type is a type constraint which is a static approximation of the 39 // possibly-dynamic type of the parameter. Particular functions may 40 // have additional requirements that a type constraint alone cannot 41 // represent. 42 Type json.RawMessage `json:"type"` 43 44 // Maybe some of the other fields in function.Parameter would be 45 // interesting to describe here too, but we'll wait to see if there 46 // is a use-case first. 47 48 Description string `json:"description,omitempty"` 49 DescriptionKind string `json:"description_kind,omitempty"` 50 } 51 52 // DescribeFunction returns a description of the signature of the given cty 53 // function, as a pointer to this package's serializable type Function. 54 func DescribeFunction(name string, f function.Function) *Function { 55 ret := &Function{ 56 Name: name, 57 } 58 59 params := f.Params() 60 ret.Params = make([]FunctionParam, len(params)) 61 typeCheckArgs := make([]cty.Type, len(params), len(params)+1) 62 for i, param := range params { 63 ret.Params[i] = describeFunctionParam(¶m) 64 typeCheckArgs[i] = param.Type 65 } 66 if varParam := f.VarParam(); varParam != nil { 67 descParam := describeFunctionParam(varParam) 68 ret.VariadicParam = &descParam 69 typeCheckArgs = append(typeCheckArgs, varParam.Type) 70 } 71 72 retType, err := f.ReturnType(typeCheckArgs) 73 if err != nil { 74 // Getting an error when type-checking with exactly the type constraints 75 // the function called for is weird, so we'll just treat it as if it 76 // has a dynamic return type instead, for our purposes here. 77 // One reason this can happen is for a function which has a variadic 78 // parameter but has logic inside it which considers it invalid to 79 // specify exactly one argument for that parameter (since that's what 80 // we did in typeCheckArgs as an approximation of a valid call above.) 81 retType = cty.DynamicPseudoType 82 } 83 84 if raw, err := retType.MarshalJSON(); err != nil { 85 // Again, we'll treat any errors as if the function is dynamically 86 // typed because it would be weird to get here. 87 ret.ReturnType = json.RawMessage(`"dynamic"`) 88 } else { 89 ret.ReturnType = json.RawMessage(raw) 90 } 91 92 // We don't currently have any sense of descriptions for functions and 93 // their parameters, so we'll just leave those fields unpopulated for now. 94 95 return ret 96 } 97 98 func describeFunctionParam(p *function.Parameter) FunctionParam { 99 ret := FunctionParam{ 100 Name: p.Name, 101 } 102 103 if raw, err := p.Type.MarshalJSON(); err != nil { 104 // We'll treat any errors as if the function is dynamically 105 // typed because it would be weird to get here. 106 ret.Type = json.RawMessage(`"dynamic"`) 107 } else { 108 ret.Type = json.RawMessage(raw) 109 } 110 111 // We don't currently have any sense of descriptions for functions and 112 // their parameters, so we'll just leave those fields unpopulated for now. 113 114 return ret 115 }