execution

package
v3.5.0 Latest Latest
Warning

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

Go to latest
Published: Apr 25, 2026 License: MIT Imports: 17 Imported by: 0

README

Execution

The execution package accepts a model.Value, parses a selector and executes the resulting AST on the value.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var FuncAbs = NewFunc(
	"abs",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if input.IsInt() {
			v, err := input.IntValue()
			if err != nil {
				return nil, fmt.Errorf("abs could not read int value: %w", err)
			}
			if v < 0 {
				v = -v
			}
			return model.NewIntValue(v), nil
		}

		v, err := input.FloatValue()
		if err != nil {
			return nil, fmt.Errorf("abs expects a numeric value: %w", err)
		}
		return model.NewFloatValue(math.Abs(v)), nil
	},
	ValidateArgsMax(1),
)

FuncAbs is a function that returns the absolute value of a number.

View Source
var FuncAdd = NewFunc(
	"add",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var foundInts, foundFloats int
		var intRes int64
		var floatRes float64
		for _, arg := range args {
			if arg.IsFloat() {
				foundFloats++
				v, err := arg.FloatValue()
				if err != nil {
					return nil, fmt.Errorf("error getting float value: %w", err)
				}
				floatRes += v
				continue
			}
			if arg.IsInt() {
				foundInts++
				v, err := arg.IntValue()
				if err != nil {
					return nil, fmt.Errorf("error getting int value: %w", err)
				}
				intRes += v
				continue
			}
			return nil, fmt.Errorf("expected int or float, got %s", arg.Type())
		}
		if foundFloats > 0 {
			return model.NewFloatValue(floatRes + float64(intRes)), nil
		}
		return model.NewIntValue(intRes), nil
	},
	ValidateArgsMin(1),
)

FuncAdd is a function that adds the given values together.

View Source
var FuncAvg = NewFunc(
	"avg",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var sum float64

		for _, arg := range args {
			if arg.IsInt() {
				intVal, err := arg.IntValue()
				if err != nil {
					return nil, err
				}
				sum += float64(intVal)
				continue
			}
			if arg.IsFloat() {
				floatVal, err := arg.FloatValue()
				if err != nil {
					return nil, err
				}
				sum += floatVal
				continue
			}
			return nil, fmt.Errorf("cannot average non-numeric value of type %s", arg.Type().String())
		}

		return model.NewFloatValue(sum / float64(len(args))), nil
	},
	ValidateArgsMin(1),
)

FuncAvg is a function that returns the average of the given numbers. Always returns a float value.

View Source
var FuncBase64Decode = NewFunc(
	"base64d",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		arg := args[0]
		strVal, err := arg.StringValue()
		if err != nil {
			return nil, err
		}
		out, err := base64.StdEncoding.DecodeString(strVal)
		if err != nil {
			return nil, err
		}
		return model.NewStringValue(string(out)), nil
	},
	ValidateArgsExactly(1),
)

FuncBase64Decode base64 decodes the given value.

View Source
var FuncBase64Encode = NewFunc(
	"base64e",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		arg := args[0]
		strVal, err := arg.StringValue()
		if err != nil {
			return nil, err
		}
		out := base64.StdEncoding.EncodeToString([]byte(strVal))
		return model.NewStringValue(out), nil
	},
	ValidateArgsExactly(1),
)

FuncBase64Encode base64 encodes the given value.

View Source
var FuncCeil = NewFunc(
	"ceil",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if input.IsInt() {
			v, err := input.IntValue()
			if err != nil {
				return nil, fmt.Errorf("ceil could not read int value: %w", err)
			}
			return model.NewIntValue(v), nil
		}

		v, err := input.FloatValue()
		if err != nil {
			return nil, fmt.Errorf("ceil expects a numeric value: %w", err)
		}
		return model.NewIntValue(int64(math.Ceil(v))), nil
	},
	ValidateArgsMax(1),
)

FuncCeil is a function that returns the smallest integer value greater than or equal to the input.

View Source
var FuncContains = NewFunc(
	"contains",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var contains bool

		target := args[0]

		length, err := data.SliceLen()
		if err != nil {
			return nil, fmt.Errorf("error getting slice length: %w", err)
		}

		for i := 0; i < length; i++ {
			v, err := data.GetSliceIndex(i)
			if err != nil {
				return nil, fmt.Errorf("error getting slice index %d: %w", i, err)
			}
			matches, err := v.Equal(target)
			if err != nil {
				continue
			}
			matchesBool, err := matches.BoolValue()
			if err != nil {
				return nil, err
			}
			if matchesBool {
				contains = true
				break
			}
		}

		return model.NewBoolValue(contains), nil
	},
	ValidateArgsExactly(1),
)

