Skip to content

Commit ae84df4

Browse files
authored
Merge pull request #987 from drov0/master
timestamp flag
2 parents f35a775 + 6ba4b71 commit ae84df4

File tree

3 files changed

+209
-2
lines changed

3 files changed

+209
-2
lines changed

flag_float64.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ type Float64Flag struct {
1818
Value float64
1919
DefaultText string
2020
Destination *float64
21-
HasBeenSet bool
21+
HasBeenSet bool
2222
}
2323

2424
// IsSet returns whether or not the flag has been set through env or file
25-
func (f *Float64Flag)IsSet() bool {
25+
func (f *Float64Flag) IsSet() bool {
2626
return f.HasBeenSet
2727
}
2828

flag_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,3 +1678,58 @@ func TestInt64Slice_Serialized_Set(t *testing.T) {
16781678
t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
16791679
}
16801680
}
1681+
1682+
func TestTimestamp_set(t *testing.T) {
1683+
ts := Timestamp{
1684+
timestamp: nil,
1685+
hasBeenSet: false,
1686+
layout: "Jan 2, 2006 at 3:04pm (MST)",
1687+
}
1688+
1689+
time1 := "Feb 3, 2013 at 7:54pm (PST)"
1690+
if err := ts.Set(time1); err != nil {
1691+
t.Fatalf("Failed to parse time %s with layout %s", time1, ts.layout)
1692+
}
1693+
if ts.hasBeenSet == false {
1694+
t.Fatalf("hasBeenSet is not true after setting a time")
1695+
}
1696+
1697+
ts.hasBeenSet = false
1698+
ts.SetLayout(time.RFC3339)
1699+
time2 := "2006-01-02T15:04:05Z"
1700+
if err := ts.Set(time2); err != nil {
1701+
t.Fatalf("Failed to parse time %s with layout %s", time2, ts.layout)
1702+
}
1703+
if ts.hasBeenSet == false {
1704+
t.Fatalf("hasBeenSet is not true after setting a time")
1705+
}
1706+
}
1707+
1708+
func TestTimestampFlagApply(t *testing.T) {
1709+
expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
1710+
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.RFC3339}
1711+
set := flag.NewFlagSet("test", 0)
1712+
_ = fl.Apply(set)
1713+
1714+
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
1715+
expect(t, err, nil)
1716+
expect(t, *fl.Value.timestamp, expectedResult)
1717+
}
1718+
1719+
func TestTimestampFlagApply_Fail_Parse_Wrong_Layout(t *testing.T) {
1720+
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "randomlayout"}
1721+
set := flag.NewFlagSet("test", 0)
1722+
_ = fl.Apply(set)
1723+
1724+
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
1725+
expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"randomlayout\": cannot parse \"2006-01-02T15:04:05Z\" as \"randomlayout\""))
1726+
}
1727+
1728+
func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) {
1729+
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "Jan 2, 2006 at 3:04pm (MST)"}
1730+
set := flag.NewFlagSet("test", 0)
1731+
_ = fl.Apply(set)
1732+
1733+
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
1734+
expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\""))
1735+
}

flag_timestamp.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package cli
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"time"
7+
)
8+
9+
// Timestamp wrap to satisfy golang's flag interface.
10+
type Timestamp struct {
11+
timestamp *time.Time
12+
hasBeenSet bool
13+
layout string
14+
}
15+
16+
// Timestamp constructor
17+
func NewTimestamp(timestamp time.Time) *Timestamp {
18+
return &Timestamp{timestamp: &timestamp}
19+
}
20+
21+
// Set the timestamp value directly
22+
func (t *Timestamp) SetTimestamp(value time.Time) {
23+
if !t.hasBeenSet {
24+
t.timestamp = &value
25+
t.hasBeenSet = true
26+
}
27+
}
28+
29+
// Set the timestamp string layout for future parsing
30+
func (t *Timestamp) SetLayout(layout string) {
31+
t.layout = layout
32+
}
33+
34+
// Parses the string value to timestamp
35+
func (t *Timestamp) Set(value string) error {
36+
timestamp, err := time.Parse(t.layout, value)
37+
if err != nil {
38+
return err
39+
}
40+
41+
t.timestamp = &timestamp
42+
t.hasBeenSet = true
43+
return nil
44+
}
45+
46+
// String returns a readable representation of this value (for usage defaults)
47+
func (t *Timestamp) String() string {
48+
return fmt.Sprintf("%#v", t.timestamp)
49+
}
50+
51+
// Value returns the timestamp value stored in the flag
52+
func (t *Timestamp) Value() *time.Time {
53+
return t.timestamp
54+
}
55+
56+
// Get returns the flag structure
57+
func (t *Timestamp) Get() interface{} {
58+
return *t
59+
}
60+
61+
// TimestampFlag is a flag with type time
62+
type TimestampFlag struct {
63+
Name string
64+
Aliases []string
65+
Usage string
66+
EnvVars []string
67+
FilePath string
68+
Required bool
69+
Hidden bool
70+
Layout string
71+
Value *Timestamp
72+
DefaultText string
73+
HasBeenSet bool
74+
}
75+
76+
// IsSet returns whether or not the flag has been set through env or file
77+
func (f *TimestampFlag) IsSet() bool {
78+
return f.HasBeenSet
79+
}
80+
81+
// String returns a readable representation of this value
82+
// (for usage defaults)
83+
func (f *TimestampFlag) String() string {
84+
return FlagStringer(f)
85+
}
86+
87+
// Names returns the names of the flag
88+
func (f *TimestampFlag) Names() []string {
89+
return flagNames(f)
90+
}
91+
92+
// IsRequired returns whether or not the flag is required
93+
func (f *TimestampFlag) IsRequired() bool {
94+
return f.Required
95+
}
96+
97+
// TakesValue returns true of the flag takes a value, otherwise false
98+
func (f *TimestampFlag) TakesValue() bool {
99+
return true
100+
}
101+
102+
// GetUsage returns the usage string for the flag
103+
func (f *TimestampFlag) GetUsage() string {
104+
return f.Usage
105+
}
106+
107+
// GetValue returns the flags value as string representation and an empty
108+
// string if the flag takes no value at all.
109+
func (f *TimestampFlag) GetValue() string {
110+
if f.Value != nil {
111+
return f.Value.timestamp.String()
112+
}
113+
return ""
114+
}
115+
116+
// Apply populates the flag given the flag set and environment
117+
func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
118+
if f.Layout == "" {
119+
return fmt.Errorf("timestamp Layout is required")
120+
}
121+
f.Value = &Timestamp{}
122+
f.Value.SetLayout(f.Layout)
123+
124+
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
125+
if err := f.Value.Set(val); err != nil {
126+
return fmt.Errorf("could not parse %q as timestamp value for flag %s: %s", val, f.Name, err)
127+
}
128+
f.HasBeenSet = true
129+
}
130+
131+
for _, name := range f.Names() {
132+
set.Var(f.Value, name, f.Usage)
133+
}
134+
return nil
135+
}
136+
137+
// Timestamp gets the timestamp from a flag name
138+
func (c *Context) Timestamp(name string) *time.Time {
139+
if fs := lookupFlagSet(name, c); fs != nil {
140+
return lookupTimestamp(name, fs)
141+
}
142+
return nil
143+
}
144+
145+
// Fetches the timestamp value from the local timestampWrap
146+
func lookupTimestamp(name string, set *flag.FlagSet) *time.Time {
147+
f := set.Lookup(name)
148+
if f != nil {
149+
return (f.Value.(*Timestamp)).Value()
150+
}
151+
return nil
152+
}

0 commit comments

Comments
 (0)