<commit_before>package reflector

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type fieldListingType int

const (
	fieldsAll fieldListingType = iota
	fieldsAnonymous
	fieldsFlattenAnonymous
	fieldsNoFlattenAnonymous
)

var (
	metadataCache map[reflect.Type]ObjMetadata
	// # of metadata cached, just for testing, not threadsafe!
	metadataCached int
)

func init() {
	metadataCache = map[reflect.Type]ObjMetadata{}
	metadataCached = 0
}

// ObjMetadata contains data which is always unique per Type
type ObjMetadata struct {
	isStruct      bool
	isPtrToStruct bool

	// If ptr to struct, this field will contain the type of that struct
	underlyingType reflect.Type

	objType reflect.Type
	objKind reflect.Kind

	fields map[string]ObjFieldMetadata

	fieldNamesAll                []string
	fieldNamesAnonymous          []string
	fieldNamesFlattenAnonymous   []string
	fieldNamesNoFlattenAnonymous []string

	methods     map[string]ObjMethodMetadata
	methodNames []string
}

func newObjMetadata(ty reflect.Type) *ObjMetadata {
	res := new(ObjMetadata)
	if ty == nil {
		res.objKind = reflect.Invalid
		return res
	}

	res.objType = ty
	res.objKind = res.objType.Kind()

	if ty.Kind() == reflect.Struct {
		res.isStruct = true
	}
	if ty.Kind() == reflect.Ptr && ty.Elem().Kind() == reflect.Struct {
		ty = ty.Elem()
		res.isPtrToStruct = true
	}
	res.underlyingType = ty

	allFields := res.getFields(res.objType, fieldsAll)

	res.fieldNamesAll = allFields
	res.fieldNamesAnonymous = res.getFields(res.objType, fieldsAnonymous)
	res.fieldNamesFlattenAnonymous = res.getFields(res.objType, fieldsFlattenAnonymous)
	res.fieldNamesNoFlattenAnonymous = res.getFields(res.objType, fieldsNoFlattenAnonymous)

	res.methods = map[string]ObjMethodMetadata{}
	res.methodNames = []string{}

	if res.objKind != reflect.Invalid {
		res.fields = map[string]ObjFieldMetadata{}
		for _, fieldName := range allFields {
			res.fields[fieldName] = *newObjFieldMetadata(res.objType, fieldName, res)
		}
		for i := 0; i < res.objType.NumMethod(); i++ {
			method := res.objType.Method(i)
			res.methodNames = append(res.methodNames, method.Name)
			res.methods[method.Name] = *newObjMethodMetadata(res.objType, method.Name, res)
		}
	}

	return res
}

// IsStructOrPtrToStruct checks if the value is a struct or a pointer to a struct
func (om *ObjMetadata) IsStructOrPtrToStruct() bool {
	return om.isStruct || om.isPtrToStruct
}

func (om *ObjMetadata) getFields(ty reflect.Type, listingType fieldListingType) []string {
	var fields []string

	if ty.Kind() == reflect.Ptr {
		ty = ty.Elem()
	}

	if ty.Kind() != reflect.Struct {
		return fields // No need to populate nonstructs
	}

	for i := 0; i < ty.NumField(); i++ {
		field := ty.Field(i)

		k := field.Type.Kind()
		if isExportable(field) {
			if listingType == fieldsAnonymous {
				if field.Anonymous {
					fields = append(fields, field.Name)
				}
			} else if listingType == fieldsAll {
				fields = append(fields, field.Name)
				if k == reflect.Struct && field.Anonymous {
					fields = append(fields, om.getFields(field.Type, listingType)...)
				}
			} else {
				if listingType == fieldsFlattenAnonymous && k == reflect.Struct && field.Anonymous {
					fields = append(fields, om.getFields(field.Type, listingType)...)
				} else {
					fields = append(fields, field.Name)
				}
			}
		}
	}

	return fields
}

