Files
kubesphere/vendor/github.com/jszwec/csvutil/cache.go
2021-03-16 10:27:20 +08:00

179 lines
3.1 KiB
Go

package csvutil
import (
"reflect"
"sort"
)
type field struct {
name string
baseType reflect.Type
typ reflect.Type
tag tag
index []int
}
type fields []field
func (fs fields) Len() int { return len(fs) }
func (fs fields) Swap(i, j int) { fs[i], fs[j] = fs[j], fs[i] }
func (fs fields) Less(i, j int) bool {
for k, n := range fs[i].index {
if n != fs[j].index[k] {
return n < fs[j].index[k]
}
}
return len(fs[i].index) < len(fs[j].index)
}
type typeKey struct {
tag string
reflect.Type
}
type fieldMap map[string]fields
func (m fieldMap) insert(f field) {
fs, ok := m[f.name]
if !ok {
m[f.name] = append(fs, f)
return
}
// insert only fields with the shortest path.
if len(fs[0].index) != len(f.index) {
return
}
// fields that are tagged have priority.
if !f.tag.empty {
m[f.name] = append([]field{f}, fs...)
return
}
m[f.name] = append(fs, f)
}
func (m fieldMap) fields() fields {
out := make(fields, 0, len(m))
for _, v := range m {
for i, f := range v {
if f.tag.empty != v[0].tag.empty {
v = v[:i]
break
}
}
if len(v) > 1 {
continue
}
out = append(out, v[0])
}
sort.Sort(out)
return out
}
func buildFields(k typeKey) fields {
type key struct {
reflect.Type
tag
}
q := fields{{typ: k.Type}}
visited := make(map[key]struct{})
fm := make(fieldMap)
for len(q) > 0 {
f := q[0]
q = q[1:]
key := key{f.typ, f.tag}
if _, ok := visited[key]; ok {
continue
}
visited[key] = struct{}{}
depth := len(f.index)
numField := f.typ.NumField()
for i := 0; i < numField; i++ {
sf := f.typ.Field(i)
if sf.PkgPath != "" && !sf.Anonymous {
// unexported field
continue
}
if sf.Anonymous {
t := sf.Type
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if sf.PkgPath != "" && t.Kind() != reflect.Struct {
// ignore embedded unexported non-struct fields.
continue
}
}
tag := parseTag(k.tag, sf)
if tag.ignore {
continue
}
if f.tag.prefix != "" {
tag.prefix += f.tag.prefix
}
ft := sf.Type
if ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
newf := field{
name: tag.prefix + tag.name,
baseType: sf.Type,
typ: ft,
tag: tag,
index: makeIndex(f.index, i),
}
if sf.Anonymous && ft.Kind() == reflect.Struct && tag.empty {
q = append(q, newf)
continue
}
if tag.inline && ft.Kind() == reflect.Struct {
q = append(q, newf)
continue
}
fm.insert(newf)
// look for duplicate nodes on the same level. Nodes won't be
// revisited, so write all fields for the current type now.
for _, v := range q {
if len(v.index) != depth {
break
}
if v.typ == f.typ && v.tag.prefix == tag.prefix {
// other nodes can have different path.
fm.insert(field{
name: tag.prefix + tag.name,
baseType: sf.Type,
typ: ft,
tag: tag,
index: makeIndex(v.index, i),
})
}
}
}
}
return fm.fields()
}
func makeIndex(index []int, v int) []int {
out := make([]int, len(index), len(index)+1)
copy(out, index)
return append(out, v)
}