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