You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
2.6 KiB
124 lines
2.6 KiB
3 years ago
|
// Package tdp is td pretty-printing and formatting facilities for types from
|
||
|
// MTProto.
|
||
|
package tdp
|
||
|
|
||
|
import (
|
||
|
"encoding/base64"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// options for formatting.
|
||
|
type options struct {
|
||
|
writeTypeID bool
|
||
|
}
|
||
|
|
||
|
// Option of formatting.
|
||
|
type Option func(o *options)
|
||
|
|
||
|
// WithTypeID adds type id tp type name.
|
||
|
func WithTypeID(o *options) {
|
||
|
o.writeTypeID = true
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
defaultIdent = " "
|
||
|
noIdent = ""
|
||
|
)
|
||
|
|
||
|
func formatValue(b *strings.Builder, prefix, fieldName string, opt options, v reflect.Value) {
|
||
|
switch v.Kind() {
|
||
|
case reflect.Struct, reflect.Ptr, reflect.Interface:
|
||
|
i, ok := v.Interface().(Object)
|
||
|
if ok {
|
||
|
format(b, prefix+defaultIdent, opt, i)
|
||
|
} else if v.CanAddr() {
|
||
|
formatValue(b, prefix, fieldName, opt, v.Addr())
|
||
|
}
|
||
|
case reflect.Slice:
|
||
|
if buf, ok := v.Interface().([]byte); ok {
|
||
|
b.WriteString(base64.RawURLEncoding.EncodeToString(buf))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
b.WriteRune('\n')
|
||
|
for i := 0; i < v.Len(); i++ {
|
||
|
vi := v.Index(i)
|
||
|
b.WriteString(prefix)
|
||
|
b.WriteString(defaultIdent)
|
||
|
b.WriteString("- ")
|
||
|
formatValue(b, prefix+defaultIdent, fieldName, opt, vi)
|
||
|
b.WriteRune('\n')
|
||
|
}
|
||
|
case reflect.Int:
|
||
|
// Special case for date.
|
||
|
var (
|
||
|
now = time.Now()
|
||
|
max = now.AddDate(0, 0, 7).Unix()
|
||
|
min = now.AddDate(-2, 0, 0).Unix()
|
||
|
)
|
||
|
i := v.Int()
|
||
|
if i > min && i < max && strings.Contains(fieldName, "date") {
|
||
|
b.WriteString(time.Unix(i, 0).UTC().Format(time.RFC3339))
|
||
|
} else {
|
||
|
b.WriteString(strconv.FormatInt(i, 10))
|
||
|
}
|
||
|
default:
|
||
|
b.WriteString(fmt.Sprint(v.Interface()))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func format(b *strings.Builder, prefix string, opt options, obj Object) {
|
||
|
if obj == nil {
|
||
|
// No type information is available. it is like Format(nil).
|
||
|
b.WriteString("<nil>")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
info := obj.TypeInfo()
|
||
|
b.WriteString(info.Name)
|
||
|
if opt.writeTypeID {
|
||
|
b.WriteRune('#')
|
||
|
b.WriteString(strconv.FormatInt(int64(info.ID), 16))
|
||
|
}
|
||
|
if info.Null {
|
||
|
b.WriteString("(nil)")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
v := reflect.ValueOf(obj).Elem()
|
||
|
for i, f := range info.Fields {
|
||
|
if i == 0 && f.SchemaName == "flags" {
|
||
|
// Flag field, skipping.
|
||
|
continue
|
||
|
}
|
||
|
if f.Null {
|
||
|
// Optional field not set, skipping.
|
||
|
continue
|
||
|
}
|
||
|
b.WriteRune('\n')
|
||
|
b.WriteString(prefix)
|
||
|
b.WriteString(defaultIdent)
|
||
|
b.WriteString(f.SchemaName)
|
||
|
b.WriteString(": ")
|
||
|
|
||
|
formatValue(b, prefix, f.SchemaName, opt, v.FieldByName(f.Name))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Format pretty-prints v into string.
|
||
|
func Format(object Object, opts ...Option) string {
|
||
|
var opt options
|
||
|
for _, o := range opts {
|
||
|
o(&opt)
|
||
|
}
|
||
|
|
||
|
var b strings.Builder
|
||
|
format(&b, noIdent, opt, object)
|
||
|
|
||
|
return b.String()
|
||
|
}
|