go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/pkg/funcs/expression.go (about)

     1  /*
     2  
     3  Copyright (c) 2023 - Present. Will Charczuk. All rights reserved.
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     5  
     6  */
     7  
     8  package funcs
     9  
    10  import (
    11  	"context"
    12  	"fmt"
    13  
    14  	"github.com/wcharczuk/go-incr"
    15  	"go.starlark.net/starlark"
    16  
    17  	"go.charczuk.com/sdk/errutil"
    18  
    19  	"go.charczuk.com/projects/nodes/pkg/incrutil"
    20  )
    21  
    22  // Expression returns a function that evaluates a given expression.
    23  func Expression[Input, Output any](expression string) func(context.Context, *incrutil.Inputs[Input]) (Output, error) {
    24  	return func(ctx context.Context, inputs *incrutil.Inputs[Input]) (output Output, err error) {
    25  		defer func() {
    26  			if r := recover(); r != nil {
    27  				err = errutil.New(r)
    28  			}
    29  		}()
    30  
    31  		inputFunc := createInputFunc(inputs)
    32  		var builtins = starlark.StringDict{
    33  			"input": starlark.NewBuiltin("input", inputFunc),
    34  		}
    35  		for k, v := range defaultBuiltins {
    36  			builtins[k] = v
    37  		}
    38  		thread := new(starlark.Thread)
    39  		if tracer := incr.GetTracer(ctx); tracer != nil {
    40  			thread.Print = func(_ *starlark.Thread, msg string) {
    41  				tracer.Print("expression:", msg)
    42  			}
    43  		}
    44  		var sd starlark.StringDict
    45  		sd, err = starlark.ExecFileOptions(&expressionFileOptions, thread, "<expr>", expression, builtins)
    46  		if err != nil {
    47  			return
    48  		}
    49  		if sd == nil {
    50  			return
    51  		}
    52  		v, ok := sd["output"]
    53  		if !ok {
    54  			err = fmt.Errorf("expression: you must use the `output` global to set the output")
    55  			return
    56  		}
    57  		if v == nil {
    58  			return
    59  		}
    60  		switch typed := v.(type) {
    61  		case *starlark.Dict:
    62  			output, err = starlarkDictToOutput[Output](typed)
    63  		case *starlark.List:
    64  			output, err = starlarkListToOutput[Output](typed)
    65  		default:
    66  			output, err = starlarkScalarToOutput[Output](v)
    67  		}
    68  		return
    69  	}
    70  }