Reasons
I'm recently learning Go with various exercises (might be more questions coming in the future)
However, I'm quite annoyed with the default argument parsing module in Go flag as it doesn't satisfy my needs most of the time.
So as a learning exercise I went ahead and created a simplified clone of python's argparse module. It isn't a 1 on 1 clone and I don't want it to be.
Module
package argparse
import (
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
type Argument struct {
ShortFlag, LongFlag, Help string
Type, Default interface{}
Required bool
}
type Argparse struct {
Programme, Description string
Arguments []Argument
}
func (p *Argparse) findArgument(arg string) *Argument {
if !strings.Contains(arg, "-") {
return nil
}
for _, a := range p.Arguments {
if a.ShortFlag == arg[1:] || a.LongFlag == arg[2:] {
return &a
}
}
return nil
}
func (p *Argparse) Parse() map[string]interface{} {
args := make(map[string]interface{})
osArgs := os.Args
for _, a := range osArgs[1:] {
if "-h" == a || "--help" == a {
p.Help()
}
}
for i := 1; i < len(osArgs); i++ {
a := p.findArgument(osArgs[i])
if a != nil {
_type := reflect.TypeOf(a.Type)
switch _type.Kind() {
case reflect.String:
if len(osArgs) == i + 1 {
fmt.Println("[ERROR] Didn't supply the argument for", a.LongFlag)
os.Exit(3)
}
args[a.LongFlag] = osArgs[i + 1]
i += 1
case reflect.Int:
if len(osArgs) == i + 1 {
fmt.Println("[ERROR] Didn't supply the argument for", a.LongFlag)
os.Exit(3)
}
retValue, err := strconv.Atoi(osArgs[i + 1])
if err != nil {
fmt.Println(osArgs[i + 1], "is not an integer")
os.Exit(3)
}
args[a.LongFlag] = retValue
i += 1
case reflect.Bool:
args[a.LongFlag] = true
default:
fmt.Println("[ERROR]: Flag", a.LongFlag, "is of type", _type.Kind(), "which is not supported")
os.Exit(3)
}
}
}
for _, arg := range p.Arguments {
if _, f := args[arg.LongFlag]; !f {
if arg.Required && arg.Default == nil {
fmt.Println("[ERROR]: Flag", arg.LongFlag, "is required and not set")
os.Exit(3)
}
args[arg.LongFlag] = arg.Default
}
}
return args
}
func (p *Argparse) AddArgument(arg Argument) {
if arg.LongFlag == "" {
fmt.Println("[ERROR]: LongFlag must be set")
os.Exit(1)
}
if arg.Type == nil {
fmt.Println("[ERROR]: Type must be set")
os.Exit(1)
}
p.Arguments = append(p.Arguments, arg)
}
func (p *Argparse) Help() {
if p.Programme == "" {
p.Programme = os.Args[0]
}
fmt.Print("Usage ", p.Programme, "\n\n")
fmt.Print(p.Description, "\n\n")
for _, a := range p.Arguments {
fmt.Print("\t", "-" + a.ShortFlag, ", ", "--" + a.LongFlag, "\t", a.Help, "\n")
}
os.Exit(0)
}
Implementation
package main
import (
"fmt"
"argparse"
)
func parseArguments() map[string]interface{} {
parser := argparse.Argparse {
Description: "Testing argparse logic",
}
parser.AddArgument(
argparse.Argument {
ShortFlag: "t", LongFlag: "text", Type: "string",
Required: true, Help: "A line of text",
},
)
parser.AddArgument(
argparse.Argument {
ShortFlag: "e", LongFlag: "enc", Type: false,
Help: "Something else", Default: false,
},
)
return parser.Parse()
}
func main() {
args := parseArguments()
for k, v := range args {
fmt.Println("Argument:", k, "=", v)
}
}
Example
$ ./test -t "-e"
Argument: text = -e
Argument: enc = false
As mentioned I'm fairly new to the language so any recommendation would be greatly appreciated!
-
\$\begingroup\$ Could this help: pkg.go.dev/github.com/akamensky/argparse? \$\endgroup\$sylvain– sylvain2021年03月03日 23:03:09 +00:00Commented Mar 3, 2021 at 23:03