functions

package
v0.40.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2026 License: Apache-2.0 Imports: 48 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AddHeaderFunc = function.New(&function.Spec{
	Description: "Returns a new response with the given header value appended",
	Params: []function.Parameter{
		{Name: "response", Type: cty.DynamicPseudoType},
		{Name: "name", Type: cty.String},
		{Name: "value", Type: cty.String},
	},
	Type: function.StaticReturnType(types.HTTPResponseObjectType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		orig, ok := types.GetHTTPResponseFromValue(args[0])
		if !ok {
			return cty.NilVal, fmt.Errorf("addheader: first argument must be an httpresponse value")
		}
		r := cloneResponse(orig)
		r.Headers.Add(textproto.CanonicalMIMEHeaderKey(args[1].AsString()), args[2].AsString())
		return types.BuildHTTPResponseObject(r), nil
	},
})

AddHeaderFunc implements addheader(response, name, value). Returns a new httpresponse with the given header appended (multi-value safe).

View Source
var BasicAuthFunc = function.New(&function.Spec{
	Description: "Returns the value for an HTTP Basic Authorization header for the given username and password",
	Params: []function.Parameter{
		{Name: "user", Type: cty.String, Description: "Username"},
		{Name: "password", Type: cty.String, Description: "Password"},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		user := args[0].AsString()
		password := args[1].AsString()
		encoded := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", user, password)))
		return cty.StringVal("Basic " + encoded), nil
	},
})

BasicAuthFunc returns the value for an HTTP Authorization header using Basic auth

View Source
var CondFunc = function.New(&function.Spec{
	Description: "Lazy conditional: cond(c1, r1, c2, r2, ..., else). Evaluates conditions in order; only the selected result expression is evaluated.",
	VarParam: &function.Parameter{
		Name: "exprs",
		Type: customdecode.ExpressionClosureType,
	},

	Type: func(args []cty.Value) (cty.Type, error) {
		if len(args) < 3 || len(args)%2 == 0 {
			return cty.NilType, fmt.Errorf("cond requires an odd number of arguments >= 3 (got %d)", len(args))
		}
		return cty.DynamicPseudoType, nil
	},
	Impl: func(args []cty.Value, _ cty.Type) (cty.Value, error) {
		for i := 0; i+1 < len(args); i += 2 {
			cv, diags := customdecode.ExpressionClosureFromVal(args[i]).Value()
			if diags.HasErrors() {
				return cty.NilVal, diagsToError(fmt.Sprintf("cond: condition #%d", i/2+1), diags)
			}
			bv, err := convert.Convert(cv, cty.Bool)
			if err != nil {
				return cty.NilVal, fmt.Errorf("cond: condition #%d: %w", i/2+1, err)
			}
			if bv.IsNull() {
				return cty.NilVal, fmt.Errorf("cond: condition #%d is null", i/2+1)
			}
			if bv.True() {
				return evalClosure(args[i+1], fmt.Sprintf("cond: result #%d", i/2+1))
			}
		}
		return evalClosure(args[len(args)-1], "cond: else")
	},
})

CondFunc is a lazy, multi-branch conditional.

Usage: cond(c1, r1, c2, r2, ..., else). Conditions are evaluated in order; only the result expression paired with the first truthy condition is evaluated. If no condition is truthy, the trailing "else" expression is evaluated. Unevaluated expressions produce no side effects.

