github.com/aavshr/aws-sdk-go@v1.41.3/service/dynamodb/dynamodbattribute/field.go (about)

     1  package dynamodbattribute
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"strings"
     7  )
     8  
     9  type field struct {
    10  	tag
    11  
    12  	Name        string
    13  	NameFromTag bool
    14  
    15  	Index []int
    16  	Type  reflect.Type
    17  }
    18  
    19  type cachedFields struct {
    20  	fields       []field
    21  	fieldsByName map[string]int
    22  }
    23  
    24  func (f *cachedFields) All() []field {
    25  	return f.fields
    26  }
    27  
    28  func (f *cachedFields) FieldByName(name string) (field, bool) {
    29  	if i, ok := f.fieldsByName[name]; ok {
    30  		return f.fields[i], ok
    31  	}
    32  	for _, f := range f.fields {
    33  		if strings.EqualFold(f.Name, name) {
    34  			return f, true
    35  		}
    36  	}
    37  	return field{}, false
    38  }
    39  
    40  func buildField(pIdx []int, i int, sf reflect.StructField, fieldTag tag) field {
    41  	f := field{
    42  		Name: sf.Name,
    43  		Type: sf.Type,
    44  		tag:  fieldTag,
    45  	}
    46  	if len(fieldTag.Name) != 0 {
    47  		f.NameFromTag = true
    48  		f.Name = fieldTag.Name
    49  	}
    50  
    51  	f.Index = make([]int, len(pIdx)+1)
    52  	copy(f.Index, pIdx)
    53  	f.Index[len(pIdx)] = i
    54  
    55  	return f
    56  }
    57  
    58  // unionStructFields returns a list of fields for the given type. Type info is cached
    59  // to avoid repeated calls into the reflect package
    60  func unionStructFields(t reflect.Type, opts MarshalOptions) *cachedFields {
    61  	if cached, ok := fieldCache.Load(t); ok {
    62  		return cached
    63  	}
    64  
    65  	f := enumFields(t, opts)
    66  	sort.Sort(fieldsByName(f))
    67  	f = visibleFields(f)
    68  
    69  	fs := &cachedFields{
    70  		fields:       f,
    71  		fieldsByName: make(map[string]int, len(f)),
    72  	}
    73  	for i, f := range fs.fields {
    74  		fs.fieldsByName[f.Name] = i
    75  	}
    76  
    77  	cached, _ := fieldCache.LoadOrStore(t, fs)
    78  	return cached
    79  }
    80  
    81  // enumFields will recursively iterate through a structure and its nested
    82  // anonymous fields.
    83  //
    84  // Based on the enoding/json struct field enumeration of the Go Stdlib
    85  // https://golang.org/src/encoding/json/encode.go typeField func.
    86  func enumFields(t reflect.Type, opts MarshalOptions) []field {
    87  	// Fields to explore
    88  	current := []field{}
    89  	next := []field{{Type: t}}
    90  
    91  	// count of queued names
    92  	count := map[reflect.Type]int{}
    93  	nextCount := map[reflect.Type]int{}
    94  
    95  	visited := map[reflect.Type]struct{}{}
    96  	fields := []field{}
    97  
    98  	for len(next) > 0 {
    99  		current, next = next, current[:0]
   100  		count, nextCount = nextCount, map[reflect.Type]int{}
   101  
   102  		for _, f := range current {
   103  			if _, ok := visited[f.Type]; ok {
   104  				continue
   105  			}
   106  			visited[f.Type] = struct{}{}
   107  
   108  			for i := 0; i < f.Type.NumField(); i++ {
   109  				sf := f.Type.Field(i)
   110  				if sf.PkgPath != "" && !sf.Anonymous {
   111  					// Ignore unexported and non-anonymous fields
   112  					// unexported but anonymous field may still be used if
   113  					// the type has exported nested fields
   114  					continue
   115  				}
   116  
   117  				fieldTag := tag{}
   118  				fieldTag.parseAVTag(sf.Tag)
   119  				// Because MarshalOptions.TagKey must be explicitly set, use it
   120  				// over JSON, which is enabled by default.
   121  				if opts.TagKey != "" && fieldTag == (tag{}) {
   122  					fieldTag.parseStructTag(opts.TagKey, sf.Tag)
   123  				} else if opts.SupportJSONTags && fieldTag == (tag{}) {
   124  					fieldTag.parseStructTag("json", sf.Tag)
   125  				}
   126  
   127  				if fieldTag.Ignore {
   128  					continue
   129  				}
   130  
   131  				ft := sf.Type
   132  				if ft.Name() == "" && ft.Kind() == reflect.Ptr {
   133  					ft = ft.Elem()
   134  				}
   135  
   136  				structField := buildField(f.Index, i, sf, fieldTag)
   137  				structField.Type = ft
   138  
   139  				if !sf.Anonymous || ft.Kind() != reflect.Struct {
   140  					fields = append(fields, structField)
   141  					if count[f.Type] > 1 {
   142  						// If there were multiple instances, add a second,
   143  						// so that the annihilation code will see a duplicate.
   144  						// It only cares about the distinction between 1 or 2,
   145  						// so don't bother generating any more copies.
   146  						fields = append(fields, structField)
   147  					}
   148  					continue
   149  				}
   150  
   151  				// Record new anon struct to explore next round
   152  				nextCount[ft]++
   153  				if nextCount[ft] == 1 {
   154  					next = append(next, structField)
   155  				}
   156  			}
   157  		}
   158  	}
   159  
   160  	return fields
   161  }
   162  
   163  // visibleFields will return a slice of fields which are visible based on
   164  // Go's standard visiblity rules with the exception of ties being broken
   165  // by depth and struct tag naming.
   166  //
   167  // Based on the enoding/json field filtering of the Go Stdlib
   168  // https://golang.org/src/encoding/json/encode.go typeField func.
   169  func visibleFields(fields []field) []field {
   170  	// Delete all fields that are hidden by the Go rules for embedded fields,
   171  	// except that fields with JSON tags are promoted.
   172  
   173  	// The fields are sorted in primary order of name, secondary order
   174  	// of field index length. Loop over names; for each name, delete
   175  	// hidden fields by choosing the one dominant field that survives.
   176  	out := fields[:0]
   177  	for advance, i := 0, 0; i < len(fields); i += advance {
   178  		// One iteration per name.
   179  		// Find the sequence of fields with the name of this first field.
   180  		fi := fields[i]
   181  		name := fi.Name
   182  		for advance = 1; i+advance < len(fields); advance++ {
   183  			fj := fields[i+advance]
   184  			if fj.Name != name {
   185  				break
   186  			}
   187  		}
   188  		if advance == 1 { // Only one field with this name
   189  			out = append(out, fi)
   190  			continue
   191  		}
   192  		dominant, ok := dominantField(fields[i : i+advance])
   193  		if ok {
   194  			out = append(out, dominant)
   195  		}
   196  	}
   197  
   198  	fields = out
   199  	sort.Sort(fieldsByIndex(fields))
   200  
   201  	return fields
   202  }
   203  
   204  // dominantField looks through the fields, all of which are known to
   205  // have the same name, to find the single field that dominates the
   206  // others using Go's embedding rules, modified by the presence of
   207  // JSON tags. If there are multiple top-level fields, the boolean
   208  // will be false: This condition is an error in Go and we skip all
   209  // the fields.
   210  //
   211  // Based on the enoding/json field filtering of the Go Stdlib
   212  // https://golang.org/src/encoding/json/encode.go dominantField func.
   213  func dominantField(fields []field) (field, bool) {
   214  	// The fields are sorted in increasing index-length order. The winner
   215  	// must therefore be one with the shortest index length. Drop all
   216  	// longer entries, which is easy: just truncate the slice.
   217  	length := len(fields[0].Index)
   218  	tagged := -1 // Index of first tagged field.
   219  	for i, f := range fields {
   220  		if len(f.Index) > length {
   221  			fields = fields[:i]
   222  			break
   223  		}
   224  		if f.NameFromTag {
   225  			if tagged >= 0 {
   226  				// Multiple tagged fields at the same level: conflict.
   227  				// Return no field.
   228  				return field{}, false
   229  			}
   230  			tagged = i
   231  		}
   232  	}
   233  	if tagged >= 0 {
   234  		return fields[tagged], true
   235  	}
   236  	// All remaining fields have the same length. If there's more than one,
   237  	// we have a conflict (two fields named "X" at the same level) and we
   238  	// return no field.
   239  	if len(fields) > 1 {
   240  		return field{}, false
   241  	}
   242  	return fields[0], true
   243  }
   244  
   245  // fieldsByName sorts field by name, breaking ties with depth,
   246  // then breaking ties with "name came from json tag", then
   247  // breaking ties with index sequence.
   248  //
   249  // Based on the enoding/json field filtering of the Go Stdlib
   250  // https://golang.org/src/encoding/json/encode.go fieldsByName type.
   251  type fieldsByName []field
   252  
   253  func (x fieldsByName) Len() int { return len(x) }
   254  
   255  func (x fieldsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
   256  
   257  func (x fieldsByName) Less(i, j int) bool {
   258  	if x[i].Name != x[j].Name {
   259  		return x[i].Name < x[j].Name
   260  	}
   261  	if len(x[i].Index) != len(x[j].Index) {
   262  		return len(x[i].Index) < len(x[j].Index)
   263  	}
   264  	if x[i].NameFromTag != x[j].NameFromTag {
   265  		return x[i].NameFromTag
   266  	}
   267  	return fieldsByIndex(x).Less(i, j)
   268  }
   269  
   270  // fieldsByIndex sorts field by index sequence.
   271  //
   272  // Based on the enoding/json field filtering of the Go Stdlib
   273  // https://golang.org/src/encoding/json/encode.go fieldsByIndex type.
   274  type fieldsByIndex []field
   275  
   276  func (x fieldsByIndex) Len() int { return len(x) }
   277  
   278  func (x fieldsByIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
   279  
   280  func (x fieldsByIndex) Less(i, j int) bool {
   281  	for k, xik := range x[i].Index {
   282  		if k >= len(x[j].Index) {
   283  			return false
   284  		}
   285  		if xik != x[j].Index[k] {
   286  			return xik < x[j].Index[k]
   287  		}
   288  	}
   289  	return len(x[i].Index) < len(x[j].Index)
   290  }