FuncContains is a function that returns the highest number.

View Source
var FuncEndsWith = NewFunc(
	"endsWith",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input string
		var suffix string
		var err error

		if len(args) == 2 {
			input, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("endsWith expects a string as the first argument: %w", err)
			}
			suffix, err = args[1].StringValue()
			if err != nil {
				return nil, fmt.Errorf("endsWith expects a string suffix as the second argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("endsWith expects data to be a string: %w", err)
			}
			suffix, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("endsWith expects a string suffix as the first argument: %w", err)
			}
		}

		return model.NewBoolValue(strings.HasSuffix(input, suffix)), nil
	},
	ValidateArgsMinMax(1, 2),
)

FuncEndsWith is a function that checks if a string ends with a given suffix.

View Source
var FuncEntries = NewFunc(
	"entries",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		if !data.IsMap() {
			return nil, fmt.Errorf("entries can only be used on maps, got %s", data.Type().String())
		}

		res := model.NewSliceValue()
		if err := data.RangeMap(func(key string, value *model.Value) error {
			entry := model.NewMapValue()
			if err := entry.SetMapKey("key", model.NewStringValue(key)); err != nil {
				return err
			}
			if err := entry.SetMapKey("value", value); err != nil {
				return err
			}
			return res.Append(entry)
		}); err != nil {
			return nil, err
		}

		return res, nil
	},
	ValidateArgsExactly(0),
)

FuncEntries converts a map into an array of {key, value} objects.

View Source
var FuncFirst = NewFunc(
	"first",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if !input.IsSlice() {
			return nil, fmt.Errorf("first expects an array, got %s", input.Type().String())
		}

		length, err := input.SliceLen()
		if err != nil {
			return nil, err
		}
		if length == 0 {
			return model.NewNullValue(), nil
		}

		return input.GetSliceIndex(0)
	},
	ValidateArgsMax(1),
)

FuncFirst is a function that returns the first element of an array.

View Source
var FuncFlatten = NewFunc(
	"flatten",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if !input.IsSlice() {
			return nil, fmt.Errorf("flatten expects an array, got %s", input.Type().String())
		}

		res := model.NewSliceValue()
		if err := input.RangeSlice(func(i int, value *model.Value) error {
			if value.IsSlice() {
				return value.RangeSlice(func(j int, inner *model.Value) error {
					return res.Append(inner)
				})
			}
			return res.Append(value)
		}); err != nil {
			return nil, err
		}

		return res, nil
	},
	ValidateArgsMax(1),
)

FuncFlatten is a function that flattens a nested array by one level.

View Source
var FuncFloor = NewFunc(
	"floor",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if input.IsInt() {
			v, err := input.IntValue()
			if err != nil {
				return nil, fmt.Errorf("floor could not read int value: %w", err)
			}
			return model.NewIntValue(v), nil
		}

		v, err := input.FloatValue()
		if err != nil {
			return nil, fmt.Errorf("floor expects a numeric value: %w", err)
		}
		return model.NewIntValue(int64(math.Floor(v))), nil
	},
	ValidateArgsMax(1),
)

FuncFloor is a function that returns the largest integer value less than or equal to the input.

View Source
var FuncFromEntries = NewFunc(
	"fromEntries",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if !input.IsSlice() {
			return nil, fmt.Errorf("fromEntries expects an array, got %s", input.Type().String())
		}

		res := model.NewMapValue()
		if err := input.RangeSlice(func(i int, entry *model.Value) error {
			if !entry.IsMap() {
				return fmt.Errorf("fromEntries expects each element to be an object, got %s at index %d", entry.Type().String(), i)
			}

			keyVal, err := entry.GetMapKey("key")
			if err != nil {
				return fmt.Errorf("fromEntries expects each element to have a \"key\" field: %w", err)
			}
			key, err := keyVal.StringValue()
			if err != nil {
				return fmt.Errorf("fromEntries expects \"key\" to be a string: %w", err)
			}

			value, err := entry.GetMapKey("value")
			if err != nil {
				return fmt.Errorf("fromEntries expects each element to have a \"value\" field: %w", err)
			}

			return res.SetMapKey(key, value)
		}); err != nil {
			return nil, err
		}

		return res, nil
	},
	ValidateArgsMax(1),
)

FuncFromEntries converts an array of {key, value} objects into a map.

