1diff --git a/Makefile b/Makefile
2index e3ed19e0cdbc143c4992c36be62a00cf8cd29b11..0c1a1f65bd08e64ab7d5342576176d22fc145fbf 100644
3--- a/Makefile
4+++ b/Makefile
5@@ -4,10 +4,14 @@ GO_BUILD?= go build -v
6
7 all: build
8
9-run:
10+run: tmpl
11 $(GO_RUN) .
12
13-build:
14+build: tmpl
15 $(GO_BUILD) \
16 -o bin/$(BIN) \
17 .
18+
19+tmpl:
20+ cd ./templates && \
21+ qtc *
22diff --git a/go.mod b/go.mod
23index ccd8d0788985ebaa95e48f2be71765140b01fd93..6ec9ad4c3ea0771cc12e0f0a3feadff13616085b 100644
24--- a/go.mod
25+++ b/go.mod
26@@ -1,3 +1,8 @@
27 module git.sr.ht/~gabrielgio/apkdoc
28
29 go 1.20
30+
31+require (
32+ github.com/valyala/bytebufferpool v1.0.0 // indirect
33+ github.com/valyala/quicktemplate v1.7.0 // indirect
34+)
35diff --git a/go.sum b/go.sum
36new file mode 100644
37index 0000000000000000000000000000000000000000..9b4d04803e4106351206e0a5975f5da6faffd9f0
38--- /dev/null
39+++ b/go.sum
40@@ -0,0 +1,21 @@
41+github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
42+github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
43+github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
44+github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
45+github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
46+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
47+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
48+github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
49+github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM=
50+github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8=
51+github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
52+golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
53+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
54+golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
55+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
56+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
57+golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
58+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
59+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
60+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
61+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
62diff --git a/main.go b/main.go
63index 9e64dbb8cf255a51a31e830010f39b975e214188..cfc56c4e483d0a58972fda410b6a563f45629d71 100644
64--- a/main.go
65+++ b/main.go
66@@ -6,9 +6,12 @@ "bufio"
67 "compress/gzip"
68 "errors"
69 "flag"
70- "fmt"
71 "io"
72 "net/http"
73+ "os"
74+
75+ "git.sr.ht/~gabrielgio/apkdoc/parser"
76+ "git.sr.ht/~gabrielgio/apkdoc/templates"
77 )
78
79 func fechIndex(url string) (io.ReadCloser, error) {
80@@ -26,6 +29,7 @@ }
81
82 func main() {
83 url := flag.String("url", "", "Url to the APKINDEX.tar.gz")
84+ output := flag.String("output", "index.md", "Output path")
85 flag.Parse()
86
87 tarStream, err := fechIndex(*url)
88@@ -55,13 +59,13 @@ }
89
90 s := bufio.NewScanner(tr)
91
92- entries := make([]*Entry, 0)
93+ entries := make([]*parser.Entry, 0)
94 lines := make([]string, 0)
95
96 for s.Scan() {
97 l := s.Text()
98 if l == "" {
99- entry := Parse(lines)
100+ entry := parser.Parse(lines)
101 entries = append(entries, entry)
102 lines = make([]string, 0)
103 } else {
104@@ -69,7 +73,10 @@ lines = append(lines, l)
105 }
106 }
107
108- for _, e := range entries {
109- fmt.Printf("%+v\n", e)
110+ file, err := os.Create(*output)
111+ if err != nil {
112+ panic("Error opening output file: " + err.Error())
113 }
114+
115+ templates.WriteMarkdownTemplate(file, entries)
116 }
117diff --git a/parser.go b/parser/parser.go
118rename from parser.go
119rename to parser/parser.go
120index 998df918ed8f8749db4ccde332ca1c3b61317405..8996b8cd81c06890f565e445197e6a8fbc4db895 100644
121--- a/parser.go
122+++ b/parser/parser.go
123@@ -1,4 +1,4 @@
124-package main
125+package parser
126
127 import (
128 "strconv"
129@@ -10,10 +10,11 @@ type (
130 // https://wiki.alpinelinux.org/wiki/Apk_spec
131 Entry struct {
132 Checksum string // C
133+ Version string // V
134 Name string // P
135 Architecture *string // A
136 PackageSize int // S
137- InstalledSize *int // I
138+ InstalledSize int // I
139 Description string // T
140 Url string // U
141 License string // L
142@@ -49,6 +50,8 @@ r, c := split(line)
143 switch r {
144 case "C":
145 entry.Checksum = c
146+ case "V":
147+ entry.Version = c
148 case "P":
149 entry.Name = c
150 case "A":
151@@ -56,7 +59,7 @@ entry.Architecture = &c
152 case "S":
153 entry.PackageSize = toInt(c)
154 case "I":
155- entry.InstalledSize = ptr(toInt(c))
156+ entry.InstalledSize = toInt(c)
157 case "T":
158 entry.Description = c
159 case "U":
160diff --git a/templates/index.md.qtpl b/templates/index.md.qtpl
161new file mode 100644
162index 0000000000000000000000000000000000000000..879795a6f96627cd2575cb99092d6718be4c4486
163--- /dev/null
164+++ b/templates/index.md.qtpl
165@@ -0,0 +1,33 @@
166+{% import "git.sr.ht/~gabrielgio/apkdoc/parser" %}
167+{% import "strings" %}
168+
169+{% code
170+func reduce(lines []string) string {
171+ return strings.Join(lines, " ")
172+}
173+%}
174+
175+{% func MarkdownTemplate(entries []*parser.Entry) %}
176+# Apks Alpine 3.18
177+{% for _, e := range entries %}
178+## {%s e.Name %}
179+
180+**Version**: {%s e.Version%}
181+**Description**: {%s e.Description%}
182+{% if e.Maintainer != nil %}**Maintainer**: {%s *e.Maintainer%}{% endif %}
183+**Checksum**:{%s e.Checksum%}
184+{% if e.Architecture != nil %}**Architecture**: {%s *e.Architecture%}{% endif %}
185+**Size**: {%d e.PackageSize%}B
186+**Installed size**: {%d e.InstalledSize%}
187+**Url**: {%s e.Url%}
188+**License**: {%s e.License%}
189+{% if e.Origin != nil %}**Origin**: {%s *e.Origin%}{% endif %}
190+{% if e.BuildTime != nil %}**Build time**: {%s e.BuildTime.String() %}{% endif %}
191+{% if e.Commit != nil %}**Commit**: {%s *e.Commit %}{% endif %}
192+{% if e.ProviderPriority != nil %}**Provider Priority**: {%d *e.ProviderPriority %}{% endif %}
193+{% if len(e.Dependencies) > 0 %}**Dependencies:**: {%s reduce(e.Dependencies) %}{% endif %}
194+{% if len(e.Provides) > 0 %}**Provides:**: {%s reduce(e.Provides) %}{% endif %}
195+{% if len(e.InstallIf) > 0 %}**Install if:**: {%s reduce(e.InstallIf) %}{% endif %}
196+{% endfor %}
197+
198+{% endfunc %}
199diff --git a/templates/index.md.qtpl.go b/templates/index.md.qtpl.go
200new file mode 100644
201index 0000000000000000000000000000000000000000..af3d34a5d9a07cdd2e9a370ac306523268c03092
202--- /dev/null
203+++ b/templates/index.md.qtpl.go
204@@ -0,0 +1,215 @@
205+// Code generated by qtc from "index.md.qtpl". DO NOT EDIT.
206+// See https://github.com/valyala/quicktemplate for details.
207+
208+//line index.md.qtpl:1
209+package templates
210+
211+//line index.md.qtpl:1
212+import "git.sr.ht/~gabrielgio/apkdoc/parser"
213+
214+//line index.md.qtpl:2
215+import "strings"
216+
217+//line index.md.qtpl:4
218+import (
219+ qtio422016 "io"
220+
221+ qt422016 "github.com/valyala/quicktemplate"
222+)
223+
224+//line index.md.qtpl:4
225+var (
226+ _ = qtio422016.Copy
227+ _ = qt422016.AcquireByteBuffer
228+)
229+
230+//line index.md.qtpl:5
231+func reduce(lines []string) string {
232+ return strings.Join(lines, " ")
233+}
234+
235+//line index.md.qtpl:10
236+func StreamMarkdownTemplate(qw422016 *qt422016.Writer, entries []*parser.Entry) {
237+//line index.md.qtpl:10
238+ qw422016.N().S(`
239+# Apks Alpine 3.18
240+`)
241+//line index.md.qtpl:12
242+ for _, e := range entries {
243+//line index.md.qtpl:12
244+ qw422016.N().S(`
245+## `)
246+//line index.md.qtpl:13
247+ qw422016.E().S(e.Name)
248+//line index.md.qtpl:13
249+ qw422016.N().S(`
250+
251+**Version**: `)
252+//line index.md.qtpl:15
253+ qw422016.E().S(e.Version)
254+//line index.md.qtpl:15
255+ qw422016.N().S(`
256+**Description**: `)
257+//line index.md.qtpl:16
258+ qw422016.E().S(e.Description)
259+//line index.md.qtpl:16
260+ qw422016.N().S(`
261+`)
262+//line index.md.qtpl:17
263+ if e.Maintainer != nil {
264+//line index.md.qtpl:17
265+ qw422016.N().S(`**Maintainer**: `)
266+//line index.md.qtpl:17
267+ qw422016.E().S(*e.Maintainer)
268+//line index.md.qtpl:17
269+ }
270+//line index.md.qtpl:17
271+ qw422016.N().S(`
272+**Checksum**:`)
273+//line index.md.qtpl:18
274+ qw422016.E().S(e.Checksum)
275+//line index.md.qtpl:18
276+ qw422016.N().S(`
277+`)
278+//line index.md.qtpl:19
279+ if e.Architecture != nil {
280+//line index.md.qtpl:19
281+ qw422016.N().S(`**Architecture**: `)
282+//line index.md.qtpl:19
283+ qw422016.E().S(*e.Architecture)
284+//line index.md.qtpl:19
285+ }
286+//line index.md.qtpl:19
287+ qw422016.N().S(`
288+**Size**: `)
289+//line index.md.qtpl:20
290+ qw422016.N().D(e.PackageSize)
291+//line index.md.qtpl:20
292+ qw422016.N().S(`B
293+**Installed size**: `)
294+//line index.md.qtpl:21
295+ qw422016.N().D(e.InstalledSize)
296+//line index.md.qtpl:21
297+ qw422016.N().S(`
298+**Url**: `)
299+//line index.md.qtpl:22
300+ qw422016.E().S(e.Url)
301+//line index.md.qtpl:22
302+ qw422016.N().S(`
303+**License**: `)
304+//line index.md.qtpl:23
305+ qw422016.E().S(e.License)
306+//line index.md.qtpl:23
307+ qw422016.N().S(`
308+`)
309+//line index.md.qtpl:24
310+ if e.Origin != nil {
311+//line index.md.qtpl:24
312+ qw422016.N().S(`**Origin**: `)
313+//line index.md.qtpl:24
314+ qw422016.E().S(*e.Origin)
315+//line index.md.qtpl:24
316+ }
317+//line index.md.qtpl:24
318+ qw422016.N().S(`
319+`)
320+//line index.md.qtpl:25
321+ if e.BuildTime != nil {
322+//line index.md.qtpl:25
323+ qw422016.N().S(`**Build time**: `)
324+//line index.md.qtpl:25
325+ qw422016.E().S(e.BuildTime.String())
326+//line index.md.qtpl:25
327+ }
328+//line index.md.qtpl:25
329+ qw422016.N().S(`
330+`)
331+//line index.md.qtpl:26
332+ if e.Commit != nil {
333+//line index.md.qtpl:26
334+ qw422016.N().S(`**Commit**: `)
335+//line index.md.qtpl:26
336+ qw422016.E().S(*e.Commit)
337+//line index.md.qtpl:26
338+ }
339+//line index.md.qtpl:26
340+ qw422016.N().S(`
341+`)
342+//line index.md.qtpl:27
343+ if e.ProviderPriority != nil {
344+//line index.md.qtpl:27
345+ qw422016.N().S(`**Provider Priority**: `)
346+//line index.md.qtpl:27
347+ qw422016.N().D(*e.ProviderPriority)
348+//line index.md.qtpl:27
349+ }
350+//line index.md.qtpl:27
351+ qw422016.N().S(`
352+`)
353+//line index.md.qtpl:28
354+ if len(e.Dependencies) > 0 {
355+//line index.md.qtpl:28
356+ qw422016.N().S(`**Dependencies:**: `)
357+//line index.md.qtpl:28
358+ qw422016.E().S(reduce(e.Dependencies))
359+//line index.md.qtpl:28
360+ }
361+//line index.md.qtpl:28
362+ qw422016.N().S(`
363+`)
364+//line index.md.qtpl:29
365+ if len(e.Provides) > 0 {
366+//line index.md.qtpl:29
367+ qw422016.N().S(`**Provides:**: `)
368+//line index.md.qtpl:29
369+ qw422016.E().S(reduce(e.Provides))
370+//line index.md.qtpl:29
371+ }
372+//line index.md.qtpl:29
373+ qw422016.N().S(`
374+`)
375+//line index.md.qtpl:30
376+ if len(e.InstallIf) > 0 {
377+//line index.md.qtpl:30
378+ qw422016.N().S(`**Install if:**: `)
379+//line index.md.qtpl:30
380+ qw422016.E().S(reduce(e.InstallIf))
381+//line index.md.qtpl:30
382+ }
383+//line index.md.qtpl:30
384+ qw422016.N().S(`
385+`)
386+//line index.md.qtpl:31
387+ }
388+//line index.md.qtpl:31
389+ qw422016.N().S(`
390+
391+`)
392+//line index.md.qtpl:33
393+}
394+
395+//line index.md.qtpl:33
396+func WriteMarkdownTemplate(qq422016 qtio422016.Writer, entries []*parser.Entry) {
397+//line index.md.qtpl:33
398+ qw422016 := qt422016.AcquireWriter(qq422016)
399+//line index.md.qtpl:33
400+ StreamMarkdownTemplate(qw422016, entries)
401+//line index.md.qtpl:33
402+ qt422016.ReleaseWriter(qw422016)
403+//line index.md.qtpl:33
404+}
405+
406+//line index.md.qtpl:33
407+func MarkdownTemplate(entries []*parser.Entry) string {
408+//line index.md.qtpl:33
409+ qb422016 := qt422016.AcquireByteBuffer()
410+//line index.md.qtpl:33
411+ WriteMarkdownTemplate(qb422016, entries)
412+//line index.md.qtpl:33
413+ qs422016 := string(qb422016.B)
414+//line index.md.qtpl:33
415+ qt422016.ReleaseByteBuffer(qb422016)
416+//line index.md.qtpl:33
417+ return qs422016
418+//line index.md.qtpl:33
419+}