1
0
mirror of https://github.com/charlienet/go-mixed.git synced 2025-07-18 00:22:41 +08:00
Files
go-mixed/structs/structs.go
2022-07-04 12:01:44 +08:00

187 lines
3.1 KiB
Go

package structs
import (
"errors"
"reflect"
)
const defaultTagName = "json"
var (
ErrInvalidCopyDestination = errors.New("copy destination is invalid")
)
type Struct struct {
opt option
raw any
value reflect.Value
fields []field
}
func New(o any, opts ...optionFunc) *Struct {
opt := acquireOptions(opts)
v := indirect(reflect.ValueOf(o))
return &Struct{
opt: opt,
raw: o,
value: v,
fields: parseFields(v, opt),
}
}
func (s *Struct) Kind() reflect.Kind {
return s.value.Kind()
}
func (s *Struct) Names() []string {
names := make([]string, len(s.fields))
fields := s.fields[:]
for i, f := range fields {
names[i] = f.name
}
return names
}
func (s *Struct) Values() []any {
values := make([]any, 0, len(s.fields))
fields := s.fields[:]
for _, fi := range fields {
v := s.value.FieldByName(fi.name)
values = append(values, v.Interface())
}
return values
}
func (s *Struct) IsZero() bool {
fields := s.fields[:]
for _, fi := range fields {
source := s.value.FieldByName(fi.name)
if !source.IsZero() {
return false
}
}
return true
}
func (s *Struct) ToMap() map[string]any {
m := make(map[string]any, len(s.fields))
fields := s.fields[:]
for _, fi := range fields {
source := s.value.FieldByName(fi.name)
if fi.shouldIgnore(source) {
continue
}
m[fi.tagName] = source.Interface()
}
return m
}
/*
struct -> map
struct -> struct
map -> struct
map -> map
*/
func (s *Struct) Copy(dest any) error {
to := indirect(reflect.ValueOf(dest))
if !to.CanAddr() {
return ErrInvalidCopyDestination
}
t := indirectType(reflect.TypeOf(dest))
for i := 0; i < t.NumField(); i++ {
destField := t.Field(i)
if fi, ok := s.getByName(destField.Name); ok {
source := s.value.FieldByName(fi.name)
if fi.shouldIgnore(source) {
continue
}
tv := to.FieldByName(destField.Name)
tv.Set(source)
}
}
return nil
}
func (s *Struct) getByName(name string) (field, bool) {
fields := s.fields[:]
for i := range fields {
f := s.fields[i]
if f.name == name {
return f, true
}
}
return field{}, false
}
func Map2Struct(o map[string]any, dst any) {
}
func IsZero(o any) bool {
v := reflect.ValueOf(o)
if v.IsZero() {
return true
}
s := New(o)
return s.IsZero()
}
func Struct2Map(o any, opts ...optionFunc) map[string]any {
return New(o, opts...).ToMap()
}
func Copy(source, dst any, opts ...optionFunc) {
New(source, opts...).Copy(dst)
}
func parseFields(t reflect.Value, opt option) []field {
typ := indirectType(t.Type())
if typ.Kind() == reflect.Struct {
return parseStructFields(typ, opt)
}
return nil
}
func parseStructFields(typ reflect.Type, opt option) []field {
num := typ.NumField()
fields := make([]field, 0, num)
for i := 0; i < num; i++ {
fi := typ.Field(i)
fields = append(fields, parseField(fi, opt))
}
return fields
}
func indirect(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
return v
}
func indirectType(t reflect.Type) reflect.Type {
if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
return t.Elem()
}
return t
}