cerrado @ f5b5f72f7eaf14692b8036c3698037c647b2423a

feat: Add qtc templating
  1diff --git a/.gitignore b/.gitignore
  2index e660fd93d3196215552065b1e63bf6a2f393ed86..776d7a2c9749474ff5daa02e3b6842d4c1d8a36d 100644
  3--- a/.gitignore
  4+++ b/.gitignore
  5@@ -1 +1,2 @@
  6 bin/
  7+static/*.css
  8diff --git a/Makefile b/Makefile
  9index c947b684f1eaa54cd28a07d37e68eee5ad7f79fc..38db3a9c283ce4cd2538f1fc454111ed9ff12035 100644
 10--- a/Makefile
 11+++ b/Makefile
 12@@ -1,12 +1,19 @@
 13-build: sass
 14+.ONESHELL:
 15+
 16+build: sass tmpl
 17 	go build -o bin/cerrado
 18 
 19-run: sass
 20+run: sass tmpl
 21 	go run .
 22 
 23 test:
 24 	go test -v --tags=unit ./...
 25 
 26 sass:
 27+	@make -p static
 28 	sassc \
 29-		-I scss scss/main.scss bin/main.css
 30+		-I scss scss/main.scss static/main.css
 31+
 32+tmpl:
 33+	cd ./templates
 34+	qtc *
 35diff --git a/go.mod b/go.mod
 36index bfbe03ad8f3b8502d8d8f9ba9d3967552b1171e1..b8b36b4cc85febdf92f178dea6604511c034b56d 100644
 37--- a/go.mod
 38+++ b/go.mod
 39@@ -5,5 +5,8 @@
 40 require (
 41 	git.sr.ht/~emersion/go-scfg v0.0.0-20240128091534-2ae16e782082
 42 	github.com/google/go-cmp v0.6.0
 43+	github.com/valyala/quicktemplate v1.7.0
 44 	golang.org/x/sync v0.7.0
 45 )
 46+
 47+require github.com/valyala/bytebufferpool v1.0.0 // indirect
 48diff --git a/go.sum b/go.sum
 49index a98204454c0ef47546d171a1dada140655d0b45e..0ba6fdb880c9cf8d7bef209163294c73990aab69 100644
 50--- a/go.sum
 51+++ b/go.sum
 52@@ -1,8 +1,29 @@
 53 git.sr.ht/~emersion/go-scfg v0.0.0-20240128091534-2ae16e782082 h1:9Udx5fm4vRtmgDIBjy2ef5QioHbzpw5oHabbhpAUyEw=
 54 git.sr.ht/~emersion/go-scfg v0.0.0-20240128091534-2ae16e782082/go.mod h1:ybgvEJTIx5XbaspSviB3KNa6OdPmAZqDoSud7z8fFlw=
 55+github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
 56+github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 57 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 58 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 59+github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 60 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 61 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 62+github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
 63+github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 64+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 65+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 66+github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
 67+github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM=
 68+github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8=
 69+github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
 70+golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 71+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 72+golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 73 golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
 74 golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 75+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 76+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 77+golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 78+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 79+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 80+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 81+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 82diff --git a/main.go b/main.go
 83index ba441fe71b8c6c0ea4aff7a8805d2e4729059e27..55f866e18d0497c7d8c44ff21ea0adf1517b90be 100644
 84--- a/main.go
 85+++ b/main.go
 86@@ -12,6 +12,7 @@ 	"time"
 87 
 88 	"git.gabrielgio.me/cerrado/pkg/config"
 89 	"git.gabrielgio.me/cerrado/pkg/worker"
 90+	"git.gabrielgio.me/cerrado/templates"
 91 )
 92 
 93 func main() {
 94@@ -51,10 +52,11 @@ 			slog.Error("Error parsing json", "error", err)
 95 			return
 96 		}
 97 
 98-		if _, err := w.Write(b); err != nil {
 99-			slog.Error("Error handling index", "error", err)
100-			return
101+		hello := &templates.HelloPage{
102+			Body: string(b),
103 		}
104+
105+		templates.WritePageTemplate(w, hello)
106 	})
107 	serverTask := worker.NewServerTask(&http.Server{Handler: mux, Addr: "0.0.0.0:8080"})
108 
109diff --git a/static/static.go b/static/static.go
110new file mode 100644
111index 0000000000000000000000000000000000000000..ada043472dff4bf3bb13f74d3e2bdb5fdbc10763
112--- /dev/null
113+++ b/static/static.go
114@@ -0,0 +1,6 @@
115+package static
116+
117+import "embed"
118+
119+//go:embed *
120+var Static embed.FS
121diff --git a/templates/base.qtpl b/templates/base.qtpl
122new file mode 100644
123index 0000000000000000000000000000000000000000..1683981b506d0ed3e46c9331cc1841189422929a
124--- /dev/null
125+++ b/templates/base.qtpl
126@@ -0,0 +1,44 @@
127+This is a base page template. All the other template pages implement this interface.
128+
129+{% import "strconv" %}
130+
131+{% interface
132+Page {
133+	Title()
134+	Content()
135+    Script()
136+}
137+
138+%}
139+
140+{% code func FromUInttoString(u *uint) string {
141+        if u != nil {
142+            return strconv.FormatUint(uint64(*u), 10)
143+        }
144+        return ""
145+    }
146+%}
147+
148+
149+Page prints a page implementing Page interface.
150+{% func PageTemplate(p Page) %}
151+<html lang="en">
152+    <head>
153+        <meta charset="utf-8">
154+        <title>img | {%= p.Title() %}</title> 
155+        <link rel="stylesheet" href="/static/main.css">
156+        <meta name="viewport" content="width=device-width, initial-scale=1" />
157+    </head>
158+    <body>
159+        <div>
160+            {%= p.Content() %}
161+        </div>
162+    </body>
163+    {%= p.Script() %}
164+</html>
165+{% endfunc %}
166+
167+{% code type BasePage struct {} %}
168+{% func (p *BasePage) Title() %}Empty{% endfunc %}
169+{% func (p *BasePage) Body() %}HelloWorld{% endfunc %}
170+{% func (p *BasePage) Script() %}{% endfunc %}
171diff --git a/templates/base.qtpl.go b/templates/base.qtpl.go
172new file mode 100644
173index 0000000000000000000000000000000000000000..8d93fa82b1607475b09d4077628d6598b628af6e
174--- /dev/null
175+++ b/templates/base.qtpl.go
176@@ -0,0 +1,217 @@
177+// Code generated by qtc from "base.qtpl". DO NOT EDIT.
178+// See https://github.com/valyala/quicktemplate for details.
179+
180+// This is a base page template. All the other template pages implement this interface.
181+//
182+
183+//line base.qtpl:3
184+package templates
185+
186+//line base.qtpl:3
187+import "strconv"
188+
189+//line base.qtpl:5
190+import (
191+	qtio422016 "io"
192+
193+	qt422016 "github.com/valyala/quicktemplate"
194+)
195+
196+//line base.qtpl:5
197+var (
198+	_ = qtio422016.Copy
199+	_ = qt422016.AcquireByteBuffer
200+)
201+
202+//line base.qtpl:6
203+type Page interface {
204+//line base.qtpl:6
205+	Title() string
206+//line base.qtpl:6
207+	StreamTitle(qw422016 *qt422016.Writer)
208+//line base.qtpl:6
209+	WriteTitle(qq422016 qtio422016.Writer)
210+//line base.qtpl:6
211+	Content() string
212+//line base.qtpl:6
213+	StreamContent(qw422016 *qt422016.Writer)
214+//line base.qtpl:6
215+	WriteContent(qq422016 qtio422016.Writer)
216+//line base.qtpl:6
217+	Script() string
218+//line base.qtpl:6
219+	StreamScript(qw422016 *qt422016.Writer)
220+//line base.qtpl:6
221+	WriteScript(qq422016 qtio422016.Writer)
222+//line base.qtpl:6
223+}
224+
225+//line base.qtpl:14
226+func FromUInttoString(u *uint) string {
227+	if u != nil {
228+		return strconv.FormatUint(uint64(*u), 10)
229+	}
230+	return ""
231+}
232+
233+// Page prints a page implementing Page interface.
234+
235+//line base.qtpl:24
236+func StreamPageTemplate(qw422016 *qt422016.Writer, p Page) {
237+//line base.qtpl:24
238+	qw422016.N().S(`
239+<html lang="en">
240+    <head>
241+        <meta charset="utf-8">
242+        <title>img | `)
243+//line base.qtpl:28
244+	p.StreamTitle(qw422016)
245+//line base.qtpl:28
246+	qw422016.N().S(`</title> 
247+        <link rel="stylesheet" href="/static/main.css">
248+        <meta name="viewport" content="width=device-width, initial-scale=1" />
249+    </head>
250+    <body>
251+        <div>
252+            `)
253+//line base.qtpl:34
254+	p.StreamContent(qw422016)
255+//line base.qtpl:34
256+	qw422016.N().S(`
257+        </div>
258+    </body>
259+    `)
260+//line base.qtpl:37
261+	p.StreamScript(qw422016)
262+//line base.qtpl:37
263+	qw422016.N().S(`
264+</html>
265+`)
266+//line base.qtpl:39
267+}
268+
269+//line base.qtpl:39
270+func WritePageTemplate(qq422016 qtio422016.Writer, p Page) {
271+//line base.qtpl:39
272+	qw422016 := qt422016.AcquireWriter(qq422016)
273+//line base.qtpl:39
274+	StreamPageTemplate(qw422016, p)
275+//line base.qtpl:39
276+	qt422016.ReleaseWriter(qw422016)
277+//line base.qtpl:39
278+}
279+
280+//line base.qtpl:39
281+func PageTemplate(p Page) string {
282+//line base.qtpl:39
283+	qb422016 := qt422016.AcquireByteBuffer()
284+//line base.qtpl:39
285+	WritePageTemplate(qb422016, p)
286+//line base.qtpl:39
287+	qs422016 := string(qb422016.B)
288+//line base.qtpl:39
289+	qt422016.ReleaseByteBuffer(qb422016)
290+//line base.qtpl:39
291+	return qs422016
292+//line base.qtpl:39
293+}
294+
295+//line base.qtpl:41
296+type BasePage struct{}
297+
298+//line base.qtpl:42
299+func (p *BasePage) StreamTitle(qw422016 *qt422016.Writer) {
300+//line base.qtpl:42
301+	qw422016.N().S(`Empty`)
302+//line base.qtpl:42
303+}
304+
305+//line base.qtpl:42
306+func (p *BasePage) WriteTitle(qq422016 qtio422016.Writer) {
307+//line base.qtpl:42
308+	qw422016 := qt422016.AcquireWriter(qq422016)
309+//line base.qtpl:42
310+	p.StreamTitle(qw422016)
311+//line base.qtpl:42
312+	qt422016.ReleaseWriter(qw422016)
313+//line base.qtpl:42
314+}
315+
316+//line base.qtpl:42
317+func (p *BasePage) Title() string {
318+//line base.qtpl:42
319+	qb422016 := qt422016.AcquireByteBuffer()
320+//line base.qtpl:42
321+	p.WriteTitle(qb422016)
322+//line base.qtpl:42
323+	qs422016 := string(qb422016.B)
324+//line base.qtpl:42
325+	qt422016.ReleaseByteBuffer(qb422016)
326+//line base.qtpl:42
327+	return qs422016
328+//line base.qtpl:42
329+}
330+
331+//line base.qtpl:43
332+func (p *BasePage) StreamBody(qw422016 *qt422016.Writer) {
333+//line base.qtpl:43
334+	qw422016.N().S(`HelloWorld`)
335+//line base.qtpl:43
336+}
337+
338+//line base.qtpl:43
339+func (p *BasePage) WriteBody(qq422016 qtio422016.Writer) {
340+//line base.qtpl:43
341+	qw422016 := qt422016.AcquireWriter(qq422016)
342+//line base.qtpl:43
343+	p.StreamBody(qw422016)
344+//line base.qtpl:43
345+	qt422016.ReleaseWriter(qw422016)
346+//line base.qtpl:43
347+}
348+
349+//line base.qtpl:43
350+func (p *BasePage) Body() string {
351+//line base.qtpl:43
352+	qb422016 := qt422016.AcquireByteBuffer()
353+//line base.qtpl:43
354+	p.WriteBody(qb422016)
355+//line base.qtpl:43
356+	qs422016 := string(qb422016.B)
357+//line base.qtpl:43
358+	qt422016.ReleaseByteBuffer(qb422016)
359+//line base.qtpl:43
360+	return qs422016
361+//line base.qtpl:43
362+}
363+
364+//line base.qtpl:44
365+func (p *BasePage) StreamScript(qw422016 *qt422016.Writer) {
366+//line base.qtpl:44
367+}
368+
369+//line base.qtpl:44
370+func (p *BasePage) WriteScript(qq422016 qtio422016.Writer) {
371+//line base.qtpl:44
372+	qw422016 := qt422016.AcquireWriter(qq422016)
373+//line base.qtpl:44
374+	p.StreamScript(qw422016)
375+//line base.qtpl:44
376+	qt422016.ReleaseWriter(qw422016)
377+//line base.qtpl:44
378+}
379+
380+//line base.qtpl:44
381+func (p *BasePage) Script() string {
382+//line base.qtpl:44
383+	qb422016 := qt422016.AcquireByteBuffer()
384+//line base.qtpl:44
385+	p.WriteScript(qb422016)
386+//line base.qtpl:44
387+	qs422016 := string(qb422016.B)
388+//line base.qtpl:44
389+	qt422016.ReleaseByteBuffer(qb422016)
390+//line base.qtpl:44
391+	return qs422016
392+//line base.qtpl:44
393+}
394diff --git a/templates/helloworld.qtpl b/templates/helloworld.qtpl
395new file mode 100644
396index 0000000000000000000000000000000000000000..02c09681378f5fad6de6fa1ec9550d59233cf887
397--- /dev/null
398+++ b/templates/helloworld.qtpl
399@@ -0,0 +1,16 @@
400+{% code
401+type HelloPage struct {
402+    Body string
403+}
404+%}
405+
406+{% func (p *HelloPage) Title() %}Hello{% endfunc %}
407+
408+{% func (p *HelloPage) Content() %}
409+HelloWorld
410+
411+{%s p.Body %}
412+{% endfunc %}
413+
414+{% func (p *HelloPage) Script() %}
415+{% endfunc %}
416diff --git a/templates/helloworld.qtpl.go b/templates/helloworld.qtpl.go
417new file mode 100644
418index 0000000000000000000000000000000000000000..9ef78f55e201f7d50d7d373dc40fa30c682169bf
419--- /dev/null
420+++ b/templates/helloworld.qtpl.go
421@@ -0,0 +1,131 @@
422+// Code generated by qtc from "helloworld.qtpl". DO NOT EDIT.
423+// See https://github.com/valyala/quicktemplate for details.
424+
425+//line helloworld.qtpl:1
426+package templates
427+
428+//line helloworld.qtpl:1
429+import (
430+	qtio422016 "io"
431+
432+	qt422016 "github.com/valyala/quicktemplate"
433+)
434+
435+//line helloworld.qtpl:1
436+var (
437+	_ = qtio422016.Copy
438+	_ = qt422016.AcquireByteBuffer
439+)
440+
441+//line helloworld.qtpl:2
442+type HelloPage struct {
443+	Body string
444+}
445+
446+//line helloworld.qtpl:7
447+func (p *HelloPage) StreamTitle(qw422016 *qt422016.Writer) {
448+//line helloworld.qtpl:7
449+	qw422016.N().S(`Hello`)
450+//line helloworld.qtpl:7
451+}
452+
453+//line helloworld.qtpl:7
454+func (p *HelloPage) WriteTitle(qq422016 qtio422016.Writer) {
455+//line helloworld.qtpl:7
456+	qw422016 := qt422016.AcquireWriter(qq422016)
457+//line helloworld.qtpl:7
458+	p.StreamTitle(qw422016)
459+//line helloworld.qtpl:7
460+	qt422016.ReleaseWriter(qw422016)
461+//line helloworld.qtpl:7
462+}
463+
464+//line helloworld.qtpl:7
465+func (p *HelloPage) Title() string {
466+//line helloworld.qtpl:7
467+	qb422016 := qt422016.AcquireByteBuffer()
468+//line helloworld.qtpl:7
469+	p.WriteTitle(qb422016)
470+//line helloworld.qtpl:7
471+	qs422016 := string(qb422016.B)
472+//line helloworld.qtpl:7
473+	qt422016.ReleaseByteBuffer(qb422016)
474+//line helloworld.qtpl:7
475+	return qs422016
476+//line helloworld.qtpl:7
477+}
478+
479+//line helloworld.qtpl:9
480+func (p *HelloPage) StreamContent(qw422016 *qt422016.Writer) {
481+//line helloworld.qtpl:9
482+	qw422016.N().S(`
483+HelloWorld
484+
485+`)
486+//line helloworld.qtpl:12
487+	qw422016.E().S(p.Body)
488+//line helloworld.qtpl:12
489+	qw422016.N().S(`
490+`)
491+//line helloworld.qtpl:13
492+}
493+
494+//line helloworld.qtpl:13
495+func (p *HelloPage) WriteContent(qq422016 qtio422016.Writer) {
496+//line helloworld.qtpl:13
497+	qw422016 := qt422016.AcquireWriter(qq422016)
498+//line helloworld.qtpl:13
499+	p.StreamContent(qw422016)
500+//line helloworld.qtpl:13
501+	qt422016.ReleaseWriter(qw422016)
502+//line helloworld.qtpl:13
503+}
504+
505+//line helloworld.qtpl:13
506+func (p *HelloPage) Content() string {
507+//line helloworld.qtpl:13
508+	qb422016 := qt422016.AcquireByteBuffer()
509+//line helloworld.qtpl:13
510+	p.WriteContent(qb422016)
511+//line helloworld.qtpl:13
512+	qs422016 := string(qb422016.B)
513+//line helloworld.qtpl:13
514+	qt422016.ReleaseByteBuffer(qb422016)
515+//line helloworld.qtpl:13
516+	return qs422016
517+//line helloworld.qtpl:13
518+}
519+
520+//line helloworld.qtpl:15
521+func (p *HelloPage) StreamScript(qw422016 *qt422016.Writer) {
522+//line helloworld.qtpl:15
523+	qw422016.N().S(`
524+`)
525+//line helloworld.qtpl:16
526+}
527+
528+//line helloworld.qtpl:16
529+func (p *HelloPage) WriteScript(qq422016 qtio422016.Writer) {
530+//line helloworld.qtpl:16
531+	qw422016 := qt422016.AcquireWriter(qq422016)
532+//line helloworld.qtpl:16
533+	p.StreamScript(qw422016)
534+//line helloworld.qtpl:16
535+	qt422016.ReleaseWriter(qw422016)
536+//line helloworld.qtpl:16
537+}
538+
539+//line helloworld.qtpl:16
540+func (p *HelloPage) Script() string {
541+//line helloworld.qtpl:16
542+	qb422016 := qt422016.AcquireByteBuffer()
543+//line helloworld.qtpl:16
544+	p.WriteScript(qb422016)
545+//line helloworld.qtpl:16
546+	qs422016 := string(qb422016.B)
547+//line helloworld.qtpl:16
548+	qt422016.ReleaseByteBuffer(qb422016)
549+//line helloworld.qtpl:16
550+	return qs422016
551+//line helloworld.qtpl:16
552+}