duration.go raw
1 package duration
2
3 import (
4 "encoding/json"
5 "fmt"
6 "strings"
7 "time"
8
9 uberatomic "go.uber.org/atomic"
10
11 "github.com/p9c/p9/pkg/opts/meta"
12 "github.com/p9c/p9/pkg/opts/opt"
13 "github.com/p9c/p9/pkg/opts/sanitizers"
14 )
15
16 // Opt stores an time.Duration configuration value
17 type Opt struct {
18 meta.Data
19 hook []Hook
20 clamp func(input time.Duration) (result time.Duration)
21 Min, Max time.Duration
22 Value *uberatomic.Duration
23 Def time.Duration
24 }
25
26 type Hook func(d time.Duration) error
27
28 // New creates a new Opt with a given default value set
29 func New(m meta.Data, def time.Duration, min, max time.Duration, hook ...Hook) *Opt {
30 return &Opt{
31 Value: uberatomic.NewDuration(def),
32 Data: m,
33 Def: def,
34 Min: min,
35 Max: max,
36 hook: hook,
37 clamp: sanitizers.ClampDuration(min, max),
38 }
39 }
40
41 // SetName sets the name for the generator
42 func (x *Opt) SetName(name string) {
43 x.Data.Option = strings.ToLower(name)
44 x.Data.Name = name
45 }
46
47 // Type returns the receiver wrapped in an interface for identifying its type
48 func (x *Opt) Type() interface{} {
49 return x
50 }
51
52 // GetMetadata returns the metadata of the opt type
53 func (x *Opt) GetMetadata() *meta.Data {
54 return &x.Data
55 }
56
57 // ReadInput sets the value from a string
58 func (x *Opt) ReadInput(input string) (o opt.Option, e error) {
59 if input == "" {
60 e = fmt.Errorf("duration opt %s %v may not be empty", x.Name(), x.Data.Aliases)
61 return
62 }
63 if strings.HasPrefix(input, "=") {
64 // the following removes leading and trailing '='
65 input = strings.Join(strings.Split(input, "=")[1:], "=")
66 }
67 var v time.Duration
68 if v, e = time.ParseDuration(input); E.Chk(e) {
69 return
70 }
71 if e = x.Set(v); E.Chk(e) {
72 }
73 return
74 }
75
76 // LoadInput sets the value from a string (this is the same as the above but differs for Strings)
77 func (x *Opt) LoadInput(input string) (o opt.Option, e error) {
78 return x.ReadInput(input)
79 }
80
81 // Name returns the name of the opt
82 func (x *Opt) Name() string {
83 return x.Data.Option
84 }
85
86 // AddHooks appends callback hooks to be run when the value is changed
87 func (x *Opt) AddHooks(hook ...Hook) {
88 x.hook = append(x.hook, hook...)
89 }
90
91 // SetHooks sets a new slice of hooks
92 func (x *Opt) SetHooks(hook ...Hook) {
93 x.hook = hook
94 }
95
96 // V returns the value stored
97 func (x *Opt) V() time.Duration {
98 return x.Value.Load()
99 }
100
101 func (x *Opt) runHooks(d time.Duration) (e error) {
102 for i := range x.hook {
103 if e = x.hook[i](d); E.Chk(e) {
104 break
105 }
106 }
107 return
108 }
109
110 // Set the value stored
111 func (x *Opt) Set(d time.Duration) (e error) {
112 d = x.clamp(d)
113 if e = x.runHooks(d); !E.Chk(e) {
114 x.Value.Store(d)
115 }
116 return
117 }
118
119 // String returns a string representation of the value
120 func (x *Opt) String() string {
121 return fmt.Sprintf("%s: %v", x.Data.Option, x.V())
122 }
123
124 // MarshalJSON returns the json representation
125 func (x *Opt) MarshalJSON() (b []byte, e error) {
126 v := x.Value.Load()
127 return json.Marshal(&v)
128 }
129
130 // UnmarshalJSON decodes a JSON representation
131 func (x *Opt) UnmarshalJSON(data []byte) (e error) {
132 v := x.Value.Load()
133 e = json.Unmarshal(data, &v)
134 e = x.Set(v)
135 return
136 }
137