strings.go raw

   1  package list
   2  
   3  import (
   4  	"encoding/json"
   5  	"fmt"
   6  	"strings"
   7  	"sync/atomic"
   8  
   9  	"github.com/p9c/p9/pkg/opts/normalize"
  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 a string slice configuration value
  17  type Opt struct {
  18  	meta.Data
  19  	hook  []Hook
  20  	Value *atomic.Value
  21  	Def   []string
  22  }
  23  
  24  type Hook func(s []string) error
  25  
  26  // New  creates a new Opt with default values set
  27  func New(m meta.Data, def []string, hook ...Hook) *Opt {
  28  	as := &atomic.Value{}
  29  	as.Store(def)
  30  	return &Opt{Value: as, Data: m, Def: def, hook: hook}
  31  }
  32  
  33  // SetName sets the name for the generator
  34  func (x *Opt) SetName(name string) {
  35  	x.Data.Option = strings.ToLower(name)
  36  	x.Data.Name = name
  37  }
  38  
  39  // Type returns the receiver wrapped in an interface for identifying its type
  40  func (x *Opt) Type() interface{} {
  41  	return x
  42  }
  43  
  44  // GetMetadata returns the metadata of the opt type
  45  func (x *Opt) GetMetadata() *meta.Data {
  46  	return &x.Data
  47  }
  48  
  49  // ReadInput adds the value from a string. For this opt this means appending to the list
  50  func (x *Opt) ReadInput(input string) (o opt.Option, e error) {
  51  	if input == "" {
  52  		e = fmt.Errorf("string opt %s %v may not be empty", x.Name(), x.Data.Aliases)
  53  		return
  54  	}
  55  	if strings.HasPrefix(input, "=") {
  56  		input = strings.Join(strings.Split(input, "=")[1:], "=")
  57  	}
  58  	// if value has a comma in it, it's a list of items, so split them and append them
  59  	slice := x.S()
  60  	if strings.Contains(input, ",") {
  61  		split := strings.Split(input, ",")
  62  		for i := range split {
  63  			var cleaned string
  64  			if cleaned, e = sanitizers.StringType(x.Data.Type, split[i], x.Data.DefaultPort); E.Chk(e) {
  65  				return
  66  			}
  67  			if cleaned != "" {
  68  				I.Ln("setting value for", x.Data.Name, cleaned)
  69  				split[i] = cleaned
  70  			}
  71  		}
  72  		e = x.Set(append(slice, split...))
  73  	} else {
  74  		var cleaned string
  75  		if cleaned, e = sanitizers.StringType(x.Data.Type, input, x.Data.DefaultPort); E.Chk(e) {
  76  			return
  77  		}
  78  		if cleaned != "" {
  79  			I.Ln("setting value for", x.Data.Name, cleaned)
  80  			input = cleaned
  81  		}
  82  		if e = x.Set(append(slice, input)); E.Chk(e) {
  83  		}
  84  
  85  	}
  86  	// ensure there is no duplicates
  87  	e = x.Set(normalize.RemoveDuplicateAddresses(x.V()))
  88  	return x, e
  89  }
  90  
  91  // LoadInput sets the value from a string. For this opt this replacing the list
  92  func (x *Opt) LoadInput(input string) (o opt.Option, e error) {
  93  	old := x.V()
  94  	_ = x.Set([]string{})
  95  	if o, e = x.ReadInput(input); E.Chk(e) {
  96  		// if input failed to parse, restore its prior state
  97  		_ = x.Set(old)
  98  	}
  99  	return
 100  }
 101  
 102  // Name returns the name of the opt
 103  func (x *Opt) Name() string {
 104  	return x.Data.Option
 105  }
 106  
 107  // AddHooks appends callback hooks to be run when the value is changed
 108  func (x *Opt) AddHooks(hook ...Hook) {
 109  	x.hook = append(x.hook, hook...)
 110  }
 111  
 112  // SetHooks sets a new slice of hooks
 113  func (x *Opt) SetHooks(hook ...Hook) {
 114  	x.hook = hook
 115  }
 116  
 117  // V returns the stored value
 118  func (x *Opt) V() []string {
 119  	return x.Value.Load().([]string)
 120  }
 121  
 122  // Len returns the length of the slice of strings
 123  func (x *Opt) Len() int {
 124  	return len(x.S())
 125  }
 126  
 127  func (x *Opt) runHooks(s []string) (e error) {
 128  	for i := range x.hook {
 129  		if e = x.hook[i](s); E.Chk(e) {
 130  			break
 131  		}
 132  	}
 133  	return
 134  }
 135  
 136  // Set the slice of strings stored
 137  func (x *Opt) Set(ss []string) (e error) {
 138  	if e = x.runHooks(ss); !E.Chk(e) {
 139  		x.Value.Store(ss)
 140  	}
 141  	return
 142  }
 143  
 144  // S returns the value as a slice of string
 145  func (x *Opt) S() []string {
 146  	return x.Value.Load().([]string)
 147  }
 148  
 149  // String returns a string representation of the value
 150  func (x *Opt) String() string {
 151  	return fmt.Sprint(x.Data.Option, ": ", x.S())
 152  }
 153  
 154  // MarshalJSON returns the json representation of
 155  func (x *Opt) MarshalJSON() (b []byte, e error) {
 156  	xs := x.Value.Load().([]string)
 157  	return json.Marshal(xs)
 158  }
 159  
 160  // UnmarshalJSON decodes a JSON representation of
 161  func (x *Opt) UnmarshalJSON(data []byte) (e error) {
 162  	var v []string
 163  	e = json.Unmarshal(data, &v)
 164  	x.Value.Store(v)
 165  	return
 166  }
 167