1const std = @import("std");
2const pek = @import("pek");
3const expect = @import("expect").expect;
4const extras = @import("extras");
5const nio = @import("nio");
6
7test {
8 const doc = comptime pek.parse(
9 \\html[lang="en"]()
10 );
11 _ = try pek.compile(struct {}, std.testing.allocator, nio.NullWriter{}, doc, {});
12}
13
14test "document" {
15 const alloc = std.testing.allocator;
16 var builder = nio.AllocatingWriter.init(alloc);
17 defer builder.deinit();
18 const doc = comptime pek.parse(
19 \\html[lang="en"](
20 \\ head(
21 \\ title("Pek Example")
22 \\ meta[charset="UTF-8"]
23 \\ meta[name="viewport" content="width=device-width,initial-scale=1"]
24 \\ )
25 \\ body(
26 \\ h1("Pek Example")
27 \\ hr
28 \\ p("This is an example HTML document written in "a[href="https://github.com/nektro/zig-pek"]("Pek")".")
29 \\ )
30 \\)
31 );
32 try pek.compile(
33 @This(),
34 alloc,
35 &builder,
36 doc,
37 .{},
38 );
39 try expect(builder.items).toEqualString(
40 \\<!DOCTYPE html>
41 \\<html lang="en">
42 \\ <head>
43 \\ <title>Pek Example</title>
44 \\ <meta charset="UTF-8" />
45 \\ <meta name="viewport" content="width=device-width,initial-scale=1" />
46 \\ </head>
47 \\ <body>
48 \\ <h1>Pek Example</h1>
49 \\ <hr />
50 \\ <p>This is an example HTML document written in <a href="https://github.com/nektro/zig-pek">Pek</a>.</p>
51 \\ </body>
52 \\</html>
53 \\
54 );
55}
56
57test "apostrophe attribute string" {
58 const alloc = std.testing.allocator;
59 var builder = nio.AllocatingWriter.init(alloc);
60 defer builder.deinit();
61 const doc = comptime pek.parse(
62 \\html[lang="en"](
63 \\ head(
64 \\ title("Pek Example")
65 \\ meta[charset="UTF-8"]
66 \\ meta[name="viewport" content="width=device-width,initial-scale=1"]
67 \\ meta[name="htmx-config" content='{"includeIndicatorStyles":false}']
68 \\ )
69 \\)
70 );
71 try pek.compile(
72 @This(),
73 alloc,
74 &builder,
75 doc,
76 .{},
77 );
78 try expect(builder.items).toEqualString(
79 \\<!DOCTYPE html>
80 \\<html lang="en">
81 \\ <head>
82 \\ <title>Pek Example</title>
83 \\ <meta charset="UTF-8" />
84 \\ <meta name="viewport" content="width=device-width,initial-scale=1" />
85 \\ <meta name="htmx-config" content='{"includeIndicatorStyles":false}' />
86 \\ </head>
87 \\</html>
88 \\
89 );
90}
91
92test "fragment" {
93 const alloc = std.testing.allocator;
94 var builder = nio.AllocatingWriter.init(alloc);
95 defer builder.deinit();
96 const doc = comptime pek.parse(
97 \\body(
98 \\ h1("Pek Example")
99 \\ hr
100 \\ p("This is an example HTML document written in "a[href="https://github.com/nektro/zig-pek"]("Pek")".")
101 \\)
102 );
103 try pek.compileInner(
104 alloc,
105 &builder,
106 doc,
107 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
108 .{},
109 );
110 try expect(builder.items).toEqualString(
111 \\<body>
112 \\ <h1>Pek Example</h1>
113 \\ <hr />
114 \\ <p>This is an example HTML document written in <a href="https://github.com/nektro/zig-pek">Pek</a>.</p>
115 \\</body>
116 \\
117 );
118}
119
120test "if: basic" {
121 const alloc = std.testing.allocator;
122 var builder = nio.AllocatingWriter.init(alloc);
123 defer builder.deinit();
124 const doc = comptime pek.parse(
125 \\body(
126 \\ h1("Pek Example")
127 \\ hr
128 \\ {#if foo}
129 \\ p("This is an example HTML document written in "a[href="https://github.com/nektro/zig-pek"]("Pek")".")
130 \\ /if/
131 \\ {#if bar}
132 \\ p("This will show up because "code("bar")" is true instead.")
133 \\ /if/
134 \\)
135 );
136 try pek.compileInner(
137 alloc,
138 &builder,
139 doc,
140 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
141 .{ .foo = false, .bar = true },
142 );
143 try expect(builder.items).toEqualString(
144 \\<body>
145 \\ <h1>Pek Example</h1>
146 \\ <hr />
147 \\ <p>This will show up because <code>bar</code> is true instead.</p>
148 \\</body>
149 \\
150 );
151}
152
153test "if: optional" {
154 const alloc = std.testing.allocator;
155 var builder = nio.AllocatingWriter.init(alloc);
156 defer builder.deinit();
157 const foo: ?u8 = null;
158 const bar: ?u8 = 24;
159 const doc = comptime pek.parse(
160 \\body(
161 \\ h1("Pek Example")
162 \\ hr
163 \\ {#if foo}
164 \\ p("This is an example HTML document written in "a[href="https://github.com/nektro/zig-pek"]("Pek")".")
165 \\ /if/
166 \\ {#if bar}
167 \\ p("This will show up because "code("bar")" is non-null instead.")
168 \\ /if/
169 \\)
170 );
171 try pek.compileInner(
172 alloc,
173 &builder,
174 doc,
175 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
176 .{ .foo = foo, .bar = bar },
177 );
178 try expect(builder.items).toEqualString(
179 \\<body>
180 \\ <h1>Pek Example</h1>
181 \\ <hr />
182 \\ <p>This will show up because <code>bar</code> is non-null instead.</p>
183 \\</body>
184 \\
185 );
186}
187
188// if else
189test "if else + field access" {
190 const alloc = std.testing.allocator;
191 var builder = nio.AllocatingWriter.init(alloc);
192 defer builder.deinit();
193 const doc = comptime pek.parse(
194 \\body(
195 \\ {#if bar.qux}
196 \\ p("This will show up because "code("bar")" is true.")
197 \\ <else>
198 \\ p("This will show up because "code("bar")" is false.")
199 \\ /if/
200 \\)
201 );
202 try pek.compileInner(
203 alloc,
204 &builder,
205 doc,
206 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
207 .{ .bar = .{ .qux = false } },
208 );
209 try expect(builder.items).toEqualString(
210 \\<body>
211 \\ <p>This will show up because <code>bar</code> is false.</p>
212 \\</body>
213 \\
214 );
215}
216
217// ifnot
218test {
219 const alloc = std.testing.allocator;
220 var builder = nio.AllocatingWriter.init(alloc);
221 defer builder.deinit();
222 const doc = comptime pek.parse(
223 \\body(
224 \\ {#ifnot bar}
225 \\ p("This will show up because "code("bar")" is false.")
226 \\ <else>
227 \\ p("This will show up because "code("bar")" is true.")
228 \\ /ifnot/
229 \\)
230 );
231 try pek.compileInner(
232 alloc,
233 &builder,
234 doc,
235 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
236 .{ .bar = true },
237 );
238 try expect(builder.items).toEqualString(
239 \\<body>
240 \\ <p>This will show up because <code>bar</code> is true.</p>
241 \\</body>
242 \\
243 );
244}
245test {
246 const alloc = std.testing.allocator;
247 var builder = nio.AllocatingWriter.init(alloc);
248 defer builder.deinit();
249 const doc = comptime pek.parse(
250 \\body(
251 \\ {#ifnot bar}
252 \\ p("This will show up because "code("bar")" is false.")
253 \\ <else>
254 \\ p("This will show up because "code("bar")" is true.")
255 \\ /ifnot/
256 \\)
257 );
258 try pek.compileInner(
259 alloc,
260 &builder,
261 doc,
262 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
263 .{ .bar = false },
264 );
265 try expect(builder.items).toEqualString(
266 \\<body>
267 \\ <p>This will show up because <code>bar</code> is false.</p>
268 \\</body>
269 \\
270 );
271}
272// ifnot optional
273test {
274 const alloc = std.testing.allocator;
275 var builder = nio.AllocatingWriter.init(alloc);
276 defer builder.deinit();
277 const foo: ?u8 = null;
278 const doc = comptime pek.parse(
279 \\body(
280 \\ {#ifnot foo}
281 \\ p("This will show up because "code("foo")" is null.")
282 \\ <else>
283 \\ p("This will show up because "code("foo")" is non-null.")
284 \\ /ifnot/
285 \\)
286 );
287 try pek.compileInner(
288 alloc,
289 &builder,
290 doc,
291 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
292 .{ .foo = foo },
293 );
294 try expect(builder.items).toEqualString(
295 \\<body>
296 \\ <p>This will show up because <code>foo</code> is null.</p>
297 \\</body>
298 \\
299 );
300}
301
302// ifequal
303test { // bool
304 const alloc = std.testing.allocator;
305 var builder = nio.AllocatingWriter.init(alloc);
306 defer builder.deinit();
307 const doc = comptime pek.parse(
308 \\body(
309 \\ {#ifequal bar true}
310 \\ p("This will show up because "code("bar")" is true.")
311 \\ <else>
312 \\ p("This will show up because "code("bar")" is false.")
313 \\ /ifequal/
314 \\)
315 );
316 try pek.compileInner(
317 alloc,
318 &builder,
319 doc,
320 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
321 .{ .bar = true },
322 );
323 try expect(builder.items).toEqualString(
324 \\<body>
325 \\ <p>This will show up because <code>bar</code> is true.</p>
326 \\</body>
327 \\
328 );
329}
330test { // bool false
331 const alloc = std.testing.allocator;
332 var builder = nio.AllocatingWriter.init(alloc);
333 defer builder.deinit();
334 const doc = comptime pek.parse(
335 \\body(
336 \\ {#ifequal bar true}
337 \\ p("This will show up because "code("bar")" is true.")
338 \\ <else>
339 \\ p("This will show up because "code("bar")" is false.")
340 \\ /ifequal/
341 \\)
342 );
343 try pek.compileInner(
344 alloc,
345 &builder,
346 doc,
347 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
348 .{ .bar = false },
349 );
350 try expect(builder.items).toEqualString(
351 \\<body>
352 \\ <p>This will show up because <code>bar</code> is false.</p>
353 \\</body>
354 \\
355 );
356}
357test { // string
358 const alloc = std.testing.allocator;
359 var builder = nio.AllocatingWriter.init(alloc);
360 defer builder.deinit();
361 const doc = comptime pek.parse(
362 \\body(
363 \\ {#ifequal bar "foo"}
364 \\ p("This will show up because "code("bar")" is 'foo'.")
365 \\ <else>
366 \\ p("This will show up because "code("bar")" is 'qux'.")
367 \\ /ifequal/
368 \\)
369 );
370 try pek.compileInner(
371 alloc,
372 &builder,
373 doc,
374 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
375 .{ .bar = @as([]const u8, "foo") },
376 );
377 try expect(builder.items).toEqualString(
378 \\<body>
379 \\ <p>This will show up because <code>bar</code> is 'foo'.</p>
380 \\</body>
381 \\
382 );
383}
384test { // string false
385 const alloc = std.testing.allocator;
386 var builder = nio.AllocatingWriter.init(alloc);
387 defer builder.deinit();
388 const doc = comptime pek.parse(
389 \\body(
390 \\ {#ifequal bar "foo"}
391 \\ p("This will show up because "code("bar")" is 'foo'.")
392 \\ <else>
393 \\ p("This will show up because "code("bar")" is 'qux'.")
394 \\ /ifequal/
395 \\)
396 );
397 try pek.compileInner(
398 alloc,
399 &builder,
400 doc,
401 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
402 .{ .bar = @as([]const u8, "qux") },
403 );
404 try expect(builder.items).toEqualString(
405 \\<body>
406 \\ <p>This will show up because <code>bar</code> is 'qux'.</p>
407 \\</body>
408 \\
409 );
410}
411test { // enum
412 const alloc = std.testing.allocator;
413 var builder = nio.AllocatingWriter.init(alloc);
414 defer builder.deinit();
415 const doc = comptime pek.parse(
416 \\body(
417 \\ {#ifequal bar "foo"}
418 \\ p("This will show up because "code("bar")" is .foo.")
419 \\ <else>
420 \\ p("This will show up because "code("bar")" is .qux.")
421 \\ /ifequal/
422 \\)
423 );
424 try pek.compileInner(
425 alloc,
426 &builder,
427 doc,
428 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
429 .{ .bar = @as(enum { foo, bar, qux }, .foo) },
430 );
431 try expect(builder.items).toEqualString(
432 \\<body>
433 \\ <p>This will show up because <code>bar</code> is .foo.</p>
434 \\</body>
435 \\
436 );
437}
438test { // enum false
439 const alloc = std.testing.allocator;
440 var builder = nio.AllocatingWriter.init(alloc);
441 defer builder.deinit();
442 const doc = comptime pek.parse(
443 \\body(
444 \\ {#ifequal bar "foo"}
445 \\ p("This will show up because "code("bar")" is .foo.")
446 \\ <else>
447 \\ p("This will show up because "code("bar")" is .qux.")
448 \\ /ifequal/
449 \\)
450 );
451 try pek.compileInner(
452 alloc,
453 &builder,
454 doc,
455 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
456 .{ .bar = @as(enum { foo, bar, qux }, .qux) },
457 );
458 try expect(builder.items).toEqualString(
459 \\<body>
460 \\ <p>This will show up because <code>bar</code> is .qux.</p>
461 \\</body>
462 \\
463 );
464}
465
466// ifnotequal
467test { // bool
468 const alloc = std.testing.allocator;
469 var builder = nio.AllocatingWriter.init(alloc);
470 defer builder.deinit();
471 const doc = comptime pek.parse(
472 \\body(
473 \\ {#ifnotequal bar true}
474 \\ p("This will show up because "code("bar")" is false.")
475 \\ <else>
476 \\ p("This will show up because "code("bar")" is true.")
477 \\ /ifnotequal/
478 \\)
479 );
480 try pek.compileInner(
481 alloc,
482 &builder,
483 doc,
484 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
485 .{ .bar = false },
486 );
487 try expect(builder.items).toEqualString(
488 \\<body>
489 \\ <p>This will show up because <code>bar</code> is false.</p>
490 \\</body>
491 \\
492 );
493}
494test { // bool false
495 const alloc = std.testing.allocator;
496 var builder = nio.AllocatingWriter.init(alloc);
497 defer builder.deinit();
498 const doc = comptime pek.parse(
499 \\body(
500 \\ {#ifnotequal bar true}
501 \\ p("This will show up because "code("bar")" is false.")
502 \\ <else>
503 \\ p("This will show up because "code("bar")" is true.")
504 \\ /ifnotequal/
505 \\)
506 );
507 try pek.compileInner(
508 alloc,
509 &builder,
510 doc,
511 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
512 .{ .bar = true },
513 );
514 try expect(builder.items).toEqualString(
515 \\<body>
516 \\ <p>This will show up because <code>bar</code> is true.</p>
517 \\</body>
518 \\
519 );
520}
521test { // string
522 const alloc = std.testing.allocator;
523 var builder = nio.AllocatingWriter.init(alloc);
524 defer builder.deinit();
525 const doc = comptime pek.parse(
526 \\body(
527 \\ {#ifnotequal bar "foo"}
528 \\ p("This will show up because "code("bar")" is 'qux'.")
529 \\ <else>
530 \\ p("This will show up because "code("bar")" is 'foo'.")
531 \\ /ifnotequal/
532 \\)
533 );
534 try pek.compileInner(
535 alloc,
536 &builder,
537 doc,
538 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
539 .{ .bar = @as([]const u8, "qux") },
540 );
541 try expect(builder.items).toEqualString(
542 \\<body>
543 \\ <p>This will show up because <code>bar</code> is 'qux'.</p>
544 \\</body>
545 \\
546 );
547}
548test { // string false
549 const alloc = std.testing.allocator;
550 var builder = nio.AllocatingWriter.init(alloc);
551 defer builder.deinit();
552 const doc = comptime pek.parse(
553 \\body(
554 \\ {#ifnotequal bar "foo"}
555 \\ p("This will show up because "code("bar")" is 'qux'.")
556 \\ <else>
557 \\ p("This will show up because "code("bar")" is 'foo'.")
558 \\ /ifnotequal/
559 \\)
560 );
561 try pek.compileInner(
562 alloc,
563 &builder,
564 doc,
565 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
566 .{ .bar = @as([]const u8, "foo") },
567 );
568 try expect(builder.items).toEqualString(
569 \\<body>
570 \\ <p>This will show up because <code>bar</code> is 'foo'.</p>
571 \\</body>
572 \\
573 );
574}
575test { // enum
576 const alloc = std.testing.allocator;
577 var builder = nio.AllocatingWriter.init(alloc);
578 defer builder.deinit();
579 const doc = comptime pek.parse(
580 \\body(
581 \\ {#ifnotequal bar "foo"}
582 \\ p("This will show up because "code("bar")" is .qux.")
583 \\ <else>
584 \\ p("This will show up because "code("bar")" is .foo.")
585 \\ /ifnotequal/
586 \\)
587 );
588 try pek.compileInner(
589 alloc,
590 &builder,
591 doc,
592 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
593 .{ .bar = @as(enum { foo, bar, qux }, .qux) },
594 );
595 try expect(builder.items).toEqualString(
596 \\<body>
597 \\ <p>This will show up because <code>bar</code> is .qux.</p>
598 \\</body>
599 \\
600 );
601}
602test { // enum false
603 const alloc = std.testing.allocator;
604 var builder = nio.AllocatingWriter.init(alloc);
605 defer builder.deinit();
606 const doc = comptime pek.parse(
607 \\body(
608 \\ {#ifnotequal bar "foo"}
609 \\ p("This will show up because "code("bar")" is .qux.")
610 \\ <else>
611 \\ p("This will show up because "code("bar")" is .foo.")
612 \\ /ifnotequal/
613 \\)
614 );
615 try pek.compileInner(
616 alloc,
617 &builder,
618 doc,
619 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
620 .{ .bar = @as(enum { foo, bar, qux }, .foo) },
621 );
622 try expect(builder.items).toEqualString(
623 \\<body>
624 \\ <p>This will show up because <code>bar</code> is .foo.</p>
625 \\</body>
626 \\
627 );
628}
629
630// replacement
631test {
632 const alloc = std.testing.allocator;
633 var builder = nio.AllocatingWriter.init(alloc);
634 defer builder.deinit();
635 const doc = comptime pek.parse(
636 \\body(
637 \\ p("This text for this link is "a[href="https://github.com/nektro/zig-pek"]({text})".")
638 \\)
639 );
640 try pek.compileInner(
641 alloc,
642 &builder,
643 doc,
644 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
645 .{ .text = @as([]const u8, "dynamic") },
646 );
647 try expect(builder.items).toEqualString(
648 \\<body>
649 \\ <p>This text for this link is <a href="https://github.com/nektro/zig-pek">dynamic</a>.</p>
650 \\</body>
651 \\
652 );
653}
654test {
655 const alloc = std.testing.allocator;
656 var builder = nio.AllocatingWriter.init(alloc);
657 defer builder.deinit();
658 const doc = comptime pek.parse(
659 \\body(
660 \\ p("This url for this link is "a[href=({url})]("dynamic")".")
661 \\)
662 );
663 try pek.compileInner(
664 alloc,
665 &builder,
666 doc,
667 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
668 .{ .url = @as([]const u8, "https://github.com/nektro/zig-pek") },
669 );
670 try expect(builder.items).toEqualString(
671 \\<body>
672 \\ <p>This url for this link is <a href="https://github.com/nektro/zig-pek">dynamic</a>.</p>
673 \\</body>
674 \\
675 );
676}
677test {
678 const alloc = std.testing.allocator;
679 var builder = nio.AllocatingWriter.init(alloc);
680 defer builder.deinit();
681 const doc = comptime pek.parse(
682 \\body(
683 \\ p("This host for this link is "a[href=("https://"{host}"/nektro/zig-pek")]("dynamic")".")
684 \\)
685 );
686 try pek.compileInner(
687 alloc,
688 &builder,
689 doc,
690 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
691 .{ .host = @as([]const u8, "github.com") },
692 );
693 try expect(builder.items).toEqualString(
694 \\<body>
695 \\ <p>This host for this link is <a href="https://github.com/nektro/zig-pek">dynamic</a>.</p>
696 \\</body>
697 \\
698 );
699}
700test {
701 const alloc = std.testing.allocator;
702 var builder = nio.AllocatingWriter.init(alloc);
703 defer builder.deinit();
704 const doc = comptime pek.parse(
705 \\body(
706 \\ p("This host for this link is "a[href=("https://"{host}"/nektro/zig-pek")]("dynamic")".")
707 \\)
708 );
709 try pek.compileInner(
710 alloc,
711 &builder,
712 doc,
713 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
714 .{ .host = "github.com".* },
715 );
716 try expect(builder.items).toEqualString(
717 \\<body>
718 \\ <p>This host for this link is <a href="https://github.com/nektro/zig-pek">dynamic</a>.</p>
719 \\</body>
720 \\
721 );
722}
723test {
724 const alloc = std.testing.allocator;
725 var builder = nio.AllocatingWriter.init(alloc);
726 defer builder.deinit();
727 const S = struct { x: []const u8, y: u32 };
728 const text: S = .{ .x = "dynamic", .y = 47 };
729 const doc = comptime pek.parse(
730 \\body(
731 \\ p("This text for this link is "a[href="https://github.com/nektro/zig-pek"]({text.x})".")
732 \\)
733 );
734 try pek.compileInner(
735 alloc,
736 &builder,
737 doc,
738 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
739 .{ .text = &text },
740 );
741 try expect(builder.items).toEqualString(
742 \\<body>
743 \\ <p>This text for this link is <a href="https://github.com/nektro/zig-pek">dynamic</a>.</p>
744 \\</body>
745 \\
746 );
747}
748
749// replacement with custom serializer
750test {
751 const S = struct {
752 name: []const u8,
753
754 pub fn nprint(s: @This(), writer: anytype) !void {
755 try writer.writeAll(s.name);
756 try writer.writeAll(".com");
757 }
758 };
759 const alloc = std.testing.allocator;
760 var builder = nio.AllocatingWriter.init(alloc);
761 defer builder.deinit();
762 const doc = comptime pek.parse(
763 \\body(
764 \\ p("This host for this link is "a[href=("https://"{host}"/nektro/zig-pek")]("dynamic")".")
765 \\)
766 );
767 try pek.compileInner(
768 alloc,
769 &builder,
770 doc,
771 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
772 .{ .host = S{ .name = "github" } },
773 );
774 try expect(builder.items).toEqualString(
775 \\<body>
776 \\ <p>This host for this link is <a href="https://github.com/nektro/zig-pek">dynamic</a>.</p>
777 \\</body>
778 \\
779 );
780}
781
782// raw replacement
783test {
784 const alloc = std.testing.allocator;
785 var builder = nio.AllocatingWriter.init(alloc);
786 defer builder.deinit();
787 const doc = comptime pek.parse(
788 \\body(
789 \\ p("This url for this link is "a[href=({{url}})]("dynamic but not escaped")".")
790 \\)
791 );
792 try pek.compileInner(
793 alloc,
794 &builder,
795 doc,
796 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
797 .{ .url = "https://github.com/nektro/zig-pek".* },
798 );
799 try expect(builder.items).toEqualString(
800 \\<body>
801 \\ <p>This url for this link is <a href="https://github.com/nektro/zig-pek">dynamic but not escaped</a>.</p>
802 \\</body>
803 \\
804 );
805}
806test {
807 const alloc = std.testing.allocator;
808 var builder = nio.AllocatingWriter.init(alloc);
809 defer builder.deinit();
810 const doc = comptime pek.parse(
811 \\body(
812 \\ p("Raw replacements can also "{{foo}}".")
813 \\)
814 );
815 try pek.compileInner(
816 alloc,
817 &builder,
818 doc,
819 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
820 .{ .foo = "contain HTML such as <button>this</button> as an escape hatch when you know content is safe".* },
821 );
822 try expect(builder.items).toEqualString(
823 \\<body>
824 \\ <p>Raw replacements can also contain HTML such as <button>this</button> as an escape hatch when you know content is safe.</p>
825 \\</body>
826 \\
827 );
828}
829
830// custom function
831test {
832 const C = struct {
833 pub fn pek__input_text(alloc: std.mem.Allocator, writer: pek.Writer, comptime opts: pek.DoOptions, args: struct { []const u8, []const u8, []const u8, []const u8 }) !void {
834 const tmpl = comptime pek.parse(
835 \\label(
836 \\ div({label})
837 \\ textarea[type="text" required="" name=({name}) placeholder=({placeholder})]({value})
838 \\)
839 );
840 try pek.compileInner(alloc, writer, tmpl, opts, .{
841 .name = args[0],
842 .label = args[1],
843 .value = args[2],
844 .placeholder = args[3],
845 });
846 }
847 };
848 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
849 defer arena.deinit();
850 const alloc = arena.allocator();
851 var builder = nio.AllocatingWriter.init(alloc);
852 defer builder.deinit();
853 var person: struct { name: []const u8 } = .{ .name = "meghan" };
854 _ = &person;
855 const doc = comptime pek.parse(
856 \\body(
857 \\ form(
858 \\ {##input_text "name" "Name" prev.name ""}
859 \\ button("Submit ▶")
860 \\ )
861 \\)
862 );
863 try pek.compileInner(
864 alloc,
865 &builder,
866 doc,
867 .{ .Ctx = C, .indent = 0, .doindent = true, .doindent2 = true },
868 .{ .prev = person },
869 );
870 try expect(builder.items).toEqualString(
871 \\<body>
872 \\ <form>
873 \\ <label>
874 \\ <div>Name</div>
875 \\ <textarea type="text" required="" name="name" placeholder="">meghan</textarea>
876 \\ </label>
877 \\ <button>Submit ▶</button>
878 \\ </form>
879 \\</body>
880 \\
881 );
882}
883
884// custom bool function
885test {
886 const S = struct {
887 id: u64,
888 };
889 const C = struct {
890 pub fn pek_is_admin(alloc: std.mem.Allocator, user: S) !bool {
891 _ = alloc;
892 return user.id < 10;
893 }
894 };
895 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
896 defer arena.deinit();
897 const alloc = arena.allocator();
898 var builder = nio.AllocatingWriter.init(alloc);
899 defer builder.deinit();
900 var person: S = .{ .id = 0 };
901 _ = &person;
902 const doc = comptime pek.parse(
903 \\body(
904 \\ {#if#is_admin user}
905 \\ p("only an admin user can see this")
906 \\ <else>
907 \\ p("unauthorized users will see this")
908 \\ /if/
909 \\)
910 );
911 try pek.compileInner(
912 alloc,
913 &builder,
914 doc,
915 .{ .Ctx = C, .indent = 0, .doindent = true, .doindent2 = true },
916 .{ .user = person },
917 );
918 try expect(builder.items).toEqualString(
919 \\<body>
920 \\ <p>only an admin user can see this</p>
921 \\</body>
922 \\
923 );
924}
925test {
926 const S = struct {
927 id: u64,
928 };
929 const C = struct {
930 pub fn pek_is_admin(alloc: std.mem.Allocator, user: S) !bool {
931 _ = alloc;
932 return user.id < 10;
933 }
934 };
935 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
936 defer arena.deinit();
937 const alloc = arena.allocator();
938 var builder = nio.AllocatingWriter.init(alloc);
939 defer builder.deinit();
940 var person: S = .{ .id = 25 };
941 _ = &person;
942 const doc = comptime pek.parse(
943 \\body(
944 \\ {#if#is_admin user}
945 \\ p("only an admin user can see this")
946 \\ <else>
947 \\ p("unauthorized users will see this")
948 \\ /if/
949 \\)
950 );
951 try pek.compileInner(
952 alloc,
953 &builder,
954 doc,
955 .{ .Ctx = C, .indent = 0, .doindent = true, .doindent2 = true },
956 .{ .user = person },
957 );
958 try expect(builder.items).toEqualString(
959 \\<body>
960 \\ <p>unauthorized users will see this</p>
961 \\</body>
962 \\
963 );
964}
965
966// each
967test {
968 const alloc = std.testing.allocator;
969 var builder = nio.AllocatingWriter.init(alloc);
970 defer builder.deinit();
971 const S = struct {
972 abbr: []const u8,
973 name: []const u8,
974 index: u8,
975 };
976 const states = [_]S{
977 .{ .abbr = "MA", .name = "Massachusetts", .index = 0 },
978 .{ .abbr = "OR", .name = "Oregon", .index = 0 },
979 .{ .abbr = "CA", .name = "California", .index = 0 },
980 };
981 const doc = comptime pek.parse(
982 \\body(
983 \\ {#each states}
984 \\ p("The US state '"{this.name}"' can be shortened to '"{this.abbr}"'.")
985 \\ /each/
986 \\)
987 );
988 try pek.compileInner(
989 alloc,
990 &builder,
991 doc,
992 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
993 .{ .states = &states },
994 );
995 try expect(builder.items).toEqualString(
996 \\<body>
997 \\ <p>The US state 'Massachusetts' can be shortened to 'MA'.</p>
998 \\ <p>The US state 'Oregon' can be shortened to 'OR'.</p>
999 \\ <p>The US state 'California' can be shortened to 'CA'.</p>
1000 \\</body>
1001 \\
1002 );
1003}
1004
1005// multi-each
1006test {
1007 const alloc = std.testing.allocator;
1008 var builder = nio.AllocatingWriter.init(alloc);
1009 defer builder.deinit();
1010 const S = struct {
1011 abbr: []const u8,
1012 name: []const u8,
1013 index: u8,
1014 };
1015 const states = extras.StaticMultiList(S).initComptime(&[_]S{
1016 .{ .abbr = "MA", .name = "Massachusetts", .index = 0 },
1017 .{ .abbr = "OR", .name = "Oregon", .index = 0 },
1018 .{ .abbr = "CA", .name = "California", .index = 0 },
1019 });
1020 const doc = comptime pek.parse(
1021 \\body(
1022 \\ {#each abbrs names}
1023 \\ p("The US state '"{that}"' can be shortened to '"{this}"'.")
1024 \\ /each/
1025 \\)
1026 );
1027 try pek.compileInner(
1028 alloc,
1029 &builder,
1030 doc,
1031 .{ .Ctx = @This(), .indent = 0, .doindent = true, .doindent2 = true },
1032 .{ .abbrs = states.items.abbr, .names = states.items.name },
1033 );
1034 try expect(builder.items).toEqualString(
1035 \\<body>
1036 \\ <p>The US state 'Massachusetts' can be shortened to 'MA'.</p>
1037 \\ <p>The US state 'Oregon' can be shortened to 'OR'.</p>
1038 \\ <p>The US state 'California' can be shortened to 'CA'.</p>
1039 \\</body>
1040 \\
1041 );
1042}