mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 19:22:24 -06:00 
			
		
		
		
	* Add go-playground/form pkg * [feature] Add support for profile fields * Add field attributes test * Validate profile fields form * Add profile field validation tests * Add Field Attributes definition to swagger --------- Co-authored-by: tobi <31960611+tsmethurst@users.noreply.github.com>
		
			
				
	
	
		
			133 lines
		
	
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package form
 | 
						|
 | 
						|
import (
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
	"sync/atomic"
 | 
						|
)
 | 
						|
 | 
						|
type cacheFields []cachedField
 | 
						|
 | 
						|
func (s cacheFields) Len() int {
 | 
						|
	return len(s)
 | 
						|
}
 | 
						|
 | 
						|
func (s cacheFields) Less(i, j int) bool {
 | 
						|
	return !s[i].isAnonymous
 | 
						|
}
 | 
						|
 | 
						|
func (s cacheFields) Swap(i, j int) {
 | 
						|
	s[i], s[j] = s[j], s[i]
 | 
						|
}
 | 
						|
 | 
						|
type cachedField struct {
 | 
						|
	idx         int
 | 
						|
	name        string
 | 
						|
	isAnonymous bool
 | 
						|
	isOmitEmpty bool
 | 
						|
}
 | 
						|
 | 
						|
type cachedStruct struct {
 | 
						|
	fields cacheFields
 | 
						|
}
 | 
						|
 | 
						|
type structCacheMap struct {
 | 
						|
	m     atomic.Value // map[reflect.Type]*cachedStruct
 | 
						|
	lock  sync.Mutex
 | 
						|
	tagFn TagNameFunc
 | 
						|
}
 | 
						|
 | 
						|
// TagNameFunc allows for adding of a custom tag name parser
 | 
						|
type TagNameFunc func(field reflect.StructField) string
 | 
						|
 | 
						|
func newStructCacheMap() *structCacheMap {
 | 
						|
 | 
						|
	sc := new(structCacheMap)
 | 
						|
	sc.m.Store(make(map[reflect.Type]*cachedStruct))
 | 
						|
 | 
						|
	return sc
 | 
						|
}
 | 
						|
 | 
						|
func (s *structCacheMap) Get(key reflect.Type) (value *cachedStruct, ok bool) {
 | 
						|
	value, ok = s.m.Load().(map[reflect.Type]*cachedStruct)[key]
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func (s *structCacheMap) Set(key reflect.Type, value *cachedStruct) {
 | 
						|
 | 
						|
	m := s.m.Load().(map[reflect.Type]*cachedStruct)
 | 
						|
 | 
						|
	nm := make(map[reflect.Type]*cachedStruct, len(m)+1)
 | 
						|
	for k, v := range m {
 | 
						|
		nm[k] = v
 | 
						|
	}
 | 
						|
	nm[key] = value
 | 
						|
	s.m.Store(nm)
 | 
						|
}
 | 
						|
 | 
						|
func (s *structCacheMap) parseStruct(mode Mode, current reflect.Value, key reflect.Type, tagName string) *cachedStruct {
 | 
						|
 | 
						|
	s.lock.Lock()
 | 
						|
 | 
						|
	// could have been multiple trying to access, but once first is done this ensures struct
 | 
						|
	// isn't parsed again.
 | 
						|
	cs, ok := s.Get(key)
 | 
						|
	if ok {
 | 
						|
		s.lock.Unlock()
 | 
						|
		return cs
 | 
						|
	}
 | 
						|
 | 
						|
	typ := current.Type()
 | 
						|
	cs = &cachedStruct{fields: make([]cachedField, 0, 4)} // init 4, betting most structs decoding into have at aleast 4 fields.
 | 
						|
 | 
						|
	numFields := current.NumField()
 | 
						|
 | 
						|
	var fld reflect.StructField
 | 
						|
	var name string
 | 
						|
	var idx int
 | 
						|
	var isOmitEmpty bool
 | 
						|
 | 
						|
	for i := 0; i < numFields; i++ {
 | 
						|
		isOmitEmpty = false
 | 
						|
		fld = typ.Field(i)
 | 
						|
 | 
						|
		if fld.PkgPath != blank && !fld.Anonymous {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if s.tagFn != nil {
 | 
						|
			name = s.tagFn(fld)
 | 
						|
		} else {
 | 
						|
			name = fld.Tag.Get(tagName)
 | 
						|
		}
 | 
						|
 | 
						|
		if name == ignore {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if mode == ModeExplicit && len(name) == 0 {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// check for omitempty
 | 
						|
		if idx = strings.LastIndexByte(name, ','); idx != -1 {
 | 
						|
			isOmitEmpty = name[idx+1:] == "omitempty"
 | 
						|
			name = name[:idx]
 | 
						|
		}
 | 
						|
 | 
						|
		if len(name) == 0 {
 | 
						|
			name = fld.Name
 | 
						|
		}
 | 
						|
 | 
						|
		cs.fields = append(cs.fields, cachedField{idx: i, name: name, isAnonymous: fld.Anonymous, isOmitEmpty: isOmitEmpty})
 | 
						|
	}
 | 
						|
 | 
						|
	sort.Sort(cs.fields)
 | 
						|
	s.Set(typ, cs)
 | 
						|
 | 
						|
	s.lock.Unlock()
 | 
						|
 | 
						|
	return cs
 | 
						|
}
 |