View Source
var DiffFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{Name: "a", Type: cty.DynamicPseudoType},
		{Name: "b", Type: cty.DynamicPseudoType},
	},
	Type: function.StaticReturnType(cty.DynamicPseudoType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		a, err := go2cty2go.CtyToAny(args[0])
		if err != nil {
			return cty.UnknownVal(cty.NilType), fmt.Errorf("unable to convert first argument: %s", err)
		}
		b, err := go2cty2go.CtyToAny(args[1])
		if err != nil {
			return cty.UnknownVal(cty.NilType), fmt.Errorf("unable to convert second argument: %s", err)
		}

		diff, err := structdiff.Diff(a, b)
		if err != nil {
			return cty.UnknownVal(cty.NilType), fmt.Errorf("unable to diff values: %s", err)
		}

		return go2cty2go.AnyToCty(diff)
	},
})
View Source
var ErrorFunc = function.New(&function.Spec{
	Description: "Returns an error with the given message",
	Params: []function.Parameter{
		{Name: "message", Type: cty.String},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return args[0], errors.New(args[0].AsString())
	},
})
View Source
var HTTPErrorFunc = function.New(&function.Spec{
	Description: "Builds an HTTP error response with the given status code and message body",
	Params: []function.Parameter{
		{Name: "status", Type: cty.Number},
		{Name: "message", Type: cty.String},
	},
	Type: function.StaticReturnType(types.HTTPResponseObjectType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		status, _ := args[0].AsBigFloat().Int64()
		r := &types.HTTPResponseWrapper{
			Status:      int(status),
			Headers:     make(http.Header),
			Body:        []byte(args[1].AsString()),
			ContentType: "text/plain; charset=utf-8",
			IsError:     true,
		}
		return types.BuildHTTPResponseObject(r), nil
	},
})

HTTPErrorFunc implements http_error(status, message). Returns an httpresponse value marked as an error with the given status and plain-text body.

View Source
var HTTPMustFunc = function.New(&function.Spec{
	Description: "Returns response unchanged if its status matches expected (default any 2xx); otherwise raises an HCL error",
	Params: []function.Parameter{
		{Name: "response", Type: cty.DynamicPseudoType, AllowDynamicType: true},
	},
	VarParam: &function.Parameter{Name: "expected", Type: cty.DynamicPseudoType, AllowNull: true, AllowDynamicType: true},
	Type:     function.StaticReturnType(types.HTTPClientResponseObjectType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		respVal := args[0]
		wrapper, ok := types.GetHTTPClientResponseFromValue(respVal)
		if !ok {
			return cty.NilVal, fmt.Errorf("http_must: first argument must be an httpclientresponse value")
		}

		check := func(status int) bool { return status >= 200 && status < 300 }
		var expectedDesc string

		if len(args) > 1 && !args[1].IsNull() {
			c, desc, err := parseHTTPMustExpected(args[1])
			if err != nil {
				return cty.NilVal, fmt.Errorf("http_must: %w", err)
			}
			check = c
			expectedDesc = desc
		} else {
			expectedDesc = "any 2xx"
		}

		if check(wrapper.R.StatusCode) {
			return respVal, nil
		}

		method := "<unknown>"
		urlStr := "<unknown>"
		if wrapper.R.Request != nil {
			if wrapper.R.Request.Method != "" {
				method = wrapper.R.Request.Method
			}
			if wrapper.R.Request.URL != nil {
				urlStr = wrapper.R.Request.URL.String()
			}
		}

		bodyExcerpt := buildHTTPMustBodyExcerpt(wrapper)

		return cty.NilVal, fmt.Errorf(
			"http_must: %s %s returned %d %s\n  expected: %s\n  body: %s",
			method,
			urlStr,
			wrapper.R.StatusCode,
			nethttp.StatusText(wrapper.R.StatusCode),
			expectedDesc,
			bodyExcerpt,
		)
	},
})

HTTPMustFunc implements http_must(response[, expected]) → response.

Returns the response unchanged when its status is acceptable; otherwise raises an HCL error containing the method, final URL, actual status, expected set, and a body excerpt.

expected may be:

  • omitted (or null) — any 2xx is acceptable
  • a single number — exact match
  • a list of numbers — any one matches
  • a list of [lo, hi] tuples — inclusive ranges
  • a mix of numbers and [lo, hi] tuples in the same list