View Source
var FuncGet = NewFunc(
	"get",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {

		arg := args[0]

		switch arg.Type() {
		case model.TypeInt:
			index, err := arg.IntValue()
			if err != nil {
				return nil, err
			}
			return data.GetSliceIndex(int(index))
		case model.TypeString:
			key, err := arg.StringValue()
			if err != nil {
				return nil, err
			}
			return data.GetMapKey(key)
		default:
			return nil, fmt.Errorf("get expects string or int argument")
		}
	},
	ValidateArgsExactly(1),
)

FuncGet is a function returns the value at the given key/index.

View Source
var FuncHas = NewFunc(
	"has",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {

		arg := args[0]

		switch arg.Type() {
		case model.TypeInt:

			if data.Type() != model.TypeSlice {
				return model.NewBoolValue(false), nil
			}
			index, err := arg.IntValue()
			if err != nil {
				return nil, err
			}
			sliceLen, err := data.SliceLen()
			if err != nil {
				return nil, err
			}
			return model.NewBoolValue(index >= 0 && index < int64(sliceLen)), nil
		case model.TypeString:

			if data.Type() != model.TypeMap {
				return model.NewBoolValue(false), nil
			}
			key, err := arg.StringValue()
			if err != nil {
				return nil, err
			}
			exists, err := data.MapKeyExists(key)
			if err != nil {
				return nil, err
			}
			return model.NewBoolValue(exists), nil
		default:
			return nil, fmt.Errorf("has expects string or int argument")
		}
	},
	ValidateArgsMin(1),
)

FuncHas is a function that true or false if the input has the given key/index.

View Source
var FuncIgnore = NewFunc(
	"ignore",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		data.MarkAsIgnore()
		return data, nil
	},
	ValidateArgsExactly(0),
)

FuncIgnore is a function that ignores the value, causing it to be rejected from a branch.

View Source
var FuncIndexOf = NewFunc(
	"indexOf",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input string
		var substr string
		var err error

		if len(args) == 2 {
			input, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("indexOf expects a string as the first argument: %w", err)
			}
			substr, err = args[1].StringValue()
			if err != nil {
				return nil, fmt.Errorf("indexOf expects a string substring as the second argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("indexOf expects data to be a string: %w", err)
			}
			substr, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("indexOf expects a string substring as the first argument: %w", err)
			}
		}

		return model.NewIntValue(int64(strings.Index(input, substr))), nil
	},
	ValidateArgsMinMax(1, 2),
)

FuncIndexOf is a function that returns the index of the first occurrence of a substring. Returns -1 if the substring is not found.

View Source
var FuncJoin = NewFunc(
	"join",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		separator, err := args[0].StringValue()
		if err != nil {
			return nil, fmt.Errorf("join expects a string separator as the first argument: %w", err)
		}

		var valuesToJoin []string

		if len(args) == 2 && args[1].IsSlice() {
			if err := args[1].RangeSlice(func(i int, value *model.Value) error {
				strVal, err := value.StringValue()
				if err != nil {
					return fmt.Errorf("could not read string value of index %d: %w", i, err)
				}
				valuesToJoin = append(valuesToJoin, strVal)
				return nil
			}); err != nil {
				return nil, err
			}
		} else if len(args) > 1 {

			for i := 1; i < len(args); i++ {
				strVal, err := args[i].StringValue()
				if err != nil {
					return nil, fmt.Errorf("could not read string value of argument index %d: %w", i, err)
				}
				valuesToJoin = append(valuesToJoin, strVal)
			}
		} else {
			if err := data.RangeSlice(func(i int, value *model.Value) error {
				strVal, err := value.StringValue()
				if err != nil {
					return fmt.Errorf("could not read string value of index %d: %w", i, err)
				}
				valuesToJoin = append(valuesToJoin, strVal)
				return nil
			}); err != nil {
				return nil, err
			}
		}

		joined := strings.Join(valuesToJoin, separator)

		return model.NewStringValue(joined), nil
	},
	ValidateArgsMin(1),
)

FuncJoin is a function that joins the given data or args to a string.

View Source
var FuncKeys = NewFunc(
	"keys",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		switch data.Type() {
		case model.TypeMap:
			keys, err := data.MapKeys()
			if err != nil {
				return nil, err
			}
			res := model.NewSliceValue()
			for _, key := range keys {
				if err := res.Append(model.NewStringValue(key)); err != nil {
					return nil, err
				}
			}
			return res, nil
		case model.TypeSlice:
			len, err := data.SliceLen()
			if err != nil {
				return nil, err
			}
			res := model.NewSliceValue()
			for i := 0; i < len; i++ {
				if err := res.Append(model.NewIntValue(int64(i))); err != nil {
					return nil, err
				}
			}
			return res, nil
		default:
			return nil, fmt.Errorf("keys can only be used on maps and slices")
		}
	},
	ValidateArgsExactly(0),
)

