@@ -5,7 +5,12 @@ package templates
5
5
6
6
import (
7
7
"fmt"
8
+ "html"
9
+ "html/template"
8
10
"reflect"
11
+
12
+ "code.gitea.io/gitea/modules/json"
13
+ "code.gitea.io/gitea/modules/setting"
9
14
)
10
15
11
16
func dictMerge (base map [string ]any , arg any ) bool {
@@ -45,3 +50,72 @@ func dict(args ...any) (map[string]any, error) {
45
50
}
46
51
return m , nil
47
52
}
53
+
54
+ func dumpVarMarshalable (v any , dumped map [uintptr ]bool ) (ret any , ok bool ) {
55
+ if v == nil {
56
+ return nil , true
57
+ }
58
+ e := reflect .ValueOf (v )
59
+ for e .Kind () == reflect .Pointer {
60
+ e = e .Elem ()
61
+ }
62
+ if e .CanAddr () {
63
+ addr := e .UnsafeAddr ()
64
+ if dumped [addr ] {
65
+ return "[dumped]" , false
66
+ }
67
+ dumped [addr ] = true
68
+ defer delete (dumped , addr )
69
+ }
70
+ switch e .Kind () {
71
+ case reflect .Bool , reflect .String ,
72
+ reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
73
+ reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 ,
74
+ reflect .Float32 , reflect .Float64 :
75
+ return e .Interface (), true
76
+ case reflect .Struct :
77
+ m := map [string ]any {}
78
+ for i := 0 ; i < e .NumField (); i ++ {
79
+ k := e .Type ().Field (i ).Name
80
+ if ! e .Type ().Field (i ).IsExported () {
81
+ continue
82
+ }
83
+ v := e .Field (i ).Interface ()
84
+ m [k ], _ = dumpVarMarshalable (v , dumped )
85
+ }
86
+ return m , true
87
+ case reflect .Map :
88
+ m := map [string ]any {}
89
+ for _ , k := range e .MapKeys () {
90
+ m [k .String ()], _ = dumpVarMarshalable (e .MapIndex (k ).Interface (), dumped )
91
+ }
92
+ return m , true
93
+ case reflect .Array , reflect .Slice :
94
+ var m []any
95
+ for i := 0 ; i < e .Len (); i ++ {
96
+ v , _ := dumpVarMarshalable (e .Index (i ).Interface (), dumped )
97
+ m = append (m , v )
98
+ }
99
+ return m , true
100
+ default :
101
+ return "[" + reflect .TypeOf (v ).String () + "]" , false
102
+ }
103
+ }
104
+
105
+ // dumpVar helps to dump a variable in a template, to help debugging and development.
106
+ func dumpVar (v any ) template.HTML {
107
+ if setting .IsProd {
108
+ return "<pre>dumpVar: only available in dev mode</pre>"
109
+ }
110
+ m , ok := dumpVarMarshalable (v , map [uintptr ]bool {})
111
+ dumpStr := ""
112
+ jsonBytes , err := json .MarshalIndent (m , "" , " " )
113
+ if err != nil {
114
+ dumpStr = fmt .Sprintf ("dumpVar: unable to marshal %T: %v" , v , err )
115
+ } else if ok {
116
+ dumpStr = fmt .Sprintf ("dumpVar: %T\n %s" , v , string (jsonBytes ))
117
+ } else {
118
+ dumpStr = fmt .Sprintf ("dumpVar: unmarshalable %T\n %s" , v , string (jsonBytes ))
119
+ }
120
+ return template .HTML ("<pre>" + html .EscapeString (dumpStr ) + "</pre>" )
121
+ }
0 commit comments