View Source
var HTTPRedirectFunc = function.New(&function.Spec{
	Description: "Builds an HTTP redirect response; http_redirect(url) defaults to 302, http_redirect(status, url) uses the given status",
	Params: []function.Parameter{
		{Name: "first", Type: cty.DynamicPseudoType},
	},
	VarParam: &function.Parameter{Name: "rest", Type: cty.DynamicPseudoType},
	Type:     function.StaticReturnType(types.HTTPResponseObjectType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		var status int
		var url string

		switch {
		case args[0].Type() == cty.String:
			if len(args) != 1 {
				return cty.NilVal, fmt.Errorf("http_redirect: url-only form takes exactly 1 argument")
			}
			status = http.StatusFound
			url = args[0].AsString()

		case args[0].Type() == cty.Number:
			if len(args) != 2 {
				return cty.NilVal, fmt.Errorf("http_redirect: status+url form takes exactly 2 arguments")
			}
			if args[1].Type() != cty.String {
				return cty.NilVal, fmt.Errorf("http_redirect: second argument must be a string URL, got %s", args[1].Type().FriendlyName())
			}
			s, _ := args[0].AsBigFloat().Int64()
			status = int(s)
			url = args[1].AsString()

		default:
			return cty.NilVal, fmt.Errorf("http_redirect: first argument must be a URL string or status number, got %s", args[0].Type().FriendlyName())
		}

		r := &types.HTTPResponseWrapper{
			Status:  status,
			Headers: http.Header{"Location": {url}},
		}
		return types.BuildHTTPResponseObject(r), nil
	},
})

HTTPRedirectFunc implements http_redirect(url) and http_redirect(status, url). When the first argument is a string it is treated as the URL with a default 302 status. When the first argument is a number it is treated as the status code and the second argument must be the URL string.

View Source
var HTTPResponseFunc = function.New(&function.Spec{
	Description: "Builds an HTTP response value with the given status code, optional body, and optional headers",
	Params: []function.Parameter{
		{Name: "status", Type: cty.Number},
	},
	VarParam: &function.Parameter{Name: "args", Type: cty.DynamicPseudoType},
	Type:     function.StaticReturnType(types.HTTPResponseObjectType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		status, _ := args[0].AsBigFloat().Int64()
		r := &types.HTTPResponseWrapper{
			Status:  int(status),
			Headers: make(http.Header),
		}

		if len(args) > 1 {
			body, ct, err := types.CoerceBodyToBytes(args[1])
			if err != nil {
				return cty.NilVal, fmt.Errorf("http_response: invalid body: %w", err)
			}
			r.Body = body
			r.ContentType = ct
		}

		if len(args) > 2 {
			if err := applyHeadersArg(r.Headers, args[2]); err != nil {
				return cty.NilVal, fmt.Errorf("http_response: invalid headers: %w", err)
			}
		}

		return types.BuildHTTPResponseObject(r), nil
	},
})

HTTPResponseFunc implements http_response(status[, body[, headers]]).

View Source
var KillFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{Name: "pid", Type: cty.Number},
		{Name: "signal", Type: cty.Number},
	},
	Type: function.StaticReturnType(cty.Bool),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		pid, accuracy := args[0].AsBigFloat().Int64()
		if accuracy != 0 {
			return cty.False, fmt.Errorf("pid must be an integer")
		}
		sig, accuracy := args[1].AsBigFloat().Int64()
		if accuracy != 0 {
			return cty.False, fmt.Errorf("signal must be an integer")
		}
		if err := syscall.Kill(int(pid), syscall.Signal(sig)); err != nil {
			return cty.False, fmt.Errorf("kill(%d, %d): %w", pid, sig, err)
		}
		return cty.True, nil
	},
})

KillFunc sends a signal to a process. Both pid and signal are integers. Returns true on success, or an error if the syscall fails.

View Source
var LLMWrapFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{Name: "content", Type: cty.String},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		content := args[0].AsString()
		return cty.StringVal("<user_input>\n" + content + "\n</user_input>"), nil
	},
})

LLMWrapFunc wraps user-controlled content in <user_input> XML-like delimiters as a prompt injection mitigation. The system prompt should reference these tags to tell the model where untrusted input begins and ends.

Input: "hello" Output: "<user_input>\nhello\n</user_input>"

View Source
var MCPAssistantMessageFunc = function.New(&function.Spec{
	Description: "Returns an assistant-role message for an MCP prompt result (few-shot example)",
	Params: []function.Parameter{
		{Name: "content", Type: cty.String, Description: "Message content"},
	},
	Type: function.StaticReturnType(MCPResultCapsuleType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return NewMCPResultCapsule(MCPResult{
			Kind: "assistant_message",
			Text: args[0].AsString(),
		}), nil
	},
})

MCPAssistantMessageFunc returns an mcp_assistantmessage function for prompt results.

