lang: core, funcs: Port some functions to CallableFunc API

Some modern features of our function engine and language might require
this new API, so port what we can and figure out the rest later.
This commit is contained in:
James Shubin
2025-03-16 23:23:57 -04:00
parent f313380480
commit 642c6b952f
29 changed files with 702 additions and 291 deletions

View File

@@ -61,8 +61,9 @@ type ReadFileFunc struct {
data *interfaces.FuncData
last types.Value // last value received to use for diff
filename *string // the active filename
result *string // last calculated output
args []types.Value
filename *string // the active filename
result types.Value // last calculated output
}
// String returns a simple name for this function. This is needed so this struct
@@ -129,7 +130,13 @@ func (obj *ReadFileFunc) Stream(ctx context.Context) error {
}
obj.last = input // store for next
filename := input.Struct()[readFileArgNameFilename].Str()
args, err := interfaces.StructToCallableArgs(input) // []types.Value, error)
if err != nil {
return err
}
obj.args = args
filename := args[0].Str()
// TODO: add validation for absolute path?
// TODO: add check for empty string
if obj.filename != nil && *obj.filename == filename {
@@ -137,48 +144,61 @@ func (obj *ReadFileFunc) Stream(ctx context.Context) error {
}
obj.filename = &filename
p := strings.TrimSuffix(obj.data.Base, "/")
if p == obj.data.Base { // didn't trim, so we fail
// programming error
return fmt.Errorf("no trailing slash on Base, got: `%s`", p)
}
path := p
if !strings.HasPrefix(*obj.filename, "/") {
return fmt.Errorf("filename was not absolute, got: `%s`", *obj.filename)
//path += "/" // be forgiving ?
}
path += *obj.filename
fs, err := obj.init.World.Fs(obj.data.FsURI) // open the remote file system
result, err := obj.Call(ctx, obj.args)
if err != nil {
return errwrap.Wrapf(err, "can't load code from file system `%s`", obj.data.FsURI)
}
// this is relative to the module dir the func is in!
content, err := fs.ReadFile(path) // open the remote file system
// We could use it directly, but it feels like less correct.
//content, err := obj.data.Fs.ReadFile(path) // open the remote file system
if err != nil {
return errwrap.Wrapf(err, "can't read file `%s` (%s)", *obj.filename, path)
return err
}
result := string(content) // convert to string
if obj.result != nil && *obj.result == result {
// if the result is still the same, skip sending an update...
if obj.result != nil && result.Cmp(obj.result) == nil {
continue // result didn't change
}
obj.result = &result // store new result
obj.result = result // store new result
case <-ctx.Done():
return nil
}
select {
case obj.init.Output <- &types.StrValue{
V: *obj.result,
}:
case obj.init.Output <- obj.result: // send
// pass
case <-ctx.Done():
return nil
}
}
}
// Call this function with the input args and return the value if it is possible
// to do so at this time.
func (obj *ReadFileFunc) Call(ctx context.Context, args []types.Value) (types.Value, error) {
filename := args[0].Str()
p := strings.TrimSuffix(obj.data.Base, "/")
if p == obj.data.Base { // didn't trim, so we fail
// programming error
return nil, fmt.Errorf("no trailing slash on Base, got: `%s`", p)
}
path := p
if !strings.HasPrefix(filename, "/") {
return nil, fmt.Errorf("filename was not absolute, got: `%s`", filename)
//path += "/" // be forgiving ?
}
path += filename
fs, err := obj.init.World.Fs(obj.data.FsURI) // open the remote file system
if err != nil {
return nil, errwrap.Wrapf(err, "can't load code from file system `%s`", obj.data.FsURI)
}
// this is relative to the module dir the func is in!
content, err := fs.ReadFile(path) // open the remote file system
// We could use it directly, but it feels like less correct.
//content, err := obj.data.Fs.ReadFile(path) // open the remote file system
if err != nil {
return nil, errwrap.Wrapf(err, "can't read file `%s` (%s)", filename, path)
}
return &types.StrValue{
V: string(content), // convert to string
}, nil
}