...

Source file src/github.com/urfave/cli/v2/sliceflag.go

Documentation: github.com/urfave/cli/v2

     1  package cli
     2  
     3  import (
     4  	"flag"
     5  	"reflect"
     6  )
     7  
     8  type (
     9  	// SliceFlag extends implementations like StringSliceFlag and IntSliceFlag with support for using slices directly,
    10  	// as Value and/or Destination.
    11  	// See also SliceFlagTarget, MultiStringFlag, MultiFloat64Flag, MultiInt64Flag, MultiIntFlag.
    12  	SliceFlag[T SliceFlagTarget[E], S ~[]E, E any] struct {
    13  		Target      T
    14  		Value       S
    15  		Destination *S
    16  	}
    17  
    18  	// SliceFlagTarget models a target implementation for use with SliceFlag.
    19  	// The three methods, SetValue, SetDestination, and GetDestination, are necessary to propagate Value and
    20  	// Destination, where Value is propagated inwards (initially), and Destination is propagated outwards (on every
    21  	// update).
    22  	SliceFlagTarget[E any] interface {
    23  		Flag
    24  		RequiredFlag
    25  		DocGenerationFlag
    26  		VisibleFlag
    27  		CategorizableFlag
    28  
    29  		// SetValue should propagate the given slice to the target, ideally as a new value.
    30  		// Note that a nil slice should nil/clear any existing value (modelled as ~[]E).
    31  		SetValue(slice []E)
    32  		// SetDestination should propagate the given slice to the target, ideally as a new value.
    33  		// Note that a nil slice should nil/clear any existing value (modelled as ~*[]E).
    34  		SetDestination(slice []E)
    35  		// GetDestination should return the current value referenced by any destination, or nil if nil/unset.
    36  		GetDestination() []E
    37  	}
    38  
    39  	// MultiStringFlag extends StringSliceFlag with support for using slices directly, as Value and/or Destination.
    40  	// See also SliceFlag.
    41  	MultiStringFlag = SliceFlag[*StringSliceFlag, []string, string]
    42  
    43  	// MultiFloat64Flag extends Float64SliceFlag with support for using slices directly, as Value and/or Destination.
    44  	// See also SliceFlag.
    45  	MultiFloat64Flag = SliceFlag[*Float64SliceFlag, []float64, float64]
    46  
    47  	// MultiInt64Flag extends Int64SliceFlag with support for using slices directly, as Value and/or Destination.
    48  	// See also SliceFlag.
    49  	MultiInt64Flag = SliceFlag[*Int64SliceFlag, []int64, int64]
    50  
    51  	// MultiIntFlag extends IntSliceFlag with support for using slices directly, as Value and/or Destination.
    52  	// See also SliceFlag.
    53  	MultiIntFlag = SliceFlag[*IntSliceFlag, []int, int]
    54  
    55  	flagValueHook struct {
    56  		value Generic
    57  		hook  func()
    58  	}
    59  )
    60  
    61  var (
    62  	// compile time assertions
    63  
    64  	_ SliceFlagTarget[string]  = (*StringSliceFlag)(nil)
    65  	_ SliceFlagTarget[string]  = (*SliceFlag[*StringSliceFlag, []string, string])(nil)
    66  	_ SliceFlagTarget[string]  = (*MultiStringFlag)(nil)
    67  	_ SliceFlagTarget[float64] = (*MultiFloat64Flag)(nil)
    68  	_ SliceFlagTarget[int64]   = (*MultiInt64Flag)(nil)
    69  	_ SliceFlagTarget[int]     = (*MultiIntFlag)(nil)
    70  
    71  	_ Generic    = (*flagValueHook)(nil)
    72  	_ Serializer = (*flagValueHook)(nil)
    73  )
    74  
    75  func (x *SliceFlag[T, S, E]) Apply(set *flag.FlagSet) error {
    76  	x.Target.SetValue(x.convertSlice(x.Value))
    77  
    78  	destination := x.Destination
    79  	if destination == nil {
    80  		x.Target.SetDestination(nil)
    81  
    82  		return x.Target.Apply(set)
    83  	}
    84  
    85  	x.Target.SetDestination(x.convertSlice(*destination))
    86  
    87  	return applyFlagValueHook(set, x.Target.Apply, func() {
    88  		*destination = x.Target.GetDestination()
    89  	})
    90  }
    91  
    92  func (x *SliceFlag[T, S, E]) convertSlice(slice S) []E {
    93  	result := make([]E, len(slice))
    94  	copy(result, slice)
    95  	return result
    96  }
    97  
    98  func (x *SliceFlag[T, S, E]) SetValue(slice S) {
    99  	x.Value = slice
   100  }
   101  
   102  func (x *SliceFlag[T, S, E]) SetDestination(slice S) {
   103  	if slice != nil {
   104  		x.Destination = &slice
   105  	} else {
   106  		x.Destination = nil
   107  	}
   108  }
   109  
   110  func (x *SliceFlag[T, S, E]) GetDestination() S {
   111  	if destination := x.Destination; destination != nil {
   112  		return *destination
   113  	}
   114  	return nil
   115  }
   116  
   117  func (x *SliceFlag[T, S, E]) String() string         { return x.Target.String() }
   118  func (x *SliceFlag[T, S, E]) Names() []string        { return x.Target.Names() }
   119  func (x *SliceFlag[T, S, E]) IsSet() bool            { return x.Target.IsSet() }
   120  func (x *SliceFlag[T, S, E]) IsRequired() bool       { return x.Target.IsRequired() }
   121  func (x *SliceFlag[T, S, E]) TakesValue() bool       { return x.Target.TakesValue() }
   122  func (x *SliceFlag[T, S, E]) GetUsage() string       { return x.Target.GetUsage() }
   123  func (x *SliceFlag[T, S, E]) GetValue() string       { return x.Target.GetValue() }
   124  func (x *SliceFlag[T, S, E]) GetDefaultText() string { return x.Target.GetDefaultText() }
   125  func (x *SliceFlag[T, S, E]) GetEnvVars() []string   { return x.Target.GetEnvVars() }
   126  func (x *SliceFlag[T, S, E]) IsVisible() bool        { return x.Target.IsVisible() }
   127  func (x *SliceFlag[T, S, E]) GetCategory() string    { return x.Target.GetCategory() }
   128  
   129  func (x *flagValueHook) Set(value string) error {
   130  	if err := x.value.Set(value); err != nil {
   131  		return err
   132  	}
   133  	x.hook()
   134  	return nil
   135  }
   136  
   137  func (x *flagValueHook) String() string {
   138  	// note: this is necessary due to the way Go's flag package handles defaults
   139  	isZeroValue := func(f flag.Value, v string) bool {
   140  		/*
   141  			https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/flag/flag.go;drc=2580d0e08d5e9f979b943758d3c49877fb2324cb;l=453
   142  
   143  			Copyright (c) 2009 The Go Authors. All rights reserved.
   144  			Redistribution and use in source and binary forms, with or without
   145  			modification, are permitted provided that the following conditions are
   146  			met:
   147  			   * Redistributions of source code must retain the above copyright
   148  			notice, this list of conditions and the following disclaimer.
   149  			   * Redistributions in binary form must reproduce the above
   150  			copyright notice, this list of conditions and the following disclaimer
   151  			in the documentation and/or other materials provided with the
   152  			distribution.
   153  			   * Neither the name of Google Inc. nor the names of its
   154  			contributors may be used to endorse or promote products derived from
   155  			this software without specific prior written permission.
   156  			THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   157  			"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   158  			LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   159  			A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   160  			OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   161  			SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   162  			LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   163  			DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   164  			THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   165  			(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   166  			OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   167  		*/
   168  		// Build a zero value of the flag's Value type, and see if the
   169  		// result of calling its String method equals the value passed in.
   170  		// This works unless the Value type is itself an interface type.
   171  		typ := reflect.TypeOf(f)
   172  		var z reflect.Value
   173  		if typ.Kind() == reflect.Pointer {
   174  			z = reflect.New(typ.Elem())
   175  		} else {
   176  			z = reflect.Zero(typ)
   177  		}
   178  		return v == z.Interface().(flag.Value).String()
   179  	}
   180  	if x.value != nil {
   181  		// only return non-empty if not the same string as returned by the zero value
   182  		if s := x.value.String(); !isZeroValue(x.value, s) {
   183  			return s
   184  		}
   185  	}
   186  	return ``
   187  }
   188  
   189  func (x *flagValueHook) Serialize() string {
   190  	if value, ok := x.value.(Serializer); ok {
   191  		return value.Serialize()
   192  	}
   193  	return x.String()
   194  }
   195  
   196  // applyFlagValueHook wraps calls apply then wraps flags to call a hook function on update and after initial apply.
   197  func applyFlagValueHook(set *flag.FlagSet, apply func(set *flag.FlagSet) error, hook func()) error {
   198  	if apply == nil || set == nil || hook == nil {
   199  		panic(`invalid input`)
   200  	}
   201  	var tmp flag.FlagSet
   202  	if err := apply(&tmp); err != nil {
   203  		return err
   204  	}
   205  	tmp.VisitAll(func(f *flag.Flag) { set.Var(&flagValueHook{value: f.Value, hook: hook}, f.Name, f.Usage) })
   206  	hook()
   207  	return nil
   208  }
   209  
   210  // newSliceFlagValue is for implementing SliceFlagTarget.SetValue and SliceFlagTarget.SetDestination.
   211  // It's e.g. as part of StringSliceFlag.SetValue, using the factory NewStringSlice.
   212  func newSliceFlagValue[R any, S ~[]E, E any](factory func(defaults ...E) *R, defaults S) *R {
   213  	if defaults == nil {
   214  		return nil
   215  	}
   216  	return factory(defaults...)
   217  }
   218  
   219  // unwrapFlagValue strips any/all *flagValueHook wrappers.
   220  func unwrapFlagValue(v flag.Value) flag.Value {
   221  	for {
   222  		h, ok := v.(*flagValueHook)
   223  		if !ok {
   224  			return v
   225  		}
   226  		v = h.value
   227  	}
   228  }
   229  
   230  // NOTE: the methods below are in this file to make use of the build constraint
   231  
   232  func (f *Float64SliceFlag) SetValue(slice []float64) {
   233  	f.Value = newSliceFlagValue(NewFloat64Slice, slice)
   234  }
   235  
   236  func (f *Float64SliceFlag) SetDestination(slice []float64) {
   237  	f.Destination = newSliceFlagValue(NewFloat64Slice, slice)
   238  }
   239  
   240  func (f *Float64SliceFlag) GetDestination() []float64 {
   241  	if destination := f.Destination; destination != nil {
   242  		return destination.Value()
   243  	}
   244  	return nil
   245  }
   246  
   247  func (f *Int64SliceFlag) SetValue(slice []int64) {
   248  	f.Value = newSliceFlagValue(NewInt64Slice, slice)
   249  }
   250  
   251  func (f *Int64SliceFlag) SetDestination(slice []int64) {
   252  	f.Destination = newSliceFlagValue(NewInt64Slice, slice)
   253  }
   254  
   255  func (f *Int64SliceFlag) GetDestination() []int64 {
   256  	if destination := f.Destination; destination != nil {
   257  		return destination.Value()
   258  	}
   259  	return nil
   260  }
   261  
   262  func (f *IntSliceFlag) SetValue(slice []int) {
   263  	f.Value = newSliceFlagValue(NewIntSlice, slice)
   264  }
   265  
   266  func (f *IntSliceFlag) SetDestination(slice []int) {
   267  	f.Destination = newSliceFlagValue(NewIntSlice, slice)
   268  }
   269  
   270  func (f *IntSliceFlag) GetDestination() []int {
   271  	if destination := f.Destination; destination != nil {
   272  		return destination.Value()
   273  	}
   274  	return nil
   275  }
   276  
   277  func (f *StringSliceFlag) SetValue(slice []string) {
   278  	f.Value = newSliceFlagValue(NewStringSlice, slice)
   279  }
   280  
   281  func (f *StringSliceFlag) SetDestination(slice []string) {
   282  	f.Destination = newSliceFlagValue(NewStringSlice, slice)
   283  }
   284  
   285  func (f *StringSliceFlag) GetDestination() []string {
   286  	if destination := f.Destination; destination != nil {
   287  		return destination.Value()
   288  	}
   289  	return nil
   290  }
   291  

View as plain text