FuncKeys returns the keys of a map or the indices of a slice.

View Source
var FuncLast = NewFunc(
	"last",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if !input.IsSlice() {
			return nil, fmt.Errorf("last expects an array, got %s", input.Type().String())
		}

		length, err := input.SliceLen()
		if err != nil {
			return nil, err
		}
		if length == 0 {
			return model.NewNullValue(), nil
		}

		return input.GetSliceIndex(length - 1)
	},
	ValidateArgsMax(1),
)

FuncLast is a function that returns the last element of an array.

View Source
var FuncLen = NewFunc(
	"len",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		arg := args[0]

		l, err := arg.Len()
		if err != nil {
			return nil, err
		}

		return model.NewIntValue(int64(l)), nil
	},
	ValidateArgsExactly(1),
)

FuncLen is a function that returns the length of the given value.

View Source
var FuncMax = NewFunc(
	"max",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		res := model.NewNullValue()
		for _, arg := range args {
			if res.IsNull() {
				res = arg
				continue
			}
			gt, err := arg.GreaterThan(res)
			if err != nil {
				return nil, err
			}
			gtBool, err := gt.BoolValue()
			if err != nil {
				return nil, err
			}
			if gtBool {
				res = arg
			}
		}
		return res, nil
	},
	ValidateArgsMin(1),
)

FuncMax is a function that returns the highest number.

View Source
var FuncMerge = NewFunc(
	"merge",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		if len(args) == 1 {
			return args[0], nil
		}

		expectedType := args[0].Type()

		switch expectedType {
		case model.TypeMap:
			break
		default:
			return nil, fmt.Errorf("merge exects a map, found %s", expectedType)
		}

		for _, a := range args {
			if a.Type() != expectedType {
				return nil, fmt.Errorf("merge expects all arguments to be of the same type. expected %s, got %s", expectedType.String(), a.Type().String())
			}
		}

		base := model.NewMapValue()

		for i := 0; i < len(args); i++ {
			next := args[i]

			nextKVs, err := next.MapKeyValues()
			if err != nil {
				return nil, fmt.Errorf("merge failed to extract key values for arg %d: %w", i, err)
			}

			for _, kv := range nextKVs {
				if err := base.SetMapKey(kv.Key, kv.Value); err != nil {
					return nil, fmt.Errorf("merge failed to set map key %s: %w", kv.Key, err)
				}
			}
		}

		return base, nil
	},
	ValidateArgsMin(1),
)

FuncMerge is a function that merges two or more items together.

View Source
var FuncMin = NewFunc(
	"min",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		res := model.NewNullValue()
		for _, arg := range args {
			if res.IsNull() {
				res = arg
				continue
			}
			lt, err := arg.LessThan(res)
			if err != nil {
				return nil, err
			}
			ltBool, err := lt.BoolValue()
			if err != nil {
				return nil, err
			}
			if ltBool {
				res = arg
			}
		}
		return res, nil
	},
	ValidateArgsMin(1),
)

FuncMin is a function that returns the smalled number.

View Source
var FuncParse = NewFunc(
	"parse",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var format parsing.Format
		var content []byte
		{
			strVal, err := args[0].StringValue()
			if err != nil {
				return nil, err
			}
			format = parsing.Format(strVal)
		}
		{
			strVal, err := args[1].StringValue()
			if err != nil {
				return nil, err
			}
			content = []byte(strVal)
		}

		reader, err := format.NewReader(parsing.DefaultReaderOptions())
		if err != nil {
			return nil, err
		}

		doc, err := reader.Read(content)
		if err != nil {
			return nil, err
		}

		return doc, nil
	},
	ValidateArgsExactly(2),
)

FuncParse parses the given data at runtime.

View Source
var FuncReadFile = NewFunc(
	"readFile",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		filepath, err := args[0].StringValue()
		if err != nil {
			return nil, fmt.Errorf("readFile: %w", err)
		}

		f, err := os.Open(filepath)
		if err != nil {
			return nil, fmt.Errorf("readFile: %w", err)
		}
		defer func() {
			_ = f.Close()
		}()

		fileBytes, err := io.ReadAll(f)
		if err != nil {
			return nil, fmt.Errorf("readFile: %w", err)
		}

		return model.NewStringValue(string(fileBytes)), nil
	},
	ValidateArgsExactly(1),
)

