mirror of
https://github.com/mjl-/mox.git
synced 2025-07-12 16:24:37 +03:00
mox!
This commit is contained in:
264
vendor/github.com/mjl-/sconf/describe.go
generated
vendored
Normal file
264
vendor/github.com/mjl-/sconf/describe.go
generated
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
package sconf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/mjl-/xfmt"
|
||||
)
|
||||
|
||||
var errNoElem = errors.New("no elements")
|
||||
|
||||
type writeError struct{ error }
|
||||
|
||||
type writer struct {
|
||||
out *bufio.Writer
|
||||
prefix string
|
||||
keepZero bool // If set, we also write zero values.
|
||||
docs bool // If set, we write comments.
|
||||
}
|
||||
|
||||
func (w *writer) error(err error) {
|
||||
panic(writeError{err})
|
||||
}
|
||||
|
||||
func (w *writer) check(err error) {
|
||||
if err != nil {
|
||||
w.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *writer) write(s string) {
|
||||
_, err := w.out.WriteString(s)
|
||||
w.check(err)
|
||||
}
|
||||
|
||||
func (w *writer) flush() {
|
||||
err := w.out.Flush()
|
||||
w.check(err)
|
||||
}
|
||||
|
||||
func (w *writer) indent() {
|
||||
w.prefix += "\t"
|
||||
}
|
||||
|
||||
func (w *writer) unindent() {
|
||||
w.prefix = w.prefix[:len(w.prefix)-1]
|
||||
}
|
||||
|
||||
func isOptional(sconfTag string) bool {
|
||||
return hasTagWord(sconfTag, "optional")
|
||||
}
|
||||
|
||||
func isIgnore(sconfTag string) bool {
|
||||
return hasTagWord(sconfTag, "-") || hasTagWord(sconfTag, "ignore")
|
||||
}
|
||||
|
||||
func hasTagWord(sconfTag, word string) bool {
|
||||
l := strings.Split(sconfTag, ",")
|
||||
for _, s := range l {
|
||||
if s == word {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *writer) describeMap(v reflect.Value) {
|
||||
t := v.Type()
|
||||
if t.Key().Kind() != reflect.String {
|
||||
w.error(fmt.Errorf("map key must be string"))
|
||||
}
|
||||
keys := v.MapKeys()
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].String() < keys[j].String()
|
||||
})
|
||||
have := false
|
||||
for _, k := range keys {
|
||||
have = true
|
||||
w.write(w.prefix)
|
||||
w.write(k.String() + ":")
|
||||
mv := v.MapIndex(k)
|
||||
if !w.keepZero && mv.Kind() == reflect.Struct && isEmptyStruct(mv) {
|
||||
w.write(" nil\n")
|
||||
continue
|
||||
}
|
||||
w.describeValue(mv)
|
||||
}
|
||||
if have {
|
||||
return
|
||||
}
|
||||
w.write(w.prefix)
|
||||
w.write("x:")
|
||||
w.describeValue(reflect.Zero(t.Elem()))
|
||||
}
|
||||
|
||||
// whether v is a zero value of a struct type with all fields optional or
|
||||
// ignored, causing it to write nothing when using Write.
|
||||
func isEmptyStruct(v reflect.Value) bool {
|
||||
if v.Kind() != reflect.Struct {
|
||||
panic("not a struct")
|
||||
}
|
||||
t := v.Type()
|
||||
n := t.NumField()
|
||||
for i := 0; i < n; i++ {
|
||||
ft := t.Field(i)
|
||||
tag := ft.Tag.Get("sconf")
|
||||
if isIgnore(tag) {
|
||||
continue
|
||||
}
|
||||
if !isOptional(tag) {
|
||||
return false
|
||||
}
|
||||
if !isZeroIgnored(v.Field(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// whether v is zero, taking ignored values into account.
|
||||
func isZeroIgnored(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Slice, reflect.Map:
|
||||
return v.Len() == 0
|
||||
case reflect.Ptr:
|
||||
return v.IsZero() || isZeroIgnored(v.Elem())
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
n := t.NumField()
|
||||
for i := 0; i < n; i++ {
|
||||
ft := t.Field(i)
|
||||
tag := ft.Tag.Get("sconf")
|
||||
if isIgnore(tag) {
|
||||
continue
|
||||
}
|
||||
if !isZeroIgnored(v.Field(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return v.IsZero()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *writer) describeStruct(v reflect.Value) {
|
||||
t := v.Type()
|
||||
n := t.NumField()
|
||||
for i := 0; i < n; i++ {
|
||||
f := t.Field(i)
|
||||
fv := v.Field(i)
|
||||
if isIgnore(f.Tag.Get("sconf")) {
|
||||
continue
|
||||
}
|
||||
if !w.keepZero && isOptional(f.Tag.Get("sconf")) && isZeroIgnored(fv) {
|
||||
continue
|
||||
}
|
||||
if w.docs {
|
||||
doc := f.Tag.Get("sconf-doc")
|
||||
optional := isOptional(f.Tag.Get("sconf"))
|
||||
if doc != "" || optional {
|
||||
s := "\n" + w.prefix + "# " + doc
|
||||
if optional {
|
||||
opt := "(optional)"
|
||||
if doc != "" {
|
||||
opt = " " + opt
|
||||
}
|
||||
s += opt
|
||||
}
|
||||
s += "\n"
|
||||
b := &strings.Builder{}
|
||||
err := xfmt.Format(b, strings.NewReader(s), xfmt.Config{MaxWidth: 80})
|
||||
w.check(err)
|
||||
w.write(b.String())
|
||||
}
|
||||
}
|
||||
w.write(w.prefix)
|
||||
w.write(f.Name + ":")
|
||||
w.describeValue(fv)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *writer) describeValue(v reflect.Value) {
|
||||
t := v.Type()
|
||||
i := v.Interface()
|
||||
|
||||
if t == durationType {
|
||||
w.write(fmt.Sprintf(" %s\n", i))
|
||||
return
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
default:
|
||||
w.error(fmt.Errorf("unsupported value %v", t.Kind()))
|
||||
return
|
||||
|
||||
case reflect.Bool:
|
||||
w.write(fmt.Sprintf(" %v\n", i))
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
w.write(fmt.Sprintf(" %d\n", i))
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
w.write(fmt.Sprintf(" %f\n", i))
|
||||
|
||||
case reflect.String:
|
||||
if strings.Contains(v.String(), "\n") {
|
||||
w.error(fmt.Errorf("unsupported multiline string"))
|
||||
}
|
||||
w.write(fmt.Sprintf(" %s\n", i))
|
||||
|
||||
case reflect.Slice:
|
||||
w.write("\n")
|
||||
w.indent()
|
||||
w.describeSlice(v)
|
||||
w.unindent()
|
||||
|
||||
case reflect.Ptr:
|
||||
var pv reflect.Value
|
||||
if v.IsNil() {
|
||||
pv = reflect.New(t.Elem()).Elem()
|
||||
} else {
|
||||
pv = v.Elem()
|
||||
}
|
||||
w.describeValue(pv)
|
||||
|
||||
case reflect.Struct:
|
||||
w.write("\n")
|
||||
w.indent()
|
||||
w.describeStruct(v)
|
||||
w.unindent()
|
||||
|
||||
case reflect.Map:
|
||||
w.write("\n")
|
||||
w.indent()
|
||||
w.describeMap(v)
|
||||
w.unindent()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *writer) describeSlice(v reflect.Value) {
|
||||
describeElem := func(vv reflect.Value) {
|
||||
w.write(w.prefix)
|
||||
w.write("-")
|
||||
w.describeValue(vv)
|
||||
}
|
||||
|
||||
n := v.Len()
|
||||
if n == 0 {
|
||||
if w.keepZero {
|
||||
describeElem(reflect.New(v.Type().Elem()))
|
||||
} else {
|
||||
w.error(errNoElem)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
describeElem(v.Index(i))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user