kis-flow/kis/faas.go
2024-04-16 15:28:59 +08:00

151 lines
5.0 KiB
Go

package kis
import (
"errors"
"fmt"
"reflect"
"runtime"
"strings"
)
// FaaS Function as a Service
// Change the type definition from:
// type FaaS func(context.Context, Flow) error
// to:
// type FaaS func(context.Context, Flow, ...interface{}) error
// This allows passing data through variadic parameters of any type.
type FaaS interface{}
// FaaSDesc describes the FaaS callback computation function.
type FaaSDesc struct {
Serialize // Serialization implementation for the current Function's data input and output
FnName string // Function name
f interface{} // FaaS function
fName string // Function name
ArgsType []reflect.Type // Function parameter types (collection)
ArgNum int // Number of function parameters
FuncType reflect.Type // Function type
FuncValue reflect.Value // Function value (function address)
}
// NewFaaSDesc creates an instance of FaaSDesc description based on the registered FnName and FaaS callback function.
func NewFaaSDesc(fnName string, f FaaS) (*FaaSDesc, error) {
// Serialization instance
var serializeImpl Serialize
// Callback function value (function address)
funcValue := reflect.ValueOf(f)
// Callback function type
funcType := funcValue.Type()
// Check if the provided FaaS pointer is a function type
if !isFuncType(funcType) {
return nil, fmt.Errorf("provided FaaS type is %s, not a function", funcType.Name())
}
// Check if the FaaS function has a return value that only includes (error)
if funcType.NumOut() != 1 || funcType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
return nil, errors.New("function must have exactly one return value of type error")
}
// FaaS function parameter types
argsType := make([]reflect.Type, funcType.NumIn())
// Get the FaaS function name
fullName := runtime.FuncForPC(funcValue.Pointer()).Name()
// Ensure that the FaaS function parameter list contains context.Context and kis.Flow
// Check if the function contains a parameter of type kis.Flow
containsKisFlow := false
// Check if the function contains a parameter of type context.Context
containsCtx := false
// Iterate over the FaaS function parameter types
for i := 0; i < funcType.NumIn(); i++ {
// Get the i-th formal parameter type
paramType := funcType.In(i)
if isFlowType(paramType) {
// Check if the function contains a parameter of type kis.Flow
containsKisFlow = true
} else if isContextType(paramType) {
// Check if the function contains a parameter of type context.Context
containsCtx = true
} else if isSliceType(paramType) {
// Get the element type of the current parameter Slice
itemType := paramType.Elem()
// If the current parameter is a pointer type, get the struct type that the pointer points to
if itemType.Kind() == reflect.Ptr {
itemType = itemType.Elem() // Get the struct type that the pointer points to
}
// Check if f implements Serialize interface
if isSerialize(itemType) {
// If the current parameter implements the Serialize interface, use the serialization implementation of the current parameter
serializeImpl = reflect.New(itemType).Interface().(Serialize)
} else {
// If the current parameter does not implement the Serialize interface, use the default serialization implementation
serializeImpl = defaultSerialize // Use global default implementation
}
}
// Append the current parameter type to the argsType collection
argsType[i] = paramType
}
if !containsKisFlow {
// If the function parameter list does not contain a parameter of type kis.Flow, return an error
return nil, errors.New("function parameters must have kis.Flow param, please use FaaS type like: [type FaaS func(context.Context, Flow, ...interface{}) error]")
}
if !containsCtx {
// If the function parameter list does not contain a parameter of type context.Context, return an error
return nil, errors.New("function parameters must have context, please use FaaS type like: [type FaaS func(context.Context, Flow, ...interface{}) error]")
}
// Return the FaaSDesc description instance
return &FaaSDesc{
Serialize: serializeImpl,
FnName: fnName,
f: f,
fName: fullName,
ArgsType: argsType,
ArgNum: len(argsType),
FuncType: funcType,
FuncValue: funcValue,
}, nil
}
// isFuncType checks whether the provided paramType is a function type
func isFuncType(paramType reflect.Type) bool {
return paramType.Kind() == reflect.Func
}
// isFlowType checks whether the provided paramType is of type kis.Flow
func isFlowType(paramType reflect.Type) bool {
var flowInterfaceType = reflect.TypeOf((*Flow)(nil)).Elem()
return paramType.Implements(flowInterfaceType)
}
// isContextType checks whether the provided paramType is of type context.Context
func isContextType(paramType reflect.Type) bool {
typeName := paramType.Name()
return strings.Contains(typeName, "Context")
}
// isSliceType checks whether the provided paramType is a slice type
func isSliceType(paramType reflect.Type) bool {
return paramType.Kind() == reflect.Slice
}