aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd/mbf/internal/pkgsite/index.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-09-15 00:06:21 +0900
committerOphestra <cat@gensokyo.uk>2026-09-15 00:06:21 +0900
commit834db962958652502aba88362dbe91df2ee618ee (patch)
tree9c06fbcab83ad56e2d90f24a8d2cf016c6c1d005 /cmd/mbf/internal/pkgsite/index.go
parentcce7a996b06e516f5afcd41d2d67c2392000b6ab (diff)
cmd/mbf: rename pkgsite
This name is less ambiguous than pkgserver. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd/mbf/internal/pkgsite/index.go')
-rw-r--r--cmd/mbf/internal/pkgsite/index.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/cmd/mbf/internal/pkgsite/index.go b/cmd/mbf/internal/pkgsite/index.go
new file mode 100644
index 00000000..760806c0
--- /dev/null
+++ b/cmd/mbf/internal/pkgsite/index.go
@@ -0,0 +1,108 @@
+package pkgsite
+
+import (
+ "cmp"
+ "errors"
+ "slices"
+ "strings"
+
+ "hakurei.app/internal/rosa"
+ "hakurei.app/pkg"
+)
+
+const (
+ declarationAscending = iota
+ declarationDescending
+ nameAscending
+ nameDescending
+ sizeAscending
+ sizeDescending
+
+ sortOrderEnd = iota - 1
+)
+
+// packageIndex refers to metadata by name and various sort orders.
+type packageIndex struct {
+ sorts [sortOrderEnd + 1][]*metadata
+ names map[string]*metadata
+ search searchCache
+ // Taken from [rosa.Report] if available.
+ handleAccess func(*error) func()
+}
+
+// metadata holds [rosa.Metadata] extended with additional information.
+type metadata struct {
+ handle rosa.ArtifactH
+ *rosa.Metadata
+
+ // Copied from [rosa.Metadata], [rosa.Unversioned] is equivalent to the zero
+ // value. Otherwise, the zero value is invalid.
+ Version string `json:"version,omitempty"`
+ // Output data size, available if present in report.
+ Size int64 `json:"size,omitempty"`
+ // Whether the underlying [pkg.Artifact] is present in the report.
+ HasReport bool `json:"report"`
+
+ // Ident string encoded ahead of time.
+ ids string
+ // Backed by [rosa.Report], access must be prepared by HandleAccess.
+ status []byte
+}
+
+// populate deterministically populates packageIndex, optionally with a report.
+func (index *packageIndex) populate(report *rosa.Report) (err error) {
+ if report != nil {
+ defer report.HandleAccess(&err)()
+ index.handleAccess = report.HandleAccess
+ }
+
+ handles := rosa.Collect()
+ work := make([]*metadata, len(handles))
+ index.names = make(map[string]*metadata)
+ ir := pkg.NewIR()
+ for i, handle := range handles {
+ meta, a := rosa.MustLoad(handle)
+ m := metadata{
+ handle: handle,
+
+ Metadata: meta,
+ Version: meta.Version,
+ }
+ if m.Version == "" {
+ return errors.New("invalid version from " + m.Name)
+ }
+ if m.Version == rosa.Unversioned {
+ m.Version = ""
+ }
+
+ if report != nil {
+ id := ir.Ident(a)
+ m.ids = pkg.Encode(id.Value())
+ m.status, m.Size = report.ArtifactOf(id)
+ m.HasReport = m.Size >= 0
+ }
+
+ work[i] = &m
+ index.names[m.Name] = &m
+ }
+
+ index.sorts[declarationAscending] = work
+ index.sorts[declarationDescending] = slices.Clone(work)
+ slices.Reverse(index.sorts[declarationDescending][:])
+
+ index.sorts[nameAscending] = slices.Clone(work)
+ slices.SortFunc(index.sorts[nameAscending][:], func(a, b *metadata) int {
+ return strings.Compare(a.Name, b.Name)
+ })
+ index.sorts[nameDescending] = slices.Clone(index.sorts[nameAscending])
+ slices.Reverse(index.sorts[nameDescending][:])
+
+ index.sorts[sizeAscending] = slices.Clone(work)
+ slices.SortFunc(index.sorts[sizeAscending][:], func(a, b *metadata) int {
+ return cmp.Compare(a.Size, b.Size)
+ })
+ index.sorts[sizeDescending] = slices.Clone(index.sorts[sizeAscending])
+ slices.Reverse(index.sorts[sizeDescending][:])
+
+ return
+}