Hone logo
Hone
Problems

Implement a Basic Command-Line Flag Package in Go

Many command-line applications need to accept configuration options, such as enabling a verbose mode, specifying an output file, or setting a numerical threshold. Implementing a custom flag parsing system can be complex. Your task is to build a simplified version of Go's built-in flag package to parse and manage command-line arguments representing flags.

This challenge will test your understanding of Go's string manipulation, data structures, and how to process command-line arguments effectively.

Problem Description

You need to create a Go package that allows users to define and parse command-line flags. The package should support defining flags of different types (string, integer, boolean) and provide a way to retrieve their values after parsing the command-line arguments.

Key Requirements:

  1. Flag Definition:
    • Users should be able to define flags with a name, a default value, and a usage string.
    • Support for three basic data types: string, int, and bool.
  2. Flag Parsing:
    • A function should parse os.Args (excluding the program name) to find and set flag values.
    • Flags should be identified by a prefix (e.g., --flag-name or -f).
    • Flag values should be parsed according to their defined type.
    • Boolean flags should support presence (e.g., --verbose implies true) and explicit setting (e.g., --verbose=false).
  3. Value Retrieval:
    • After parsing, users should be able to retrieve the value of a defined flag.
  4. Error Handling:
    • Handle cases like missing flag values, incorrect type conversions, or undefined flags.
    • Provide informative error messages.
  5. Usage Information:
    • A function to generate and print a help message listing all defined flags, their defaults, and usage strings.

Expected Behavior:

  • When the program runs, it should parse os.Args.
  • If a flag is provided on the command line, its value should override the default.
  • If a flag is not provided, its default value should be used.
  • If an error occurs during parsing (e.g., invalid value), the program should report the error and potentially exit.
  • A dedicated command (e.g., --help or -h) should print the usage message.

Edge Cases to Consider:

  • Flags with no values provided (e.g., --output).
  • Boolean flags explicitly set to false (e.g., --verbose=false).
  • Arguments that are not flags (should ideally be ignored or handled as positional arguments, though for this challenge, ignoring them is acceptable if they don't conflict with flag parsing).
  • Duplicate flag definitions.
  • Flags with names that might conflict with Go keywords or built-in functions.

Examples

Example 1:

// Assume a program `my_app` is compiled from your flag package.
// Command: go run my_app.go --name="Alice" --age=30 --verbose
//
// User code might look like this:
// var name string
// var age int
// var verbose bool
//
// func init() {
//     flag.StringVar(&name, "name", "Guest", "Your name")
//     flag.IntVar(&age, "age", 18, "Your age")
//     flag.BoolVar(&verbose, "verbose", false, "Enable verbose output")
// }
//
// func main() {
//     flag.Parse()
//     fmt.Printf("Name: %s, Age: %d, Verbose: %t\n", name, age, verbose)
// }

Output:
Name: Alice, Age: 30, Verbose: true
Explanation: The `name` flag was set to "Alice", `age` to 30, and `verbose` to true.

Example 2:

// Command: go run my_app.go --age=25 --name=""
//
// (Same user code as Example 1)

Output:
Name: , Age: 25, Verbose: false
Explanation: `name` was set to an empty string, `age` to 25, and `verbose` remained its default `false` because it was not provided.

Example 3: (Error Case)

// Command: go run my_app.go --age=twenty
//
// (Same user code as Example 1)

Output:
Error: invalid argument "twenty" for flag -age: strconv.Atoi: parsing "twenty": invalid syntax
Usage:
  -age int
        Your age (default 18)
  -name string
        Your name (default "Guest")
  -verbose
        Enable verbose output (default false)
Explanation: The `age` flag received a non-integer value. The program reports the error and prints the usage message.

Example 4: (Help Flag)

// Command: go run my_app.go --help
//
// (Same user code as Example 1)

Output:
Usage:
  -age int
        Your age (default 18)
  -name string
        Your name (default "Guest")
  -verbose
        Enable verbose output (default false)
Explanation: The `--help` flag triggers the display of the usage information for all defined flags.

Constraints

  • Flag Naming: Flag names must start with a hyphen (-). They can be single characters (short flags) or multiple characters (long flags), e.g., -v, --verbose.
  • Value Assignment: Values are assigned using an equals sign (=) or by taking the next argument. For example, --name="Alice" or --name Alice. Boolean flags can be set with --flag=true, --flag=false, or simply by their presence (e.g., --verbose implies true).
  • Data Types: Support string, int, and bool.
  • Argument Handling: The parser should stop processing flags once it encounters a non-flag argument (or -- to signify the end of flags), similar to how many shell utilities behave.
  • Performance: While not a strict performance bottleneck, avoid excessively inefficient string operations or data structures.
  • Package Structure: Your solution should be a Go package (e.g., flag or myflag).
  • Standard Library Usage: You may use standard library packages like os, fmt, strings, strconv, and regexp (if you choose to use regex for parsing).

Notes

  • Consider how to associate flag names with their corresponding variable pointers.
  • A map could be useful for storing flag definitions.
  • Think about how to differentiate between short (-f) and long (--flag) flags.
  • The init() function in Go is a good place to define flags before main() is executed.
  • For boolean flags, you'll need to handle cases where the flag is present without an explicit value (implying true) and cases where it's present with false.
  • When printing usage, ensure the default values are clearly indicated.
Loading editor...
go