mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-26 17:41:30 +02:00
158 lines
3.6 KiB
Go
158 lines
3.6 KiB
Go
package networkmapdb
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/rs/xid"
|
|
)
|
|
|
|
var ErrNoRows = errors.New("no rows in result set")
|
|
|
|
const (
|
|
NMAP_STRUCT_TAG = "nmap"
|
|
NMAP_SKIP = "skip"
|
|
NMAP_MAP_TO = "map_to"
|
|
NMAP_JSON = "json"
|
|
)
|
|
|
|
type fieldTag struct {
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
func tagFromString(t string) fieldTag {
|
|
kv := strings.Split(t, ":")
|
|
if len(kv) == 1 {
|
|
return fieldTag{Key: strings.TrimSpace(kv[0])}
|
|
}
|
|
return fieldTag{Key: strings.TrimSpace(kv[0]), Value: strings.TrimSpace(kv[1])}
|
|
}
|
|
|
|
func FromSqlTypesToSharedTypes(src reflect.Value, dst reflect.Value) error {
|
|
typ := src.Elem().Type()
|
|
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
f := typ.Field(i)
|
|
|
|
fieldTags := make(map[string]string)
|
|
if v := f.Tag.Get(NMAP_STRUCT_TAG); v != "" {
|
|
for _, t := range strings.Split(v, ",") {
|
|
kv := tagFromString(t)
|
|
fieldTags[kv.Key] = kv.Value
|
|
}
|
|
}
|
|
if _, ok := fieldTags[NMAP_SKIP]; ok {
|
|
continue
|
|
}
|
|
if f.PkgPath != "" { // skip unexported fields
|
|
continue
|
|
}
|
|
dstFieldName := f.Name
|
|
if override, ok := fieldTags[NMAP_MAP_TO]; ok {
|
|
dstFieldName = override
|
|
}
|
|
|
|
dstField := dst.Elem().FieldByName(dstFieldName)
|
|
if !dstField.IsValid() {
|
|
return errors.New("unsupported type in destination field: " + dstFieldName)
|
|
}
|
|
|
|
srcField := src.Elem().Field(i)
|
|
srcFieldType := srcField.Type().String()
|
|
switch srcFieldType {
|
|
case "string":
|
|
s := srcField.Interface().(string)
|
|
dstField.SetString(s)
|
|
case "sql.NullString":
|
|
s := srcField.Interface().(sql.NullString)
|
|
if s.Valid {
|
|
dstField.SetString(s.String)
|
|
}
|
|
if (dstFieldName == "PublicId" || dstFieldName == "PublicID") && s.String == "" {
|
|
dstField.SetString(xid.New().String()) // TODO (dmitri) this needs to be removed to support delta updates
|
|
}
|
|
case "sql.NullTime":
|
|
s := srcField.Interface().(sql.NullTime)
|
|
if s.Valid {
|
|
if dstField.Kind() == reflect.Ptr {
|
|
t := reflect.ValueOf(&s.Time).Elem()
|
|
dstField.Set(t.Addr())
|
|
} else {
|
|
dstField.Set(reflect.ValueOf(s.Time))
|
|
}
|
|
}
|
|
case "sql.NullBool":
|
|
s := srcField.Interface().(sql.NullBool)
|
|
if s.Valid {
|
|
dstField.SetBool(s.Bool)
|
|
}
|
|
case "sql.NullInt64":
|
|
s := srcField.Interface().(sql.NullInt64)
|
|
if s.Valid {
|
|
dstField.SetInt(s.Int64)
|
|
}
|
|
case "json.RawMessage":
|
|
s := srcField.Interface().(json.RawMessage)
|
|
if len(s) == 0 {
|
|
continue
|
|
}
|
|
if err := json.Unmarshal(s, dstField.Addr().Interface()); err != nil {
|
|
return err
|
|
}
|
|
case "[]byte", "[]uint8":
|
|
s := srcField.Interface().([]byte)
|
|
if _, ok := fieldTags[NMAP_JSON]; !ok || len(s) == 0 {
|
|
continue
|
|
}
|
|
if err := json.Unmarshal(s, dstField.Addr().Interface()); err != nil {
|
|
return err
|
|
}
|
|
case "[]string":
|
|
if srcField.IsNil() {
|
|
continue
|
|
}
|
|
dstv := reflect.MakeSlice(dstField.Type(), srcField.Len(), srcField.Cap())
|
|
reflect.Copy(dstv, srcField)
|
|
dstField.Set(dstv)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func StructFields(s any) []any {
|
|
src := reflect.ValueOf(s)
|
|
toret := make([]any, 0)
|
|
typ := src.Elem().Type()
|
|
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
f := typ.Field(i)
|
|
if f.PkgPath != "" { // skip unexported fields
|
|
continue
|
|
}
|
|
|
|
srcField := src.Elem().Field(i)
|
|
toret = append(toret, srcField.Addr().Interface())
|
|
}
|
|
|
|
return toret
|
|
}
|
|
|
|
func ConvertAllToSharedTypes[T any, T1 any](allsrc []T) ([]T1, error) {
|
|
toret := make([]T1, 0, len(allsrc))
|
|
for _, src := range allsrc {
|
|
var dst T1
|
|
err := FromSqlTypesToSharedTypes(
|
|
reflect.ValueOf(&src), reflect.ValueOf(&dst))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
toret = append(toret, dst)
|
|
}
|
|
return toret, nil
|
|
}
|