FuncReadFile reads the given filepath at runtime.

View Source
var FuncReplace = NewFunc(
	"replace",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		inputData := data
		if len(args)%2 != 0 {
			inputData = args[0]
			args = args[1:]
		}

		argStrings := make([]string, len(args))
		for i, arg := range args {
			s, err := arg.StringValue()
			if err != nil {
				return nil, err
			}
			argStrings[i] = s
		}
		replacer := strings.NewReplacer(argStrings...)

		inputString, err := inputData.StringValue()
		if err != nil {
			return nil, err
		}

		outputString := replacer.Replace(inputString)

		return model.NewStringValue(outputString), nil
	},
	ValidateArgsMin(2),
)

FuncReplace is a function that replaces all occurrences of a substring with another string.

View Source
var FuncReverse = NewFunc(
	"reverse",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		arg := args[0]

		switch arg.Type() {
		case model.TypeString:
			return arg.StringIndexRange(-1, 0)
		case model.TypeSlice:
			return arg.SliceIndexRange(-1, 0)
		default:
			return nil, fmt.Errorf("reverse expects a slice or string, got %s", arg.Type())
		}
	},
	ValidateArgsExactly(1),
)

FuncReverse is a function that reverses the input.

View Source
var FuncRound = NewFunc(
	"round",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if input.IsInt() {
			v, err := input.IntValue()
			if err != nil {
				return nil, fmt.Errorf("round could not read int value: %w", err)
			}
			return model.NewIntValue(v), nil
		}

		v, err := input.FloatValue()
		if err != nil {
			return nil, fmt.Errorf("round expects a numeric value: %w", err)
		}
		return model.NewIntValue(int64(math.Round(v))), nil
	},
	ValidateArgsMax(1),
)

FuncRound is a function that rounds a number to the nearest integer.

View Source
var FuncSplit = NewFunc(
	"split",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		separator, err := args[0].StringValue()
		if err != nil {
			return nil, fmt.Errorf("split expects a string separator as the first argument: %w", err)
		}

		var input string
		if len(args) == 2 {
			input, err = args[1].StringValue()
			if err != nil {
				return nil, fmt.Errorf("split expects a string as the second argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("split expects data to be a string: %w", err)
			}
		}

		parts := strings.Split(input, separator)

		res := model.NewSliceValue()
		for _, part := range parts {
			if err := res.Append(model.NewStringValue(part)); err != nil {
				return nil, fmt.Errorf("could not append split result: %w", err)
			}
		}

		return res, nil
	},
	ValidateArgsMinMax(1, 2),
)

FuncSplit is a function that splits a string by a separator into an array.

View Source
var FuncStartsWith = NewFunc(
	"startsWith",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input string
		var prefix string
		var err error

		if len(args) == 2 {
			input, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("startsWith expects a string as the first argument: %w", err)
			}
			prefix, err = args[1].StringValue()
			if err != nil {
				return nil, fmt.Errorf("startsWith expects a string prefix as the second argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("startsWith expects data to be a string: %w", err)
			}
			prefix, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("startsWith expects a string prefix as the first argument: %w", err)
			}
		}

		return model.NewBoolValue(strings.HasPrefix(input, prefix)), nil
	},
	ValidateArgsMinMax(1, 2),
)

FuncStartsWith is a function that checks if a string starts with a given prefix.

View Source
var FuncStringify = NewFunc(
	"stringify",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		formatStr, err := args[0].StringValue()
		if err != nil {
			return nil, err
		}
		format := parsing.Format(formatStr)

		input := data
		if len(args) == 2 {
			input = args[1]
		}

		writer, err := format.NewWriter(parsing.DefaultWriterOptions())
		if err != nil {
			return nil, err
		}

		b, err := writer.Write(input)
		if err != nil {
			return nil, err
		}

		b = bytes.TrimSuffix(b, []byte("\n"))

		return model.NewStringValue(string(b)), nil
	},
	ValidateArgsMinMax(1, 2),
)

FuncStringify serializes a structured value into a format string.

