in document/internal/serde/field.go [68:135]
func enumFields(t reflect.Type) []Field {
// Fields to explore
current := []Field{}
next := []Field{{Type: t}}
// count of queued names
count := map[reflect.Type]int{}
nextCount := map[reflect.Type]int{}
visited := map[reflect.Type]struct{}{}
fields := []Field{}
for len(next) > 0 {
current, next = next, current[:0]
count, nextCount = nextCount, map[reflect.Type]int{}
for _, f := range current {
if _, ok := visited[f.Type]; ok {
continue
}
visited[f.Type] = struct{}{}
for i := 0; i < f.Type.NumField(); i++ {
sf := f.Type.Field(i)
if sf.PkgPath != "" && !sf.Anonymous {
// Ignore unexported and non-anonymous fields
// unexported but anonymous field may still be used if
// the type has exported nested fields
continue
}
fieldTag := ParseTag(sf.Tag.Get(tagKey))
if fieldTag.Ignore {
continue
}
ft := sf.Type
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
structField := buildField(f.Index, i, sf, fieldTag)
structField.Type = ft
if !sf.Anonymous || ft.Kind() != reflect.Struct {
fields = append(fields, structField)
if count[f.Type] > 1 {
// If there were multiple instances, add a second,
// so that the annihilation code will see a duplicate.
// It only cares about the distinction between 1 or 2,
// so don't bother generating any more copies.
fields = append(fields, structField)
}
continue
}
// Record new anon struct to explore next round
nextCount[ft]++
if nextCount[ft] == 1 {
next = append(next, structField)
}
}
}
}
return fields
}