mirror of
https://github.com/agent-substrate/substrate.git
synced 2026-11-13 10:43:28 +08:00
This mostly eliminates the need to hand-write validation code. This PR is a long series of commits which add DV for most of Actor and all of Atespace. Here is a map to the commits: * The first few take a dep on a new Kubernetes tag, import the code into third_party, and apply a single patch. Because we have different Go modules for tools, I had to do it twice. When that patch lands, we can revert these commits, but that won't be until the 1.38 cycle in a few months. * The next commits slowly add DV support, so a human can review each of them in a reasonable amount of time. The emphasis is on great test cases. * I made a bad choice early on as to where to generate the code into, so I moved it. Rebasing on that was exceedingly hard, so I left it as a move. * This required changing update/go-generate -> update/codegen -- we need to get the ordering of tools right, which `go generate` does not guarantee. * Then I added a "middle" layer called "ServiceImpl" between the RPC and storage layers. This allows things like workflow to call the same business logic as the RPC layer, including validation. Lots of test fixes. * Then I finished the Create() and Update() paths for Actor. Those represent the "right" (or closest to) way to implement resources, and tests for validation. I strongly encourage reviewers to read it commit-by-commit. Rebasing this is VERY tedious, so the sooner it lands or dies completely, the better. Then we can start converting the rest. `./hack/run-tool.sh validation-gen --docs` will produce some docs on the tool and the available tags. @laoj2 @juli4n @EItanya @HavenXia @lalitc375 @yongruilin @jpbetz FYI
141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package jsonname
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// DefaultJSONNameProvider is the default cache for types.
|
|
var DefaultJSONNameProvider = NewNameProvider() //nolint:gochecknoglobals // default settings, for backward compatible package-level settings
|
|
|
|
var _ providerIface = (*NameProvider)(nil)
|
|
|
|
// NameProvider represents an object capable of translating from go property names to json property
|
|
// names.
|
|
//
|
|
// This type is thread-safe.
|
|
//
|
|
// See [github.com/go-openapi/jsonpointer.Pointer] for an example.
|
|
type NameProvider struct {
|
|
lock *sync.Mutex
|
|
index map[reflect.Type]nameIndex
|
|
}
|
|
|
|
type nameIndex struct {
|
|
jsonNames map[string]string
|
|
goNames map[string]string
|
|
}
|
|
|
|
// NewNameProvider creates a new name provider.
|
|
func NewNameProvider() *NameProvider {
|
|
return &NameProvider{
|
|
lock: &sync.Mutex{},
|
|
index: make(map[reflect.Type]nameIndex),
|
|
}
|
|
}
|
|
|
|
func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) {
|
|
for i := range tpe.NumField() {
|
|
targetDes := tpe.Field(i)
|
|
|
|
if targetDes.PkgPath != "" { // unexported
|
|
continue
|
|
}
|
|
|
|
if targetDes.Anonymous { // walk embedded structures tree down first
|
|
buildnameIndex(targetDes.Type, idx, reverseIdx)
|
|
continue
|
|
}
|
|
|
|
if tag := targetDes.Tag.Get("json"); tag != "" {
|
|
|
|
parts := strings.Split(tag, ",")
|
|
if len(parts) == 0 {
|
|
continue
|
|
}
|
|
|
|
nm := parts[0]
|
|
if nm == "-" {
|
|
continue
|
|
}
|
|
if nm == "" { // empty string means we want to use the Go name
|
|
nm = targetDes.Name
|
|
}
|
|
|
|
idx[nm] = targetDes.Name
|
|
reverseIdx[targetDes.Name] = nm
|
|
}
|
|
}
|
|
}
|
|
|
|
func newNameIndex(tpe reflect.Type) nameIndex {
|
|
idx := make(map[string]string, tpe.NumField())
|
|
reverseIdx := make(map[string]string, tpe.NumField())
|
|
|
|
buildnameIndex(tpe, idx, reverseIdx)
|
|
return nameIndex{jsonNames: idx, goNames: reverseIdx}
|
|
}
|
|
|
|
// GetJSONNames gets all the json property names for a type.
|
|
func (n *NameProvider) GetJSONNames(subject any) []string {
|
|
n.lock.Lock()
|
|
defer n.lock.Unlock()
|
|
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
|
names, ok := n.index[tpe]
|
|
if !ok {
|
|
names = n.makeNameIndex(tpe)
|
|
}
|
|
|
|
res := make([]string, 0, len(names.jsonNames))
|
|
for k := range names.jsonNames {
|
|
res = append(res, k)
|
|
}
|
|
return res
|
|
}
|
|
|
|
// GetJSONName gets the json name for a go property name.
|
|
func (n *NameProvider) GetJSONName(subject any, name string) (string, bool) {
|
|
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
|
return n.GetJSONNameForType(tpe, name)
|
|
}
|
|
|
|
// GetJSONNameForType gets the json name for a go property name on a given type.
|
|
func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) {
|
|
n.lock.Lock()
|
|
defer n.lock.Unlock()
|
|
names, ok := n.index[tpe]
|
|
if !ok {
|
|
names = n.makeNameIndex(tpe)
|
|
}
|
|
nme, ok := names.goNames[name]
|
|
return nme, ok
|
|
}
|
|
|
|
// GetGoName gets the go name for a json property name.
|
|
func (n *NameProvider) GetGoName(subject any, name string) (string, bool) {
|
|
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
|
return n.GetGoNameForType(tpe, name)
|
|
}
|
|
|
|
// GetGoNameForType gets the go name for a given type for a json property name.
|
|
func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) {
|
|
n.lock.Lock()
|
|
defer n.lock.Unlock()
|
|
names, ok := n.index[tpe]
|
|
if !ok {
|
|
names = n.makeNameIndex(tpe)
|
|
}
|
|
nme, ok := names.jsonNames[name]
|
|
return nme, ok
|
|
}
|
|
|
|
func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex {
|
|
names := newNameIndex(tpe)
|
|
n.index[tpe] = names
|
|
return names
|
|
}
|