View Source
var FuncSum = NewFunc(
	"sum",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		returnType := model.TypeInt

		for _, arg := range args {
			if arg.IsInt() {
				continue
			}
			if arg.IsFloat() {
				returnType = model.TypeFloat
				break
			}
			return nil, fmt.Errorf("cannot sum non-numeric value of type %s", arg.Type().String())
		}

		switch returnType {
		case model.TypeInt:
			var sum int64
			for _, arg := range args {
				if arg.IsInt() {
					intVal, err := arg.IntValue()
					if err != nil {
						return nil, err
					}
					sum += intVal
					continue
				}

				floatVal, err := arg.FloatValue()
				if err != nil {
					return nil, err
				}
				sum += int64(floatVal)
			}
			return model.NewIntValue(sum), nil
		case model.TypeFloat:
			var sum float64
			for _, arg := range args {
				if arg.IsInt() {
					intVal, err := arg.IntValue()
					if err != nil {
						return nil, err
					}
					sum += float64(intVal)
					continue
				}

				floatVal, err := arg.FloatValue()
				if err != nil {
					return nil, err
				}
				sum += floatVal
			}
			return model.NewFloatValue(sum), nil
		default:
			return nil, fmt.Errorf("unsupported return type %s", returnType.String())
		}
	},
	ValidateArgsMin(1),
)

FuncSum is a function that returns the sum of the given numbers.

View Source
var FuncToBool = NewFunc(
	"toBool",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		switch args[0].Type() {
		case model.TypeBool:
			b, err := args[0].BoolValue()
			if err != nil {
				return nil, err
			}
			return model.NewBoolValue(b), nil
		case model.TypeString:
			s, err := args[0].StringValue()
			if err != nil {
				return nil, err
			}
			switch strings.ToLower(s) {
			case "true", "1", "yes":
				return model.NewBoolValue(true), nil
			case "false", "0", "no", "":
				return model.NewBoolValue(false), nil
			default:
				return nil, fmt.Errorf("cannot convert string %q to bool", s)
			}
		case model.TypeInt:
			i, err := args[0].IntValue()
			if err != nil {
				return nil, err
			}
			return model.NewBoolValue(i != 0), nil
		case model.TypeFloat:
			f, err := args[0].FloatValue()
			if err != nil {
				return nil, err
			}
			return model.NewBoolValue(f != 0), nil
		case model.TypeNull:
			return model.NewBoolValue(false), nil
		default:
			return nil, fmt.Errorf("cannot convert %s to bool", args[0].Type())
		}
	},
	ValidateArgsExactly(1),
)

FuncToBool is a function that converts the given value to a bool.

View Source
var FuncToFloat = NewFunc(
	"toFloat",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		switch args[0].Type() {
		case model.TypeString:
			stringValue, err := args[0].StringValue()
			if err != nil {
				return nil, err
			}

			i, err := strconv.ParseFloat(stringValue, 64)
			if err != nil {
				return nil, err
			}

			return model.NewFloatValue(i), nil
		case model.TypeInt:
			i, err := args[0].IntValue()
			if err != nil {
				return nil, err
			}
			return model.NewFloatValue(float64(i)), nil
		case model.TypeFloat:
			return args[0], nil
		case model.TypeBool:
			i, err := args[0].BoolValue()
			if err != nil {
				return nil, err
			}
			if i {
				return model.NewFloatValue(1), nil
			}
			return model.NewFloatValue(0), nil
		default:
			return nil, fmt.Errorf("cannot convert %s to float", args[0].Type())
		}
	},
	ValidateArgsExactly(1),
)

FuncToFloat is a function that converts the given value to a string.

View Source
var FuncToInt = NewFunc(
	"toInt",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		switch args[0].Type() {
		case model.TypeString:
			stringValue, err := args[0].StringValue()
			if err != nil {
				return nil, err
			}

			i, err := strconv.ParseInt(stringValue, 0, 64)
			if err != nil {
				return nil, err
			}

			return model.NewIntValue(i), nil
		case model.TypeInt:
			return args[0], nil
		case model.TypeFloat:
			i, err := args[0].FloatValue()
			if err != nil {
				return nil, err
			}
			return model.NewIntValue(int64(i)), nil
		case model.TypeBool:
			i, err := args[0].BoolValue()
			if err != nil {
				return nil, err
			}
			if i {
				return model.NewIntValue(1), nil
			}
			return model.NewIntValue(0), nil
		default:
			return nil, fmt.Errorf("cannot convert %s to int", args[0].Type())
		}
	},
	ValidateArgsExactly(1),
)

FuncToInt is a function that converts the given value to a string.

View Source
var FuncToLower = NewFunc(
	"toLower",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input string
		var err error
		if len(args) == 1 {
			input, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("toLower expects a string argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("toLower expects data to be a string: %w", err)
			}
		}
		return model.NewStringValue(strings.ToLower(input)), nil
	},
	ValidateArgsMax(1),
)

FuncToLower is a function that converts a string to lowercase.

