aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile13
-rw-r--r--go.mod3
-rw-r--r--main.go75
-rw-r--r--parser.go85
5 files changed, 178 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f331ccd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+bin/
+vendor/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e3ed19e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+BIN?=apkdoc
+GO_RUN?= go run -v
+GO_BUILD?= go build -v
+
+all: build
+
+run:
+ $(GO_RUN) .
+
+build:
+ $(GO_BUILD) \
+ -o bin/$(BIN) \
+ .
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ccd8d07
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module git.sr.ht/~gabrielgio/apkdoc
+
+go 1.20
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..9e64dbb
--- /dev/null
+++ b/main.go
@@ -0,0 +1,75 @@
+package main
+
+import (
+ "archive/tar"
+ "bufio"
+ "compress/gzip"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "net/http"
+)
+
+func fechIndex(url string) (io.ReadCloser, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+
+ if resp.StatusCode != 200 {
+ return nil, errors.New("Invlid response")
+ }
+
+ return resp.Body, nil
+}
+
+func main() {
+ url := flag.String("url", "", "Url to the APKINDEX.tar.gz")
+ flag.Parse()
+
+ tarStream, err := fechIndex(*url)
+ if err != nil {
+ panic("Error fecthing the index: " + err.Error())
+ }
+
+ defer tarStream.Close()
+
+ archive, err := gzip.NewReader(tarStream)
+ if err != nil {
+ panic("Error creating gzip reader: " + err.Error())
+ }
+
+ tr := tar.NewReader(archive)
+
+ for {
+ h, err := tr.Next()
+ if err != nil {
+ panic("Error reading next tar entry: " + err.Error())
+ }
+
+ if h.FileInfo().Name() == "APKINDEX" {
+ break
+ }
+ }
+
+ s := bufio.NewScanner(tr)
+
+ entries := make([]*Entry, 0)
+ lines := make([]string, 0)
+
+ for s.Scan() {
+ l := s.Text()
+ if l == "" {
+ entry := Parse(lines)
+ entries = append(entries, entry)
+ lines = make([]string, 0)
+ } else {
+ lines = append(lines, l)
+ }
+ }
+
+ for _, e := range entries {
+ fmt.Printf("%+v\n", e)
+ }
+}
diff --git a/parser.go b/parser.go
new file mode 100644
index 0000000..998df91
--- /dev/null
+++ b/parser.go
@@ -0,0 +1,85 @@
+package main
+
+import (
+ "strconv"
+ "strings"
+ "time"
+)
+
+type (
+ // https://wiki.alpinelinux.org/wiki/Apk_spec
+ Entry struct {
+ Checksum string // C
+ Name string // P
+ Architecture *string // A
+ PackageSize int // S
+ InstalledSize *int // I
+ Description string // T
+ Url string // U
+ License string // L
+ Origin *string // o
+ Maintainer *string // m
+ BuildTime *time.Time // t
+ Commit *string // c
+ ProviderPriority *int // k
+ Dependencies []string // D
+ Provides []string // p
+ InstallIf []string // i
+ }
+)
+
+func ptr[T any](v T) *T {
+ return &v
+}
+
+func split(line string) (string, string) {
+ parts := strings.SplitN(line, ":", 2)
+ return parts[0], parts[1]
+}
+
+func toInt(v string) int {
+ i, _ := strconv.Atoi(v)
+ return i
+}
+
+func Parse(lines []string) *Entry {
+ entry := &Entry{}
+ for _, line := range lines {
+ r, c := split(line)
+ switch r {
+ case "C":
+ entry.Checksum = c
+ case "P":
+ entry.Name = c
+ case "A":
+ entry.Architecture = &c
+ case "S":
+ entry.PackageSize = toInt(c)
+ case "I":
+ entry.InstalledSize = ptr(toInt(c))
+ case "T":
+ entry.Description = c
+ case "U":
+ entry.Url = c
+ case "L":
+ entry.License = c
+ case "o":
+ entry.Origin = &c
+ case "m":
+ entry.Maintainer = &c
+ case "t":
+ entry.BuildTime = ptr(time.Unix(int64(toInt(c)), 0))
+ case "c":
+ entry.Commit = &c
+ case "k":
+ entry.ProviderPriority = ptr(toInt(c))
+ case "D":
+ entry.Dependencies = strings.Split(c, " ")
+ case "p":
+ entry.Dependencies = strings.Split(c, " ")
+ case "i":
+ entry.Dependencies = strings.Split(c, " ")
+ }
+ }
+ return entry
+}