mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-06-02 21:39:25 +00:00
51c156cca1
Signed-off-by: kim <grufwub@gmail.com>
893 lines
20 KiB
Go
893 lines
20 KiB
Go
package format
|
|
|
|
import (
|
|
"reflect"
|
|
"strconv"
|
|
"unicode/utf8"
|
|
|
|
"codeberg.org/gruf/go-byteutil"
|
|
)
|
|
|
|
const (
|
|
// Flag bit constants, note they are prioritised in this order.
|
|
IsKeyBit = uint8(1) << 0 // set to indicate key formatting
|
|
VboseBit = uint8(1) << 1 // set to indicate verbose formatting
|
|
IsValBit = uint8(1) << 2 // set to indicate value formatting
|
|
PanicBit = uint8(1) << 3 // set after panic to prevent recursion
|
|
)
|
|
|
|
// format provides formatting of values into a Buffer.
|
|
type format struct {
|
|
// Flags are the currently set value flags.
|
|
Flags uint8
|
|
|
|
// Derefs is the current value dereference count.
|
|
Derefs uint8
|
|
|
|
// CurDepth is the current Format iterator depth.
|
|
CurDepth uint8
|
|
|
|
// VType is the current value type.
|
|
VType string
|
|
|
|
// Config is the set Formatter config (MUST NOT be nil).
|
|
Config *Formatter
|
|
|
|
// Buffer is the currently set output buffer.
|
|
Buffer *byteutil.Buffer
|
|
}
|
|
|
|
// AtMaxDepth returns whether format is currently at max depth.
|
|
func (f format) AtMaxDepth() bool {
|
|
return f.CurDepth > f.Config.MaxDepth
|
|
}
|
|
|
|
// Key returns whether the isKey flag is set.
|
|
func (f format) Key() bool {
|
|
return (f.Flags & IsKeyBit) != 0
|
|
}
|
|
|
|
// Value returns whether the isVal flag is set.
|
|
func (f format) Value() bool {
|
|
return (f.Flags & IsValBit) != 0
|
|
}
|
|
|
|
// Verbose returns whether the verbose flag is set.
|
|
func (f format) Verbose() bool {
|
|
return (f.Flags & VboseBit) != 0
|
|
}
|
|
|
|
// Panic returns whether the panic flag is set.
|
|
func (f format) Panic() bool {
|
|
return (f.Flags & PanicBit) != 0
|
|
}
|
|
|
|
// SetKey returns format instance with the IsKey bit set to true,
|
|
// note this resets the dereference count.
|
|
func (f format) SetKey() format {
|
|
flags := f.Flags | IsKeyBit
|
|
flags &= ^IsValBit
|
|
return format{
|
|
Flags: flags,
|
|
CurDepth: f.CurDepth,
|
|
Config: f.Config,
|
|
Buffer: f.Buffer,
|
|
}
|
|
}
|
|
|
|
// SetValue returns format instance with the IsVal bit set to true,
|
|
// note this resets the dereference count.
|
|
func (f format) SetValue() format {
|
|
flags := f.Flags | IsValBit
|
|
flags &= ^IsKeyBit
|
|
return format{
|
|
Flags: flags,
|
|
CurDepth: f.CurDepth,
|
|
Config: f.Config,
|
|
Buffer: f.Buffer,
|
|
}
|
|
}
|
|
|
|
// SetVerbose returns format instance with the Vbose bit set to true,
|
|
// note this resets the dereference count.
|
|
func (f format) SetVerbose() format {
|
|
return format{
|
|
Flags: f.Flags | VboseBit,
|
|
CurDepth: f.CurDepth,
|
|
Config: f.Config,
|
|
Buffer: f.Buffer,
|
|
}
|
|
}
|
|
|
|
// SetPanic returns format instance with the panic bit set to true,
|
|
// note this resets the dereference count and sets IsVal (unsetting IsKey) bit.
|
|
func (f format) SetPanic() format {
|
|
flags := f.Flags | PanicBit
|
|
flags |= IsValBit
|
|
flags &= ^IsKeyBit
|
|
return format{
|
|
Flags: flags,
|
|
CurDepth: f.CurDepth,
|
|
Config: f.Config,
|
|
Buffer: f.Buffer,
|
|
}
|
|
}
|
|
|
|
// IncrDepth returns format instance with depth incremented and derefs reset.
|
|
func (f format) IncrDepth() format {
|
|
return format{
|
|
Flags: f.Flags,
|
|
Derefs: f.Derefs,
|
|
CurDepth: f.CurDepth + 1,
|
|
Config: f.Config,
|
|
Buffer: f.Buffer,
|
|
}
|
|
}
|
|
|
|
// IncrDerefs returns format instance with dereference count incremented.
|
|
func (f format) IncrDerefs() format {
|
|
return format{
|
|
Flags: f.Flags,
|
|
Derefs: f.Derefs + 1,
|
|
CurDepth: f.CurDepth,
|
|
Config: f.Config,
|
|
Buffer: f.Buffer,
|
|
}
|
|
}
|
|
|
|
func (f format) AppendType() {
|
|
const derefs = `********************************` +
|
|
`********************************` +
|
|
`********************************` +
|
|
`********************************` +
|
|
`********************************` +
|
|
`********************************` +
|
|
`********************************` +
|
|
`********************************`
|
|
f.Buffer.B = append(f.Buffer.B, derefs[:f.Derefs]...)
|
|
f.Buffer.B = append(f.Buffer.B, f.VType...)
|
|
}
|
|
|
|
func (f format) AppendNil() {
|
|
if !f.Verbose() {
|
|
f.Buffer.B = append(f.Buffer.B, `nil`...)
|
|
return
|
|
}
|
|
|
|
// Append nil with type
|
|
f.Buffer.B = append(f.Buffer.B, '(')
|
|
f.AppendType()
|
|
f.Buffer.B = append(f.Buffer.B, `)(nil`...)
|
|
f.Buffer.B = append(f.Buffer.B, ')')
|
|
}
|
|
|
|
func (f format) AppendByte(b byte) {
|
|
switch {
|
|
// Always quoted
|
|
case f.Key():
|
|
f.Buffer.B = append(f.Buffer.B, '\'')
|
|
f.Buffer.B = append(f.Buffer.B, Byte2Str(b)...)
|
|
f.Buffer.B = append(f.Buffer.B, '\'')
|
|
|
|
// Always quoted ASCII with type
|
|
case f.Verbose():
|
|
f._AppendPrimitiveTyped(func(f format) {
|
|
f.Buffer.B = append(f.Buffer.B, '\'')
|
|
f.Buffer.B = append(f.Buffer.B, Byte2Str(b)...)
|
|
f.Buffer.B = append(f.Buffer.B, '\'')
|
|
})
|
|
|
|
// Always quoted
|
|
case f.Value():
|
|
f.Buffer.B = append(f.Buffer.B, '\'')
|
|
f.Buffer.B = append(f.Buffer.B, Byte2Str(b)...)
|
|
f.Buffer.B = append(f.Buffer.B, '\'')
|
|
|
|
// Append as raw byte
|
|
default:
|
|
f.Buffer.B = append(f.Buffer.B, b)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendBytes(b []byte) {
|
|
switch {
|
|
// Bytes CAN be nil formatted
|
|
case b == nil:
|
|
f.AppendNil()
|
|
|
|
// Quoted only if spaces/requires escaping
|
|
case f.Key():
|
|
f.AppendStringSafe(b2s(b))
|
|
|
|
// Append as separate ASCII quoted bytes in slice
|
|
case f.Verbose():
|
|
f._AppendArrayTyped(func(f format) {
|
|
for i := 0; i < len(b); i++ {
|
|
f.Buffer.B = append(f.Buffer.B, '\'')
|
|
f.Buffer.B = append(f.Buffer.B, Byte2Str(b[i])...)
|
|
f.Buffer.B = append(f.Buffer.B, `',`...)
|
|
}
|
|
if len(b) > 0 {
|
|
f.Buffer.Truncate(1)
|
|
}
|
|
})
|
|
|
|
// Quoted only if spaces/requires escaping
|
|
case f.Value():
|
|
f.AppendStringSafe(b2s(b))
|
|
|
|
// Append as raw bytes
|
|
default:
|
|
f.Buffer.B = append(f.Buffer.B, b...)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendRune(r rune) {
|
|
switch {
|
|
// Quoted only if spaces/requires escaping
|
|
case f.Key():
|
|
f.AppendRuneKey(r)
|
|
|
|
// Always quoted ASCII with type
|
|
case f.Verbose():
|
|
f._AppendPrimitiveTyped(func(f format) {
|
|
f.Buffer.B = strconv.AppendQuoteRuneToASCII(f.Buffer.B, r)
|
|
})
|
|
|
|
// Always quoted value
|
|
case f.Value():
|
|
f.Buffer.B = strconv.AppendQuoteRune(f.Buffer.B, r)
|
|
|
|
// Append as raw rune
|
|
default:
|
|
f.Buffer.WriteRune(r)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendRuneKey(r rune) {
|
|
if utf8.RuneLen(r) > 1 && (r < ' ' && r != '\t') || r == '`' || r == '\u007F' {
|
|
// Quote and escape this rune
|
|
f.Buffer.B = strconv.AppendQuoteRuneToASCII(f.Buffer.B, r)
|
|
} else {
|
|
// Simply append rune
|
|
f.Buffer.WriteRune(r)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendRunes(r []rune) {
|
|
switch {
|
|
// Runes CAN be nil formatted
|
|
case r == nil:
|
|
f.AppendNil()
|
|
|
|
// Quoted only if spaces/requires escaping
|
|
case f.Key():
|
|
f.AppendStringSafe(string(r))
|
|
|
|
// Append as separate ASCII quoted bytes in slice
|
|
case f.Verbose():
|
|
f._AppendArrayTyped(func(f format) {
|
|
for i := 0; i < len(r); i++ {
|
|
f.Buffer.B = strconv.AppendQuoteRuneToASCII(f.Buffer.B, r[i])
|
|
f.Buffer.B = append(f.Buffer.B, ',')
|
|
}
|
|
if len(r) > 0 {
|
|
f.Buffer.Truncate(1)
|
|
}
|
|
})
|
|
|
|
// Quoted only if spaces/requires escaping
|
|
case f.Value():
|
|
f.AppendStringSafe(string(r))
|
|
|
|
// Append as raw bytes
|
|
default:
|
|
for i := 0; i < len(r); i++ {
|
|
f.Buffer.WriteRune(r[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f format) AppendString(s string) {
|
|
switch {
|
|
// Quoted only if spaces/requires escaping
|
|
case f.Key():
|
|
f.AppendStringSafe(s)
|
|
|
|
// Always quoted with type
|
|
case f.Verbose():
|
|
f._AppendPrimitiveTyped(func(f format) {
|
|
f.AppendStringQuoted(s)
|
|
})
|
|
|
|
// Quoted only if spaces/requires escaping
|
|
case f.Value():
|
|
f.AppendStringSafe(s)
|
|
|
|
// All else
|
|
default:
|
|
f.Buffer.B = append(f.Buffer.B, s...)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendStringSafe(s string) {
|
|
if len(s) > SingleTermLine || !IsSafeASCII(s) {
|
|
// Requires quoting AND escaping
|
|
f.Buffer.B = strconv.AppendQuote(f.Buffer.B, s)
|
|
} else if ContainsDoubleQuote(s) {
|
|
// Contains double quotes, needs escaping
|
|
f.Buffer.B = AppendEscape(f.Buffer.B, s)
|
|
} else if len(s) == 0 || ContainsSpaceOrTab(s) {
|
|
// Contains space / empty, needs quotes
|
|
f.Buffer.B = append(f.Buffer.B, '"')
|
|
f.Buffer.B = append(f.Buffer.B, s...)
|
|
f.Buffer.B = append(f.Buffer.B, '"')
|
|
} else {
|
|
// All else write as-is
|
|
f.Buffer.B = append(f.Buffer.B, s...)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendStringQuoted(s string) {
|
|
if len(s) > SingleTermLine || !IsSafeASCII(s) {
|
|
// Requires quoting AND escaping
|
|
f.Buffer.B = strconv.AppendQuote(f.Buffer.B, s)
|
|
} else if ContainsDoubleQuote(s) {
|
|
// Contains double quotes, needs escaping
|
|
f.Buffer.B = append(f.Buffer.B, '"')
|
|
f.Buffer.B = AppendEscape(f.Buffer.B, s)
|
|
f.Buffer.B = append(f.Buffer.B, '"')
|
|
} else {
|
|
// Simply append with quotes
|
|
f.Buffer.B = append(f.Buffer.B, '"')
|
|
f.Buffer.B = append(f.Buffer.B, s...)
|
|
f.Buffer.B = append(f.Buffer.B, '"')
|
|
}
|
|
}
|
|
|
|
func (f format) AppendBool(b bool) {
|
|
if f.Verbose() {
|
|
// Append as bool with type information
|
|
f._AppendPrimitiveTyped(func(f format) {
|
|
f.Buffer.B = strconv.AppendBool(f.Buffer.B, b)
|
|
})
|
|
} else {
|
|
// Simply append as bool
|
|
f.Buffer.B = strconv.AppendBool(f.Buffer.B, b)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendInt(i int64) {
|
|
f._AppendPrimitiveType(func(f format) {
|
|
f.Buffer.B = strconv.AppendInt(f.Buffer.B, i, 10)
|
|
})
|
|
}
|
|
|
|
func (f format) AppendUint(u uint64) {
|
|
f._AppendPrimitiveType(func(f format) {
|
|
f.Buffer.B = strconv.AppendUint(f.Buffer.B, u, 10)
|
|
})
|
|
}
|
|
|
|
func (f format) AppendFloat(l float64) {
|
|
f._AppendPrimitiveType(func(f format) {
|
|
f.AppendFloatValue(l)
|
|
})
|
|
}
|
|
|
|
func (f format) AppendFloatValue(l float64) {
|
|
f.Buffer.B = strconv.AppendFloat(f.Buffer.B, l, 'f', -1, 64)
|
|
}
|
|
|
|
func (f format) AppendComplex(c complex128) {
|
|
f._AppendPrimitiveType(func(f format) {
|
|
f.AppendFloatValue(real(c))
|
|
f.Buffer.B = append(f.Buffer.B, '+')
|
|
f.AppendFloatValue(imag(c))
|
|
f.Buffer.B = append(f.Buffer.B, 'i')
|
|
})
|
|
}
|
|
|
|
func (f format) AppendPtr(u uint64) {
|
|
f._AppendPtrType(func(f format) {
|
|
if u == 0 {
|
|
// Append as nil
|
|
f.Buffer.B = append(f.Buffer.B, `nil`...)
|
|
} else {
|
|
// Append as hex number
|
|
f.Buffer.B = append(f.Buffer.B, `0x`...)
|
|
f.Buffer.B = strconv.AppendUint(f.Buffer.B, u, 16)
|
|
}
|
|
})
|
|
}
|
|
|
|
func (f format) AppendInterfaceOrReflect(i interface{}) {
|
|
if !f.AppendInterface(i) {
|
|
// Interface append failed, used reflected value + type
|
|
f.AppendReflectValue(reflect.ValueOf(i), reflect.TypeOf(i))
|
|
}
|
|
}
|
|
|
|
func (f format) AppendInterfaceOrReflectNext(v reflect.Value, t reflect.Type) {
|
|
// Check we haven't hit max
|
|
if f.AtMaxDepth() {
|
|
f.Buffer.B = append(f.Buffer.B, `...`...)
|
|
return
|
|
}
|
|
|
|
// Incr the depth
|
|
f = f.IncrDepth()
|
|
|
|
// Make actual call
|
|
f.AppendReflectOrInterface(v, t)
|
|
}
|
|
|
|
func (f format) AppendReflectOrInterface(v reflect.Value, t reflect.Type) {
|
|
if !v.CanInterface() ||
|
|
!f.AppendInterface(v.Interface()) {
|
|
// Interface append failed, use reflect
|
|
f.AppendReflectValue(v, t)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendInterface(i interface{}) bool {
|
|
switch i := i.(type) {
|
|
// Reflect types
|
|
case reflect.Type:
|
|
f.AppendReflectType(i)
|
|
case reflect.Value:
|
|
f.Buffer.B = append(f.Buffer.B, `reflect.Value`...)
|
|
f.Buffer.B = append(f.Buffer.B, '(')
|
|
f.Buffer.B = append(f.Buffer.B, i.String()...)
|
|
f.Buffer.B = append(f.Buffer.B, ')')
|
|
|
|
// Bytes, runes and string types
|
|
case rune:
|
|
f.VType = `int32`
|
|
f.AppendRune(i)
|
|
case []rune:
|
|
f.VType = `[]int32`
|
|
f.AppendRunes(i)
|
|
case byte:
|
|
f.VType = `uint8`
|
|
f.AppendByte(i)
|
|
case []byte:
|
|
f.VType = `[]uint8`
|
|
f.AppendBytes(i)
|
|
case string:
|
|
f.VType = `string`
|
|
f.AppendString(i)
|
|
|
|
// Int types
|
|
case int:
|
|
f.VType = `int`
|
|
f.AppendInt(int64(i))
|
|
case int8:
|
|
f.VType = `int8`
|
|
f.AppendInt(int64(i))
|
|
case int16:
|
|
f.VType = `int16`
|
|
f.AppendInt(int64(i))
|
|
case int64:
|
|
f.VType = `int64`
|
|
f.AppendInt(int64(i))
|
|
|
|
// Uint types
|
|
case uint:
|
|
f.VType = `uint`
|
|
f.AppendUint(uint64(i))
|
|
case uint16:
|
|
f.VType = `uint16`
|
|
f.AppendUint(uint64(i))
|
|
case uint32:
|
|
f.VType = `uint32`
|
|
f.AppendUint(uint64(i))
|
|
case uint64:
|
|
f.VType = `uint64`
|
|
f.AppendUint(uint64(i))
|
|
|
|
// Float types
|
|
case float32:
|
|
f.VType = `float32`
|
|
f.AppendFloat(float64(i))
|
|
case float64:
|
|
f.VType = `float64`
|
|
f.AppendFloat(float64(i))
|
|
|
|
// Bool type
|
|
case bool:
|
|
f.VType = `bool`
|
|
f.AppendBool(i)
|
|
|
|
// Complex types
|
|
case complex64:
|
|
f.VType = `complex64`
|
|
f.AppendComplex(complex128(i))
|
|
case complex128:
|
|
f.VType = `complex128`
|
|
f.AppendComplex(complex128(i))
|
|
|
|
// Method types
|
|
case error:
|
|
return f._AppendMethodType(func() string {
|
|
return i.Error()
|
|
}, i)
|
|
case interface{ String() string }:
|
|
return f._AppendMethodType(func() string {
|
|
return i.String()
|
|
}, i)
|
|
|
|
// No quick handler
|
|
default:
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (f format) AppendReflectType(t reflect.Type) {
|
|
switch f.VType = `reflect.Type`; {
|
|
case isNil(t) /* safer nil check */ :
|
|
f.AppendNil()
|
|
case f.Verbose():
|
|
f.AppendType()
|
|
f.Buffer.B = append(f.Buffer.B, '(')
|
|
f.Buffer.B = append(f.Buffer.B, t.String()...)
|
|
f.Buffer.B = append(f.Buffer.B, ')')
|
|
default:
|
|
f.Buffer.B = append(f.Buffer.B, t.String()...)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendReflectValue(v reflect.Value, t reflect.Type) {
|
|
switch v.Kind() {
|
|
// String/byte types
|
|
case reflect.String:
|
|
f.VType = t.String()
|
|
f.AppendString(v.String())
|
|
case reflect.Uint8:
|
|
f.VType = t.String()
|
|
f.AppendByte(byte(v.Uint()))
|
|
case reflect.Int32:
|
|
f.VType = t.String()
|
|
f.AppendRune(rune(v.Int()))
|
|
|
|
// Float tpyes
|
|
case reflect.Float32, reflect.Float64:
|
|
f.VType = t.String()
|
|
f.AppendFloat(v.Float())
|
|
|
|
// Int types
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int64:
|
|
f.VType = t.String()
|
|
f.AppendInt(v.Int())
|
|
|
|
// Uint types
|
|
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
f.VType = t.String()
|
|
f.AppendUint(v.Uint())
|
|
|
|
// Complex types
|
|
case reflect.Complex64, reflect.Complex128:
|
|
f.VType = t.String()
|
|
f.AppendComplex(v.Complex())
|
|
|
|
// Bool type
|
|
case reflect.Bool:
|
|
f.VType = t.String()
|
|
f.AppendBool(v.Bool())
|
|
|
|
// Slice and array types
|
|
case reflect.Array:
|
|
f.AppendArray(v, t)
|
|
case reflect.Slice:
|
|
f.AppendSlice(v, t)
|
|
|
|
// Map types
|
|
case reflect.Map:
|
|
f.AppendMap(v, t)
|
|
|
|
// Struct types
|
|
case reflect.Struct:
|
|
f.AppendStruct(v, t)
|
|
|
|
// Interface type
|
|
case reflect.Interface:
|
|
if v.IsNil() {
|
|
// Append nil ptr type
|
|
f.VType = t.String()
|
|
f.AppendNil()
|
|
} else {
|
|
// Append interface
|
|
v = v.Elem()
|
|
t = v.Type()
|
|
f.AppendReflectOrInterface(v, t)
|
|
}
|
|
|
|
// Deref'able ptr type
|
|
case reflect.Ptr:
|
|
if v.IsNil() {
|
|
// Append nil ptr type
|
|
f.VType = t.String()
|
|
f.AppendNil()
|
|
} else {
|
|
// Deref to next level
|
|
f = f.IncrDerefs()
|
|
v, t = v.Elem(), t.Elem()
|
|
f.AppendReflectOrInterface(v, t)
|
|
}
|
|
|
|
// 'raw' pointer types
|
|
case reflect.UnsafePointer, reflect.Func, reflect.Chan:
|
|
f.VType = t.String()
|
|
f.AppendPtr(uint64(v.Pointer()))
|
|
case reflect.Uintptr:
|
|
f.VType = t.String()
|
|
f.AppendPtr(v.Uint())
|
|
|
|
// Zero reflect value
|
|
case reflect.Invalid:
|
|
f.Buffer.B = append(f.Buffer.B, `nil`...)
|
|
|
|
// All others
|
|
default:
|
|
f.VType = t.String()
|
|
f.AppendType()
|
|
}
|
|
}
|
|
|
|
func (f format) AppendSlice(v reflect.Value, t reflect.Type) {
|
|
// Get slice value type
|
|
f.VType = t.String()
|
|
|
|
if t.Elem().Kind() == reflect.Uint8 {
|
|
// This is a byte slice
|
|
f.AppendBytes(v.Bytes())
|
|
return
|
|
}
|
|
|
|
if v.IsNil() {
|
|
// Nil slice
|
|
f.AppendNil()
|
|
return
|
|
}
|
|
|
|
if f.Verbose() {
|
|
// Append array with type information
|
|
f._AppendArrayTyped(func(f format) {
|
|
f.AppendArrayElems(v, t)
|
|
})
|
|
} else {
|
|
// Simply append array as elems
|
|
f._AppendArray(func(f format) {
|
|
f.AppendArrayElems(v, t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func (f format) AppendArray(v reflect.Value, t reflect.Type) {
|
|
// Get array value type
|
|
f.VType = t.String()
|
|
|
|
if f.Verbose() {
|
|
// Append array with type information
|
|
f._AppendArrayTyped(func(f format) {
|
|
f.AppendArrayElems(v, t)
|
|
})
|
|
} else {
|
|
// Simply append array as elems
|
|
f._AppendArray(func(f format) {
|
|
f.AppendArrayElems(v, t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func (f format) AppendArrayElems(v reflect.Value, t reflect.Type) {
|
|
// Get no. elems
|
|
n := v.Len()
|
|
|
|
// Get elem type
|
|
et := t.Elem()
|
|
|
|
// Append values
|
|
for i := 0; i < n; i++ {
|
|
f.SetValue().AppendInterfaceOrReflectNext(v.Index(i), et)
|
|
f.Buffer.B = append(f.Buffer.B, ',')
|
|
}
|
|
|
|
// Drop last comma
|
|
if n > 0 {
|
|
f.Buffer.Truncate(1)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendMap(v reflect.Value, t reflect.Type) {
|
|
// Get value type
|
|
f.VType = t.String()
|
|
|
|
if v.IsNil() {
|
|
// Nil map -- no fields
|
|
f.AppendNil()
|
|
return
|
|
}
|
|
|
|
// Append field formatted map fields
|
|
f._AppendFieldType(func(f format) {
|
|
f.AppendMapFields(v, t)
|
|
})
|
|
}
|
|
|
|
func (f format) AppendMapFields(v reflect.Value, t reflect.Type) {
|
|
// Get a map iterator
|
|
r := v.MapRange()
|
|
n := v.Len()
|
|
|
|
// Get key/val types
|
|
kt := t.Key()
|
|
kv := t.Elem()
|
|
|
|
// Iterate pairs
|
|
for r.Next() {
|
|
f.SetKey().AppendInterfaceOrReflectNext(r.Key(), kt)
|
|
f.Buffer.B = append(f.Buffer.B, '=')
|
|
f.SetValue().AppendInterfaceOrReflectNext(r.Value(), kv)
|
|
f.Buffer.B = append(f.Buffer.B, ' ')
|
|
}
|
|
|
|
// Drop last space
|
|
if n > 0 {
|
|
f.Buffer.Truncate(1)
|
|
}
|
|
}
|
|
|
|
func (f format) AppendStruct(v reflect.Value, t reflect.Type) {
|
|
// Get value type
|
|
f.VType = t.String()
|
|
|
|
// Append field formatted struct fields
|
|
f._AppendFieldType(func(f format) {
|
|
f.AppendStructFields(v, t)
|
|
})
|
|
}
|
|
|
|
func (f format) AppendStructFields(v reflect.Value, t reflect.Type) {
|
|
// Get field no.
|
|
n := v.NumField()
|
|
|
|
// Iterate struct fields
|
|
for i := 0; i < n; i++ {
|
|
vfield := v.Field(i)
|
|
tfield := t.Field(i)
|
|
|
|
// Append field name
|
|
f.AppendStringSafe(tfield.Name)
|
|
f.Buffer.B = append(f.Buffer.B, '=')
|
|
f.SetValue().AppendInterfaceOrReflectNext(vfield, tfield.Type)
|
|
|
|
// Append separator
|
|
f.Buffer.B = append(f.Buffer.B, ' ')
|
|
}
|
|
|
|
// Drop last space
|
|
if n > 0 {
|
|
f.Buffer.Truncate(1)
|
|
}
|
|
}
|
|
|
|
func (f format) _AppendMethodType(method func() string, i interface{}) (ok bool) {
|
|
// Verbose -- no methods
|
|
if f.Verbose() {
|
|
return false
|
|
}
|
|
|
|
// Catch nil type
|
|
if isNil(i) {
|
|
f.AppendNil()
|
|
return true
|
|
}
|
|
|
|
// Catch any panics
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
// DON'T recurse catchPanic()
|
|
if f.Panic() {
|
|
panic(r)
|
|
}
|
|
|
|
// Attempt to decode panic into buf
|
|
f.Buffer.B = append(f.Buffer.B, `!{PANIC=`...)
|
|
f.SetPanic().AppendInterfaceOrReflect(r)
|
|
f.Buffer.B = append(f.Buffer.B, '}')
|
|
|
|
// Ensure no further attempts
|
|
// to format after return
|
|
ok = true
|
|
}
|
|
}()
|
|
|
|
// Get method result
|
|
result := method()
|
|
|
|
switch {
|
|
// Append as key formatted
|
|
case f.Key():
|
|
f.AppendStringSafe(result)
|
|
|
|
// Append as always quoted
|
|
case f.Value():
|
|
f.AppendStringQuoted(result)
|
|
|
|
// Append as-is
|
|
default:
|
|
f.Buffer.B = append(f.Buffer.B, result...)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// _AppendPrimitiveType is a helper to append prefix/suffix for primitives (numbers/bools/bytes/runes).
|
|
func (f format) _AppendPrimitiveType(appendPrimitive func(format)) {
|
|
if f.Verbose() {
|
|
// Append value with type information
|
|
f._AppendPrimitiveTyped(appendPrimitive)
|
|
} else {
|
|
// Append simply as-is
|
|
appendPrimitive(f)
|
|
}
|
|
}
|
|
|
|
// _AppendPrimitiveTyped is a helper to append prefix/suffix for primitives (numbers/bools/bytes/runes) with their types (if deref'd).
|
|
func (f format) _AppendPrimitiveTyped(appendPrimitive func(format)) {
|
|
if f.Derefs > 0 {
|
|
// Is deref'd, append type info
|
|
f.Buffer.B = append(f.Buffer.B, '(')
|
|
f.AppendType()
|
|
f.Buffer.WriteString(`)(`)
|
|
appendPrimitive(f)
|
|
f.Buffer.B = append(f.Buffer.B, ')')
|
|
} else {
|
|
// Simply append value
|
|
appendPrimitive(f)
|
|
}
|
|
}
|
|
|
|
// _AppendPtrType is a helper to append prefix/suffix for ptr types (with type if necessary).
|
|
func (f format) _AppendPtrType(appendPtr func(format)) {
|
|
if f.Verbose() {
|
|
// Append value with type information
|
|
f.Buffer.B = append(f.Buffer.B, '(')
|
|
f.AppendType()
|
|
f.Buffer.WriteString(`)(`)
|
|
appendPtr(f)
|
|
f.Buffer.B = append(f.Buffer.B, ')')
|
|
} else {
|
|
// Append simply as-is
|
|
appendPtr(f)
|
|
}
|
|
}
|
|
|
|
// _AppendArray is a helper to append prefix/suffix for array-types.
|
|
func (f format) _AppendArray(appendArray func(format)) {
|
|
f.Buffer.B = append(f.Buffer.B, '[')
|
|
appendArray(f)
|
|
f.Buffer.B = append(f.Buffer.B, ']')
|
|
}
|
|
|
|
// _AppendArrayTyped is a helper to append prefix/suffix for array-types with their types.
|
|
func (f format) _AppendArrayTyped(appendArray func(format)) {
|
|
f.AppendType()
|
|
f.Buffer.B = append(f.Buffer.B, '{')
|
|
appendArray(f)
|
|
f.Buffer.B = append(f.Buffer.B, '}')
|
|
}
|
|
|
|
// _AppendFields is a helper to append prefix/suffix for field-types (with type if necessary).
|
|
func (f format) _AppendFieldType(appendFields func(format)) {
|
|
if f.Verbose() {
|
|
f.AppendType()
|
|
}
|
|
f.Buffer.B = append(f.Buffer.B, '{')
|
|
appendFields(f)
|
|
f.Buffer.B = append(f.Buffer.B, '}')
|
|
}
|