View Source
var MCPErrorFunc = function.New(&function.Spec{
	Description: "Returns an error result for an MCP tool call",
	Params: []function.Parameter{
		{Name: "message", Type: cty.String, Description: "Error message"},
	},
	Type: function.StaticReturnType(MCPResultCapsuleType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return NewMCPResultCapsule(MCPResult{
			Kind: "error",
			Text: args[0].AsString(),
		}), nil
	},
})

MCPErrorFunc returns an mcp_error function that signals a tool error result.

View Source
var MCPImageFunc = function.New(&function.Spec{
	Description: "Returns image content for an MCP resource or tool result",
	Params: []function.Parameter{
		{Name: "data", Type: cty.DynamicPseudoType, Description: "Base64-encoded image string or bytes capsule"},
	},
	VarParam: &function.Parameter{
		Name:        "mime_type",
		Type:        cty.String,
		Description: "MIME type of the image (required when data is a base64 string; optional when data is a bytes capsule)",
	},
	Type: function.StaticReturnType(MCPResultCapsuleType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		var imageData []byte
		var mimeType string

		switch {
		case args[0].Type() == cty.String:
			var err error
			imageData, err = base64.StdEncoding.DecodeString(args[0].AsString())
			if err != nil {
				return cty.NilVal, fmt.Errorf("mcp_image: invalid base64 data: %w", err)
			}
			if len(args) < 2 {
				return cty.NilVal, fmt.Errorf("mcp_image: mime_type is required when data is a base64 string")
			}
			mimeType = args[1].AsString()
		default:
			b, err := bytescty.GetBytesFromValue(args[0])
			if err != nil {
				return cty.NilVal, fmt.Errorf("mcp_image: data must be a base64 string or bytes value, got %s", args[0].Type().FriendlyName())
			}
			imageData = b.Data
			mimeType = b.ContentType
			if len(args) > 1 {
				mimeType = args[1].AsString()
			}
		}

		return NewMCPResultCapsule(MCPResult{
			Kind:     "image",
			Data:     imageData,
			MIMEType: mimeType,
		}), nil
	},
})

MCPImageFunc returns an mcp_image function that produces image content.

Accepted call forms:

mcp_image(base64_string, mime_type)  - original form, both args required
mcp_image(bytes_capsule)             - mime_type taken from bytes content type
mcp_image(bytes_capsule, mime_type)  - mime_type overrides bytes content type
View Source
var MCPResultCapsuleType = cty.CapsuleWithOps("mcp_result", reflect.TypeOf(MCPResult{}), &cty.CapsuleOps{
	GoString: func(val interface{}) string {
		r := val.(*MCPResult)
		return fmt.Sprintf("mcp_result(%s)", r.Kind)
	},
	TypeGoString: func(_ reflect.Type) string {
		return "mcp_result"
	},
})

MCPResultCapsuleType is the cty capsule type for MCPResult values.

View Source
var MCPUserMessageFunc = function.New(&function.Spec{
	Description: "Returns a user-role message for an MCP prompt result",
	Params: []function.Parameter{
		{Name: "content", Type: cty.String, Description: "Message content"},
	},
	Type: function.StaticReturnType(MCPResultCapsuleType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return NewMCPResultCapsule(MCPResult{
			Kind: "user_message",
			Text: args[0].AsString(),
		}), nil
	},
})

MCPUserMessageFunc returns an mcp_usermessage function for prompt results.

View Source
var PatchFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{Name: "target", Type: cty.DynamicPseudoType},
		{Name: "patch", Type: cty.DynamicPseudoType},
	},
	Type: function.StaticReturnType(cty.DynamicPseudoType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		target, err := go2cty2go.CtyToAny(args[0])
		if err != nil {
			return cty.UnknownVal(cty.NilType), fmt.Errorf("unable to convert target argument: %s", err)
		}
		patch, err := go2cty2go.CtyToAny(args[1])
		if err != nil {
			return cty.UnknownVal(cty.NilType), fmt.Errorf("unable to convert patch argument: %s", err)
		}

		patchMap, ok := patch.(map[string]any)
		if !ok {
			return cty.UnknownVal(cty.NilType), fmt.Errorf("patch must be a map")
		}

		targetMap, ok := target.(map[string]any)
		if !ok {
			return cty.UnknownVal(cty.NilType), fmt.Errorf("target must be a map")
		}

		err = structdiff.Apply(&targetMap, patchMap)
		if err != nil {
			return cty.UnknownVal(cty.NilType), fmt.Errorf("unable to apply patch: %s", err)
		}

		return go2cty2go.AnyToCty(targetMap)
	},
})
View Source
var RemoveHeaderFunc = function.New(&function.Spec{
	Description: "Returns a new response with all values for the given header removed",
	Params: []function.Parameter{
		{Name: "response", Type: cty.DynamicPseudoType},
		{Name: "name", Type: cty.String},
	},
	Type: function.StaticReturnType(types.HTTPResponseObjectType),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		orig, ok := types.GetHTTPResponseFromValue(args[0])
		if !ok {
			return cty.NilVal, fmt.Errorf("removeheader: first argument must be an httpresponse value")
		}
		r := cloneResponse(orig)
		r.Headers.Del(args[1].AsString())
		return types.BuildHTTPResponseObject(r), nil
	},
})

