1 package binary
2 3 import (
4 "encoding/json"
5 "fmt"
6 "strings"
7 8 uberatomic "go.uber.org/atomic"
9 10 "github.com/p9c/p9/pkg/opts/meta"
11 "github.com/p9c/p9/pkg/opts/opt"
12 )
13 14 // Opt stores an boolean configuration value
15 type Opt struct {
16 meta.Data
17 hook []Hook
18 value *uberatomic.Bool
19 Def bool
20 }
21 22 type Hook func(b bool) error
23 24 // New creates a new Opt with default values set
25 func New(m meta.Data, def bool, hook ...Hook) *Opt {
26 return &Opt{value: uberatomic.NewBool(def), Data: m, Def: def, hook: hook}
27 }
28 29 // SetName sets the name for the generator
30 func (x *Opt) SetName(name string) {
31 x.Data.Option = strings.ToLower(name)
32 x.Data.Name = name
33 }
34 35 // Type returns the receiver wrapped in an interface for identifying its type
36 func (x *Opt) Type() interface{} {
37 return x
38 }
39 40 // GetMetadata returns the metadata of the opt type
41 func (x *Opt) GetMetadata() *meta.Data {
42 return &x.Data
43 }
44 45 // ReadInput sets the value from a string.
46 // The value can be right up against the keyword or separated by a '='.
47 func (x *Opt) ReadInput(input string) (o opt.Option, e error) {
48 // if the input is empty, the user intends the opposite of the default
49 if input == "" {
50 x.value.Store(!x.Def)
51 return
52 }
53 if strings.HasPrefix(input, "=") {
54 // the following removes leading and trailing characters
55 input = strings.Join(strings.Split(input, "=")[1:], "=")
56 }
57 input = strings.ToLower(input)
58 switch input {
59 case "t", "true", "+":
60 e = x.Set(true)
61 case "f", "false", "-":
62 e = x.Set(false)
63 default:
64 e = fmt.Errorf("input on opt %s: '%s' is not valid for a boolean flag", x.Name(), input)
65 }
66 return
67 }
68 69 // LoadInput sets the value from a string (this is the same as the above but differs for Strings)
70 func (x *Opt) LoadInput(input string) (o opt.Option, e error) {
71 return x.ReadInput(input)
72 }
73 74 // Name returns the name of the opt
75 func (x *Opt) Name() string {
76 return x.Data.Option
77 }
78 79 // AddHooks appends callback hooks to be run when the value is changed
80 func (x *Opt) AddHooks(hook ...Hook) {
81 x.hook = append(x.hook, hook...)
82 }
83 84 // SetHooks sets a new slice of hooks
85 func (x *Opt) SetHooks(hook ...Hook) {
86 x.hook = hook
87 }
88 89 // True returns whether the value is set to true (it returns the value)
90 func (x *Opt) True() bool {
91 return x.value.Load()
92 }
93 94 // False returns whether the value is false (it returns the inverse of the value)
95 func (x *Opt) False() bool {
96 return !x.value.Load()
97 }
98 99 // Flip changes the value to its opposite
100 func (x *Opt) Flip() {
101 I.Ln("flipping", x.Name(), "to", !x.value.Load())
102 x.value.Toggle()
103 }
104 105 func (x *Opt) runHooks(b bool) (e error) {
106 for i := range x.hook {
107 if e = x.hook[i](b); E.Chk(e) {
108 break
109 }
110 }
111 return
112 }
113 114 // Set changes the value currently stored
115 func (x *Opt) Set(b bool) (e error) {
116 if e = x.runHooks(b); E.Chk(e) {
117 I.Ln("setting", x.Name(), "to", b)
118 x.value.Store(b)
119 }
120 return
121 }
122 123 // String returns a string form of the value
124 func (x *Opt) String() string {
125 return fmt.Sprint(x.Data.Option, ": ", x.True())
126 }
127 128 // T sets the value to true
129 func (x *Opt) T() *Opt {
130 x.value.Store(true)
131 return x
132 }
133 134 // F sets the value to false
135 func (x *Opt) F() *Opt {
136 x.value.Store(false)
137 return x
138 }
139 140 // MarshalJSON returns the json representation of a Opt
141 func (x *Opt) MarshalJSON() (b []byte, e error) {
142 v := x.value.Load()
143 return json.Marshal(&v)
144 }
145 146 // UnmarshalJSON decodes a JSON representation of a Opt
147 func (x *Opt) UnmarshalJSON(data []byte) (e error) {
148 v := x.value.Load()
149 if e = json.Unmarshal(data, &v); E.Chk(e) {
150 return
151 }
152 e = x.Set(v)
153 return
154 }
155