// ObjFieldMetadata contains data which is always unique per Type/Field
type ObjFieldMetadata struct {
	name string

	structField reflect.StructField

	fieldKind reflect.Kind
	fieldType reflect.Type

	// Valid here is not yet the final info about an actual field validity, because value field still have .IsValid()
	valid bool<commit_msg>Fix cyclomatic complexity in (*ObjMetadata) getFields<commit_after>package reflector

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type fieldListingType int

const (
	fieldsAll fieldListingType = iota
	fieldsAnonymous
	fieldsFlattenAnonymous
	fieldsNoFlattenAnonymous
)

var (
	metadataCache map[reflect.Type]ObjMetadata
	// # of metadata cached, just for testing, not threadsafe!
	metadataCached int
)

func init() {
	metadataCache = map[reflect.Type]ObjMetadata{}
	metadataCached = 0
}

// ObjMetadata contains data which is always unique per Type
type ObjMetadata struct {
	isStruct      bool
	isPtrToStruct bool

	// If ptr to struct, this field will contain the type of that struct
	underlyingType reflect.Type

	objType reflect.Type
	objKind reflect.Kind

	fields map[string]ObjFieldMetadata

	fieldNamesAll                []string
	fieldNamesAnonymous          []string
	fieldNamesFlattenAnonymous   []string
	fieldNamesNoFlattenAnonymous []string

	methods     map[string]ObjMethodMetadata
	methodNames []string
}

func newObjMetadata(ty reflect.Type) *ObjMetadata {
	res := new(ObjMetadata)
	if ty == nil {
		res.objKind = reflect.Invalid
		return res
	}

	res.objType = ty
	res.objKind = res.objType.Kind()

	if ty.Kind() == reflect.Struct {
		res.isStruct = true
	}
	if ty.Kind() == reflect.Ptr && ty.Elem().Kind() == reflect.Struct {
		ty = ty.Elem()
		res.isPtrToStruct = true
	}
	res.underlyingType = ty

	allFields := res.getFields(res.objType, fieldsAll)

	res.fieldNamesAll = allFields
	res.fieldNamesAnonymous = res.getFields(res.objType, fieldsAnonymous)
	res.fieldNamesFlattenAnonymous = res.getFields(res.objType, fieldsFlattenAnonymous)
	res.fieldNamesNoFlattenAnonymous = res.getFields(res.objType, fieldsNoFlattenAnonymous)

	res.methods = map[string]ObjMethodMetadata{}
	res.methodNames = []string{}

	if res.objKind != reflect.Invalid {
		res.fields = map[string]ObjFieldMetadata{}
		for _, fieldName := range allFields {
			res.fields[fieldName] = *newObjFieldMetadata(res.objType, fieldName, res)
		}
		for i := 0; i < res.objType.NumMethod(); i++ {
			method := res.objType.Method(i)
			res.methodNames = append(res.methodNames, method.Name)
			res.methods[method.Name] = *newObjMethodMetadata(res.objType, method.Name, res)
		}
	}

	return res
}

// IsStructOrPtrToStruct checks if the value is a struct or a pointer to a struct
func (om *ObjMetadata) IsStructOrPtrToStruct() bool {
	return om.isStruct || om.isPtrToStruct
}

func (om *ObjMetadata) appendFields(fields []string, field reflect.StructField, listingType fieldListingType) []string {
	k := field.Type.Kind()
	if isExportable(field) {
		if listingType == fieldsAnonymous {
			if field.Anonymous {
				fields = append(fields, field.Name)
			}
		} else if listingType == fieldsAll {
			fields = append(fields, field.Name)
			if k == reflect.Struct && field.Anonymous {
				fields = append(fields, om.getFields(field.Type, listingType)...)
			}
		} else {
			if listingType == fieldsFlattenAnonymous && k == reflect.Struct && field.Anonymous {
				fields = append(fields, om.getFields(field.Type, listingType)...)
			} else {
				fields = append(fields, field.Name)
			}
		}
	}
	return fields
}

func (om *ObjMetadata) getFields(ty reflect.Type, listingType fieldListingType) []string {
	var fields []string

	if ty.Kind() == reflect.Ptr {
		ty = ty.Elem()
	}

	if ty.Kind() != reflect.Struct {
		return fields // No need to populate nonstructs
	}

	for i := 0; i < ty.NumField(); i++ {
		f := ty.Field(i)
		fields = om.appendFields(fields, f, listingType)
	}

	return fields
}

// ObjFieldMetadata contains data which is always unique per Type/Field
type ObjFieldMetadata struct {
	name string

	structField reflect.StructField

	fieldKind reflect.Kind
	fieldType reflect.Type

	// Valid here is not yet the final info about an actual field validity, because value field still have .IsValid()
	valid bool
{'fraction_non_alphanumeric': 0.10650342285413375, 'fraction_numerical': 0.0007898894154818325, 'mean_word_length': 3.9947403024326102, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4516549', 'n_tokens_mistral': 2587, 'n_tokens_neox': 2374, 'n_words': 634}