View Source
var FuncToString = NewFunc(
	"toString",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		switch args[0].Type() {
		case model.TypeString:
			stringValue, err := args[0].StringValue()
			if err != nil {
				return nil, err
			}
			model.NewStringValue(stringValue)
			return args[0], nil
		case model.TypeInt:
			i, err := args[0].IntValue()
			if err != nil {
				return nil, err
			}
			return model.NewStringValue(fmt.Sprintf("%d", i)), nil
		case model.TypeFloat:
			i, err := args[0].FloatValue()
			if err != nil {
				return nil, err
			}
			return model.NewStringValue(fmt.Sprintf("%g", i)), nil
		case model.TypeBool:
			i, err := args[0].BoolValue()
			if err != nil {
				return nil, err
			}
			return model.NewStringValue(fmt.Sprintf("%v", i)), nil
		default:
			return nil, fmt.Errorf("cannot convert %s to string", args[0].Type())
		}
	},
	ValidateArgsExactly(1),
)

FuncToString is a function that converts the given value to a string.

View Source
var FuncToUpper = NewFunc(
	"toUpper",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input string
		var err error
		if len(args) == 1 {
			input, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("toUpper expects a string argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("toUpper expects data to be a string: %w", err)
			}
		}
		return model.NewStringValue(strings.ToUpper(input)), nil
	},
	ValidateArgsMax(1),
)

FuncToUpper is a function that converts a string to uppercase.

View Source
var FuncTrim = NewFunc(
	"trim",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input string
		var err error
		if len(args) == 1 {
			input, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("trim expects a string argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("trim expects data to be a string: %w", err)
			}
		}
		return model.NewStringValue(strings.TrimSpace(input)), nil
	},
	ValidateArgsMax(1),
)

FuncTrim is a function that trims whitespace from both ends of a string.

View Source
var FuncTrimPrefix = NewFunc(
	"trimPrefix",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input string
		var prefix string
		var err error

		if len(args) == 2 {
			input, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("trimPrefix expects a string as the first argument: %w", err)
			}
			prefix, err = args[1].StringValue()
			if err != nil {
				return nil, fmt.Errorf("trimPrefix expects a string prefix as the second argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("trimPrefix expects data to be a string: %w", err)
			}
			prefix, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("trimPrefix expects a string prefix as the first argument: %w", err)
			}
		}

		return model.NewStringValue(strings.TrimPrefix(input, prefix)), nil
	},
	ValidateArgsMinMax(1, 2),
)

FuncTrimPrefix is a function that trims a prefix from a string.

View Source
var FuncTrimSuffix = NewFunc(
	"trimSuffix",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input string
		var suffix string
		var err error

		if len(args) == 2 {
			input, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("trimSuffix expects a string as the first argument: %w", err)
			}
			suffix, err = args[1].StringValue()
			if err != nil {
				return nil, fmt.Errorf("trimSuffix expects a string suffix as the second argument: %w", err)
			}
		} else {
			input, err = data.StringValue()
			if err != nil {
				return nil, fmt.Errorf("trimSuffix expects data to be a string: %w", err)
			}
			suffix, err = args[0].StringValue()
			if err != nil {
				return nil, fmt.Errorf("trimSuffix expects a string suffix as the first argument: %w", err)
			}
		}

		return model.NewStringValue(strings.TrimSuffix(input, suffix)), nil
	},
	ValidateArgsMinMax(1, 2),
)

FuncTrimSuffix is a function that trims a suffix from a string.

View Source
var FuncTypeOf = NewFunc(
	"typeOf",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		return model.NewStringValue(args[0].Type().String()), nil
	},
	ValidateArgsExactly(1),
)

FuncTypeOf is a function that returns the type of the first argument as a string.

View Source
var FuncUnique = NewFunc(
	"unique",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		var input *model.Value
		if len(args) == 1 {
			input = args[0]
		} else {
			input = data
		}

		if !input.IsSlice() {
			return nil, fmt.Errorf("unique expects an array, got %s", input.Type().String())
		}

		res := model.NewSliceValue()
		if err := input.RangeSlice(func(i int, value *model.Value) error {
			duplicate := false
			_ = res.RangeSlice(func(j int, existing *model.Value) error {
				equal, err := value.EqualTypeValue(existing)
				if err != nil {
					return nil
				}
				if equal {
					duplicate = true
				}
				return nil
			})
			if !duplicate {
				return res.Append(value)
			}
			return nil
		}); err != nil {
			return nil, err
		}

		return res, nil
	},
	ValidateArgsMax(1),
)

FuncUnique is a function that removes duplicate values from an array.

