mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? Added a new special `MSPointer` type to the CHASM framework ## Why? To support components invoking methods from the underlying mutable state. Specifically, Nexus callbacks will need to get the Nexus completion from the mutable state. ## How did you test it? updated unit tests
145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
package chasm
|
|
|
|
import (
|
|
"iter"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"go.temporal.io/api/serviceerror"
|
|
)
|
|
|
|
const (
|
|
chasmFieldTypePrefix = "chasm.Field["
|
|
chasmMapTypePrefix = "chasm.Map["
|
|
chasmMSPointerType = "chasm.MSPointer"
|
|
|
|
fieldNameTag = "name"
|
|
)
|
|
|
|
type fieldKind uint8
|
|
|
|
const (
|
|
fieldKindUnspecified fieldKind = iota
|
|
fieldKindData
|
|
fieldKindSubField
|
|
fieldKindSubMap
|
|
fieldKindMutableState
|
|
)
|
|
|
|
type fieldInfo struct {
|
|
val reflect.Value
|
|
typ reflect.Type
|
|
name string
|
|
kind fieldKind
|
|
err error
|
|
}
|
|
|
|
// fieldsOf iterates across all CHASM-managed fields of a struct. Other fields
|
|
// are not yielded.
|
|
//
|
|
//nolint:revive // cognitive complexity 26 (> max enabled 25)
|
|
func fieldsOf(valueV reflect.Value) iter.Seq[fieldInfo] {
|
|
valueT := valueV.Type()
|
|
dataFieldName := ""
|
|
return func(yield func(fi fieldInfo) bool) {
|
|
for i := 0; i < valueT.Elem().NumField(); i++ {
|
|
fieldV := valueV.Elem().Field(i)
|
|
fieldT := fieldV.Type()
|
|
if fieldT == UnimplementedComponentT {
|
|
continue
|
|
}
|
|
|
|
fieldN := fieldName(valueT.Elem().Field(i))
|
|
var fieldErr error
|
|
fieldK := fieldKindUnspecified
|
|
if fieldT.AssignableTo(protoMessageT) {
|
|
if dataFieldName != "" {
|
|
fieldErr = serviceerror.NewInternalf("%s.%s: only one data field %s (implements proto.Message) allowed in component", valueT, fieldN, dataFieldName)
|
|
}
|
|
dataFieldName = fieldN
|
|
fieldK = fieldKindData
|
|
} else {
|
|
prefix := genericTypePrefix(fieldT)
|
|
if strings.HasPrefix(prefix, "*") {
|
|
switch prefix[1:] {
|
|
case chasmFieldTypePrefix, chasmMapTypePrefix, chasmMSPointerType:
|
|
fieldErr = serviceerror.NewInternalf("%s.%s: CHASM fields must not be pointers", valueT, fieldN)
|
|
default:
|
|
continue
|
|
}
|
|
} else {
|
|
switch prefix {
|
|
case chasmFieldTypePrefix:
|
|
fieldK = fieldKindSubField
|
|
case chasmMapTypePrefix:
|
|
fieldK = fieldKindSubMap
|
|
case chasmMSPointerType:
|
|
fieldK = fieldKindMutableState
|
|
default:
|
|
continue // Skip non-CHASM fields.
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if !yield(fieldInfo{val: fieldV, typ: fieldT, name: fieldN, kind: fieldK, err: fieldErr}) {
|
|
return
|
|
}
|
|
}
|
|
// If the data field is not found, generate one more fake field with only an error set.
|
|
if dataFieldName == "" {
|
|
yield(fieldInfo{err: serviceerror.NewInternalf("%s: no data field (implements proto.Message) found", valueT)})
|
|
}
|
|
}
|
|
}
|
|
|
|
// unmanagedFieldsOf yields all non-CHASM managed fields of a struct.
|
|
func unmanagedFieldsOf(valueT reflect.Type) iter.Seq[fieldInfo] {
|
|
return func(yield func(fi fieldInfo) bool) {
|
|
if valueT.Kind() == reflect.Pointer {
|
|
valueT = valueT.Elem()
|
|
}
|
|
for i := range valueT.NumField() {
|
|
fieldT := valueT.Field(i).Type
|
|
if fieldT == UnimplementedComponentT {
|
|
continue
|
|
}
|
|
|
|
// Skip the data field, which is always CHASM-managed.
|
|
if fieldT.AssignableTo(protoMessageT) {
|
|
continue
|
|
}
|
|
|
|
fieldN := fieldName(valueT.Field(i))
|
|
prefix := genericTypePrefix(fieldT)
|
|
switch prefix {
|
|
case chasmFieldTypePrefix, chasmMapTypePrefix, chasmMSPointerType:
|
|
continue // Skip CHASM fields.
|
|
default:
|
|
if !yield(fieldInfo{typ: fieldT, name: fieldN}) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func genericTypePrefix(t reflect.Type) string {
|
|
tn := t.String()
|
|
if tn == chasmMSPointerType {
|
|
return chasmMSPointerType
|
|
}
|
|
bracketPos := strings.Index(tn, "[")
|
|
if bracketPos == -1 {
|
|
return ""
|
|
}
|
|
return tn[:bracketPos+1]
|
|
}
|
|
|
|
func fieldName(f reflect.StructField) string {
|
|
if tagName := f.Tag.Get(fieldNameTag); tagName != "" {
|
|
return tagName
|
|
}
|
|
return f.Name
|
|
}
|