1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
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
}
|