gapr/gapr.go

182 lines
3.5 KiB
Go
Raw Permalink Normal View History

2023-01-30 16:40:50 +00:00
package gapr
import (
"errors"
"math/rand"
"reflect"
"strconv"
"strings"
)
var (
ErrNotSupportedType = errors.New("input is not a supported type")
ErrNotStringKey = errors.New("key type is not string - not supported right now")
)
2023-01-30 21:06:18 +00:00
func New(opts ...Option) *Gapr {
o := defaultOptions()
for _, opt := range opts {
opt.apply(&o)
}
2023-01-30 16:40:50 +00:00
return &Gapr{
2023-01-30 21:06:18 +00:00
tagName: o.TagName,
rand: rand.New(rand.NewSource(o.RandomSeed)),
2023-01-30 16:40:50 +00:00
}
}
type Gapr struct {
2023-01-30 21:06:18 +00:00
tagName string
rand *rand.Rand
2023-01-30 16:40:50 +00:00
}
func (g *Gapr) Map(input any) (any, error) {
t := reflect.TypeOf(input)
v := reflect.ValueOf(input)
if t.Kind() == reflect.Pointer {
t = t.Elem()
2023-01-30 17:14:53 +00:00
v = v.Elem()
2023-01-30 16:40:50 +00:00
}
2023-01-30 21:06:18 +00:00
if !canMap(t, v) {
2023-01-30 16:40:50 +00:00
return nil, ErrNotSupportedType
}
2023-01-31 20:14:34 +00:00
//nolint:exhaustive // handled with default case
2023-01-30 16:40:50 +00:00
switch t.Kind() {
case reflect.Map:
return g.mapMap(t, v)
case reflect.Struct, reflect.Interface:
return g.mapStruct(t, v)
case reflect.Slice, reflect.Array:
2023-01-30 16:54:14 +00:00
return g.mapSliceOrArray(v)
2023-01-30 16:40:50 +00:00
default:
return nil, ErrNotSupportedType
}
}
func (g *Gapr) mapStruct(t reflect.Type, v reflect.Value) (any, error) {
numberOfFields := t.NumField()
mapped := make(map[string]any, numberOfFields)
for i := 0; i < numberOfFields; i++ {
drop, fieldName, err := g.fieldMeta(t.Field(i))
if err != nil {
return nil, err
}
if drop {
continue
}
if mappedVal, err := g.mapField(v.Field(i)); err != nil {
return nil, err
} else {
mapped[fieldName] = mappedVal
}
}
return mapped, nil
}
func (g *Gapr) mapField(v reflect.Value) (any, error) {
2023-01-30 21:06:18 +00:00
if canMap(v.Type(), v) {
2023-01-30 16:40:50 +00:00
return g.Map(v.Interface())
} else {
return v.Interface(), nil
}
}
func (g *Gapr) mapMap(t reflect.Type, v reflect.Value) (any, error) {
mapped := make(map[string]any, v.Len())
if t.Key().Kind() != reflect.String {
return nil, ErrNotStringKey
}
iter := v.MapRange()
for iter.Next() {
2023-01-30 21:06:18 +00:00
if canMap(iter.Value().Type(), iter.Value()) {
2023-01-30 16:40:50 +00:00
if mappedValue, err := g.Map(iter.Value().Interface()); err != nil {
return nil, err
} else {
mapped[iter.Key().String()] = mappedValue
}
} else {
mapped[iter.Key().String()] = iter.Value().Interface()
}
}
return mapped, nil
}
2023-01-30 16:54:14 +00:00
func (g *Gapr) mapSliceOrArray(v reflect.Value) (any, error) {
2023-01-30 16:40:50 +00:00
var (
length = v.Len()
2023-01-31 20:14:34 +00:00
target = reflect.ValueOf(make([]any, length))
2023-01-30 16:40:50 +00:00
)
for i := 0; i < length; i++ {
idxVal := v.Index(i)
2023-01-30 21:06:18 +00:00
if canMap(idxVal.Type(), idxVal) {
2023-01-30 16:40:50 +00:00
if mappedVal, err := g.Map(idxVal.Interface()); err != nil {
return nil, err
} else {
target.Index(i).Set(reflect.ValueOf(mappedVal))
}
} else {
target.Index(i).Set(idxVal)
}
}
return target.Interface(), nil
}
func (g *Gapr) fieldMeta(f reflect.StructField) (drop bool, fieldName string, err error) {
fieldName = f.Name
2023-01-30 21:06:18 +00:00
tagVal, present := f.Tag.Lookup(g.tagName)
2023-01-30 16:40:50 +00:00
if !present {
return false, f.Name, nil
}
tagSplit := strings.Split(tagVal, ",")
2023-01-30 16:59:21 +00:00
if len(tagSplit) < 1 {
2023-01-30 16:40:50 +00:00
return false, f.Name, nil
}
2023-01-30 21:06:18 +00:00
if tagSplit[0] != "" {
dropProbability, err := strconv.ParseFloat(tagSplit[0], 64)
if err != nil {
return false, "", err
}
drop = g.rand.Float64() < dropProbability
2023-01-30 16:40:50 +00:00
}
2023-01-30 16:59:21 +00:00
if len(tagSplit) > 1 && tagSplit[1] != "" {
2023-01-30 16:40:50 +00:00
fieldName = strings.TrimSpace(tagSplit[1])
}
return drop, fieldName, nil
}
2023-01-30 21:06:18 +00:00
func canMap(t reflect.Type, v reflect.Value) bool {
2023-01-30 16:40:50 +00:00
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
2023-01-30 21:06:18 +00:00
if t.Kind() == reflect.Interface {
t = reflect.TypeOf(v.Interface())
}
2023-01-31 20:14:34 +00:00
//nolint:exhaustive // handled with default case
2023-01-30 16:40:50 +00:00
switch t.Kind() {
2023-01-30 21:06:18 +00:00
case reflect.Struct, reflect.Map, reflect.Slice, reflect.Array:
2023-01-30 16:40:50 +00:00
return true
default:
return false
}
}