-
-
Notifications
You must be signed in to change notification settings - Fork 136
/
tdp.go
123 lines (109 loc) · 2.56 KB
/
tdp.go
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
// 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()
}