104 lines
3.3 KiB
Go
104 lines
3.3 KiB
Go
package configtypes
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// This file hosts small reflection helpers shared by config tooling that walks
|
|
// the configuration structs — currently the `configdoc` generator and the admin
|
|
// config redaction (Pro). Keeping them here, in OSS, gives both a single source
|
|
// of truth for how config fields are classified, named, and flattened. See also
|
|
// NameForEnv in namespace.go.
|
|
|
|
// JSONKey returns the configured key for a struct field: the first segment of the
|
|
// json tag (falling back to the mapstructure tag), or the Go field name when no
|
|
// tag is present.
|
|
func JSONKey(f reflect.StructField) string {
|
|
tag := f.Tag.Get("json")
|
|
if tag == "" {
|
|
tag = f.Tag.Get("mapstructure")
|
|
}
|
|
if tag == "" {
|
|
return f.Name
|
|
}
|
|
if name := strings.Split(tag, ",")[0]; name != "" {
|
|
return name
|
|
}
|
|
return f.Name
|
|
}
|
|
|
|
// IsSquash reports whether a struct field is squashed/inlined into its parent
|
|
// (an embedded struct, a mapstructure ",squash", or a yaml ",inline"). Such
|
|
// fields contribute their own fields to the parent rather than a nested object.
|
|
func IsSquash(f reflect.StructField) bool {
|
|
return f.Anonymous ||
|
|
strings.Contains(f.Tag.Get("mapstructure"), "squash") ||
|
|
strings.Contains(f.Tag.Get("yaml"), "inline")
|
|
}
|
|
|
|
// IsComplexType reports whether t is object-like — a struct or a slice/array of
|
|
// structs — and therefore cannot be populated from a single environment variable
|
|
// (its keys are set separately). Scalars, slices of scalars, and maps are not
|
|
// complex (each maps to one env var). time.Time is treated as a scalar.
|
|
func IsComplexType(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Pointer:
|
|
return t.Elem().Kind() == reflect.Struct && !isTimeType(t.Elem())
|
|
case reflect.Struct:
|
|
return !isTimeType(t)
|
|
case reflect.Slice, reflect.Array:
|
|
elem := t.Elem()
|
|
if elem.Kind() == reflect.Pointer {
|
|
elem = elem.Elem()
|
|
}
|
|
return elem.Kind() == reflect.Struct && !isTimeType(elem)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isTimeType(t reflect.Type) bool {
|
|
return t.PkgPath() == "time" && t.Name() == "Time"
|
|
}
|
|
|
|
// TypeLabel renders a human-friendly type label for a config field type. Struct
|
|
// and slice-of-struct types show their Go type name (e.g. "TLSConfig",
|
|
// "[]Consumer"); every other type shows its Go type string with the internal
|
|
// "configtypes." package qualifier stripped (e.g. "Duration", "PEMData", "int64",
|
|
// "[]string", "map[string]string"). Used by both the configdoc generator and the
|
|
// admin config UI (Pro) so the two surfaces present identical type labels.
|
|
func TypeLabel(t reflect.Type) string {
|
|
if t.Kind() == reflect.Pointer {
|
|
if t.Elem().Kind() == reflect.Struct {
|
|
if isTimeType(t.Elem()) {
|
|
return stripConfigtypesQualifier(t.String())
|
|
}
|
|
return t.Elem().Name()
|
|
}
|
|
}
|
|
if t.Kind() == reflect.Struct {
|
|
if isTimeType(t) {
|
|
return stripConfigtypesQualifier(t.String())
|
|
}
|
|
return t.Name()
|
|
}
|
|
if t.Kind() == reflect.Slice {
|
|
elem := t.Elem()
|
|
if elem.Kind() == reflect.Pointer {
|
|
elem = elem.Elem()
|
|
}
|
|
if elem.Kind() == reflect.Struct {
|
|
if isTimeType(elem) {
|
|
return stripConfigtypesQualifier(t.String())
|
|
}
|
|
return "[]" + elem.Name()
|
|
}
|
|
}
|
|
return stripConfigtypesQualifier(t.String())
|
|
}
|
|
|
|
func stripConfigtypesQualifier(typeStr string) string {
|
|
return strings.ReplaceAll(typeStr, "configtypes.", "")
|
|
}
|