feat(cli): add Go module, subcommand dispatch, frontend registry

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <[email protected]>
This commit is contained in:
2026-08-19 16:27:51 -04:00
co-authored by Sisyphus
commit 57253f0523
6 changed files with 227 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
// Package binary converts Arch Linux binary packages (.pkg.tar.zst) into
// Zeta packages.
package binary
import (
"errors"
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry"
)
// frontend implements registry.Frontend for Arch binary packages.
type frontend struct{}
func (frontend) Name() string { return "arch-binary" }
func (frontend) Convert(input string, opts registry.Options) error {
// Implemented in a later todo (.pkg.tar.zst -> tarball + package.lua).
return errors.New("not implemented")
}
func init() {
registry.Register(frontend{})
}
+23
View File
@@ -0,0 +1,23 @@
// Package src converts Arch Linux source packages (PKGBUILD/.SRCINFO) into
// Zeta recipes.
package src
import (
"errors"
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry"
)
// frontend implements registry.Frontend for Arch source packages.
type frontend struct{}
func (frontend) Name() string { return "arch-src" }
func (frontend) Convert(input string, opts registry.Options) error {
// Implemented in a later todo (PKGBUILD/.SRCINFO -> .recipe + build.sh).
return errors.New("not implemented")
}
func init() {
registry.Register(frontend{})
}
+67
View File
@@ -0,0 +1,67 @@
// Package registry provides a global registry of package-format frontends.
//
// Frontends self-register via init() from their own packages, so a future
// format (e.g. Gentoo ebuild, void-src) only needs a new package that imports
// this one — nothing else changes.
package registry
import (
"fmt"
"sort"
"sync"
)
// Options carries per-conversion settings shared by all frontends.
type Options struct {
Output string // directory to write converted output into
Repo string // repository base URL (binary frontend)
Arch string // target architecture (source frontend)
}
// Frontend converts one input of a specific package format to Zeta.
type Frontend interface {
// Name returns the frontend's CLI subcommand name.
Name() string
// Convert converts input into Zeta format under opts.Output.
Convert(input string, opts Options) error
}
var (
mu sync.RWMutex
frontends = make(map[string]Frontend)
)
// Register adds f to the global registry. It panics if a frontend with the
// same Name is already registered.
func Register(f Frontend) {
mu.Lock()
defer mu.Unlock()
name := f.Name()
if name == "" {
panic("registry: frontend with empty name")
}
if _, dup := frontends[name]; dup {
panic(fmt.Sprintf("registry: duplicate frontend %q", name))
}
frontends[name] = f
}
// List returns the names of all registered frontends, sorted.
func List() []string {
mu.RLock()
defer mu.RUnlock()
names := make([]string, 0, len(frontends))
for name := range frontends {
names = append(names, name)
}
sort.Strings(names)
return names
}
// Get returns the frontend registered under name.
func Get(name string) (Frontend, bool) {
mu.RLock()
defer mu.RUnlock()
f, ok := frontends[name]
return f, ok
}