Skip to content

Commit

Permalink
Extract and improve command-line argument parsing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
presbrey committed Jan 31, 2025
1 parent cd2d786 commit af57d89
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 17 deletions.
47 changes: 30 additions & 17 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,25 +512,38 @@ func (lm *LightyMux) Run(ctx context.Context, configFile string) error {
return nil
}

func main() {
var opts = new(Options)
// parseConfig parses command line flags and environment variables into Options
func parseArgs(args []string) (*Options, string, error) {
opts := new(Options)

if err := env.Parse(&opts); err != nil {
fmt.Printf("Error parsing env vars: %v\n", err)
os.Exit(1)
if err := env.Parse(opts); err != nil {
return nil, "", fmt.Errorf("error parsing env vars: %w", err)
}

// Create a new FlagSet for testing purposes
fs := flag.NewFlagSet("lightymux", flag.ContinueOnError)
fs.StringVar(&opts.HTTPAddr, "http", opts.HTTPAddr, "HTTP listen address (e.g., :8080)")
fs.BoolVar(&opts.Verbose, "verbose", opts.Verbose, "Enable verbose logging")
fs.BoolVar(&opts.LogRequests, "log-requests", opts.LogRequests, "Log incoming requests")
fs.BoolVar(&opts.LogResponses, "log-responses", opts.LogResponses, "Log outgoing responses")
fs.BoolVar(&opts.LogErrors, "log-errors", opts.LogErrors, "Log proxy errors")
fs.StringVar(&opts.LogFile, "log-file", opts.LogFile, "Log to file instead of stderr")

if err := fs.Parse(args); err != nil {
return nil, "", err
}

if fs.NArg() != 1 {
return nil, "", fmt.Errorf("exactly one config file argument is required")
}

// Allow command line flags to override env vars
flag.StringVar(&opts.HTTPAddr, "http", opts.HTTPAddr, "HTTP listen address (e.g., :8080)")
flag.BoolVar(&opts.Verbose, "verbose", opts.Verbose, "Enable verbose logging")
flag.BoolVar(&opts.LogRequests, "log-requests", opts.LogRequests, "Log incoming requests")
flag.BoolVar(&opts.LogResponses, "log-responses", opts.LogResponses, "Log outgoing responses")
flag.BoolVar(&opts.LogErrors, "log-errors", opts.LogErrors, "Log proxy errors")
flag.StringVar(&opts.LogFile, "log-file", opts.LogFile, "Log to file instead of stderr")
flag.Parse()

if flag.NArg() != 1 {
fmt.Println("Usage: ./lightymux [flags] <config_file>")
return opts, fs.Arg(0), nil
}

func main() {
opts, configFile, err := parseArgs(os.Args[1:])
if err != nil {
fmt.Printf("Error parsing configuration: %v\n", err)
flag.PrintDefaults()
os.Exit(1)
}
Expand All @@ -541,7 +554,7 @@ func main() {
os.Exit(1)
}

if err := lm.Run(context.Background(), flag.Arg(0)); err != nil {
if err := lm.Run(context.Background(), configFile); err != nil {
fmt.Printf("Error running LightyMux: %v\n", err)
os.Exit(1)
}
Expand Down
91 changes: 91 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -995,3 +995,94 @@ routes:
})
}
}

func TestParseArgs(t *testing.T) {
// Save original environment
origEnv := os.Environ()
defer func() {
os.Clearenv()
for _, e := range origEnv {
parts := strings.SplitN(e, "=", 2)
os.Setenv(parts[0], parts[1])
}
}()

tests := []struct {
name string
args []string
env map[string]string
wantOpts *Options
wantConfig string
wantErrText string
}{
{
name: "valid args and config",
args: []string{"-http=:8080", "-verbose", "config.yaml"},
env: map[string]string{},
wantOpts: &Options{
HTTPAddr: ":8080",
Verbose: true,
LogRequests: false,
},
wantConfig: "config.yaml",
},
{
name: "environment variables",
args: []string{"config.yaml"},
env: map[string]string{
"HTTP_ADDR": ":9090",
"LOG_REQUESTS": "true",
"LOG_RESPONSES": "true",
},
wantOpts: &Options{
HTTPAddr: ":9090",
LogRequests: true,
LogResponses: true,
},
wantConfig: "config.yaml",
},
{
name: "missing config file",
args: []string{"-http=:8080"},
env: map[string]string{},
wantErrText: "exactly one config file argument is required",
},
{
name: "too many arguments",
args: []string{"config1.yaml", "config2.yaml"},
env: map[string]string{},
wantErrText: "exactly one config file argument is required",
},
{
name: "invalid flag",
args: []string{"-invalid-flag", "config.yaml"},
env: map[string]string{},
wantErrText: "flag provided but not defined: -invalid-flag",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Clear environment and set test values
os.Clearenv()
for k, v := range tt.env {
os.Setenv(k, v)
}

opts, config, err := parseArgs(tt.args)

if tt.wantErrText != "" {
assert.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErrText)
return
}

assert.NoError(t, err)
assert.Equal(t, tt.wantConfig, config)
assert.Equal(t, tt.wantOpts.HTTPAddr, opts.HTTPAddr)
assert.Equal(t, tt.wantOpts.Verbose, opts.Verbose)
assert.Equal(t, tt.wantOpts.LogRequests, opts.LogRequests)
assert.Equal(t, tt.wantOpts.LogResponses, opts.LogResponses)
})
}
}

0 comments on commit af57d89

Please sign in to comment.