apkdoc @ dd2a1c38a5a9f561d58b58da56d3230fd715fcde

  1diff --git a/.gitignore b/.gitignore
  2new file mode 100644
  3index 0000000000000000000000000000000000000000..f331ccd56052fda427b7b1b8b581d13c01147937
  4--- /dev/null
  5+++ b/.gitignore
  6@@ -0,0 +1,2 @@
  7+bin/
  8+vendor/
  9diff --git a/Makefile b/Makefile
 10new file mode 100644
 11index 0000000000000000000000000000000000000000..e3ed19e0cdbc143c4992c36be62a00cf8cd29b11
 12--- /dev/null
 13+++ b/Makefile
 14@@ -0,0 +1,13 @@
 15+BIN?=apkdoc
 16+GO_RUN?= go run -v
 17+GO_BUILD?= go build -v
 18+
 19+all: build
 20+
 21+run:
 22+	$(GO_RUN) .
 23+
 24+build:
 25+	$(GO_BUILD) \
 26+		-o bin/$(BIN) \
 27+		.
 28diff --git a/go.mod b/go.mod
 29new file mode 100644
 30index 0000000000000000000000000000000000000000..ccd8d0788985ebaa95e48f2be71765140b01fd93
 31--- /dev/null
 32+++ b/go.mod
 33@@ -0,0 +1,3 @@
 34+module git.sr.ht/~gabrielgio/apkdoc
 35+
 36+go 1.20
 37diff --git a/main.go b/main.go
 38new file mode 100644
 39index 0000000000000000000000000000000000000000..9e64dbb8cf255a51a31e830010f39b975e214188
 40--- /dev/null
 41+++ b/main.go
 42@@ -0,0 +1,75 @@
 43+package main
 44+
 45+import (
 46+	"archive/tar"
 47+	"bufio"
 48+	"compress/gzip"
 49+	"errors"
 50+	"flag"
 51+	"fmt"
 52+	"io"
 53+	"net/http"
 54+)
 55+
 56+func fechIndex(url string) (io.ReadCloser, error) {
 57+	resp, err := http.Get(url)
 58+	if err != nil {
 59+		return nil, err
 60+	}
 61+
 62+	if resp.StatusCode != 200 {
 63+		return nil, errors.New("Invlid response")
 64+	}
 65+
 66+	return resp.Body, nil
 67+}
 68+
 69+func main() {
 70+	url := flag.String("url", "", "Url to the APKINDEX.tar.gz")
 71+	flag.Parse()
 72+
 73+	tarStream, err := fechIndex(*url)
 74+	if err != nil {
 75+		panic("Error fecthing the index: " + err.Error())
 76+	}
 77+
 78+	defer tarStream.Close()
 79+
 80+	archive, err := gzip.NewReader(tarStream)
 81+	if err != nil {
 82+		panic("Error creating gzip reader: " + err.Error())
 83+	}
 84+
 85+	tr := tar.NewReader(archive)
 86+
 87+	for {
 88+		h, err := tr.Next()
 89+		if err != nil {
 90+			panic("Error reading next tar entry: " + err.Error())
 91+		}
 92+
 93+		if h.FileInfo().Name() == "APKINDEX" {
 94+			break
 95+		}
 96+	}
 97+
 98+	s := bufio.NewScanner(tr)
 99+
100+	entries := make([]*Entry, 0)
101+	lines := make([]string, 0)
102+
103+	for s.Scan() {
104+		l := s.Text()
105+		if l == "" {
106+			entry := Parse(lines)
107+			entries = append(entries, entry)
108+			lines = make([]string, 0)
109+		} else {
110+			lines = append(lines, l)
111+		}
112+	}
113+
114+	for _, e := range entries {
115+		fmt.Printf("%+v\n", e)
116+	}
117+}
118diff --git a/parser.go b/parser.go
119new file mode 100644
120index 0000000000000000000000000000000000000000..998df918ed8f8749db4ccde332ca1c3b61317405
121--- /dev/null
122+++ b/parser.go
123@@ -0,0 +1,85 @@
124+package main
125+
126+import (
127+	"strconv"
128+	"strings"
129+	"time"
130+)
131+
132+type (
133+	// https://wiki.alpinelinux.org/wiki/Apk_spec
134+	Entry struct {
135+		Checksum         string     // C
136+		Name             string     // P
137+		Architecture     *string    // A
138+		PackageSize      int        // S
139+		InstalledSize    *int       // I
140+		Description      string     // T
141+		Url              string     // U
142+		License          string     // L
143+		Origin           *string    // o
144+		Maintainer       *string    // m
145+		BuildTime        *time.Time // t
146+		Commit           *string    // c
147+		ProviderPriority *int       // k
148+		Dependencies     []string   // D
149+		Provides         []string   // p
150+		InstallIf        []string   // i
151+	}
152+)
153+
154+func ptr[T any](v T) *T {
155+	return &v
156+}
157+
158+func split(line string) (string, string) {
159+	parts := strings.SplitN(line, ":", 2)
160+	return parts[0], parts[1]
161+}
162+
163+func toInt(v string) int {
164+	i, _ := strconv.Atoi(v)
165+	return i
166+}
167+
168+func Parse(lines []string) *Entry {
169+	entry := &Entry{}
170+	for _, line := range lines {
171+		r, c := split(line)
172+		switch r {
173+		case "C":
174+			entry.Checksum = c
175+		case "P":
176+			entry.Name = c
177+		case "A":
178+			entry.Architecture = &c
179+		case "S":
180+			entry.PackageSize = toInt(c)
181+		case "I":
182+			entry.InstalledSize = ptr(toInt(c))
183+		case "T":
184+			entry.Description = c
185+		case "U":
186+			entry.Url = c
187+		case "L":
188+			entry.License = c
189+		case "o":
190+			entry.Origin = &c
191+		case "m":
192+			entry.Maintainer = &c
193+		case "t":
194+			entry.BuildTime = ptr(time.Unix(int64(toInt(c)), 0))
195+		case "c":
196+			entry.Commit = &c
197+		case "k":
198+			entry.ProviderPriority = ptr(toInt(c))
199+		case "D":
200+			entry.Dependencies = strings.Split(c, " ")
201+		case "p":
202+			entry.Dependencies = strings.Split(c, " ")
203+		case "i":
204+			entry.Dependencies = strings.Split(c, " ")
205+		}
206+	}
207+	return entry
208+}