From 1c80e84f8a5ef502f5d8570efceac6116f7a0215 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 14 May 2018 16:32:27 +0100 Subject: [PATCH] fs: Implement Scan method for SizeSuffix and Duration --- fs/parseduration.go | 28 ++++++++++++++++++++++++---- fs/parseduration_test.go | 33 +++++++++++++++++++++++++++++++++ fs/sizesuffix.go | 9 +++++++++ fs/sizesuffix_test.go | 9 +++++++++ 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/fs/parseduration.go b/fs/parseduration.go index 4df6fb6d9..4da162c87 100644 --- a/fs/parseduration.go +++ b/fs/parseduration.go @@ -1,6 +1,8 @@ package fs import ( + "fmt" + "math" "strconv" "strings" "time" @@ -17,6 +19,13 @@ func (d Duration) String() string { if d == DurationOff { return "off" } + for i := len(ageSuffixes) - 2; i >= 0; i-- { + ageSuffix := &ageSuffixes[i] + if math.Abs(float64(d)) >= float64(ageSuffix.Multiplier) { + timeUnits := float64(d) / float64(ageSuffix.Multiplier) + return strconv.FormatFloat(timeUnits, 'f', -1, 64) + ageSuffix.Suffix + } + } return time.Duration(d).String() } @@ -30,10 +39,6 @@ var ageSuffixes = []struct { Suffix string Multiplier time.Duration }{ - {Suffix: "ms", Multiplier: time.Millisecond}, - {Suffix: "s", Multiplier: time.Second}, - {Suffix: "m", Multiplier: time.Minute}, - {Suffix: "h", Multiplier: time.Hour}, {Suffix: "d", Multiplier: time.Hour * 24}, {Suffix: "w", Multiplier: time.Hour * 24 * 7}, {Suffix: "M", Multiplier: time.Hour * 24 * 30}, @@ -51,6 +56,12 @@ func ParseDuration(age string) (time.Duration, error) { return time.Duration(DurationOff), nil } + // Attempt to parse as a time.Duration first + d, err := time.ParseDuration(age) + if err == nil { + return d, nil + } + for _, ageSuffix := range ageSuffixes { if strings.HasSuffix(age, ageSuffix.Suffix) { numberString := age[:len(age)-len(ageSuffix.Suffix)] @@ -81,3 +92,12 @@ func (d *Duration) Set(s string) error { func (d Duration) Type() string { return "duration" } + +// Scan implements the fmt.Scanner interface +func (d *Duration) Scan(s fmt.ScanState, ch rune) error { + token, err := s.Token(true, nil) + if err != nil { + return err + } + return d.Set(string(token)) +} diff --git a/fs/parseduration_test.go b/fs/parseduration_test.go index 4ea6541bf..3a486549b 100644 --- a/fs/parseduration_test.go +++ b/fs/parseduration_test.go @@ -1,6 +1,7 @@ package fs import ( + "fmt" "testing" "time" @@ -23,6 +24,7 @@ func TestParseDuration(t *testing.T) { {"1ms", time.Millisecond, false}, {"1s", time.Second, false}, {"1m", time.Minute, false}, + {"1.5m", (3 * time.Minute) / 2, false}, {"1h", time.Hour, false}, {"1d", time.Hour * 24, false}, {"1w", time.Hour * 24 * 7, false}, @@ -33,6 +35,7 @@ func TestParseDuration(t *testing.T) { {"1.s", time.Second, false}, {"1x", 0, true}, {"off", time.Duration(DurationOff), false}, + {"1h2m3s", time.Hour + 2*time.Minute + 3*time.Second, false}, } { duration, err := ParseDuration(test.in) if test.err { @@ -52,9 +55,39 @@ func TestDurationString(t *testing.T) { {time.Duration(0), "0s"}, {time.Second, "1s"}, {time.Minute, "1m0s"}, + {time.Millisecond, "1ms"}, + {time.Second, "1s"}, + {(3 * time.Minute) / 2, "1m30s"}, + {time.Hour, "1h0m0s"}, + {time.Hour * 24, "1d"}, + {time.Hour * 24 * 7, "1w"}, + {time.Hour * 24 * 30, "1M"}, + {time.Hour * 24 * 365, "1y"}, + {time.Hour * 24 * 365 * 3 / 2, "1.5y"}, + {-time.Second, "-1s"}, + {time.Second, "1s"}, {time.Duration(DurationOff), "off"}, + {time.Hour + 2*time.Minute + 3*time.Second, "1h2m3s"}, + {time.Hour * 24, "1d"}, + {time.Hour * 24 * 7, "1w"}, + {time.Hour * 24 * 30, "1M"}, + {time.Hour * 24 * 365, "1y"}, + {time.Hour * 24 * 365 * 3 / 2, "1.5y"}, + {-time.Hour * 24 * 365 * 3 / 2, "-1.5y"}, } { got := Duration(test.in).String() assert.Equal(t, test.want, got) + // Test the reverse + reverse, err := ParseDuration(test.want) + assert.NoError(t, err) + assert.Equal(t, test.in, reverse) } } + +func TestDurationScan(t *testing.T) { + var v Duration + n, err := fmt.Sscan(" 17m ", &v) + require.NoError(t, err) + assert.Equal(t, 1, n) + assert.Equal(t, Duration(17*60*time.Second), v) +} diff --git a/fs/sizesuffix.go b/fs/sizesuffix.go index adf2493fe..205b8d011 100644 --- a/fs/sizesuffix.go +++ b/fs/sizesuffix.go @@ -110,3 +110,12 @@ func (x *SizeSuffix) Set(s string) error { func (x *SizeSuffix) Type() string { return "int64" } + +// Scan implements the fmt.Scanner interface +func (x *SizeSuffix) Scan(s fmt.ScanState, ch rune) error { + token, err := s.Token(true, nil) + if err != nil { + return err + } + return x.Set(string(token)) +} diff --git a/fs/sizesuffix_test.go b/fs/sizesuffix_test.go index 25529f997..4e820dd17 100644 --- a/fs/sizesuffix_test.go +++ b/fs/sizesuffix_test.go @@ -1,6 +1,7 @@ package fs import ( + "fmt" "testing" "github.com/spf13/pflag" @@ -93,3 +94,11 @@ func TestSizeSuffixSet(t *testing.T) { assert.Equal(t, test.want, int64(ss)) } } + +func TestSizeSuffixScan(t *testing.T) { + var v SizeSuffix + n, err := fmt.Sscan(" 17M ", &v) + require.NoError(t, err) + assert.Equal(t, 1, n) + assert.Equal(t, SizeSuffix(17<<20), v) +}