DEV Community 👩‍💻👨‍💻

Clavin June
Clavin June

Posted on • Originally published at clavinjune.dev on

Ways to Define Custom Command-Line Flags in Golang

Photo by @joshuafuller on Unsplash

Introduction

If you're familiar with command-line interfaces, you might have seen flags like -h, -v, --name foo, etc. Some of the flags use primitive data types like string, int, bool, etc. The rest of the flags are custom flags that using specific patterns or data structure like IP, URL, Array, Map, etc. Golang provides a built-in library called flag to define command-line flags. Some of the provided functions to define flags are straight forward like flag.String, flag.Int, etc. But, how about the custom flags? In this article, you will learn some ways to define custom command-line flags in Golang.

Example

Let's say you want to create a custom flags that accepts a list of string values. The usage of the flag should be like this:

$ go run main.go --list a --list b,c,d
[a b c d]
Enter fullscreen mode Exit fullscreen mode

Below are some ways how to define the custom flag.

Flag Var

flag.Var is a way to define a custom flag. It accepts a custom struct that implements flag.Value interface.

type ListFlag []string

func (f *ListFlag) String() string {
    b, _ := json.Marshal(*f)
    return string(b)
}

func (f *ListFlag) Set(value string) error {
    for _, str := range strings.Split(value, ",") {
        *f = append(*f, str)
    }
    return nil
}

func main() {
    // foo and bar as a default value
    list := ListFlag([]string{"foo", "bar"})
    flag.Var(&list, "list", "your flag usage")

    flag.Parse()
    fmt.Println(list) // [foo bar a b c d]
}
Enter fullscreen mode Exit fullscreen mode

The important thing to note is the Set method. Everytime your flag is called, the Set method will be called.

Flag Func

flag.Func is a straight forward way to define a custom flag. It uses flag.Var under the hood so you don't need to create a custom struct.

func main() {
    list := ListFlag([]string{"foo", "bar"})
    flag.Func("list", "your flag usage", func(s string) error {
        for _, str := range strings.Split(s, ",") {
            list = append(list, str)
        }
        return nil
    })

    flag.Parse()
    fmt.Println(list) // [foo bar a b c d]
}
Enter fullscreen mode Exit fullscreen mode

As you see, the last parameter of flag.Func is a Set method you implemented in the previous example.

Flag TextVar

flag.TextVar is a new way introduced in go1.19. It uses flag.Var under the hood like flag.Func, but it accepts a encoding.TextUnmarshaler and encoding.TextMarshaler interface instead of flag.Value interface. It means you can use built-in struct like big.Int, netip.Addr, and time.Time as flags without needed to implement a custom struct.

type ListFlag []string

func (f *ListFlag) MarshalText() ([]byte, error) {
    return json.Marshal(*f)
}

func (f *ListFlag) UnmarshalText(b []byte) error {
    for _, str := range strings.Split(string(b), ",") {
        *f = append(*f, str)
    }
    return nil
}

func main() {
    list := ListFlag([]string{"foo", "bar"})
    flag.TextVar(&list, "list", &list, "your flag usage")

    flag.Parse()
    fmt.Println(list)
}
Enter fullscreen mode Exit fullscreen mode

As you see, it's similar to flag.Var but it uses MarshalText and UnmarshalText instead of String and Set method.

Conclusion

Now you know some ways to define custom command-line flags in Golang. If you want to learn more about flag package, you can read the official documentation here.

Thank you for reading!

Top comments (0)

🌚 Life is too short to browse without dark mode