134 lines
3 KiB
Go
134 lines
3 KiB
Go
package format
|
|
|
|
import (
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/nicholas-fedor/shoutrrr/pkg/types"
|
|
"github.com/nicholas-fedor/shoutrrr/pkg/util"
|
|
)
|
|
|
|
// DefaultBase represents the default numeric base (decimal) for fields.
|
|
const DefaultBase = 10
|
|
|
|
// FieldInfo is the meta data about a config field.
|
|
type FieldInfo struct {
|
|
Name string
|
|
Type reflect.Type
|
|
EnumFormatter types.EnumFormatter
|
|
Description string
|
|
DefaultValue string
|
|
Template string
|
|
Required bool
|
|
URLParts []URLPart
|
|
Title bool
|
|
Base int
|
|
Keys []string
|
|
ItemSeparator rune
|
|
}
|
|
|
|
// IsEnum returns whether a EnumFormatter has been assigned to the field and that it is of a suitable type.
|
|
func (fi *FieldInfo) IsEnum() bool {
|
|
return fi.EnumFormatter != nil && fi.Type.Kind() == reflect.Int
|
|
}
|
|
|
|
// IsURLPart returns whether the field is serialized as the specified part of an URL.
|
|
func (fi *FieldInfo) IsURLPart(part URLPart) bool {
|
|
for _, up := range fi.URLParts {
|
|
if up == part {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func getStructFieldInfo(structType reflect.Type, enums map[string]types.EnumFormatter) []FieldInfo {
|
|
numFields := structType.NumField()
|
|
fields := make([]FieldInfo, 0, numFields)
|
|
maxKeyLen := 0
|
|
|
|
for i := range numFields {
|
|
fieldDef := structType.Field(i)
|
|
|
|
if isHiddenField(fieldDef) {
|
|
// This is an embedded or private field, which should not be part of the Config output
|
|
continue
|
|
}
|
|
|
|
info := FieldInfo{
|
|
Name: fieldDef.Name,
|
|
Type: fieldDef.Type,
|
|
Required: true,
|
|
Title: false,
|
|
ItemSeparator: ',',
|
|
}
|
|
|
|
if util.IsNumeric(fieldDef.Type.Kind()) {
|
|
info.Base = getFieldBase(fieldDef)
|
|
}
|
|
|
|
if tag, ok := fieldDef.Tag.Lookup("desc"); ok {
|
|
info.Description = tag
|
|
}
|
|
|
|
if tag, ok := fieldDef.Tag.Lookup("tpl"); ok {
|
|
info.Template = tag
|
|
}
|
|
|
|
if tag, ok := fieldDef.Tag.Lookup("default"); ok {
|
|
info.Required = false
|
|
info.DefaultValue = tag
|
|
}
|
|
|
|
if _, ok := fieldDef.Tag.Lookup("optional"); ok {
|
|
info.Required = false
|
|
}
|
|
|
|
if _, ok := fieldDef.Tag.Lookup("title"); ok {
|
|
info.Title = true
|
|
}
|
|
|
|
if tag, ok := fieldDef.Tag.Lookup("url"); ok {
|
|
info.URLParts = ParseURLParts(tag)
|
|
}
|
|
|
|
if tag, ok := fieldDef.Tag.Lookup("key"); ok {
|
|
tag := strings.ToLower(tag)
|
|
info.Keys = strings.Split(tag, ",")
|
|
}
|
|
|
|
if tag, ok := fieldDef.Tag.Lookup("sep"); ok {
|
|
info.ItemSeparator = rune(tag[0])
|
|
}
|
|
|
|
if ef, isEnum := enums[fieldDef.Name]; isEnum {
|
|
info.EnumFormatter = ef
|
|
}
|
|
|
|
fields = append(fields, info)
|
|
|
|
keyLen := len(fieldDef.Name)
|
|
if keyLen > maxKeyLen {
|
|
maxKeyLen = keyLen
|
|
}
|
|
}
|
|
|
|
return fields
|
|
}
|
|
|
|
func isHiddenField(field reflect.StructField) bool {
|
|
return field.Anonymous || strings.ToUpper(field.Name[0:1]) != field.Name[0:1]
|
|
}
|
|
|
|
func getFieldBase(field reflect.StructField) int {
|
|
if tag, ok := field.Tag.Lookup("base"); ok {
|
|
if base, err := strconv.ParseUint(tag, 10, 8); err == nil {
|
|
return int(base)
|
|
}
|
|
}
|
|
|
|
// Default to base 10 if not tagged
|
|
return DefaultBase
|
|
}
|