tbotd/vendor/src/github.com/asdine/storm/extract.go

167 lines
3.0 KiB
Go

package storm
import (
"reflect"
"github.com/asdine/storm/index"
"github.com/boltdb/bolt"
"github.com/fatih/structs"
)
// Storm tags
const (
tagID = "id"
tagIdx = "index"
tagUniqueIdx = "unique"
tagInline = "inline"
indexPrefix = "__storm_index_"
)
type indexInfo struct {
Type string
Field *structs.Field
}
// modelInfo is a structure gathering all the relevant informations about a model
type modelInfo struct {
Name string
Indexes map[string]indexInfo
ID identInfo
data interface{}
}
func (m *modelInfo) AddIndex(f *structs.Field, indexType string, override bool) {
fieldName := f.Name()
if _, ok := m.Indexes[fieldName]; !ok || override {
m.Indexes[fieldName] = indexInfo{
Type: indexType,
Field: f,
}
}
}
func (m *modelInfo) AllByType(indexType string) []indexInfo {
var idx []indexInfo
for k := range m.Indexes {
if m.Indexes[k].Type == indexType {
idx = append(idx, m.Indexes[k])
}
}
return idx
}
func extract(data interface{}, mi ...*modelInfo) (*modelInfo, error) {
s := structs.New(data)
fields := s.Fields()
var child bool
var m *modelInfo
if len(mi) > 0 {
m = mi[0]
child = true
} else {
m = &modelInfo{}
m.Indexes = make(map[string]indexInfo)
m.data = data
}
if m.Name == "" {
m.Name = s.Name()
}
for _, f := range fields {
if !f.IsExported() {
continue
}
err := extractField(f, m, child)
if err != nil {
return nil, err
}
}
// ID field or tag detected
if m.ID.Field != nil {
if m.ID.Field.IsZero() {
m.ID.IsZero = true
} else {
m.ID.Value = m.ID.Field.Value()
}
}
if child {
return m, nil
}
if m.ID.Field == nil {
return nil, ErrNoID
}
if m.Name == "" {
return nil, ErrNoName
}
return m, nil
}
func extractField(f *structs.Field, m *modelInfo, isChild bool) error {
tag := f.Tag("storm")
if tag != "" {
switch tag {
case "id":
m.ID.Field = f
case tagUniqueIdx, tagIdx:
m.AddIndex(f, tag, !isChild)
case tagInline:
if structs.IsStruct(f.Value()) {
_, err := extract(f.Value(), m)
if err != nil {
return err
}
}
default:
return ErrUnknownTag
}
}
// the field is named ID and no ID field has been detected before
if f.Name() == "ID" && m.ID.Field == nil {
m.ID.Field = f
}
return nil
}
// Prefill the most requested informations
type identInfo struct {
Field *structs.Field
IsZero bool
Value interface{}
}
func (i *identInfo) Type() reflect.Type {
return reflect.TypeOf(i.Field.Value())
}
func (i *identInfo) IsOfIntegerFamily() bool {
return i.Field != nil && i.Field.Kind() >= reflect.Int && i.Field.Kind() <= reflect.Uint64
}
func getIndex(bucket *bolt.Bucket, idxKind string, fieldName string) (index.Index, error) {
var idx index.Index
var err error
switch idxKind {
case tagUniqueIdx:
idx, err = index.NewUniqueIndex(bucket, []byte(indexPrefix+fieldName))
case tagIdx:
idx, err = index.NewListIndex(bucket, []byte(indexPrefix+fieldName))
default:
err = ErrIdxNotFound
}
return idx, err
}