execution

package
v3.2.1 Latest Latest
Warning

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

Go to latest
Published: Jan 5, 2026 License: MIT Imports: 15 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 (
	// DefaultFuncCollection is the default collection of functions that can be executed.
	DefaultFuncCollection = NewFuncCollection(
		FuncLen,
		FuncAdd,
		FuncToString,
		FuncToInt,
		FuncToFloat,
		FuncMerge,
		FuncReverse,
		FuncTypeOf,
		FuncMax,
		FuncMin,
		FuncIgnore,
		FuncBase64Encode,
		FuncBase64Decode,
		FuncParse,
		FuncReadFile,
		FuncHas,
		FuncGet,
		FuncContains,
		FuncSum,
		FuncJoin,
	)
)
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 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 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 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:

			if data.Type() != model.TypeSlice {
				return model.NewBoolValue(false), nil
			}
			index, err := arg.IntValue()
			if err != nil {
				return nil, err
			}
			return data.GetSliceIndex(int(index))
		case model.TypeString:

			if data.Type() != model.TypeMap {
				return model.NewBoolValue(false), nil
			}
			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")
		}
	},
	ValidateArgsMin(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 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 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 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 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 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, 10, 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 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 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.

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