github.com/urso/go-structform@v0.0.2/gotype/unfold_struct.go (about)

     1  package gotype
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"unicode"
     8  	"unicode/utf8"
     9  	"unsafe"
    10  
    11  	structform "github.com/urso/go-structform"
    12  )
    13  
    14  type unfolderStruct struct {
    15  	unfolderErrExpectKey
    16  	fields map[string]fieldUnfolder
    17  }
    18  
    19  type unfolderStructStart struct {
    20  	unfolderErrObjectStart
    21  }
    22  
    23  type fieldUnfolder struct {
    24  	offset    uintptr
    25  	initState func(ctx *unfoldCtx, sp unsafe.Pointer)
    26  }
    27  
    28  var (
    29  	_singletonUnfolderStructStart = &unfolderStructStart{}
    30  
    31  	_ignoredField = &fieldUnfolder{
    32  		initState: _singletonUnfoldIgnorePtr.initState,
    33  	}
    34  )
    35  
    36  func createUnfolderReflStruct(ctx *unfoldCtx, t reflect.Type) (*unfolderStruct, error) {
    37  	// assume t is pointer to struct
    38  	t = t.Elem()
    39  
    40  	fields, err := fieldUnfolders(ctx, t)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	u := &unfolderStruct{fields: fields}
    46  	return u, nil
    47  }
    48  
    49  func fieldUnfolders(ctx *unfoldCtx, t reflect.Type) (map[string]fieldUnfolder, error) {
    50  	count := t.NumField()
    51  	fields := map[string]fieldUnfolder{}
    52  
    53  	for i := 0; i < count; i++ {
    54  		st := t.Field(i)
    55  
    56  		name := st.Name
    57  		rune, _ := utf8.DecodeRuneInString(name)
    58  		if !unicode.IsUpper(rune) {
    59  			continue
    60  		}
    61  
    62  		tagName, tagOpts := parseTags(st.Tag.Get(ctx.opts.tag))
    63  		if tagOpts.squash {
    64  			if st.Type.Kind() != reflect.Struct {
    65  				return nil, errSquashNeedObject
    66  			}
    67  
    68  			sub, err := fieldUnfolders(ctx, st.Type)
    69  			if err != nil {
    70  				return nil, err
    71  			}
    72  
    73  			for name, fu := range sub {
    74  				fu.offset += st.Offset
    75  				if _, exists := fields[name]; exists {
    76  					return nil, fmt.Errorf("duplicate field name %v", name)
    77  				}
    78  
    79  				fields[name] = fu
    80  			}
    81  		} else {
    82  			if tagName != "" {
    83  				name = tagName
    84  			} else {
    85  				name = strings.ToLower(name)
    86  			}
    87  
    88  			if _, exists := fields[name]; exists {
    89  				return nil, fmt.Errorf("duplicate field name %v", name)
    90  			}
    91  
    92  			fu, err := makeFieldUnfolder(ctx, st)
    93  			if err != nil {
    94  				return nil, err
    95  			}
    96  
    97  			fields[name] = fu
    98  		}
    99  	}
   100  
   101  	return fields, nil
   102  }
   103  
   104  func makeFieldUnfolder(ctx *unfoldCtx, st reflect.StructField) (fieldUnfolder, error) {
   105  	fu := fieldUnfolder{offset: st.Offset}
   106  
   107  	if pu := lookupGoPtrUnfolder(st.Type); pu != nil {
   108  		fu.initState = pu.initState
   109  	} else {
   110  		targetType := reflect.PtrTo(st.Type)
   111  		ru, err := lookupReflUnfolder(ctx, targetType)
   112  		if err != nil {
   113  			return fu, err
   114  		}
   115  
   116  		if su, ok := ru.(*unfolderStruct); ok {
   117  			fu.initState = su.initStatePtr
   118  		} else {
   119  			fu.initState = wrapReflUnfolder(st.Type, ru)
   120  		}
   121  	}
   122  
   123  	return fu, nil
   124  }
   125  
   126  func wrapReflUnfolder(t reflect.Type, ru reflUnfolder) func(*unfoldCtx, unsafe.Pointer) {
   127  	return func(ctx *unfoldCtx, ptr unsafe.Pointer) {
   128  		v := reflect.NewAt(t, ptr)
   129  		ru.initState(ctx, v)
   130  	}
   131  }
   132  
   133  func (u *unfolderStruct) initState(ctx *unfoldCtx, v reflect.Value) {
   134  	u.initStatePtr(ctx, unsafe.Pointer(v.Pointer()))
   135  }
   136  
   137  func (u *unfolderStruct) initStatePtr(ctx *unfoldCtx, ptr unsafe.Pointer) {
   138  	ctx.ptr.push(ptr)
   139  	ctx.unfolder.push(u)
   140  	ctx.unfolder.push(_singletonUnfolderStructStart)
   141  }
   142  
   143  func (u *unfolderStructStart) OnObjectStart(ctx *unfoldCtx, l int, bt structform.BaseType) error {
   144  	ctx.unfolder.pop()
   145  	return nil
   146  }
   147  
   148  func (u *unfolderStruct) OnObjectFinished(ctx *unfoldCtx) error {
   149  	ctx.unfolder.pop()
   150  	ctx.ptr.pop()
   151  	return nil
   152  }
   153  
   154  func (u *unfolderStruct) OnChildObjectDone(ctx *unfoldCtx) error { return nil }
   155  func (u *unfolderStruct) OnChildArrayDone(ctx *unfoldCtx) error  { return nil }
   156  
   157  func (u *unfolderStruct) OnKeyRef(ctx *unfoldCtx, key []byte) error {
   158  	return u.OnKey(ctx, bytes2Str(key))
   159  }
   160  
   161  func (u *unfolderStruct) OnKey(ctx *unfoldCtx, key string) error {
   162  	field, exists := u.fields[key]
   163  	if !exists {
   164  		_ignoredField.initState(ctx, nil)
   165  		return nil
   166  	}
   167  
   168  	structPtr := ctx.ptr.current
   169  	fieldAddr := uintptr(structPtr) + field.offset
   170  	fieldPtr := unsafe.Pointer(fieldAddr)
   171  	field.initState(ctx, fieldPtr)
   172  	return nil
   173  }