mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? Follow up to #7761: 1. Use more concrete types instead of `comparable`. 2. Use `softassert` for "compile" time errors. 3. Rename `chasm.Collection` to `chasm.Map` but left proto `CollectionAttributes` intact. This will allow to add support for other collection type in future (slice, array). ## Why? It is better to narrow key type as much as possible. Other types are not supported anyway. ## How did you test it? - [ ] built - [ ] run locally and tested manually - [x] covered by existing tests - [x] added new unit test(s) - [ ] added new functional test(s)
99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
package chasm
|
|
|
|
import (
|
|
"iter"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"go.temporal.io/api/serviceerror"
|
|
)
|
|
|
|
const (
|
|
chasmFieldTypePrefix = "chasm.Field["
|
|
chasmMapTypePrefix = "chasm.Map["
|
|
|
|
fieldNameTag = "name"
|
|
)
|
|
|
|
type fieldKind uint8
|
|
|
|
const (
|
|
fieldKindUnspecified fieldKind = iota
|
|
fieldKindData
|
|
fieldKindSubField
|
|
fieldKindSubMap
|
|
)
|
|
|
|
type fieldInfo struct {
|
|
val reflect.Value
|
|
typ reflect.Type
|
|
name string
|
|
kind fieldKind
|
|
err error
|
|
}
|
|
|
|
//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, "*") {
|
|
fieldErr = serviceerror.NewInternalf("%s.%s: chasm field type %s must not be a pointer", valueT, fieldN, fieldT)
|
|
} else {
|
|
switch prefix {
|
|
case chasmFieldTypePrefix:
|
|
fieldK = fieldKindSubField
|
|
case chasmMapTypePrefix:
|
|
fieldK = fieldKindSubMap
|
|
default:
|
|
fieldErr = serviceerror.NewInternalf("%s.%s: unsupported field type %s: must implement proto.Message, or be chasm.Field[T] or chasm.Map[T]", valueT, fieldN, fieldT)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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)})
|
|
}
|
|
}
|
|
}
|
|
|
|
func genericTypePrefix(t reflect.Type) string {
|
|
tn := t.String()
|
|
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
|
|
}
|