feat(cli): wire arch-binary subcommand
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <[email protected]>
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry"
|
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry"
|
||||||
|
|
||||||
@@ -39,6 +40,29 @@ func main() {
|
|||||||
os.Exit(run(os.Args[1:]))
|
os.Exit(run(os.Args[1:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normalizeArgs reorders args into [flags and their values..., positionals...]
|
||||||
|
// because stdlib flag.Parse stops at the first non-flag token, which would
|
||||||
|
// misparse flags placed after the positional input.
|
||||||
|
func normalizeArgs(args []string, valueFlags map[string]bool) []string {
|
||||||
|
var flags, positionals []string
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
tok := args[i]
|
||||||
|
if !strings.HasPrefix(tok, "-") || tok == "-" {
|
||||||
|
positionals = append(positionals, tok)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
flags = append(flags, tok)
|
||||||
|
if !strings.Contains(tok, "=") {
|
||||||
|
name := tok
|
||||||
|
if valueFlags[name] && i+1 < len(args) {
|
||||||
|
i++
|
||||||
|
flags = append(flags, args[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return append(flags, positionals...)
|
||||||
|
}
|
||||||
|
|
||||||
func run(args []string) int {
|
func run(args []string) int {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
fmt.Fprintln(os.Stderr, "error: no command specified")
|
fmt.Fprintln(os.Stderr, "error: no command specified")
|
||||||
@@ -67,21 +91,25 @@ func run(args []string) int {
|
|||||||
fs := flag.NewFlagSet(frontend.Name(), flag.ContinueOnError)
|
fs := flag.NewFlagSet(frontend.Name(), flag.ContinueOnError)
|
||||||
fs.SetOutput(os.Stderr)
|
fs.SetOutput(os.Stderr)
|
||||||
opts := registry.Options{Output: "."}
|
opts := registry.Options{Output: "."}
|
||||||
|
valueFlags := make(map[string]bool)
|
||||||
switch frontend.Name() {
|
switch frontend.Name() {
|
||||||
case "arch-binary":
|
case "arch-binary":
|
||||||
opts.Repo = defaultRepo
|
opts.Repo = defaultRepo
|
||||||
fs.StringVar(&opts.Repo, "repo", opts.Repo, "repository base URL")
|
fs.StringVar(&opts.Repo, "repo", opts.Repo, "repository base URL")
|
||||||
|
valueFlags["--repo"] = true
|
||||||
case "arch-src":
|
case "arch-src":
|
||||||
opts.Arch = "x86_64"
|
opts.Arch = "x86_64"
|
||||||
fs.StringVar(&opts.Arch, "arch", opts.Arch, "target architecture")
|
fs.StringVar(&opts.Arch, "arch", opts.Arch, "target architecture")
|
||||||
|
valueFlags["--arch"] = true
|
||||||
}
|
}
|
||||||
fs.StringVar(&opts.Output, "output", opts.Output, "output directory")
|
fs.StringVar(&opts.Output, "output", opts.Output, "output directory")
|
||||||
|
valueFlags["--output"] = true
|
||||||
fs.Usage = func() {
|
fs.Usage = func() {
|
||||||
fmt.Fprintf(os.Stderr, "usage: zeta-reconstruct %s [flags] <input>\n", frontend.Name())
|
fmt.Fprintf(os.Stderr, "usage: zeta-reconstruct %s [flags] <input>\n", frontend.Name())
|
||||||
fmt.Fprintln(os.Stderr, "Run 'zeta-reconstruct --help' for usage.")
|
fmt.Fprintln(os.Stderr, "Run 'zeta-reconstruct --help' for usage.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := fs.Parse(args[1:]); err != nil {
|
if err := fs.Parse(normalizeArgs(args[1:], valueFlags)); err != nil {
|
||||||
if err == flag.ErrHelp {
|
if err == flag.ErrHelp {
|
||||||
fs.Usage()
|
fs.Usage()
|
||||||
return 0
|
return 0
|
||||||
@@ -100,6 +128,11 @@ func run(args []string) int {
|
|||||||
fs.Usage()
|
fs.Usage()
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
if frontend.Name() == "arch-binary" && !strings.HasSuffix(rest[0], ".pkg.tar.zst") {
|
||||||
|
fmt.Fprintln(os.Stderr, "error: arch-binary input must end with .pkg.tar.zst")
|
||||||
|
fs.Usage()
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
if err := frontend.Convert(rest[0], opts); err != nil {
|
if err := frontend.Convert(rest[0], opts); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNormalizeArgs(t *testing.T) {
|
||||||
|
valueFlags := map[string]bool{
|
||||||
|
"--output": true,
|
||||||
|
"--repo": true,
|
||||||
|
"--arch": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in []string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "flags after positional",
|
||||||
|
in: []string{"input.pkg.tar.zst", "--output", "/tmp/out"},
|
||||||
|
want: []string{"--output", "/tmp/out", "input.pkg.tar.zst"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "flags before positional",
|
||||||
|
in: []string{"--output", "/tmp/out", "input.pkg.tar.zst"},
|
||||||
|
want: []string{"--output", "/tmp/out", "input.pkg.tar.zst"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "flag=value form",
|
||||||
|
in: []string{"input.pkg.tar.zst", "--output=/tmp/out"},
|
||||||
|
want: []string{"--output=/tmp/out", "input.pkg.tar.zst"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown flag stays a flag",
|
||||||
|
in: []string{"input.pkg.tar.zst", "--bogus"},
|
||||||
|
want: []string{"--bogus", "input.pkg.tar.zst"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "value-taking flag at end without value",
|
||||||
|
in: []string{"input.pkg.tar.zst", "--output"},
|
||||||
|
want: []string{"--output", "input.pkg.tar.zst"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mixed flags and positionals keep order",
|
||||||
|
in: []string{"--repo", "https://x", "a.zst", "--output", "/tmp/out", "b.zst"},
|
||||||
|
want: []string{"--repo", "https://x", "--output", "/tmp/out", "a.zst", "b.zst"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := normalizeArgs(tt.in, valueFlags)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Fatalf("normalizeArgs(%v) = %v, want %v", tt.in, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user