RemoveHeaderFunc implements removeheader(response, name). Returns a new httpresponse with all values for the given header removed.

View Source
var SQLMustFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name:             "result",
			Type:             cty.DynamicPseudoType,
			AllowNull:        true,
			AllowDynamicType: true,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		return args[0].Type(), nil
	},
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		result := args[0]
		if !result.IsKnown() {
			return cty.UnknownVal(retType), nil
		}
		if result.IsNull() || !result.Type().IsObjectType() || !result.Type().HasAttribute("error") {
			return cty.NilVal, fmt.Errorf("sql_must: argument is not a result object (no \"error\" field)")
		}

		errVal := result.GetAttr("error")
		if errVal.IsNull() {
			return result, nil
		}

		return cty.NilVal, fmt.Errorf("sql_must: %s", formatSQLError(errVal))
	},
})

SQLMustFunc turns the "error rides in the result object" convention used by SQL `call()` into a fail-fast: given a result object, if its `error` field is non-null it raises an evaluation error built from the error fields; otherwise it returns the result unchanged.

# branch on the error...
r = call(ctx, client.db, "INSERT ...")
# ...or just fail the action if it errored:
r = sql_must(call(ctx, client.db, "INSERT ..."))
View Source
var SetCookieFunc = function.New(&function.Spec{
	Description: "Formats a Set-Cookie header value from a cookie definition object",
	Params: []function.Parameter{
		{Name: "cookie", Type: cty.DynamicPseudoType},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		val := args[0]
		if !val.Type().IsObjectType() {
			return cty.NilVal, fmt.Errorf("setcookie: argument must be an object, got %s", val.Type().FriendlyName())
		}

		if !val.Type().HasAttribute("name") || !val.Type().HasAttribute("value") {
			return cty.NilVal, fmt.Errorf("setcookie: object must have 'name' and 'value' attributes")
		}

		c := &http.Cookie{
			Name:  val.GetAttr("name").AsString(),
			Value: val.GetAttr("value").AsString(),
		}

		if val.Type().HasAttribute("path") {
			c.Path = val.GetAttr("path").AsString()
		}
		if val.Type().HasAttribute("domain") {
			c.Domain = val.GetAttr("domain").AsString()
		}
		if val.Type().HasAttribute("expires") {
			exp := val.GetAttr("expires")
			switch {
			case exp.Type() == timecty.TimeCapsuleType:
				t, _ := timecty.GetTime(exp)
				c.Expires = t
			case exp.Type() == timecty.DurationCapsuleType:
				d, _ := timecty.GetDuration(exp)
				c.Expires = time.Now().Add(d)
			case exp.Type() == cty.String:
				s := exp.AsString()
				if s != "" {
					t, err := time.Parse(time.RFC3339, s)
					if err != nil {
						return cty.NilVal, fmt.Errorf("setcookie: invalid expires value %q: %w", s, err)
					}
					c.Expires = t
				}
			default:
				return cty.NilVal, fmt.Errorf("setcookie: expires must be a time, duration, or RFC3339 string, got %s", exp.Type().FriendlyName())
			}
		}
		if val.Type().HasAttribute("max_age") {
			f, _ := val.GetAttr("max_age").AsBigFloat().Int64()
			c.MaxAge = int(f)
		}
		if val.Type().HasAttribute("secure") {
			c.Secure = val.GetAttr("secure").True()
		}
		if val.Type().HasAttribute("http_only") {
			c.HttpOnly = val.GetAttr("http_only").True()
		}
		if val.Type().HasAttribute("same_site") {
			switch strings.ToLower(val.GetAttr("same_site").AsString()) {
			case "strict":
				c.SameSite = http.SameSiteStrictMode
			case "lax":
				c.SameSite = http.SameSiteLaxMode
			case "none":
				c.SameSite = http.SameSiteNoneMode
			default:
				c.SameSite = http.SameSiteDefaultMode
			}
		}
		if val.Type().HasAttribute("partitioned") {
			c.Partitioned = val.GetAttr("partitioned").True()
		}

		return cty.StringVal(c.String()), nil
	},
})

