...

Source file src/github.com/aws/smithy-go/document/internal/serde/field.go

Documentation: github.com/aws/smithy-go/document/internal/serde

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

View as plain text