1 package treeprint
2
3 import (
4 "fmt"
5 "reflect"
6 "strings"
7 )
8
9 type StructTreeOption int
10
11 const (
12 StructNameTree StructTreeOption = iota
13 StructValueTree
14 StructTagTree
15 StructTypeTree
16 StructTypeSizeTree
17 )
18
19 func FromStruct(v interface{}, opt ...StructTreeOption) (Tree, error) {
20 var treeOpt StructTreeOption
21 if len(opt) > 0 {
22 treeOpt = opt[0]
23 }
24 switch treeOpt {
25 case StructNameTree:
26 tree := New()
27 err := nameTree(tree, v)
28 return tree, err
29 case StructValueTree:
30 tree := New()
31 err := valueTree(tree, v)
32 return tree, err
33 case StructTagTree:
34 tree := New()
35 err := tagTree(tree, v)
36 return tree, err
37 case StructTypeTree:
38 tree := New()
39 err := typeTree(tree, v)
40 return tree, err
41 case StructTypeSizeTree:
42 tree := New()
43 err := typeSizeTree(tree, v)
44 return tree, err
45 default:
46 err := fmt.Errorf("treeprint: invalid StructTreeOption %v", treeOpt)
47 return nil, err
48 }
49 }
50
51 type FmtFunc func(name string, v interface{}) (string, bool)
52
53 func FromStructWithMeta(v interface{}, fmtFunc FmtFunc) (Tree, error) {
54 if fmtFunc == nil {
55 tree := New()
56 err := nameTree(tree, v)
57 return tree, err
58 }
59 tree := New()
60 err := metaTree(tree, v, fmtFunc)
61 return tree, err
62 }
63
64 func Repr(v interface{}) string {
65 tree := New()
66 vType := reflect.TypeOf(v)
67 vValue := reflect.ValueOf(v)
68 _, val, isStruct := getValue(vType, &vValue)
69 if !isStruct {
70 return fmt.Sprintf("%+v", val.Interface())
71 }
72 err := valueTree(tree, val.Interface())
73 if err != nil {
74 return err.Error()
75 }
76 return tree.String()
77 }
78
79 func nameTree(tree Tree, v interface{}) error {
80 typ, val, err := checkType(v)
81 if err != nil {
82 return err
83 }
84 fields := typ.NumField()
85 for i := 0; i < fields; i++ {
86 field := typ.Field(i)
87 fieldValue := val.Field(i)
88 name, skip, omit := getMeta(field.Name, field.Tag)
89 if skip || omit && isEmpty(&fieldValue) {
90 continue
91 }
92 typ, val, isStruct := getValue(field.Type, &fieldValue)
93 if !isStruct {
94 tree.AddNode(name)
95 continue
96 } else if subNum := typ.NumField(); subNum == 0 {
97 tree.AddNode(name)
98 continue
99 }
100 branch := tree.AddBranch(name)
101 if err := nameTree(branch, val.Interface()); err != nil {
102 err := fmt.Errorf("%v on struct branch %s", err, name)
103 return err
104 }
105 }
106 return nil
107 }
108
109 func getMeta(fieldName string, tag reflect.StructTag) (name string, skip, omit bool) {
110 if tagStr := tag.Get("tree"); len(tagStr) > 0 {
111 name, omit = tagSpec(tagStr)
112 }
113 if name == "-" {
114 return fieldName, true, omit
115 }
116 if len(name) == 0 {
117 name = fieldName
118 } else if trimmed := strings.TrimSpace(name); len(trimmed) == 0 {
119 name = fieldName
120 }
121 return
122 }
123
124 func valueTree(tree Tree, v interface{}) error {
125 typ, val, err := checkType(v)
126 if err != nil {
127 return err
128 }
129 fields := typ.NumField()
130 for i := 0; i < fields; i++ {
131 field := typ.Field(i)
132 fieldValue := val.Field(i)
133 name, skip, omit := getMeta(field.Name, field.Tag)
134 if skip || omit && isEmpty(&fieldValue) {
135 continue
136 }
137 typ, val, isStruct := getValue(field.Type, &fieldValue)
138 if !isStruct {
139 tree.AddMetaNode(val.Interface(), name)
140 continue
141 } else if subNum := typ.NumField(); subNum == 0 {
142 tree.AddMetaNode(val.Interface(), name)
143 continue
144 }
145 branch := tree.AddBranch(name)
146 if err := valueTree(branch, val.Interface()); err != nil {
147 err := fmt.Errorf("%v on struct branch %s", err, name)
148 return err
149 }
150 }
151 return nil
152 }
153
154 func tagTree(tree Tree, v interface{}) error {
155 typ, val, err := checkType(v)
156 if err != nil {
157 return err
158 }
159 fields := typ.NumField()
160 for i := 0; i < fields; i++ {
161 field := typ.Field(i)
162 fieldValue := val.Field(i)
163 name, skip, omit := getMeta(field.Name, field.Tag)
164 if skip || omit && isEmpty(&fieldValue) {
165 continue
166 }
167 filteredTag := filterTags(field.Tag)
168 typ, val, isStruct := getValue(field.Type, &fieldValue)
169 if !isStruct {
170 tree.AddMetaNode(filteredTag, name)
171 continue
172 } else if subNum := typ.NumField(); subNum == 0 {
173 tree.AddMetaNode(filteredTag, name)
174 continue
175 }
176 branch := tree.AddMetaBranch(filteredTag, name)
177 if err := tagTree(branch, val.Interface()); err != nil {
178 err := fmt.Errorf("%v on struct branch %s", err, name)
179 return err
180 }
181 }
182 return nil
183 }
184
185 func typeTree(tree Tree, v interface{}) error {
186 typ, val, err := checkType(v)
187 if err != nil {
188 return err
189 }
190 fields := typ.NumField()
191 for i := 0; i < fields; i++ {
192 field := typ.Field(i)
193 fieldValue := val.Field(i)
194 name, skip, omit := getMeta(field.Name, field.Tag)
195 if skip || omit && isEmpty(&fieldValue) {
196 continue
197 }
198 typ, val, isStruct := getValue(field.Type, &fieldValue)
199 typename := fmt.Sprintf("%T", val.Interface())
200 if !isStruct {
201 tree.AddMetaNode(typename, name)
202 continue
203 } else if subNum := typ.NumField(); subNum == 0 {
204 tree.AddMetaNode(typename, name)
205 continue
206 }
207 branch := tree.AddMetaBranch(typename, name)
208 if err := typeTree(branch, val.Interface()); err != nil {
209 err := fmt.Errorf("%v on struct branch %s", err, name)
210 return err
211 }
212 }
213 return nil
214 }
215
216 func typeSizeTree(tree Tree, v interface{}) error {
217 typ, val, err := checkType(v)
218 if err != nil {
219 return err
220 }
221 fields := typ.NumField()
222 for i := 0; i < fields; i++ {
223 field := typ.Field(i)
224 fieldValue := val.Field(i)
225 name, skip, omit := getMeta(field.Name, field.Tag)
226 if skip || omit && isEmpty(&fieldValue) {
227 continue
228 }
229 typ, val, isStruct := getValue(field.Type, &fieldValue)
230 typesize := typ.Size()
231 if !isStruct {
232 tree.AddMetaNode(typesize, name)
233 continue
234 } else if subNum := typ.NumField(); subNum == 0 {
235 tree.AddMetaNode(typesize, name)
236 continue
237 }
238 branch := tree.AddMetaBranch(typesize, name)
239 if err := typeSizeTree(branch, val.Interface()); err != nil {
240 err := fmt.Errorf("%v on struct branch %s", err, name)
241 return err
242 }
243 }
244 return nil
245 }
246
247 func metaTree(tree Tree, v interface{}, fmtFunc FmtFunc) error {
248 typ, val, err := checkType(v)
249 if err != nil {
250 return err
251 }
252 fields := typ.NumField()
253 for i := 0; i < fields; i++ {
254 field := typ.Field(i)
255 fieldValue := val.Field(i)
256 name, skip, omit := getMeta(field.Name, field.Tag)
257 if skip || omit && isEmpty(&fieldValue) {
258 continue
259 }
260 typ, val, isStruct := getValue(field.Type, &fieldValue)
261 formatted, show := fmtFunc(name, val.Interface())
262 if !isStruct {
263 if show {
264 tree.AddMetaNode(formatted, name)
265 continue
266 }
267 tree.AddNode(name)
268 continue
269 } else if subNum := typ.NumField(); subNum == 0 {
270 if show {
271 tree.AddMetaNode(formatted, name)
272 continue
273 }
274 tree.AddNode(name)
275 continue
276 }
277 var branch Tree
278 if show {
279 branch = tree.AddMetaBranch(formatted, name)
280 } else {
281 branch = tree.AddBranch(name)
282 }
283 if err := metaTree(branch, val.Interface(), fmtFunc); err != nil {
284 err := fmt.Errorf("%v on struct branch %s", err, name)
285 return err
286 }
287 }
288 return nil
289 }
290
291 func getValue(typ reflect.Type, val *reflect.Value) (reflect.Type, *reflect.Value, bool) {
292 switch typ.Kind() {
293 case reflect.Ptr:
294 typ = typ.Elem()
295 if typ.Kind() == reflect.Struct {
296 elem := val.Elem()
297 return typ, &elem, true
298 }
299 case reflect.Struct:
300 return typ, val, true
301 }
302 return typ, val, false
303 }
304
305 func checkType(v interface{}) (reflect.Type, *reflect.Value, error) {
306 typ := reflect.TypeOf(v)
307 val := reflect.ValueOf(v)
308 switch typ.Kind() {
309 case reflect.Ptr:
310 typ = typ.Elem()
311 if typ.Kind() != reflect.Struct {
312 err := fmt.Errorf("treeprint: %T is not a struct we could work with", v)
313 return nil, nil, err
314 }
315 val = val.Elem()
316 case reflect.Struct:
317 default:
318 err := fmt.Errorf("treeprint: %T is not a struct we could work with", v)
319 return nil, nil, err
320 }
321 return typ, &val, nil
322 }
323
View as plain text