github.com/hedzr/evendeep@v0.4.8/flags/flags.go (about)

     1  package flags
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"reflect"
     7  	"sort"
     8  	"strings"
     9  
    10  	"github.com/hedzr/evendeep/flags/cms"
    11  )
    12  
    13  // Flags is an efficient manager for a group of CopyMergeStrategy items.
    14  type Flags map[cms.CopyMergeStrategy]bool
    15  
    16  // New creates a new instance for Flags.
    17  func New(ftf ...cms.CopyMergeStrategy) Flags {
    18  	return newFlags(ftf...)
    19  }
    20  
    21  func newFlags(ftf ...cms.CopyMergeStrategy) Flags {
    22  	lazyInitFieldTagsFlags()
    23  	flags := make(Flags)
    24  	flags.WithFlags(ftf...)
    25  	return flags
    26  }
    27  
    28  func (flags Flags) Clone() (n Flags) {
    29  	n = make(Flags)
    30  	for k, v := range flags {
    31  		n[k] = v
    32  	}
    33  	return
    34  }
    35  
    36  func (flags Flags) String() string {
    37  	var sb, sbfinal strings.Builder
    38  
    39  	for fx, ok := range flags {
    40  		if ok {
    41  			if sb.Len() > 0 {
    42  				sb.WriteRune(',')
    43  			}
    44  			sb.WriteString(fx.String())
    45  		}
    46  	}
    47  
    48  	sbfinal.WriteRune('[')
    49  	sbfinal.WriteString(sb.String())
    50  	sbfinal.WriteRune(']')
    51  
    52  	return sbfinal.String()
    53  }
    54  
    55  func (flags Flags) StringEx() string {
    56  	var sb, sbfinal strings.Builder
    57  	var cache = make(Flags)
    58  
    59  	for fx, ok := range flags {
    60  		if ok {
    61  			cache[fx] = ok
    62  		}
    63  	}
    64  
    65  	for i := cms.Default; i < cms.MaxStrategy; i++ {
    66  		if flags.IsGroupedFlagOK(i) {
    67  			cache[i] = true
    68  		}
    69  	}
    70  
    71  	var keys []int
    72  	for fx, ok := range cache {
    73  		if ok {
    74  			keys = append(keys, int(fx))
    75  		}
    76  	}
    77  	sort.Ints(keys)
    78  
    79  	for _, fx := range keys {
    80  		if sb.Len() > 0 {
    81  			sb.WriteRune(',')
    82  		}
    83  		sb.WriteString((cms.CopyMergeStrategy(fx)).String())
    84  	}
    85  
    86  	sbfinal.WriteRune('[')
    87  	sbfinal.WriteString(sb.String())
    88  	sbfinal.WriteRune(']')
    89  
    90  	return sbfinal.String()
    91  }
    92  
    93  func (flags Flags) Format(s fmt.State, verb rune) {
    94  	switch verb {
    95  	case 'v':
    96  		if s.Flag('+') {
    97  			_, _ = fmt.Fprintf(s, "%+v", flags.StringEx())
    98  			return
    99  		}
   100  		fallthrough
   101  	case 's':
   102  		_, _ = io.WriteString(s, flags.String())
   103  	case 'q':
   104  		_, _ = fmt.Fprintf(s, "%q", flags.String())
   105  	}
   106  }
   107  
   108  func (flags Flags) WithFlags(flg ...cms.CopyMergeStrategy) Flags {
   109  	for _, f := range flg {
   110  		flags[f] = true
   111  		toggleTheRadio(f, flags)
   112  	}
   113  	return flags
   114  }
   115  
   116  func (flags Flags) IsFlagOK(ftf cms.CopyMergeStrategy) bool {
   117  	if flags != nil {
   118  		return flags[ftf]
   119  	}
   120  	return false
   121  }
   122  
   123  func (flags Flags) testGroupedFlag(ftf cms.CopyMergeStrategy) (result cms.CopyMergeStrategy) { //nolint:lll,unused //i know that
   124  	var ok, val bool
   125  	result = cms.InvalidStrategy
   126  
   127  	if val, ok = flags[ftf]; ok && val {
   128  		result = ftf
   129  		return
   130  	}
   131  
   132  	if _, ok = mKnownFieldTagFlagsConflictLeaders[ftf]; ok {
   133  		// ftf is a leader
   134  		ok = false
   135  		for f := range mKnownFieldTagFlagsConflict[ftf] {
   136  			if val, ok = flags[f]; ok && val {
   137  				result = f
   138  				break
   139  			}
   140  		}
   141  		if !ok {
   142  			result = ftf
   143  		}
   144  		return
   145  	}
   146  
   147  	leader := cms.InvalidStrategy
   148  	found := false
   149  	for f := range mKnownFieldTagFlagsConflict[ftf] {
   150  		if _, ok = mKnownFieldTagFlagsConflictLeaders[f]; ok {
   151  			leader = f
   152  		}
   153  		if val, ok = flags[f]; ok && val {
   154  			result = f
   155  			found = true
   156  			break
   157  		}
   158  	}
   159  	if !found {
   160  		result = leader
   161  	}
   162  	return
   163  }
   164  
   165  func (flags Flags) leader(ff cms.CopyMergeStrategy,
   166  	vm map[cms.CopyMergeStrategy]struct{}) (leader cms.CopyMergeStrategy) {
   167  	leader = cms.InvalidStrategy
   168  	if _, ok1 := mKnownFieldTagFlagsConflictLeaders[ff]; ok1 {
   169  		leader = ff
   170  	}
   171  	for f := range vm {
   172  		if _, ok1 := mKnownFieldTagFlagsConflictLeaders[f]; ok1 {
   173  			leader = f
   174  		}
   175  	}
   176  	return
   177  }
   178  
   179  // IsGroupedFlagOK test if any of ftf is exists.
   180  //
   181  // If one of ftf is the leader (a.k.a. the first one) of a toggleable
   182  // group (such as map-copy and map-merge), and, any of the group is
   183  // not exists (either map-copy and map-merge), IsGroupedFlagOK will
   184  // report true just like map-copy was in Flags.
   185  func (flags Flags) IsGroupedFlagOK(ftf ...cms.CopyMergeStrategy) (ok bool) { //nolint:gocognit //i know that
   186  	if flags != nil {
   187  		for _, ff := range ftf {
   188  			if _, ok = flags[ff]; ok {
   189  				return
   190  			}
   191  		}
   192  	}
   193  
   194  	for _, ff := range ftf {
   195  		if vm, ok1 := mKnownFieldTagFlagsConflict[ff]; ok1 {
   196  			// find the default one (named as `leader` from a radio-group of flags
   197  			leader := flags.leader(ff, vm)
   198  
   199  			var found, val bool
   200  			for f := range vm {
   201  				if val, found = flags[f]; found && val {
   202  					break
   203  				}
   204  			}
   205  
   206  			if !found {
   207  				// while the testing `ff` is a leader in certain a
   208  				// radio-group, and any of the flags of the group are not
   209  				// in flags map set, that assume the leader is exists.
   210  				//
   211  				// For example, when checking ftf = SliceCopy and any one
   212  				// of SliceXXX not in flags, though the test is ok.
   213  				if ff == leader {
   214  					ok = true
   215  				}
   216  			}
   217  		}
   218  	}
   219  	return
   220  }
   221  
   222  func (flags Flags) IsAnyFlagsOK(ftf ...cms.CopyMergeStrategy) bool {
   223  	if flags != nil {
   224  		for _, f := range ftf {
   225  			if val, ok := flags[f]; val && ok {
   226  				return true
   227  			}
   228  		}
   229  	}
   230  	return false
   231  }
   232  
   233  func (flags Flags) IsAllFlagsOK(ftf ...cms.CopyMergeStrategy) bool {
   234  	if flags != nil {
   235  		for _, f := range ftf {
   236  			if val, ok := flags[f]; !ok || !val {
   237  				return false
   238  			}
   239  		}
   240  	}
   241  	return true
   242  }
   243  
   244  func toggleTheRadio(f cms.CopyMergeStrategy, flags Flags) {
   245  	if m, ok := mKnownFieldTagFlagsConflict[f]; ok {
   246  		for fx := range m {
   247  			if fx != f {
   248  				if _, ok = flags[fx]; ok {
   249  					delete(flags, fx)
   250  				}
   251  			}
   252  		}
   253  	}
   254  }
   255  
   256  func aCheck(flags Flags, tags string) (targetNameRule NameConvertRule) {
   257  	for i, wh := range strings.Split(tags, ",") {
   258  		if i == 0 && wh != "-" {
   259  			targetNameRule = NameConvertRule(wh)
   260  			continue
   261  		}
   262  
   263  		ftf := cms.Default.Parse(wh)
   264  		if ftf == cms.InvalidStrategy {
   265  			if wh == "ignore" {
   266  				flags[cms.Ignore] = true
   267  			}
   268  		} else {
   269  			flags[ftf] = true
   270  		}
   271  
   272  		toggleTheRadio(ftf, flags)
   273  	}
   274  	return
   275  }
   276  
   277  // Parse _
   278  // use "copy" if tagName is empty.
   279  func Parse(s reflect.StructTag, tagName string) (flags Flags, targetNameRule NameConvertRule) {
   280  	lazyInitFieldTagsFlags()
   281  
   282  	flags = New()
   283  
   284  	if tagName == "" {
   285  		tagName = "copy"
   286  	}
   287  
   288  	tags := s.Get(tagName)
   289  	targetNameRule = aCheck(flags, tags)
   290  
   291  	// for i, wh := range strings.Split(tags, ",") {
   292  	// 	if i == 0 && wh != "-" {
   293  	// 		targetNameRule = NameConvertRule(wh)
   294  	// 		continue
   295  	// 	}
   296  
   297  	// 	ftf := cms.Default.Parse(wh)
   298  	// 	if ftf == cms.InvalidStrategy {
   299  	// 		if wh == "ignore" {
   300  	// 			flags[cms.Ignore] = true
   301  	// 		}
   302  	// 	} else {
   303  	// 		flags[ftf] = true
   304  	// 	}
   305  
   306  	// 	toggleTheRadio(ftf, flags)
   307  	// }
   308  
   309  	for k := range mKnownFieldTagFlagsConflictLeaders {
   310  		var ok bool
   311  		if _, ok = flags[k]; ok {
   312  			continue
   313  		}
   314  		for k1 := range mKnownFieldTagFlagsConflict[k] {
   315  			if _, ok = flags[k1]; ok {
   316  				break
   317  			}
   318  		}
   319  
   320  		if !ok {
   321  			// set default mode
   322  			flags[k] = true
   323  		}
   324  	}
   325  	return
   326  }
   327  
   328  // NameConvertRule wraps the rule with string representations into a struct.
   329  type NameConvertRule string
   330  type nameConvertRule struct {
   331  	Valid     bool
   332  	IsIgnored bool
   333  	From      string
   334  	To        string
   335  }
   336  
   337  func (s NameConvertRule) Valid() bool      { return s != "" && s.get().Valid }
   338  func (s NameConvertRule) IsIgnored() bool  { return s.get().IsIgnored }
   339  func (s NameConvertRule) FromName() string { return s.get().From }
   340  func (s NameConvertRule) ToName() string   { return s.get().To }
   341  
   342  func (s NameConvertRule) get() (r nameConvertRule) {
   343  	a := strings.Split(string(s), "->")
   344  	if len(a) > 0 {
   345  		switch {
   346  		case a[0] == "-":
   347  			r.IsIgnored = true
   348  		case len(a) == 1:
   349  			r.To = strings.TrimSpace(a[0])
   350  			r.Valid = true
   351  		default:
   352  			r.From = strings.TrimSpace(a[0])
   353  			r.To = strings.TrimSpace(a[1])
   354  			r.Valid = true
   355  		}
   356  	}
   357  	// dbglog.Log("      nameConvertRule: %+v", r)
   358  	return
   359  }