222 lines
4.7 KiB
Go
222 lines
4.7 KiB
Go
|
package tdb
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
|
||
|
// "git.keganmyers.com/terribleplan/tdb/stringy"
|
||
|
|
||
|
"github.com/golang/protobuf/proto"
|
||
|
bolt "go.etcd.io/bbolt"
|
||
|
)
|
||
|
|
||
|
// these types are simply to help pass around things and know what they are
|
||
|
// some would argue that having such types is bad practice, but I see it as
|
||
|
// a necessary evil since the reflection in golang is fairly bare-bones
|
||
|
|
||
|
// dbValue - a non-pointer thing stored by the database
|
||
|
|
||
|
type dbValue reflect.Value
|
||
|
|
||
|
func (val dbValue) Ptr() dbPtrValue {
|
||
|
return dbPtrValue(reflect.Value(val).Addr())
|
||
|
}
|
||
|
|
||
|
func (val dbValue) Type() *dbType {
|
||
|
return &dbType{
|
||
|
T: reflect.Value(val).Type(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (val dbValue) dangerous_Field(f dbField) reflect.Value {
|
||
|
return reflect.Value(val).FieldByIndex(reflect.StructField(f).Index)
|
||
|
}
|
||
|
|
||
|
func (val dbValue) Marshal() ([]byte, error) {
|
||
|
return proto.Marshal(reflect.Value(val).Addr().Interface().(proto.Message))
|
||
|
}
|
||
|
|
||
|
// dbPtrValue - a pointer to a thing stored by the database
|
||
|
|
||
|
func dbPtrValueOf(p proto.Message) dbPtrValue {
|
||
|
return dbPtrValue(reflect.ValueOf(p))
|
||
|
}
|
||
|
|
||
|
type dbPtrValue reflect.Value
|
||
|
|
||
|
func (ptrVal dbPtrValue) Val() dbValue {
|
||
|
return dbValue(reflect.Value(ptrVal).Elem())
|
||
|
}
|
||
|
|
||
|
func (ptrVal dbPtrValue) PtrType() *dbPtrType {
|
||
|
return &dbPtrType{
|
||
|
T: reflect.Value(ptrVal).Type(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (ptrVal dbPtrValue) Proto() proto.Message {
|
||
|
return reflect.Value(ptrVal).Interface().(proto.Message)
|
||
|
}
|
||
|
|
||
|
func (ptrVal dbPtrValue) IsOfPtrType(pt *dbPtrType) bool {
|
||
|
return pt.T == reflect.Value(ptrVal).Type()
|
||
|
}
|
||
|
|
||
|
func (ptrVal dbPtrValue) IsNil() bool {
|
||
|
return reflect.Value(ptrVal).IsNil()
|
||
|
}
|
||
|
|
||
|
func (ptrVal dbPtrValue) dangerous_Field(f dbField) reflect.Value {
|
||
|
return reflect.Value(ptrVal).Elem().FieldByIndex(reflect.StructField(f).Index)
|
||
|
}
|
||
|
|
||
|
func (ptrVal dbPtrValue) Marshal() ([]byte, error) {
|
||
|
return proto.Marshal(reflect.Value(ptrVal).Interface().(proto.Message))
|
||
|
}
|
||
|
|
||
|
// dbType - the type of a non-pointer thing stored by the database
|
||
|
|
||
|
func dbTypeOf(p proto.Message) *dbType {
|
||
|
t := reflect.TypeOf(p).Elem()
|
||
|
|
||
|
typeString := t.String()
|
||
|
nameComponents := strings.Split(typeString, ".")
|
||
|
name := nameComponents[len(nameComponents)-1]
|
||
|
if name[0] == '*' {
|
||
|
name = name[1:]
|
||
|
}
|
||
|
|
||
|
if name == "" {
|
||
|
panic(errors.New("[tdb] [internal] ]Unable to reliably determine name of thing"))
|
||
|
}
|
||
|
|
||
|
return &dbType{
|
||
|
Name: name,
|
||
|
T: t,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type dbType struct {
|
||
|
Name string
|
||
|
T reflect.Type
|
||
|
}
|
||
|
|
||
|
func (t *dbType) New() dbPtrValue {
|
||
|
return dbPtrValue(reflect.New(t.T))
|
||
|
}
|
||
|
|
||
|
func (t *dbType) PtrType() *dbPtrType {
|
||
|
return &dbPtrType{
|
||
|
Name: "*" + t.Name,
|
||
|
T: reflect.PtrTo(t.T),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (t *dbType) IdField() dbField {
|
||
|
idField := t.NamedField("Id")
|
||
|
|
||
|
if idField.Type.Kind() != reflect.Uint64 {
|
||
|
panic(fmt.Errorf("[tdb] [internal] %s's 'Id' field is not a uint64", t.Name))
|
||
|
}
|
||
|
|
||
|
return idField
|
||
|
}
|
||
|
|
||
|
func (t *dbType) NamedField(name string) dbField {
|
||
|
field, exists := t.T.FieldByName(name)
|
||
|
|
||
|
if !exists {
|
||
|
panic(fmt.Errorf("[tdb] [internal] %s lacks a '%s' field", t.Name, name))
|
||
|
}
|
||
|
|
||
|
return dbField(field)
|
||
|
}
|
||
|
|
||
|
// dbPtrType - the type of a pointer to a thing stored by the database
|
||
|
|
||
|
type dbPtrType struct {
|
||
|
Name string
|
||
|
T reflect.Type
|
||
|
}
|
||
|
|
||
|
func (ptr *dbPtrType) New() dbPtrValue {
|
||
|
return dbPtrValue(reflect.New(ptr.T.Elem()))
|
||
|
}
|
||
|
|
||
|
func (ptr *dbPtrType) Type() dbType {
|
||
|
return dbType{
|
||
|
Name: ptr.Name[1:],
|
||
|
T: ptr.T.Elem(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (ptr *dbPtrType) String() string {
|
||
|
return ptr.T.String()
|
||
|
}
|
||
|
|
||
|
func (ptr *dbPtrType) Zero() dbPtrValue {
|
||
|
return dbPtrValue(reflect.Zero(ptr.T))
|
||
|
}
|
||
|
|
||
|
func (ptr *dbPtrType) Unmarshal(data []byte) (dbPtrValue, error) {
|
||
|
pv := ptr.New()
|
||
|
if err := proto.Unmarshal(data, pv.Proto()); err != nil {
|
||
|
return ptr.Zero(), err
|
||
|
}
|
||
|
return pv, nil
|
||
|
}
|
||
|
|
||
|
func (ptr *dbPtrType) IdField() dbField {
|
||
|
idField := ptr.NamedField("Id")
|
||
|
|
||
|
if idField.Type.Kind() != reflect.Uint64 {
|
||
|
panic(fmt.Errorf("[tdb] [internal] %s's 'Id' field is not a uint64", ptr.Name))
|
||
|
}
|
||
|
|
||
|
return idField
|
||
|
}
|
||
|
|
||
|
func (ptr *dbPtrType) NamedField(name string) dbField {
|
||
|
field, exists := ptr.T.Elem().FieldByName(name)
|
||
|
|
||
|
if !exists {
|
||
|
panic(fmt.Errorf("[tdb] [internal] %s lacks a '%s' field", ptr.Name, name))
|
||
|
}
|
||
|
|
||
|
return dbField(field)
|
||
|
}
|
||
|
|
||
|
// dbField - a field on a struct... this one is quite unnecessary
|
||
|
|
||
|
type dbField reflect.StructField
|
||
|
|
||
|
func (f dbField) IsUint64() bool {
|
||
|
return f.Type.Kind() == reflect.Uint64
|
||
|
}
|
||
|
|
||
|
func (f dbField) IsUint64Slice() bool {
|
||
|
return f.Type.Kind() == reflect.Slice && f.Type.Elem().Kind() == reflect.Uint64
|
||
|
}
|
||
|
|
||
|
func (f dbField) IsSliceish() bool {
|
||
|
fieldKind := f.Type.Kind()
|
||
|
return fieldKind == reflect.Array || fieldKind == reflect.Slice
|
||
|
}
|
||
|
|
||
|
// Tx - is it dumb to provide this just so consumers of this package don't have to include bolt?
|
||
|
// I think not
|
||
|
|
||
|
type Tx struct {
|
||
|
btx *bolt.Tx
|
||
|
}
|
||
|
|
||
|
func convertTx(btx *bolt.Tx) *Tx {
|
||
|
return &Tx{btx: btx}
|
||
|
}
|
||
|
|
||
|
func (tx *Tx) tx() *bolt.Tx {
|
||
|
return tx.btx
|
||
|
}
|