From b78e8b1320f15add1cb9106f09544b5a04f9906d Mon Sep 17 00:00:00 2001 From: Bel LaPointe Date: Sun, 24 Mar 2019 10:41:07 -0600 Subject: [PATCH] add float and time --- arg.go | 49 +++++++++++++++++++++++++++++++ arg_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ argset.go | 13 +++++++- argset_test.go | 21 +++++++++++++ type.go | 6 ++++ type_test.go | 6 ++++ 6 files changed, 174 insertions(+), 1 deletion(-) diff --git a/arg.go b/arg.go index 1759685..3c28780 100644 --- a/arg.go +++ b/arg.go @@ -106,6 +106,55 @@ func (a *Arg) Set(value interface{}) error { a.Value = i err = nil } + case TIME: + if i, ok := value.(time.Duration); ok { + a.Value = time.Now().Add(i) + err = nil + } else if j, ok := value.(time.Time); ok { + a.Value = j + err = nil + } else if s, ok := value.(string); !ok { + } else if i, err = time.ParseDuration(s); err == nil { + a.Value = time.Now().Add(i) + err = nil + } else { + layouts := []string{ + "01-02-2006", + "01/02/2006", + "01-02-06", + "01/02/06", + "2006-01-02", + "2006/01/02", + "06-01-02", + "06/01/02", + "2006-01-02 15:04:05.999999999 -0700 MST", + } + for k := range layouts { + if j, err = time.Parse(layouts[k], s); err == nil { + a.Value = j + err = nil + break + } + } + } + case FLOAT: + if i, ok := value.(float32); ok { + a.Value = i + err = nil + } else if j, ok := value.(float64); ok { + a.Value = float32(j) + err = nil + } else if k, ok := value.(int); ok { + a.Value = float32(k) + err = nil + } else if s, ok := value.(string); !ok { + } else if j, err = strconv.ParseFloat(s, 32); err == nil { + a.Value = float32(j) + err = nil + } else if k, err = strconv.Atoi(s); err == nil { + a.Value = float32(k) + err = nil + } } return err } diff --git a/arg_test.go b/arg_test.go index 2c43ac5..bc9df81 100644 --- a/arg_test.go +++ b/arg_test.go @@ -15,6 +15,46 @@ func TestGets(t *testing.T) { v interface{} ok bool }{ + { + t: FLOAT, + v: "a", + ok: false, + }, + { + t: FLOAT, + v: "1.0", + ok: true, + }, + { + t: FLOAT, + v: 1.0, + ok: true, + }, + { + t: FLOAT, + v: 1, + ok: true, + }, + { + t: FLOAT, + v: "1", + ok: true, + }, + { + t: TIME, + v: "2019", + ok: false, + }, + { + t: TIME, + v: "2019-03-22", + ok: true, + }, + { + t: TIME, + v: "-24h", + ok: true, + }, { t: STRING, v: 5, @@ -86,6 +126,46 @@ func TestSets(t *testing.T) { v interface{} ok bool }{ + { + t: FLOAT, + v: "a", + ok: false, + }, + { + t: FLOAT, + v: "1.0", + ok: true, + }, + { + t: FLOAT, + v: 1.0, + ok: true, + }, + { + t: FLOAT, + v: 1, + ok: true, + }, + { + t: FLOAT, + v: "1", + ok: true, + }, + { + t: TIME, + v: "2019", + ok: false, + }, + { + t: TIME, + v: "2019-03-22", + ok: true, + }, + { + t: TIME, + v: "-24h", + ok: true, + }, { t: STRING, v: 5, diff --git a/argset.go b/argset.go index 18febe5..4036283 100644 --- a/argset.go +++ b/argset.go @@ -77,6 +77,7 @@ func (as *ArgSet) setValueFromEnv() error { } func (as *ArgSet) setValueFromFlags() error { + var err error fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) for i := range as.args { arg := as.args[i] @@ -89,6 +90,10 @@ func (as *ArgSet) setValueFromFlags() error { arg.Default = fs.Bool(arg.Flag, arg.Value.(bool), arg.Help) case DURATION: arg.Default = fs.Duration(arg.Flag, arg.Value.(time.Duration), arg.Help) + case TIME: + arg.Default = fs.String(arg.Flag, arg.Value.(time.Time).String(), arg.Help) + case FLOAT: + arg.Default = fs.Float64(arg.Flag, float64(arg.Value.(float32)), arg.Help) default: return errors.New("unknown type, cannot set from flag") } @@ -107,7 +112,13 @@ func (as *ArgSet) setValueFromFlags() error { arg.Value = *arg.Default.(*bool) case DURATION: arg.Value = *arg.Default.(*time.Duration) + case TIME: + arg.Value = *arg.Default.(*string) + err = arg.Set(arg.Value) + case FLOAT: + arg.Value = *arg.Default.(*float64) + err = arg.Set(arg.Value) } } - return nil + return err } diff --git a/argset_test.go b/argset_test.go index a7005eb..64af2fb 100644 --- a/argset_test.go +++ b/argset_test.go @@ -3,6 +3,7 @@ package args import ( "os" "testing" + "time" ) func TestNewArgSet(t *testing.T) { @@ -46,6 +47,26 @@ func TestParse(t *testing.T) { } } +func TestParseFloatTime(t *testing.T) { + osArgs := append([]string{}, os.Args[:]...) + defer func() { + os.Args = osArgs + }() + os.Args = []string{"nothing", "-flag", "5.5"} + os.Setenv("ENV", "2019-01-01") + as := NewArgSet() + as.Append(FLOAT, "flag", "help", 1.1) + as.Append(TIME, "env", "help", "2019-03-22") + wantTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) + if err := as.Parse(); err != nil { + t.Fatalf("cannot parse legal: %v", err) + } else if arg := as.Get("flag"); arg == nil || arg.Value != float32(5.5) { + t.Errorf("cannot set from flag: got %v (%v / %T != %v / %T)", arg, arg.Value, arg.Value, 5.5, 5.5) + } else if arg := as.Get("env"); arg == nil || arg.Value != wantTime { + t.Errorf("cannot set from env: got %v (%v / %T != %v / %T)", arg, arg.Value, arg.Value, wantTime, wantTime) + } +} + func TestGet(t *testing.T) { as := NewArgSet() as.Append(INT, "testkey1", "help", 1) diff --git a/type.go b/type.go index acd1588..d019eab 100644 --- a/type.go +++ b/type.go @@ -12,6 +12,8 @@ const ( STRING = Type(iota) BOOL = Type(iota) DURATION = Type(iota) + TIME = Type(iota) + FLOAT = Type(iota) ) func (t Type) String() string { @@ -25,6 +27,10 @@ func (t Type) String() string { i = false case DURATION: i = time.Duration(0) + case TIME: + i = time.Time{} + case FLOAT: + i = float32(0) } return fmt.Sprintf("%T", i) } diff --git a/type_test.go b/type_test.go index f52e047..374e7d0 100644 --- a/type_test.go +++ b/type_test.go @@ -15,4 +15,10 @@ func TestType(t *testing.T) { if DURATION.String() != "time.Duration" { t.Errorf("wrong string for DURATION") } + if TIME.String() != "time.Time" { + t.Errorf("wrong string for TIME") + } + if FLOAT.String() != "float32" { + t.Errorf("wrong string for FLOAT") + } }