mirror of
https://gitea.com/xorm/cachestore
synced 2025-10-06 00:02:51 +02:00
126 lines
2.8 KiB
Go
126 lines
2.8 KiB
Go
// Copyright 2012 Gary Burd
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package redisx
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type fieldSpec struct {
|
|
name string
|
|
index []int
|
|
omitEmpty bool
|
|
}
|
|
|
|
type structSpec struct {
|
|
m map[string]*fieldSpec
|
|
l []*fieldSpec
|
|
}
|
|
|
|
func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
|
|
return ss.m[string(name)]
|
|
}
|
|
|
|
func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
switch {
|
|
case f.PkgPath != "":
|
|
// Ignore unexported fields.
|
|
case f.Anonymous:
|
|
// TODO: Handle pointers. Requires change to decoder and
|
|
// protection against infinite recursion.
|
|
if f.Type.Kind() == reflect.Struct {
|
|
compileStructSpec(f.Type, depth, append(index, i), ss)
|
|
}
|
|
default:
|
|
fs := &fieldSpec{name: f.Name}
|
|
tag := f.Tag.Get("redis")
|
|
p := strings.Split(tag, ",")
|
|
if len(p) > 0 {
|
|
if p[0] == "-" {
|
|
continue
|
|
}
|
|
if len(p[0]) > 0 {
|
|
fs.name = p[0]
|
|
}
|
|
for _, s := range p[1:] {
|
|
switch s {
|
|
case "omitempty":
|
|
fs.omitEmpty = true
|
|
default:
|
|
panic(errors.New("redigo: unknown field flag " + s + " for type " + t.Name()))
|
|
}
|
|
}
|
|
}
|
|
d, found := depth[fs.name]
|
|
if !found {
|
|
d = 1 << 30
|
|
}
|
|
switch {
|
|
case len(index) == d:
|
|
// At same depth, remove from result.
|
|
delete(ss.m, fs.name)
|
|
j := 0
|
|
for i := 0; i < len(ss.l); i++ {
|
|
if fs.name != ss.l[i].name {
|
|
ss.l[j] = ss.l[i]
|
|
j += 1
|
|
}
|
|
}
|
|
ss.l = ss.l[:j]
|
|
case len(index) < d:
|
|
fs.index = make([]int, len(index)+1)
|
|
copy(fs.index, index)
|
|
fs.index[len(index)] = i
|
|
depth[fs.name] = len(index)
|
|
ss.m[fs.name] = fs
|
|
ss.l = append(ss.l, fs)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var (
|
|
structSpecMutex sync.RWMutex
|
|
structSpecCache = make(map[reflect.Type]*structSpec)
|
|
defaultFieldSpec = &fieldSpec{}
|
|
)
|
|
|
|
func structSpecForType(t reflect.Type) *structSpec {
|
|
|
|
structSpecMutex.RLock()
|
|
ss, found := structSpecCache[t]
|
|
structSpecMutex.RUnlock()
|
|
if found {
|
|
return ss
|
|
}
|
|
|
|
structSpecMutex.Lock()
|
|
defer structSpecMutex.Unlock()
|
|
ss, found = structSpecCache[t]
|
|
if found {
|
|
return ss
|
|
}
|
|
|
|
ss = &structSpec{m: make(map[string]*fieldSpec)}
|
|
compileStructSpec(t, make(map[string]int), nil, ss)
|
|
structSpecCache[t] = ss
|
|
return ss
|
|
}
|