binary.go raw

   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