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

     1  package gotype
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/urso/go-structform"
     7  )
     8  
     9  type foldFn func(c *foldContext, v interface{}) error
    10  
    11  type reFoldFn func(c *foldContext, v reflect.Value) error
    12  
    13  type visitor interface {
    14  	structform.ExtVisitor
    15  }
    16  
    17  type Iterator struct {
    18  	ctx foldContext
    19  }
    20  
    21  type Folder interface {
    22  	Fold(structform.ExtVisitor) error
    23  }
    24  
    25  type foldContext struct {
    26  	visitor
    27  	userReg map[reflect.Type]reFoldFn
    28  	reg     *typeFoldRegistry
    29  	opts    options
    30  }
    31  
    32  func Fold(v interface{}, vs structform.Visitor, opts ...Option) error {
    33  	if it, err := NewIterator(vs, opts...); err == nil {
    34  		return it.Fold(v)
    35  	}
    36  	return nil
    37  }
    38  
    39  func NewIterator(vs structform.Visitor, opts ...Option) (*Iterator, error) {
    40  	reg := newTypeFoldRegistry()
    41  	O, err := applyOpts(opts)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	var userReg map[reflect.Type]reFoldFn
    47  	if O.foldFns != nil {
    48  		userReg = map[reflect.Type]reFoldFn{}
    49  		for typ, folder := range O.foldFns {
    50  			reg.set(typ, folder)
    51  		}
    52  	}
    53  
    54  	it := &Iterator{
    55  		ctx: foldContext{
    56  			visitor: structform.EnsureExtVisitor(vs).(visitor),
    57  			userReg: userReg,
    58  			reg:     reg,
    59  			opts: options{
    60  				tag: "struct",
    61  			},
    62  		},
    63  	}
    64  
    65  	return it, nil
    66  }
    67  
    68  func (i *Iterator) Fold(v interface{}) error {
    69  	return foldInterfaceValue(&i.ctx, v)
    70  }
    71  
    72  func foldInterfaceValue(C *foldContext, v interface{}) error {
    73  	if C.userReg != nil {
    74  		t := reflect.TypeOf(v)
    75  		if f := C.userReg[t]; f != nil {
    76  			return f(C, reflect.ValueOf(v))
    77  		}
    78  	}
    79  
    80  	if f := getFoldGoTypes(v); f != nil {
    81  		return f(C, v)
    82  	}
    83  
    84  	if f, ok := v.(Folder); ok {
    85  		return f.Fold(C.visitor)
    86  	}
    87  
    88  	if tmp, f := getFoldConvert(v); f != nil {
    89  		return f(C, tmp)
    90  	}
    91  
    92  	return foldAnyReflect(C, reflect.ValueOf(v))
    93  }
    94  
    95  func getFoldConvert(v interface{}) (interface{}, foldFn) {
    96  	t := reflect.TypeOf(v)
    97  	cast := false
    98  
    99  	switch t.Kind() {
   100  	case reflect.Map:
   101  		if cast = t.Name() != ""; cast {
   102  			mt := reflect.MapOf(t.Key(), t.Elem())
   103  			v = reflect.ValueOf(v).Convert(mt).Interface()
   104  		}
   105  	case reflect.Slice:
   106  		if cast = t.Name() != ""; cast {
   107  			mt := reflect.SliceOf(t.Elem())
   108  			v = reflect.ValueOf(v).Convert(mt).Interface()
   109  		}
   110  	case reflect.Array:
   111  		if cast = t.Name() != ""; cast {
   112  			mt := reflect.ArrayOf(t.Len(), t.Elem())
   113  			v = reflect.ValueOf(v).Convert(mt).Interface()
   114  		}
   115  	}
   116  
   117  	return v, getFoldGoTypes(v)
   118  }
   119  
   120  func getFoldGoTypes(v interface{}) foldFn {
   121  	switch v.(type) {
   122  	case nil:
   123  		return foldNil
   124  
   125  	case bool:
   126  		return foldBool
   127  	case []bool:
   128  		return foldArrBool
   129  	case map[string]bool:
   130  		return foldMapBool
   131  
   132  	case int8:
   133  		return foldInt8
   134  	case int16:
   135  		return foldInt16
   136  	case int32:
   137  		return foldInt32
   138  	case int64:
   139  		return foldInt64
   140  	case int:
   141  		return foldInt
   142  
   143  	case []int8:
   144  		return foldArrInt8
   145  	case []int16:
   146  		return foldArrInt16
   147  	case []int32:
   148  		return foldArrInt32
   149  	case []int64:
   150  		return foldArrInt64
   151  	case []int:
   152  		return foldArrInt
   153  
   154  	case map[string]int8:
   155  		return foldMapInt8
   156  	case map[string]int16:
   157  		return foldMapInt16
   158  	case map[string]int32:
   159  		return foldMapInt32
   160  	case map[string]int64:
   161  		return foldMapInt64
   162  	case map[string]int:
   163  		return foldMapInt
   164  
   165  		/*
   166  			case byte:
   167  				return visitByte
   168  		*/
   169  	case uint8:
   170  		return foldUint8
   171  	case uint16:
   172  		return foldUint16
   173  	case uint32:
   174  		return foldUint32
   175  	case uint64:
   176  		return foldUint64
   177  	case uint:
   178  		return foldUint
   179  
   180  	case []byte:
   181  		return foldBytes
   182  		/*
   183  			case []uint8:
   184  				return visitArrUint8
   185  		*/
   186  	case []uint16:
   187  		return foldArrUint16
   188  	case []uint32:
   189  		return foldArrUint32
   190  	case []uint64:
   191  		return foldArrUint64
   192  	case []uint:
   193  		return foldArrUint
   194  
   195  	case map[string]uint8:
   196  		return foldMapUint8
   197  	case map[string]uint16:
   198  		return foldMapUint16
   199  	case map[string]uint32:
   200  		return foldMapUint32
   201  	case map[string]uint64:
   202  		return foldMapUint64
   203  	case map[string]uint:
   204  		return foldMapUint
   205  
   206  	case float32:
   207  		return foldFloat32
   208  	case float64:
   209  		return foldFloat64
   210  
   211  	case []float32:
   212  		return foldArrFloat32
   213  	case []float64:
   214  		return foldArrFloat64
   215  
   216  	case map[string]float32:
   217  		return foldMapFloat32
   218  	case map[string]float64:
   219  		return foldMapFloat64
   220  
   221  	case string:
   222  		return foldString
   223  	case []string:
   224  		return foldArrString
   225  	case map[string]string:
   226  		return foldMapString
   227  
   228  	case []interface{}:
   229  		return foldArrInterface
   230  	case map[string]interface{}:
   231  		return foldMapInterface
   232  	}
   233  
   234  	return nil
   235  }