summaryrefslogtreecommitdiff
path: root/internal/cli/cli.go
diff options
context:
space:
mode:
authorJames O'Doherty <james@theodohertyfamily.com>2026-05-22 09:18:55 -0400
committerJames O'Doherty <james@theodohertyfamily.com>2026-05-22 09:18:55 -0400
commit96d75d9f1fab87365d7e6b5070eed3a5757c3484 (patch)
treee01144dbb5338826d36f1b07444ebd78407c3bf4 /internal/cli/cli.go
parent756ba94292b408cc4f23d137b2c4c52009b2b38d (diff)
Refactor CLI for testability and implement hermetic config path injection
Diffstat (limited to 'internal/cli/cli.go')
-rw-r--r--internal/cli/cli.go114
1 files changed, 114 insertions, 0 deletions
diff --git a/internal/cli/cli.go b/internal/cli/cli.go
new file mode 100644
index 0000000..cb95202
--- /dev/null
+++ b/internal/cli/cli.go
@@ -0,0 +1,114 @@
+package cli
+
+import (
+ "flag"
+ "fmt"
+
+ "git.theodohertyfamily.com/tools/wg-wrap/internal/config"
+)
+
+type App struct {
+ Args []string
+ ConfigDir string // Optional override for profile storage location
+}
+
+func NewApp(args []string) *App {
+ return &App{Args: args}
+}
+
+func (a *App) Run() error {
+ // Handle subcommands first (profile list, import, configure, delete, stop)
+ if len(a.Args) > 1 && a.Args[1] == "profile" {
+ return a.handleProfileCmd()
+ }
+
+ cfg := &config.Config{}
+
+ fs := flag.NewFlagSet("wg-wrap", flag.ExitOnError)
+ fs.StringVar(&cfg.Profile, "profile", "", "WireGuard profile to use (filename without extension in ~/.config/wg-wrap/profiles/)")
+ fs.StringVar(&cfg.DNSServer, "dns-server", "", "Override DNS server to use")
+
+ args := a.Args[1:]
+ sepIdx := -1
+ for i, arg := range args {
+ if arg == "--" {
+ sepIdx = i
+ break
+ }
+ }
+
+ var flagsToParse []string
+ if sepIdx != -1 {
+ flagsToParse = args[:sepIdx]
+ cfg.Command = args[sepIdx+1:]
+ } else {
+ flagsToParse = args
+ }
+
+ err := fs.Parse(flagsToParse)
+ if err != nil {
+ return fmt.Errorf("error parsing flags: %w", err)
+ }
+
+ if sepIdx == -1 {
+ cfg.Command = fs.Args()
+ }
+
+ if len(cfg.Command) == 0 {
+ return fmt.Errorf("no command provided. use --help for usage")
+ }
+
+ if cfg.Profile == "" {
+ cfg.Profile = "default"
+ }
+
+ profilesDir := a.ConfigDir
+ if profilesDir == "" {
+ profilesDir = config.GetDefaultProfilesDir()
+ }
+
+ fmt.Printf("Profile: %s\n", cfg.Profile)
+ fmt.Printf("Profiles Directory: %s\n", profilesDir)
+ fmt.Printf("DNS Server: %s\n", cfg.DNSServer)
+ fmt.Printf("Command: %v\n", cfg.Command)
+ return nil
+}
+
+func (a *App) handleProfileCmd() error {
+ if len(a.Args) < 3 {
+ return fmt.Errorf("usage: wg-wrap profile <list|import|configure|delete|stop> [args]")
+ }
+
+ subCmd := a.Args[2]
+ switch subCmd {
+ case "list":
+ fmt.Println("Listing profiles...")
+ return fmt.Errorf("profile list not yet implemented")
+ case "import":
+ if len(a.Args) < 4 {
+ return fmt.Errorf("usage: wg-wrap profile import <path>")
+ }
+ fmt.Printf("Importing profile from %s...\n", a.Args[3])
+ return fmt.Errorf("profile import not yet implemented")
+ case "configure":
+ if len(a.Args) < 4 {
+ return fmt.Errorf("usage: wg-wrap profile configure <name>")
+ }
+ fmt.Printf("Configuring profile %s...\n", a.Args[3])
+ return fmt.Errorf("profile configure not yet implemented")
+ case "delete":
+ if len(a.Args) < 4 {
+ return fmt.Errorf("usage: wg-wrap profile delete <name>")
+ }
+ fmt.Printf("Deleting profile %s...\n", a.Args[3])
+ return fmt.Errorf("profile delete not yet implemented")
+ case "stop":
+ if len(a.Args) < 4 {
+ return fmt.Errorf("usage: wg-wrap profile stop <name>")
+ }
+ fmt.Printf("Stopping profile %s and unpinning namespace...\n", a.Args[3])
+ return fmt.Errorf("profile stop not yet implemented")
+ default:
+ return fmt.Errorf("unknown profile subcommand: %s", subCmd)
+ }
+}