2023-05-02 10:33:06 -05:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2023-02-14 08:08:47 -06:00
|
|
|
package jsonfunction
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
|
2023-08-17 07:45:11 -05:00
|
|
|
"github.com/placeholderplaceholderplaceholder/opentf/internal/tfdiags"
|
2023-02-14 08:08:47 -06:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/zclconf/go-cty/cty/function"
|
|
|
|
)
|
|
|
|
|
|
|
|
// FormatVersion represents the version of the json format and will be
|
|
|
|
// incremented for any change to this format that requires changes to a
|
|
|
|
// consuming parser.
|
|
|
|
const FormatVersion = "1.0"
|
|
|
|
|
|
|
|
// functions is the top-level object returned when exporting function signatures
|
|
|
|
type functions struct {
|
|
|
|
FormatVersion string `json:"format_version"`
|
|
|
|
Signatures map[string]*FunctionSignature `json:"function_signatures,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// FunctionSignature represents a function signature.
|
|
|
|
type FunctionSignature struct {
|
|
|
|
// Description is an optional human-readable description
|
|
|
|
// of the function
|
|
|
|
Description string `json:"description,omitempty"`
|
|
|
|
|
|
|
|
// ReturnTypes is the ctyjson representation of the function's
|
|
|
|
// return types based on supplying all parameters using
|
|
|
|
// dynamic types. Functions can have dynamic return types.
|
|
|
|
ReturnType cty.Type `json:"return_type"`
|
|
|
|
|
|
|
|
// Parameters describes the function's fixed positional parameters.
|
|
|
|
Parameters []*parameter `json:"parameters,omitempty"`
|
|
|
|
|
|
|
|
// VariadicParameter describes the function's variadic
|
|
|
|
// parameters, if any are supported.
|
|
|
|
VariadicParameter *parameter `json:"variadic_parameter,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func newFunctions() *functions {
|
|
|
|
signatures := make(map[string]*FunctionSignature)
|
|
|
|
return &functions{
|
|
|
|
FormatVersion: FormatVersion,
|
|
|
|
Signatures: signatures,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Marshal(f map[string]function.Function) ([]byte, tfdiags.Diagnostics) {
|
|
|
|
var diags tfdiags.Diagnostics
|
|
|
|
signatures := newFunctions()
|
|
|
|
|
|
|
|
for name, v := range f {
|
|
|
|
if name == "can" {
|
|
|
|
signatures.Signatures[name] = marshalCan(v)
|
|
|
|
} else if name == "try" {
|
|
|
|
signatures.Signatures[name] = marshalTry(v)
|
|
|
|
} else {
|
|
|
|
signature, err := marshalFunction(v)
|
|
|
|
if err != nil {
|
|
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
|
|
tfdiags.Error,
|
|
|
|
fmt.Sprintf("Failed to serialize function %q", name),
|
|
|
|
err.Error(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
signatures.Signatures[name] = signature
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if diags.HasErrors() {
|
|
|
|
return nil, diags
|
|
|
|
}
|
|
|
|
|
|
|
|
ret, err := json.Marshal(signatures)
|
|
|
|
if err != nil {
|
|
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
|
|
tfdiags.Error,
|
|
|
|
"Failed to serialize functions",
|
|
|
|
err.Error(),
|
|
|
|
))
|
|
|
|
return nil, diags
|
|
|
|
}
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func marshalFunction(f function.Function) (*FunctionSignature, error) {
|
|
|
|
var err error
|
|
|
|
var vp *parameter
|
|
|
|
if f.VarParam() != nil {
|
|
|
|
vp = marshalParameter(f.VarParam())
|
|
|
|
}
|
|
|
|
|
|
|
|
var p []*parameter
|
|
|
|
if len(f.Params()) > 0 {
|
|
|
|
p = marshalParameters(f.Params())
|
|
|
|
}
|
|
|
|
|
|
|
|
r, err := getReturnType(f)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &FunctionSignature{
|
|
|
|
Description: f.Description(),
|
|
|
|
ReturnType: r,
|
|
|
|
Parameters: p,
|
|
|
|
VariadicParameter: vp,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// marshalTry returns a static function signature for the try function.
|
|
|
|
// We need this exception because the function implementation uses capsule
|
|
|
|
// types that we can't marshal.
|
|
|
|
func marshalTry(try function.Function) *FunctionSignature {
|
|
|
|
return &FunctionSignature{
|
|
|
|
Description: try.Description(),
|
|
|
|
ReturnType: cty.DynamicPseudoType,
|
|
|
|
VariadicParameter: ¶meter{
|
|
|
|
Name: try.VarParam().Name,
|
|
|
|
Description: try.VarParam().Description,
|
|
|
|
IsNullable: try.VarParam().AllowNull,
|
|
|
|
Type: cty.DynamicPseudoType,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// marshalCan returns a static function signature for the can function.
|
|
|
|
// We need this exception because the function implementation uses capsule
|
|
|
|
// types that we can't marshal.
|
|
|
|
func marshalCan(can function.Function) *FunctionSignature {
|
|
|
|
return &FunctionSignature{
|
|
|
|
Description: can.Description(),
|
|
|
|
ReturnType: cty.Bool,
|
|
|
|
Parameters: []*parameter{
|
|
|
|
{
|
|
|
|
Name: can.Params()[0].Name,
|
|
|
|
Description: can.Params()[0].Description,
|
|
|
|
IsNullable: can.Params()[0].AllowNull,
|
|
|
|
Type: cty.DynamicPseudoType,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|