20
20
--- @class xml-generator
21
21
local export = {}
22
22
23
+ --- @class XML.Children
24
+ --- @field [ integer] XML.Node | string | fun (): XML.Node
25
+
26
+ --- @class XML.AttributeTable : XML.Children
27
+ --- @field [ string] string | boolean | number
28
+
23
29
--- @class XML.Node
30
+ --- @operator call (XML.Children ): XML.Node
24
31
--- @field tag string
25
- --- @field children ( XML.Node | string | fun (): XML.Node ) []
26
- --- @field attributes { [string] : ( string | boolean ) }
32
+ --- @field children XML.Children
33
+ --- @field attributes XML.AttributeTable
27
34
28
35
--- quotes are allowed in text, not in attributes
29
36
--- @param str string
@@ -62,7 +69,7 @@ function export.node_to_string(node)
62
69
if type (v ) == " boolean" then
63
70
if v then html = html .. " " .. k end
64
71
else
65
- html = html .. " " .. k .. " =\" " .. export .sanitize_attributes (v ) .. " \" "
72
+ html = html .. " " .. k .. " =\" " .. export .sanitize_attributes (tostring ( v ) ) .. " \" "
66
73
end
67
74
end
68
75
@@ -81,90 +88,84 @@ function export.node_to_string(node)
81
88
return html
82
89
end
83
90
84
- --- @generic T
85
- --- @param fn T
86
- --- @return T
87
- function export .declare_xml_generator (fn )
88
- local tbl = setmetatable ({}, {
89
- --- @param self table
90
- --- @param tag_name string
91
- __index = function (self , tag_name )
92
- --- @param attributes { [string] : string , [integer] : (XML.Node | string | fun (): XML.Node ) } | string
93
- --- @return table | fun ( children : ( XML.Node | string | fun (): XML.Node ) [] ): XML.Node
94
- return function (attributes )
95
- --- @type XML.Node
96
- local node = {
97
- tag = tag_name ,
98
- children = {},
99
- attributes = {}
100
- }
101
-
102
- -- if we have a situation such as
103
- --[[
104
- tag "string"
105
- ]]
106
- --
107
- -- then the content is the `string`
108
- local tname = typename (attributes )
109
- if tname ~= " table" and tname ~= " HTML.Node" then
110
- node .attributes = attributes and { tostring (attributes ) } or {}
111
- elseif tname == " XML.Node" then
112
- --- local tag = div { p "hi" }
113
- --- div(tag)
114
- node .children = { attributes }
115
- attributes = {}
116
- else
117
- node .attributes = attributes --[[ @as any]]
118
- end
91
+ --- @class XML.GeneratorTable
92
+ --- @field [ string] fun (attributes : XML.AttributeTable | string | XML.Node ): XML.Node
93
+
94
+ --- @type XML.GeneratorTable
95
+ export .generator_metatable = setmetatable ({}, {
96
+ --- @param self XML.GeneratorTable
97
+ --- @param tag_name string
98
+ __index = function (self , tag_name )
99
+ --- @param attributes { [string] : string , [integer] : (XML.Node | string | fun (): XML.Node ) } | string
100
+ --- @return table | fun ( children : ( XML.Node | string | fun (): XML.Node ) [] ): XML.Node
101
+ return function (attributes )
102
+ --- @type XML.Node
103
+ local node = {
104
+ tag = tag_name ,
105
+ children = {},
106
+ attributes = {}
107
+ }
119
108
120
- for i , v in ipairs (node .attributes ) do
121
- if type (v ) == " function" then
122
- export .declare_xml_generator (v )
123
- v = coroutine.wrap (v )
124
- for sub in v do
125
- node .children [# node .children + 1 ] = sub
126
- end
127
- else
128
- node .children [# node .children + 1 ] = v
129
- end
109
+ -- if we have a situation such as
110
+ --[[
111
+ tag "string"
112
+ ]]
113
+ --
114
+ -- then the content is the `string`
115
+ local tname = typename (attributes )
116
+ if tname ~= " table" and tname ~= " HTML.Node" then
117
+ node .attributes = attributes and { tostring (attributes ) } or {}
118
+ elseif tname == " XML.Node" then
119
+ --- local tag = div { p "hi" }
120
+ --- div(tag)
121
+ node .children = { attributes }
122
+ attributes = {}
123
+ else
124
+ node .attributes = attributes --[[ @as any]]
125
+ end
130
126
131
- node .attributes [i ] = nil
127
+ for i , v in ipairs (node .attributes ) do
128
+ if type (v ) == " function" then
129
+ v = coroutine.wrap (v )
130
+ for sub in v do
131
+ node .children [# node .children + 1 ] = sub
132
+ end
133
+ else
134
+ node .children [# node .children + 1 ] = v
132
135
end
133
136
134
- return setmetatable ( node , {
135
- __type = " XML.Node " ,
137
+ node . attributes [ i ] = nil
138
+ end
136
139
137
- __tostring = export .node_to_string ,
140
+ return setmetatable (node , {
141
+ __type = " XML.Node" ,
138
142
139
- --- @param self XML.Node
140
- --- @param children (XML.Node | string | fun (): XML.Node ) []
141
- __call = function (self , children )
142
- if type (children ) ~= " table" then
143
- children = { tostring (children ) }
144
- end
143
+ __tostring = export .node_to_string ,
145
144
146
- for _ , v in ipairs (children ) do
147
- if type (v ) == " function" then
148
- export .declare_xml_generator (v )
149
- v = coroutine.wrap (v )
150
- for sub in v do
151
- self .children [# self .children + 1 ] = sub
152
- end
153
- else
154
- self .children [# self .children + 1 ] = v
145
+ --- @param self XML.Node
146
+ --- @param children XML.Children
147
+ __call = function (self , children )
148
+ if type (children ) ~= " table" then
149
+ children = { tostring (children ) }
150
+ end
151
+
152
+ for _ , v in ipairs (children ) do
153
+ if type (v ) == " function" then
154
+ v = coroutine.wrap (v )
155
+ for sub in v do
156
+ self .children [# self .children + 1 ] = sub
155
157
end
158
+ else
159
+ self .children [# self .children + 1 ] = v
156
160
end
157
-
158
- return self
159
161
end
160
- })
161
- end
162
- end
163
- })
164
162
165
- setfenv (fn , tbl )
166
- return fn
167
- end
163
+ return self
164
+ end
165
+ })
166
+ end
167
+ end
168
+ })
168
169
169
170
--- Usage:
170
171
--[=[
@@ -188,48 +189,45 @@ end)
188
189
189
190
```
190
191
]=]
191
- --- @param ctx fun (): table
192
- --- @return string
193
- function export .generate_xml (ctx )
194
- return export .node_to_string (export .declare_xml_generator (ctx )())
195
- end
196
-
197
- --- @param ctx fun (): table
192
+ --- @param ctx fun ( html : XML.GeneratorTable ): XML.Node
198
193
--- @return XML.Node
199
- function export .generate_xml_node (ctx )
200
- return export .declare_xml_generator (ctx )()
201
- end
194
+ function export .generate_node (ctx ) return ctx (export .generator_metatable ) end
195
+
196
+ --- @param ctx fun ( html : XML.GeneratorTable ): table
197
+ --- @return string
198
+ function export .generate (ctx ) return tostring (export .generate_node (ctx )) end
202
199
203
200
--- Turns a lua table into an html table, recursively, with multiple levels of nesting
201
+ --- @param xml XML.GeneratorTable
204
202
--- @param tbl table
205
203
--- @return XML.Node
206
- function export .table ( tbl )
204
+ function export .html_table ( xml , tbl )
207
205
--- @diagnostic disable : undefined-global
208
- return table {
206
+ return xml . table {
209
207
function ()
210
208
local function getval (v )
211
209
if type (v ) ~= " table" or (getmetatable (v ) or {}).__tostring then
212
210
return tostring (v )
213
211
end
214
- return html_table (v )
212
+ return export . html_table (xml , v )
215
213
end
216
214
217
215
for i , v in ipairs (tbl ) do
218
- yield (
219
- tr {
220
- td (tostring (i )),
221
- td (getval (v )),
216
+ coroutine. yield (
217
+ xml . tr {
218
+ xml . td (tostring (i )),
219
+ xml . td (getval (v )),
222
220
}
223
221
)
224
222
225
223
tbl [i ] = nil
226
224
end
227
225
228
226
for k , v in pairs (tbl ) do
229
- yield (
230
- tr {
231
- td (tostring (k )),
232
- td (getval (v )),
227
+ coroutine. yield (
228
+ xml . tr {
229
+ xml . td (tostring (k )),
230
+ xml . td (getval (v )),
233
231
}
234
232
)
235
233
end
@@ -238,8 +236,6 @@ function export.table(tbl)
238
236
--- @diagnostic enable : undefined-global
239
237
end
240
238
241
- export .declare_xml_generator (export .table )
242
-
243
239
--- @alias OptionalStringCollection string | string[]
244
240
--- @param css { [OptionalStringCollection] : { [OptionalStringCollection] : (OptionalStringCollection ) } }
245
241
--- @return XML.Node
@@ -257,11 +253,7 @@ function export.style(css)
257
253
css_str = css_str .. " }\n "
258
254
end
259
255
260
- return export .generate_xml_node (function ()
261
- --- @diagnostic disable : undefined-global
262
- return style (css_str )
263
- --- @diagnostic enable : undefined-global
264
- end )
256
+ return export .generate_node (function (xml ) return xml .style (css_str ) end )
265
257
end
266
258
267
259
return export
0 commit comments