1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
package cli
import (
"fmt"
"math"
"strings"
"time"
"github.com/spf13/pflag"
)
// Per-component upper bounds. A cache-safety grace window is measured in days
// to a few years; anything past these is a typo or an attempt to overflow the
// cutoff arithmetic. They are chosen so cutoff = now.AddDate(-y,-m,-d) can never
// overflow the int year addition or the int64 range of time.Time (representable
// to ~year 292 billion), so the destructive cutoff can never silently wrap.
const (
maxDurationYears = 100000
maxDurationMonths = 100000 * 12
maxDurationDays = 100000 * 366
)
var _ pflag.Value = (*duration)(nil)
// duration is a calendar-aware grace window . It holds a tuple
// (years, months, days) rather than a time.Duration.
type duration struct {
years int
months int
days int
}
func registerDurationFlag(flags *pflag.FlagSet, v *duration, name string, def string, usage string) {
if err := v.Set(def); err != nil {
panic(fmt.Sprintf("invalid default for --%s: %v", name, err))
}
flags.Var(v, name, usage)
}
// Type implements the `pflags.Value` interface.
func (d *duration) Type() string {
return "duration"
}
// String implements the `pflags.Value` interface.
func (d *duration) String() string {
sb := &strings.Builder{}
if d.years != 0 {
fmt.Fprintf(sb, "%dy", d.years)
}
if d.months != 0 {
fmt.Fprintf(sb, "%dm", d.months)
}
if d.days != 0 {
fmt.Fprintf(sb, "%dd", d.days)
}
if sb.Len() == 0 {
return "0d"
}
return sb.String()
}
// Set parses a combinable value like "1y2m10d", "7d" or "30d". Each component is
// a non-negative integer followed by one of the units y, m (month), d, and each
// unit may appear at most once. An empty value or a value with no components is
// rejected.
//
// Set implements the `pflags.Value` interface.
func (d *duration) Set(raw string) error {
parsed := duration{}
seen := map[byte]bool{}
components := 0
for i := 0; i < len(raw); {
c := raw[i]
if c < '0' || c > '9' {
return usageError{fmt.Sprintf("invalid duration %q", raw)}
}
n := 0
for i < len(raw) && raw[i] >= '0' && raw[i] <= '9' {
digit := int(raw[i] - '0')
if n > (math.MaxInt-digit)/10 {
return usageError{fmt.Sprintf("invalid duration %q (out of range)", raw)}
}
n = n*10 + digit
i++
}
if i >= len(raw) {
return usageError{fmt.Sprintf("invalid duration %q (number %d has no unit)", raw, n)}
}
unit := raw[i]
if seen[unit] {
return usageError{fmt.Sprintf("invalid duration %q (unit %q repeated)", raw, string(unit))}
}
switch unit {
case 'y':
if n > maxDurationYears {
return usageError{fmt.Sprintf("invalid duration %q (years exceeds %d)", raw, maxDurationYears)}
}
parsed.years = n
case 'm':
if n > maxDurationMonths {
return usageError{fmt.Sprintf("invalid duration %q (months exceeds %d)", raw, maxDurationMonths)}
}
parsed.months = n
case 'd':
if n > maxDurationDays {
return usageError{fmt.Sprintf("invalid duration %q (days exceeds %d)", raw, maxDurationDays)}
}
parsed.days = n
default:
return usageError{fmt.Sprintf("invalid duration %q (unknown unit %q)", raw, string(unit))}
}
seen[unit] = true
components++
i++
}
if components == 0 {
return usageError{fmt.Sprintf("invalid duration %q", raw)}
}
*d = parsed
return nil
}
func (d duration) cutoff(tm time.Time) time.Time {
return tm.AddDate(-d.years, -d.months, -d.days)
}
func (d duration) isZero() bool {
return d.years == 0 && d.months == 0 && d.days == 0
}
|