diff --git a/lib/args.go b/lib/args.go index f8c8a79..fd13a71 100644 --- a/lib/args.go +++ b/lib/args.go @@ -2,7 +2,6 @@ package moon import ( "fmt" - "log" "os" "reflect" "strings" @@ -55,22 +54,69 @@ func parseArgs(args []string, dest interface{}) (map[string]interface{}, error) req, ok := longs[key] if !ok { - // ignore unknown options silently? - log.Printf("no such long opt: %s", key) - continue + return nil, fmt.Errorf("unrecognized long opt: %s", key) } if req.t.Kind() == reflect.Bool { - out[key] = true + out[req.name] = true continue } + // this is horrible d, err := ReadString(fmt.Sprintf("%s: %s", key, val)) // :( if err != nil { return nil, fmt.Errorf("unable to parse cli argument %s: %s", key, err) } - out[key] = d.items[key] + out[req.name] = d.items[key] } else if strings.HasPrefix(arg, "-") { - panic("i'm not doing short args yet") + arg = strings.TrimPrefix(arg, "-") + if strings.ContainsRune(arg, '=') { + runes := []rune(arg) + if len(runes) == 1 { // -= + // no clue what to do here + return nil, fmt.Errorf("unable to parse cli arguments: weird -=?") + } + if runes[1] != '=' { + return nil, fmt.Errorf("you may only use one short flag with an equals sign") + } + req, ok := shorts[string(runes[0])] + if !ok { + return nil, fmt.Errorf("unrecognized short opt: %c", runes[0]) + } + d, err := ReadString(fmt.Sprintf("key: %s", runes[2:])) + if err != nil { + return nil, fmt.Errorf("unable to parse cli argument %c: %s", runes[0], err) + } + out[req.name] = d.items["key"] + } else { + runes := []rune(arg) + for j := 0; j < len(runes); j++ { + r := runes[j] + req, ok := shorts[string(r)] + if !ok { + return nil, fmt.Errorf("unrecognized short opt: %c", r) + } + if req.t.Kind() == reflect.Bool { + out[req.name] = true + continue + } + if j != len(runes)-1 { + // what a totally fucking preposterous error message + return nil, fmt.Errorf("illegal short opt: %c: a "+ + "non-boolean short flag may only appear as the"+ + " terminal option in a run of short opts", r) + } + i++ + if i >= len(args) { + return nil, fmt.Errorf("arg %s is missing a value", req.name) + } + val := args[i] + d, err := ReadString(fmt.Sprintf("key: %s", val)) + if err != nil { + return nil, fmt.Errorf("error parsing cli arg %s: %s", req.name, err) + } + out[req.name] = d.items["key"] + } + } } else { break } diff --git a/lib/args_test.go b/lib/args_test.go index 35658a4..c5cd614 100644 --- a/lib/args_test.go +++ b/lib/args_test.go @@ -8,10 +8,13 @@ func TestArgs(t *testing.T) { var one struct { Host string `name: host; short: h; default: localhost` Port int `name: port; short: p; required: true` + User string `name: user; short: u` + ZipIt bool `name: zip; short: z` + Verbose bool `name: verbose; short: v` UseSSL bool `name: ssl_enabled; long: ssl-enabled` CertPath string `name: ssl_cert; long: ssl-cert` } - args := []string{"program", "--host=example.com", "--port", "9000"} + args := []string{"program", "--host=example.com", "--port", "9000", "-u", "fart", "-zv"} vals, err := parseArgs(args, &one) if err != nil { t.Error(err) @@ -25,4 +28,16 @@ func TestArgs(t *testing.T) { if vals["port"] != 9000 { t.Errorf("expected port 9000, saw port %d", vals["port"]) } + + if vals["user"] != "fart" { + t.Errorf("expected user 'fart', saw user '%s'", vals["user"]) + } + + if vals["zip"] != true { + t.Errorf("expected zip to be true, is false") + } + + if vals["verbose"] != true { + t.Errorf("expected verbose to be true, is false") + } } diff --git a/lib/req.go b/lib/req.go index b3463b7..ea3f015 100644 --- a/lib/req.go +++ b/lib/req.go @@ -60,8 +60,9 @@ func field2req(field reflect.StructField) (*req, error) { t: field.Type, long: field.Name, } - // it's really easy to cause infinite recursion here, since this is used by - // some of the higher up functions, so we're going to hack the document directly + + // this is called by Fill, so we have to do Fill's work by hand, otherwise + // they would be mutually recursive. errors := map[string]error{ "name": doc.Get("name", &req.name),