SetCookieFunc implements setcookie(cookieObj). Formats a Set-Cookie header value from an object with cookie fields. The name and value fields are required; all others are optional.

View Source
var SwitchFunc = function.New(&function.Spec{
	Description: "Switch dispatch: switch(on, v1, r1, v2, r2, ..., default?). Evaluates `on` once, then each vN until a match; returns the matching rN, or the optional default. Errors if nothing matches and no default was given. Each branch is evaluated at most once.",
	VarParam: &function.Parameter{
		Name: "exprs",
		Type: customdecode.ExpressionClosureType,
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		if len(args) < 3 {
			return cty.NilType, fmt.Errorf("switch requires at least 3 arguments (got %d)", len(args))
		}
		return cty.DynamicPseudoType, nil
	},
	Impl: func(args []cty.Value, _ cty.Type) (cty.Value, error) {
		hasDefault := len(args)%2 == 0
		caseEnd := len(args)
		if hasDefault {
			caseEnd = len(args) - 1
		}
		on, err := evalClosure(args[0], "switch: on")
		if err != nil {
			return cty.NilVal, err
		}
		for i := 1; i+1 < caseEnd; i += 2 {
			caseNum := (i + 1) / 2
			v, err := evalClosure(args[i], fmt.Sprintf("switch: case #%d value", caseNum))
			if err != nil {
				return cty.NilVal, err
			}
			if on.RawEquals(v) {
				return evalClosure(args[i+1], fmt.Sprintf("switch: case #%d result", caseNum))
			}
		}
		if hasDefault {
			return evalClosure(args[len(args)-1], "switch: default")
		}
		return cty.NilVal, errors.New("switch: no case matched and no default was given")
	},
})

SwitchFunc dispatches on a single value against a series of (match, result) pairs, with an optional trailing default.

Usage: switch(on, v1, r1, v2, r2, ..., default?). The trailing default is optional; with it the total argument count is even, without it odd.

`on` is evaluated exactly once. Then for each (vN, rN) pair, vN is evaluated and compared to `on` for equality; on the first match, rN is evaluated and returned. If no vN matches, the default (when present) is evaluated and returned; if no default was given, switch() errors. Case values past the matching arm are not evaluated, nor are unselected results or the default.

Equality uses cty.Value.RawEquals — exact structural equality. Type mismatches (e.g. number 200 vs string "200") count as no match rather than erroring.

View Source
var TryFunc = function.New(&function.Spec{
	Description: "Try each expression in order; return the first that evaluates without error. Evaluates each expression at most once (unlike stock HCL try()).",
	VarParam: &function.Parameter{
		Name: "exprs",
		Type: customdecode.ExpressionClosureType,
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		if len(args) == 0 {
			return cty.NilType, errors.New("try requires at least one argument")
		}
		return cty.DynamicPseudoType, nil
	},
	Impl: func(args []cty.Value, _ cty.Type) (cty.Value, error) {
		var accum hcl.Diagnostics
		for _, a := range args {
			v, diags := customdecode.ExpressionClosureFromVal(a).Value()
			if diags.HasErrors() {
				accum = append(accum, diags...)
				continue
			}

			if !v.IsWhollyKnown() {
				return cty.DynamicVal, nil
			}
			return v, nil
		}
		return cty.NilVal, diagsToError("no try expression succeeded", accum)
	},
})

