github.com/RevenueMonster/sqlike@v1.0.6/reflext/reflext.go (about)

     1  package reflext
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  // Structer :
    11  type Structer interface {
    12  	Fields() []StructFielder
    13  	Properties() []StructFielder
    14  	LookUpFieldByName(name string) (StructFielder, bool)
    15  	GetByTraversal(index []int) StructFielder
    16  }
    17  
    18  // StructFielder :
    19  type StructFielder interface {
    20  	// New name of struct field
    21  	Name() string
    22  
    23  	// reflect.Type of the field
    24  	Type() reflect.Type
    25  
    26  	// index position of the field
    27  	Index() []int
    28  
    29  	Tag() StructTag
    30  
    31  	// if the field is struct, parent will not be nil
    32  	// this will be the parent struct of current struct
    33  	Parent() StructFielder
    34  
    35  	ParentByTraversal(cb func(StructFielder) bool) StructFielder
    36  
    37  	// if the field is struct, children will not be nil
    38  	// this will be the fields of current struct
    39  	Children() []StructFielder
    40  
    41  	// determine the field is nullable
    42  	IsNullable() bool
    43  
    44  	// determine the field is embedded struct
    45  	IsEmbedded() bool
    46  }
    47  
    48  // StructTag :
    49  type StructTag struct {
    50  	originalName string
    51  	name         string
    52  	opts         map[string]string
    53  }
    54  
    55  // Name :
    56  func (st StructTag) Name() string {
    57  	return st.name
    58  }
    59  
    60  // OriginalName :
    61  func (st StructTag) OriginalName() string {
    62  	return st.originalName
    63  }
    64  
    65  // Get :
    66  func (st StructTag) Get(key string) string {
    67  	return st.opts[key]
    68  }
    69  
    70  // LookUp :
    71  func (st StructTag) LookUp(key string) (val string, exist bool) {
    72  	val, exist = st.opts[key]
    73  	return
    74  }
    75  
    76  // StructField :
    77  type StructField struct {
    78  	id       string
    79  	idx      []int
    80  	name     string
    81  	path     string
    82  	t        reflect.Type
    83  	null     bool
    84  	tag      StructTag
    85  	embed    bool
    86  	parent   StructFielder
    87  	children []StructFielder
    88  }
    89  
    90  var _ StructFielder = (*StructField)(nil)
    91  
    92  // Name :
    93  func (sf *StructField) Name() string {
    94  	return sf.path
    95  }
    96  
    97  // Type :
    98  func (sf *StructField) Type() reflect.Type {
    99  	return sf.t
   100  }
   101  
   102  // Tag :
   103  func (sf *StructField) Tag() StructTag {
   104  	return sf.tag
   105  }
   106  
   107  // Index :
   108  func (sf *StructField) Index() []int {
   109  	return sf.idx
   110  }
   111  
   112  // Parent :
   113  func (sf *StructField) Parent() StructFielder {
   114  	return sf.parent
   115  }
   116  
   117  // Children :
   118  func (sf *StructField) Children() []StructFielder {
   119  	return sf.children
   120  }
   121  
   122  // IsNullable :
   123  func (sf *StructField) IsNullable() bool {
   124  	return sf.null
   125  }
   126  
   127  // IsEmbedded :
   128  func (sf *StructField) IsEmbedded() bool {
   129  	return sf.embed
   130  }
   131  
   132  // ParentByTraversal :
   133  func (sf *StructField) ParentByTraversal(cb func(StructFielder) bool) StructFielder {
   134  	prnt := sf.parent
   135  	for prnt != nil {
   136  		if cb(prnt) {
   137  			break
   138  		}
   139  		prnt = prnt.Parent()
   140  	}
   141  	return prnt
   142  }
   143  
   144  // Struct :
   145  type Struct struct {
   146  	tree       StructFielder
   147  	fields     Fields // all fields belong to this struct
   148  	properties Fields // available properties in sequence
   149  	indexes    map[string]StructFielder
   150  	names      map[string]StructFielder
   151  }
   152  
   153  var _ Structer = (*Struct)(nil)
   154  
   155  // Fields :
   156  func (s *Struct) Fields() []StructFielder {
   157  	return append(make(Fields, 0, len(s.fields)), s.fields...)
   158  }
   159  
   160  // Properties :
   161  func (s *Struct) Properties() []StructFielder {
   162  	return append(make(Fields, 0, len(s.properties)), s.properties...)
   163  }
   164  
   165  // LookUpFieldByName :
   166  func (s *Struct) LookUpFieldByName(name string) (StructFielder, bool) {
   167  	x, ok := s.names[name]
   168  	return x, ok
   169  }
   170  
   171  // GetByTraversal :
   172  func (s *Struct) GetByTraversal(index []int) StructFielder {
   173  	if len(index) == 0 {
   174  		return nil
   175  	}
   176  
   177  	tree := s.tree
   178  	for _, i := range index {
   179  		children := tree.Children()
   180  		if i >= len(children) || children[i] == nil {
   181  			return nil
   182  		}
   183  		tree = children[i]
   184  	}
   185  	return tree
   186  }
   187  
   188  // Fields :
   189  type Fields []StructFielder
   190  
   191  func (x Fields) FindIndex(cb func(f StructFielder) bool) int {
   192  	for idx, f := range x {
   193  		if cb(f) {
   194  			return idx
   195  		}
   196  	}
   197  	return -1
   198  }
   199  
   200  func (x Fields) Len() int { return len(x) }
   201  
   202  func (x Fields) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
   203  
   204  func (x Fields) Less(i, j int) bool {
   205  	for k, xik := range x[i].Index() {
   206  		if k >= len(x[j].Index()) {
   207  			return false
   208  		}
   209  		if xik != x[j].Index()[k] {
   210  			return xik < x[j].Index()[k]
   211  		}
   212  	}
   213  	return len(x[i].Index()) < len(x[j].Index())
   214  }
   215  
   216  type typeQueue struct {
   217  	t  reflect.Type
   218  	sf *StructField
   219  	pp string // parent path
   220  }
   221  
   222  func getCodec(t reflect.Type, tagName string, fmtFunc FormatFunc) *Struct {
   223  	fields := make([]StructFielder, 0)
   224  
   225  	root := &StructField{}
   226  	queue := []typeQueue{}
   227  	queue = append(queue, typeQueue{Deref(t), root, ""})
   228  
   229  	for len(queue) > 0 {
   230  		q := queue[0]
   231  		q.sf.children = make([]StructFielder, 0)
   232  
   233  		for i := 0; i < q.t.NumField(); i++ {
   234  			f := q.t.Field(i)
   235  
   236  			// skip unexported fields
   237  			if len(f.PkgPath) != 0 && !f.Anonymous {
   238  				continue
   239  			}
   240  
   241  			tag := parseTag(f, tagName, fmtFunc)
   242  			if tag.name == "-" {
   243  				continue
   244  			}
   245  
   246  			sf := &StructField{
   247  				id:       strings.TrimLeft(q.sf.id+"."+strconv.Itoa(i), "."),
   248  				name:     f.Name,
   249  				path:     tag.name,
   250  				null:     q.sf.null || IsNullable(f.Type),
   251  				t:        f.Type,
   252  				tag:      tag,
   253  				children: make([]StructFielder, 0),
   254  			}
   255  
   256  			if len(q.sf.Index()) > 0 {
   257  				sf.parent = q.sf
   258  			}
   259  
   260  			if sf.path == "" {
   261  				sf.path = sf.tag.name
   262  			}
   263  
   264  			if q.pp != "" {
   265  				sf.path = q.pp + "." + sf.path
   266  			}
   267  
   268  			ft := Deref(f.Type)
   269  			q.sf.children = append(q.sf.children, sf)
   270  			sf.idx = appendSlice(q.sf.idx, i)
   271  			sf.embed = ft.Kind() == reflect.Struct && f.Anonymous
   272  
   273  			if ft.Kind() == reflect.Struct {
   274  				// check recursive, prevent infinite loop
   275  				if q.t == ft {
   276  					goto nextStep
   277  				}
   278  
   279  				// embedded struct
   280  				path := sf.path
   281  				if f.Anonymous {
   282  					if sf.tag.OriginalName() == "" {
   283  						path = q.pp
   284  					}
   285  					// queue = append(queue, typeQueue{ft, sf, path})
   286  				}
   287  
   288  				queue = append(queue, typeQueue{ft, sf, path})
   289  			}
   290  
   291  		nextStep:
   292  			fields = append(fields, sf)
   293  		}
   294  
   295  		queue = queue[1:]
   296  	}
   297  
   298  	codec := &Struct{
   299  		tree:       root,
   300  		fields:     fields,
   301  		properties: make([]StructFielder, 0, len(fields)),
   302  		indexes:    make(map[string]StructFielder),
   303  		names:      make(map[string]StructFielder),
   304  	}
   305  
   306  	lname := ""
   307  	sort.Sort(codec.fields)
   308  
   309  	for _, sf := range codec.fields {
   310  		codec.indexes[sf.(*StructField).id] = sf
   311  		if sf.Name() != "" && !sf.IsEmbedded() {
   312  			lname = strings.ToLower(sf.Name())
   313  			codec.names[sf.Name()] = sf
   314  
   315  			idx := codec.properties.FindIndex(func(each StructFielder) bool {
   316  				return strings.ToLower(each.Tag().name) == lname
   317  			})
   318  			if idx > -1 {
   319  				// remove item in the slice if the field name is same (overriding embedded struct field)
   320  				codec.properties = append(codec.properties[:idx], codec.properties[idx+1:]...)
   321  			}
   322  
   323  			prnt := sf.ParentByTraversal(func(f StructFielder) bool {
   324  				return !f.IsEmbedded()
   325  			})
   326  			if len(sf.Index()) > 1 &&
   327  				sf.Parent() != nil && prnt != nil {
   328  				continue
   329  			}
   330  
   331  			// not nested embedded struct or embedded struct
   332  			codec.properties = append(codec.properties, sf)
   333  		}
   334  	}
   335  
   336  	return codec
   337  }
   338  
   339  func appendSlice(s []int, i int) []int {
   340  	x := make([]int, len(s)+1)
   341  	copy(x, s)
   342  	x[len(x)-1] = i
   343  	return x
   344  }
   345  
   346  func parseTag(f reflect.StructField, tagName string, fmtFunc FormatFunc) (st StructTag) {
   347  	parts := strings.Split(f.Tag.Get(tagName), ",")
   348  	name := strings.TrimSpace(parts[0])
   349  	st.originalName = name
   350  	if name == "" {
   351  		name = f.Name
   352  		if fmtFunc != nil {
   353  			name = fmtFunc(name)
   354  		}
   355  	}
   356  	st.name = name
   357  	st.opts = make(map[string]string)
   358  	if len(parts) > 1 {
   359  		for _, opt := range parts[1:] {
   360  			opt = strings.TrimSpace(opt)
   361  			if strings.Contains(opt, "=") {
   362  				kv := strings.SplitN(opt, "=", 2)
   363  				k := strings.TrimSpace(strings.ToLower(kv[0]))
   364  				st.opts[k] = strings.TrimSpace(kv[1])
   365  				continue
   366  			}
   367  			opt = strings.ToLower(opt)
   368  			st.opts[opt] = ""
   369  		}
   370  	}
   371  	return
   372  }