// 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 scan 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 && p[0] != "-" { 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 }