int.go raw
1 package integer
2
3 import (
4 "encoding/json"
5 "fmt"
6 "strconv"
7 "strings"
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 int configuration value
17 type Opt struct {
18 meta.Data
19 hook []Hook
20 Min, Max int
21 clamp func(input int) (result int)
22 Value *uberatomic.Int64
23 Def int64
24 }
25
26 type Hook func(i int) error
27
28 // New creates a new Opt with a given default value
29 func New(m meta.Data, def int64, min, max int, hook ...Hook) *Opt {
30 return &Opt{
31 Value: uberatomic.NewInt64(def),
32 Data: m,
33 Def: def,
34 Min: min,
35 Max: max,
36 hook: hook,
37 clamp: sanitizers.ClampInt(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("integer number 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 int64
68 if v, e = strconv.ParseInt(input, 10, 64); E.Chk(e) {
69 return
70 }
71 if e = x.Set(int(v)); E.Chk(e) {
72 }
73 return x, e
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 stored int
97 func (x *Opt) V() int {
98 return int(x.Value.Load())
99 }
100
101 func (x *Opt) runHooks(ii int) (e error) {
102 for i := range x.hook {
103 if e = x.hook[i](ii); E.Chk(e) {
104 break
105 }
106 }
107 return
108 }
109
110 // Set the value stored
111 func (x *Opt) Set(i int) (e error) {
112 i = x.clamp(i)
113 if e = x.runHooks(i); !E.Chk(e) {
114 x.Value.Store(int64(i))
115 }
116 return
117 }
118
119 // String returns the string stored
120 func (x *Opt) String() string {
121 return fmt.Sprintf("%s: %d", x.Data.Option, x.V())
122 }
123
124 // MarshalJSON returns the json representation of
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 of
131 func (x *Opt) UnmarshalJSON(data []byte) (e error) {
132 v := x.Value.Load()
133 e = json.Unmarshal(data, &v)
134 e = x.Set(int(v))
135 return
136 }
137