Files
poiesis/internal/functions/types.go
2026-01-29 21:31:49 -05:00

93 lines
1.8 KiB
Go

package functions
import (
"context"
"errors"
"reflect"
)
type Function interface {
Name() string
Types() map[string]string
Definition() string
IsAsync() bool
Arguments() []reflect.Type
Call(context.Context, []any) (any, error)
}
type GoFunc[A Args, R any] func(context.Context, A) (R, error)
type Args interface {
Validate() error
}
type functionImpl[A Args, R any] struct {
name string
fn GoFunc[A, R]
definition string
types map[string]string
isAsync bool
}
func (b *functionImpl[A, R]) Name() string {
return b.name
}
func (b *functionImpl[A, R]) Types() map[string]string {
return b.types
}
func (b *functionImpl[A, R]) Definition() string {
return b.definition
}
func (b *functionImpl[A, R]) IsAsync() bool {
return b.isAsync
}
func (b *functionImpl[A, R]) Function() any {
return b.fn
}
func (b *functionImpl[A, R]) Arguments() []reflect.Type {
// Collect Argument Types
var allTypes []reflect.Type
rType := reflect.TypeFor[A]()
for i := range rType.NumField() {
allTypes = append(allTypes, rType.Field(i).Type)
}
return allTypes
}
func (b *functionImpl[A, R]) Call(ctx context.Context, allArgs []any) (any, error) {
return b.CallGeneric(ctx, allArgs)
}
func (b *functionImpl[A, R]) CallGeneric(ctx context.Context, allArgs []any) (zeroR R, err error) {
// Populate Arguments
var fnArgs A
aVal := reflect.ValueOf(&fnArgs).Elem()
// Populate Fields
for i := range min(aVal.NumField(), len(allArgs)) {
field := aVal.Field(i)
// Validate Field is Settable
if !field.CanSet() {
return zeroR, errors.New("cannot set field")
}
// Validate and Set Field Value
argVal := reflect.ValueOf(allArgs[i])
if !argVal.Type().AssignableTo(field.Type()) {
return zeroR, errors.New("cannot assign field")
}
field.Set(argVal)
}
// Execute Function
return b.fn(ctx, fnArgs)
}