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 }