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