-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgax_bui.go
227 lines (185 loc) · 5.21 KB
/
gax_bui.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package gax
import (
"fmt"
r "reflect"
)
/*
Short for "fragment" or "document fragment". Shortcut for making `Bui` with
these children.
*/
func F(vals ...any) (bui Bui) {
bui.F(vals...)
return
}
/*
Short for "builder" or "builder for UI". Has methods for generating HTML/XML
markup, declarative but efficient. See `E`, `F`, and `Bui.E` for 99% of the API
you will use.
When used as a child (see `Bui.E`, `Bui.F`, `Bui.Child`), this also indicates
pre-escaped markup, appending itself to another `Bui` without HTML/XML
escaping. For strings, see `Str`.
*/
type Bui []byte
// Implement `Ren`. Appends itself without HTML/XML escaping.
func (self Bui) Render(bui *Bui) { bui.NonEscBytes(self.Bytes()) }
// Free cast to `[]byte`.
func (self Bui) Bytes() []byte { return self }
// Free cast to `string`.
func (self Bui) String() string { return bytesString(self) }
/*
One of the primary APIs. Counterpart to the function `E`. Short for "element"
or "HTML element". Writes an HTML/XML tag, with attributes and inner content.
For a runnable example, see the definition of `Bui`.
Special rules for children:
* `nil` is ignored.
* `func()`, `func(*Bui)`, or `Ren.Render` is called for side effects.
* `[]any` is recursively walked.
* `[]Ren` is walked, calling `Ren.Render` on each val.
* `[]T` where `T` implements `Ren` is walked, calling `Ren.Render` on each val.
* Other values are stringified and escaped via `TextWri`.
To write text without escaping, use `Str` for strings and `Bui` for byte
slices.
*/
func (self *Bui) E(tag string, attrs Attrs, children ...any) {
self.Begin(tag, attrs)
self.F(children...)
self.End(tag)
}
/*
Mostly for internal use. Writes the beginning of an HTML/XML element, with
optional attrs. Supports HTML special cases; see `Bui.Attrs`. Sanity-checks the
tag. Using an invalid tag causes a panic.
*/
func (self *Bui) Begin(tag string, attrs Attrs) {
validTag(tag)
self.NonEscString(`<`)
self.NonEscString(tag)
self.Attrs(attrs...)
self.NonEscString(`>`)
}
/*
Mostly for internal use. Writes the end of an HTML/XML element. Supports HTML
void elements, also known as self-closing tags: if `Void.Has(tag)`, this method
is a nop. Sanity-checks the tag. Using an invalid tag causes a panic.
*/
func (self *Bui) End(tag string) {
validTag(tag)
if !Void.Has(tag) {
self.NonEscString(`</`)
self.NonEscString(tag)
self.NonEscString(`>`)
}
}
/*
Mostly for internal use. Writes HTML/XML attributes. Supports HTML special
cases; see `Bui.Attr`.
*/
func (self *Bui) Attrs(vals ...Attr) { *self = Bui(Attrs(vals).AppendTo(*self)) }
/*
Mostly for internal use. Writes an HTML/XML attribute, preceded with a space.
Supports HTML bool attrs: if `Bool.Has(key)`, the attribute value may be
adjusted for spec compliance. Automatically escapes the attribute value.
Sanity-checks the attribute name. Using an invalid name causes a panic.
*/
func (self *Bui) Attr(val Attr) { *self = Bui(val.AppendTo(*self)) }
// Writes multiple children via `Bui.Child`. Like the "tail part" of `Bui.E`.
// Counterpart to the function `F`.
func (self *Bui) F(vals ...any) {
for _, val := range vals {
self.Child(val)
}
}
// Shorter alias for `Bui.Child`.
func (self *Bui) C(val any) { self.Child(val) }
/*
Mostly for internal use. Writes without escaping. Intended for markup.
For writing `string`, see `Bui.NonEscString`. For escaping, see `Bui.EscBytes`.
*/
func (self *Bui) NonEscBytes(val []byte) {
*self = append(*self, val...)
}
/*
Mostly for internal use. Writes without escaping. Intended for markup.
For writing `[]byte`, see `Bui.NonEscBytes`. For escaping, see `Bui.EscString`.
*/
func (self *Bui) NonEscString(val string) {
*self = append(*self, val...)
}
/*
Writes text content, escaping if necessary. For writing `string`, see
`Bui.EscString`.
*/
func (self *Bui) EscBytes(val []byte) {
_, _ = (*TextWri)(self).Write(val)
}
/*
Writes text content, escaping if necessary. For writing `[]byte`, see
`Bui.EscBytes`.
*/
func (self *Bui) EscString(val string) {
_, _ = (*TextWri)(self).WriteString(val)
}
// Shorter alias for `Bui.EscString`.
func (self *Bui) T(val string) { self.EscString(val) }
// Shorter alias for `Bui.EscString`.
func (self *Bui) Text(val string) { self.EscString(val) }
/*
Mostly for internal use. Writes an arbitrary child. See `Bui.E` for the list of
special rules.
*/
func (self *Bui) Child(val any) {
switch val := val.(type) {
case nil:
case string:
self.EscString(val)
case []byte:
self.EscBytes(val)
case func():
if val != nil {
val()
}
case func(*Bui):
if val != nil {
val(self)
}
case func() any:
if val != nil {
self.Child(val())
}
case Ren:
if val != nil {
val.Render(self)
}
case []any:
self.F(val...)
case []Ren:
for _, val := range val {
if val != nil {
val.Render(self)
}
}
default:
self.unknown(val)
}
}
func (self *Bui) unknown(src any) {
if src == nil {
return
}
val := r.ValueOf(src)
if isRvalNil(val) {
return
}
typ := val.Type()
if typ.Kind() == r.Slice && typ.Elem().Implements(typeRen) {
for ind := range iter(val.Len()) {
val.Index(ind).Convert(typeRen).Interface().(Ren).Render(self)
}
return
}
switch typ.Kind() {
case r.Invalid, r.Func:
panic(fmt.Errorf(`[gax] can't render %T`, src))
}
fmt.Fprint((*TextWri)(self), src)
}