float.go raw
1 package float
2
3 import (
4 "encoding/json"
5 "fmt"
6 "strconv"
7 "strings"
8
9 "github.com/p9c/p9/pkg/opts/meta"
10 "github.com/p9c/p9/pkg/opts/opt"
11 "github.com/p9c/p9/pkg/opts/sanitizers"
12
13 uberatomic "go.uber.org/atomic"
14 )
15
16 // Opt stores an float64 configuration value
17 type Opt struct {
18 meta.Data
19 hook []Hook
20 Min, Max float64
21 clamp func(input float64) (result float64)
22 Value *uberatomic.Float64
23 Def float64
24 }
25
26 type Hook func(f float64) error
27
28 // New returns a new Opt value set to a default value
29 func New(m meta.Data, def float64, min, max float64, hook ...Hook) *Opt {
30 return &Opt{
31 Value: uberatomic.NewFloat64(def),
32 Data: m,
33 Def: def,
34 Min: min,
35 Max: max,
36 hook: hook,
37 clamp: sanitizers.ClampFloat(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("floating point 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 float64
68 if v, e = strconv.ParseFloat(input, 64); E.Chk(e) {
69 return
70 }
71 if e = x.Set(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 value stored
97 func (x *Opt) V() float64 {
98 return x.Value.Load()
99 }
100
101 func (x *Opt) runHooks(f float64) (e error) {
102 for i := range x.hook {
103 if e = x.hook[i](f); E.Chk(e) {
104 break
105 }
106 }
107 return
108 }
109
110 // Set the value stored
111 func (x *Opt) Set(f float64) (e error) {
112 f = x.clamp(f)
113 if e = x.runHooks(f); !E.Chk(e) {
114 x.Value.Store(f)
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: %0.8f", 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(v)
135 return
136 }
137