View Source
var FuncValues = NewFunc(
	"values",
	func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error) {
		if !data.IsMap() {
			return nil, fmt.Errorf("values can only be used on maps, got %s", data.Type().String())
		}

		res := model.NewSliceValue()
		if err := data.RangeMap(func(key string, value *model.Value) error {
			return res.Append(value)
		}); err != nil {
			return nil, err
		}

		return res, nil
	},
	ValidateArgsExactly(0),
)

FuncValues returns the values of a map as an array.

Functions

func ExecuteAST

func ExecuteAST(ctx context.Context, expr ast.Expr, value *model.Value, options *Options) (*model.Value, error)

ExecuteAST executes the given AST with the given input.

func ExecuteSelector

func ExecuteSelector(ctx context.Context, selectorStr string, value *model.Value, opts *Options) (*model.Value, error)

ExecuteSelector parses the selector and executes the resulting AST with the given input.

func ExecutorDepth

func ExecutorDepth(ctx context.Context) int

func ExecutorID

func ExecutorID(ctx context.Context) string

func ExecutorPath

func ExecutorPath(ctx context.Context) string

func WithExecutorID

func WithExecutorID(ctx context.Context, executorID string) context.Context

Types

type ArgsValidator

type ArgsValidator func(ctx context.Context, name string, args model.Values) error

ArgsValidator is a function that validates the arguments passed to a function.

func ValidateArgsExactly

func ValidateArgsExactly(expected int) ArgsValidator

ValidateArgsExactly returns an ArgsValidator that validates that the number of arguments passed to a function is exactly the expected number.

func ValidateArgsMax

func ValidateArgsMax(expected int) ArgsValidator

ValidateArgsMax returns an ArgsValidator that validates that the number of arguments passed to a function is at most the expected number.

func ValidateArgsMin

func ValidateArgsMin(expected int) ArgsValidator

ValidateArgsMin returns an ArgsValidator that validates that the number of arguments passed to a function is at least the expected number.

func ValidateArgsMinMax

func ValidateArgsMinMax(min int, max int) ArgsValidator

ValidateArgsMinMax returns an ArgsValidator that validates that the number of arguments passed to a function is between the min and max expected numbers.

type ExecuteOptionFn

type ExecuteOptionFn func(*Options)

ExecuteOptionFn is a function that can be used to set options on the execution of the selector.

func WithFuncs

func WithFuncs(fc FuncCollection) ExecuteOptionFn

WithFuncs sets the functions that can be used in the selector.

func WithUnstable

func WithUnstable() ExecuteOptionFn

WithUnstable allows access to potentially unstable features.

func WithVariable

func WithVariable(key string, val *model.Value) ExecuteOptionFn

WithVariable sets a variable for use in the selector.

func WithoutUnstable

func WithoutUnstable() ExecuteOptionFn

WithoutUnstable disallows access to potentially unstable features.

type Func

type Func struct {
	// contains filtered or unexported fields
}

Func represents a function that can be executed.

func NewFunc

func NewFunc(name string, handler FuncFn, argsValidator ArgsValidator) *Func

NewFunc creates a new Func.

func (*Func) Handler

func (f *Func) Handler() FuncFn

Handler returns a FuncFn that can be used to execute the function.

type FuncCollection

type FuncCollection map[string]FuncFn

FuncCollection is a collection of functions that can be executed.

func NewFuncCollection

func NewFuncCollection(funcs ...*Func) FuncCollection

NewFuncCollection creates a new FuncCollection with the given functions.

func (FuncCollection) Copy

func (fc FuncCollection) Copy() FuncCollection

Copy returns a copy of the FuncCollection.

func (FuncCollection) Delete

func (fc FuncCollection) Delete(names ...string) FuncCollection

Delete deletes the functions with the given names.

func (FuncCollection) Get

func (fc FuncCollection) Get(name string) (FuncFn, bool)

Get returns the function with the given name.

func (FuncCollection) Register

func (fc FuncCollection) Register(funcs ...*Func) FuncCollection

Register registers the given functions with the FuncCollection.

type FuncFn

type FuncFn func(ctx context.Context, data *model.Value, args model.Values) (*model.Value, error)

FuncFn is a function that can be executed.

type Options

type Options struct {
	Funcs    FuncCollection
	Vars     map[string]*model.Value
	Unstable bool
}

Options contains the options for the execution of the selector.

func NewOptions

func NewOptions(opts ...ExecuteOptionFn) *Options

NewOptions creates a new Options struct with the given options.

Jump to

Keyboard shortcuts

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