TryFunc evaluates each argument in order and returns the first whose evaluation produces no diagnostics. Unlike upstream tryfunc.TryFunc, this returns cty.DynamicPseudoType from the Type callback so each selected expression is evaluated exactly once (upstream's concrete-type inference evaluates the successful branch twice, which is unsafe for side-effectful expressions).

View Source
var TypeOfFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{Name: "value", Type: cty.DynamicPseudoType},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return cty.StringVal(args[0].Type().FriendlyName()), nil
	},
})

TypeOfFunc returns the friendly name of the type of a given value

Functions

func GetHTTPClientFunctions added in v0.26.0

func GetHTTPClientFunctions(config *cfg.Config) map[string]function.Function

GetHTTPClientFunctions returns the eight http_* verb functions, capturing the supplied config so verb functions can evaluate lazy expressions like retry.on_response against the same eval context the rest of vinculum uses. config may be nil in tests that do not exercise hooks.

func GetHTTPResponseFunctions added in v0.20.0

func GetHTTPResponseFunctions() map[string]function.Function

GetHTTPResponseFunctions returns all HTTP response functions for global registration.

func GetLogFunctions

func GetLogFunctions(logger *zap.Logger) map[string]function.Function

GetLogFunctions returns HCL functions for logging with zap logger

func GetMcpFunctions added in v0.10.0

func GetMcpFunctions() map[string]function.Function

GetMcpFunctions returns all MCP-specific cty functions. These are included in the global function set so they are available in any action expression, including bus subscriptions that construct MCP values for async handlers.

func GetStandardLibraryFunctions

func GetStandardLibraryFunctions() map[string]function.Function

GetStandardLibraryFunctions returns a map of all cty standard library functions suitable for providing to an HCL evaluation context.

func MakeFileAppendFunc added in v0.15.0

func MakeFileAppendFunc(baseDir string) function.Function

MakeFileAppendFunc constructs a function that appends a string to a file, creating the file if it does not exist. The path must be within baseDir.

func MakeFileBytesFunc added in v0.19.0

func MakeFileBytesFunc(baseDir string) function.Function

MakeFileBytesFunc returns a filebytes function restricted to baseDir. filebytes(path) or filebytes(path, content_type)

func MakeFileWriteFunc added in v0.15.0

func MakeFileWriteFunc(baseDir string) function.Function

MakeFileWriteFunc constructs a function that writes a string to a file, creating or overwriting it. The path must be within baseDir.

func MakeGoTemplateFileFunc added in v0.15.0

func MakeGoTemplateFileFunc(
	baseDir string,
	constants map[string]cty.Value,
) function.Function

MakeGoTemplateFileFunc constructs a gotemplatefile(path, vars) function. It reads a Go text/template file, evaluates it with the given vars merged on top of the standard VCL constants (env, sys, var, metric, etc.), and returns the rendered string. The path is resolved relative to baseDir.

constants is the live Config.Constants map. Unlike templatefile(), no funcsGetter is needed because Go templates use their own FuncMap.

func MakeTemplateFileFunc added in v0.15.0

func MakeTemplateFileFunc(
	baseDir string,
	constants map[string]cty.Value,
	funcsGetter func() map[string]function.Function,
) function.Function

MakeTemplateFileFunc constructs a templatefile(path, vars) function. It reads an HCL template file, evaluates it with the given vars merged into the standard VCL variable scope (env, sys, var, metric, etc.), and returns the result. The path is resolved relative to baseDir.

constants is the live Config.Constants map (captured by reference so that var/metric/bus/server/client entries added during block processing are visible at evaluation time). funcsGetter returns all available functions excluding templatefile itself, preventing recursion.

func NewMCPResultCapsule added in v0.10.0

func NewMCPResultCapsule(r MCPResult) cty.Value

NewMCPResultCapsule wraps an MCPResult in a cty capsule value.

Types

type MCPResult added in v0.10.0

type MCPResult struct {
	// Kind is one of: "image", "error", "user_message", "assistant_message"
	Kind     string
	Text     string
	Data     []byte // decoded bytes for images
	MIMEType string
}

MCPResult holds typed return values from MCP action expressions. It is wrapped in a cty capsule so it can pass through HCL evaluation.

func GetMCPResult added in v0.10.0

func GetMCPResult(val cty.Value) *MCPResult

GetMCPResult extracts an MCPResult from a cty capsule value. Returns nil if the value is not an MCPResult capsule.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL